SW Programming/Embedded

[Book] 임베디드(Embedded) OS 개발 프로젝트 (이우만 저, 인사이트 출판) - 부팅(Main 함수 부르기 까지)

Mr.공 2023. 7. 26. 14:13

임베디드 OS개발 프로젝트 (책)

인사이트 임베디드 OS 개발 프로젝트 (이만우 저) 책을 공부하면서 같이 알면 좋을 것 같은 것들과

책 만들때(2020)와 현재(2023년)와의 차이로 인해 책과 맞지 않는 부분에 대해 적어보자.

 

1.컴파일러 (gcc-arm-none-eabi) 설치

  • LTS 20.02 이상 버전을 사용한다면 책에서 sudo apt install gcc-arm-none-eabi 를 보내는 부분을 Skip하고https://goobgood.tistory.com/31 를 참고 하길 바란다.
  • LTS 20.02 에서 sudo apt install gcc-arm-none-eabi를 사용하면 gcc-arm-none-gdb가 없기 때문에 다시 지우고 설치해야 하는 번거로움이 있다.

 

2. GDB명령어

  저자가 처음에는 gdb명령어를 하나하나 다 설명하다가 어느 순간부터는 생략한다.
  일일이 매번 설명하면 책도 두꺼워지고 지루해지기 때문이다.
  그때 앞으로 가서 찾게 되는데 그것을 방지하고자 앞에서 소개하던 gdb명령어들을 아래에 적어봤다.

  • target remote:1234
    • qemu로 elf를 올려서 돌릴 때 gdb port를 1234를 사용한다.
      따라서 gdb를 실행하고 현재 돌고 있는 프로그램(elf)에 접속하기 위해 target command를 보낸다.
  • file build/xxx.axf
    • 참고로 위에서 xxx는 임의의 파일이름이다.
    • elf의 확장자명이 axf이다.
    • 즉, file 명령어를 통해 elf을 읽어 들이라는 뜻이다.
    • file은 elf에 포함되어 있는 디버깅 심벌을 읽어들인다.
  • infor register
    • 현재 CPU의 register정보(값)을 보여준다.
    • 너무 길면 중간에 한번 끊어주는데
      이때 q(quit)를 누르면 그만 보여준다. 계속해서 보려면 c(contine)를 누르면 된다.
    • 줄여서 i r로 보낸다.
  • step
    • 어셈블리어를 한 스텝씩 실행한다.
    • 줄여서 s로 보낸다.
  • continue
    • 다음 실행을 쭉 이어 나간다.
    • 줄여서 c로 보낸다.
  • x/<숫자, 진법옵션> 읽어보고자하는 주소값
    • 특정 주소값에서 '숫자'byte 만큼 Data를 보여준다.
      진법옵션에 따라 16진수 10진수로 보여준다.
    • 숫자는 읽고자 하는 Data의 갯수를 의미한다. 숫자 옆에 b, h, w를 써넣기도 하는데
      b는 1byte, h는 2byte, w는 4byte를 의미한다.
      b,h,w는 하나의 Data를 몇 byte로 표시 할 것인지를 말한다.
    • 아래 그림을 참고하면 이해가 된다. (x/2wx 0x00 와 x/2hx 0x00를 보냈을 때 결과)

    • 만약 b,h,w를 사용하지 않는다면 기본은 4byte(w)의 형식이나
      한번이라도 b,h,w를 사용 했다면 그전에 사용했던 형식을 따른다. (아래 그림 참고)

  

3. 어셈블리어 (ARM용)

  저자가 어셈블리어를 작성 후 밑에 line별 설명을 해주기는 하지만

  그 외에(설명하지 않은 것 중에) 내가 찾아보고 알게 된 것들에 대해 적어봤다.

  http://www.jkelec.co.kr/img/lecture/arm_arch/arm_arch_4.html 를 참고하였다.

 

  • 먼저 ARM 어셈블리의 명령어 구조를 조금 알아야 한다.
  •  구조는 아래와 같다.그리고 명령어 실행 방향은 오른쪽에서 왼쪽(<-) 이다.
    • 명령어, a, b, c 
    • a는 Destination Regiter라고 하여 Rd라고 불린다.
    • b는 Source Register 이고 Operand 1이라고 불린다. Rn이라고 적기도 함
    • c도 Source Register 이고 Operand 2라고 불린다. Rm이라고 적기도 한다.
    • b(Operand 1)은 항상 Register를 써야 하지만 c(Operand 2)는 Register를 쓸수도 있고 Immedidate Value를 쓸수 있다.
    • Immedidate Value란 r1, r0와 같은 Register표현이 아닌 '#'으로 시작하고 숫자를 적는 것을 말한다. (예, #0x18) 
      단, Immedidate Value로 사용할 수 있는 값과 없는 값이 있다.
      자세한 내용은 http://www.jkelec.co.kr/img/lecture/arm_arch/arm_arch_4.html을 참고

  • LDR a, b
    • Load register from memory
    • b의 값을 a에 copy하라는 의미
    • 가끔 b에 '='으로 시작하는 값을 쓰기도 하는데 이것은 a에 직접적인 값을 넣기위해 사용한다.
      =는 LDR에서만 가능하다.
  • B a
    • Branch
    • r15에 (PC, 프로그램 카운터) 에 a를 copy하라는 의미
      즉, a로 이동해라 라는 의미
  • MRS a, cpsr
    • Move PSR(Program Status Register) to register
    • cprs(current PSR)
    • cpsr를 a에 copy해라라는 의미 (현재 PSR값을 가져와라)
    • 참고로 Program Status Register를 알려면 ARM아키텍처를 알아야 한다.
  • MSR cpsr, b
    • Move Reigster to PSR
      MRS의 반대계념
    • b를 cpsr에 copy해라라는 의미 (현재 PSR값에 대입해라)
  • BIC a, b, c
    • Bit Clear
    • c에 bit 연산 NOT을 하고(반대로 뒤집고) b에 bit연산 AND를 한 값을 a에 대입한다.
    • 만약 r1, r0, #0x1F 라면 0x1f = 0001 1111이므로 NOT을 붙여 1110 0000(0xE0)으로 만들고
      r0와 AND 연산을 시킨 후 r1에 넣는다. (즉, r1의 하위 5bit[4:0] 를 모두 0으로 clear한 것)
  • ORR a, b, c
    • OR(비트연산)
    • b와 c를 비트연산 OR을 하고 나온 값을 a에 넣는다.

공부한 내용

 

1. 임베디드 종류에 맞는 컴파일러를 써야한다.

ARM에는 ARM용 컴파일러, 윈도우에는 윈도우용 컴파일러 이런식...

그런데 컴파일을 실행하려는 환경과 컴파일의 결과물이 실행될 환경이 다른 경우에 사용하는 컴파일러를 크로스 컴파일러 라고 한다.

크로스 컴파일러의 대표적이 예가 GCC이다.

GCC는 윈도우도 지원하고 ARM도 지원한다.

ARM용 GCC가 gcc-arm-xxx-xxxabi 으로 표현되는 패키지이다.

참고로 마지막에 abi는 Application Binary Interface의 약자이며 우리가 쓰는것은 eabi (Embedded Application Binary Interface)이다.

 

2. 리셋벡터

ARM코어가 있는 보드에 전원이 인가되면 제일 먼저 하는 것이 리셋벡터에 있는 명령어를 실행하는 것이다.

(엄밀히 말하면 익셉션 벡터의 리셋 벡터에 있는 명령어를 읽는 것. 익셉션 벡터는 ARM아키텍처 지식 필요)

여기서 리셋벡터란 0x00000000 주소를 말한다.

즉, 0x00000000에 있는 명령어를 실행하는 것이 전원이 들어가면 제일 먼저 하는 행동이다.

따라서 사용자는 0x00000000에 명령어를 만들어 넣어줘야 한다.

그런데 C 언어로는 직접 0x00000000 주소에 어떠한 명령어를 바로 넣어 줄 수 없다.

따라서 기계어로 넣어야 하는데 기계어를 사람이 만들 수가 없으니 기계어를 사람이 볼 수 있게 만든 어셈블리어를 사용해야 한다.

참고로 어셈블리어는 확장자가 .S(혹은 소문자 s도 되는듯)이다.

만약 현재 하고 있는 임베디드 프로젝트가 있다면 해당 프로젝트에서 .S파일을 한번 찾아 보는 것도 좋을 듯 하다.(find -name *.s - linux기준)

 

3. 실행파일 ELF(Excutable and Linkable Format)

그런데 어셈블리어로만 만들어서는 아무런 것도 할 수 없다.

실제로 ARM코어가 들어 보드에 넣고 실행할 수 있는 파일을 만들어야 한다.(윈도우를 예를 들면 .exe파일 같은 것)

ARM에서 실행할 수 있는 파일을 elf파일이라고 한다.

확장자는 axf이다. (사실 object파일도 ELF파일이라 할 수 있음)

ELF파일을 만들기 위해서는 여러개의 오브젝트파일을 연결해 주는 링커(Linker)의 도움이 필요하다.

그런데 링커는 링커 스크립트라는 파일을 읽어서 동작한다.

참고로 링커 스크립트 파일의 확장자는 .ld 이다.

이렇게 링커 스크립트 파일과 어셈블리어를 이용하여 axf파일을 만들 수 있다.

 

4. Makefile

사실 일일이 손으로 칠 수 있다면 Makefile은 필요 없다.

하지만 나중에 점점 많은 소스 파일을 관리하기 위해서는 Make 명령어를 사용하는 것이 좋다.

Makefile을 만들면 make xxx 으로 make 명령어를 보낼 수 있다.

Make가 싫다면 python도 좋고 perl도 좋다. (개인취향에 따라 고르면 된다.)

Makefile 자체에도 많은 프로그래밍 문법이 존재한다.

아래 Site에 기본적인 Make의 문법 설명이 잘되어 있다.

사실 아래 SIte는 Make뿐만 아니라 기본적인 C/C++ 문법과 컴퓨터 구조 등에 대해서도 굉장히 잘 설명 되어 있다.

꼭 읽어보길 바란다.

참고로 Make를 조금 쉽게 만들기 위해 나온 Tool이 CMake이다. (아래 Site에 CMake도 있으니 같이 공부하자!)

https://modoocode.com/311

 

씹어먹는 C++ - <19 - 1. Make 사용 가이드 (Makefile 만들기)>

 

modoocode.com

 

5. ARM 아키텍처

0x00000000부터 시작하는 것은 알겠는데... 어떠한 명령어를 넣어 줘야 하는지 막막하다.

시작할 때 넣어줘야하는 명령어를 만들기 위해서는 ARM 아키텍처를 알아야 한다.

 


책에서는 설명 안되어 있어서 내가 찾아서 한것들