== Makefile 리눅스에서 동작하는 gcc는 기본적으로 make 유틸리티를 사용하여 컴파일한다. RAD 툴을 사용하던 프로그래머라면 이 make 유틸리티가 다소 어렵게 느껴질지 모르지만 리눅스 프로그래머거나 기존 펌웨어 프로그래머라면 항상 접하는 유틸리티이다. 리눅스에서 사용되는 make 유틸리티는 매우 유용한 도구이고 개념만 익히고 나면 쉽다는 것을 알 수 있다. make 유틸리티는 기본적으로 Makefile이라는 이름을 갖는 파일을 필요로 한다. 이 Makefile이 바로 컴파일에 관련된 모든 조건을 기록해 놓는 파일이다. 보통 이 Makefile은 구성이 비슷하기 때문에 한번 자신만의 구성을 만들면 이후에 조금씩 수정해서 쓰면 된다. make에 관련된 자세한 사용법은 kldp.or.kr 사이트에 가보거나 시중 서점에 단행본으로 나와 있기 때문에 본격적으로 배우고 싶다면 찾아보기 바란다. 크로스 컴파일을 위한 Makefile은 일반적인 응용 프로그램을 만드는 Makefile과 몇 가지 다른 점이 있는데 먼저 필자가 만든 Makefile을 보고서 꼭 필요한 내용만 의미를 파악해 보자.
<리스트 3> Makefile
가장 처음 보이는 <리스트 3>의 (1)은 컴파일러로 사용될 수행 프로그램을 기술한 것이다. 여기서 CC는 컴파일러, LD는 링커, OC는 elf 포맷의 실행 파일을 이진 코드로 만들기 위해서 사용되는 프로그램을 정의하고 있다. start.S가 컴파일되어 이진 파일로 만들어지는 과정을 살펴보면 가장 처음 CC에 의해서 지정된 컴파일러에 의해서 start.S는 start.o로 만들어진다. 이렇게 만들어진 start.o는 LD에 의해서 지정된 링커에 의해서 elf 형태의 ledtest-elf32란 이름을 갖는 실행 파일이 된다. 이것을 그대로 롬으로 쓸 수는 없는데, 이유는 elf 포맷의 실행 파일은 수행 코드와 데이터만 있는 것이 아니고 수행에 필요한 기타 정보들이 기록되어 있기 때문이다. 이런 정보를 제거하기 위해서 OC로 지정된 오브젝트 유틸리티 프로그램을 이용해 모든 정보가 제거된 이진 파일인 ledtest_org를 만든다. 이 과정을 추적해 보자. 먼저 make란 명령을 수행하면 Makefile을 이용하여 각 단계를 수행한다. make 명령은 Makefile에서 (2)와 같이 all:로 표현된 부분을 찾는다. 여기서 all을 만들기 위해서 의존적인 $(PRE_TARGET)를 찾는데, 이것은 PRE_TARGET = ledtest-elf32라는 명령에 의해서 ledtest-elf32로 지정되어 있다. make는 all을 만들기 위해서 ledtest-elf32가 처리되어 있어야 하므로 다시 ledtest-elf32:에 관련된 것을 찾는다. <리스트 3>의 (3)에서 make가 $(PRE_TARGET)은 다시 $(OBJS)에 의존적이라는 것을 발견하고 이것을 처리하는 과정을 수행한다. OBJS = start.o로 선언되어 있으므로 make는 다시 start.o에 대하여 검색한다. 여기서 지금까지 선두에 선언된 (4) 문장으로 진행된다. 이것의 의미는 확장자가 .o로 끝나는 것은 확장자를 제외한 동일한 이름의 확장자가 .S인 파일에 의존적임을 표시한다. 이 문장이 컴파일을 수행하게 해주는 최종 조건인 것이다. 이후 (4) 문장에 의해서 start.S 파일은 start.o로 컴파일되는데 이를 수행하는 문장이 (5)이다. 이 문장을 풀어 쓰면 다음과 같은 매우 긴 문장으로 치환되어 수행된다.
armv5l-linux-gcc -c -nostdinc -I. -I$(TOPDIR)/include -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common -pipe -mapcs-32 -march=armv5 -Wa,-mxscale -mtune=strongarm -mshort-load-bytes -msoft-float -fno-builtin -o start.o start.S
이 문장의 대부분은 컴파일러 옵션인데 중요한 몇 가지만 살펴보자.
-c 컴파일만 하고 링크는 하지 말라는 의미이다. -nostdinc 표준 헤더 파일 디렉토리를 참조하지 말라는 의미이다. -march=armv5 arm 명령을 version 5로 컴파일하라는 의미이다. 여기서는 PXA255가 armv5라는 의미이다. -mxscale 프로세서 명령 타입이 xscale임을 알려주는 것이다. -mtune=strongarm SA1100 타입으로 최적화하라는 의미이다. 이것은 xscale의 sa1100와 유사하기 때문이다. -msoft-float 실수 연산에 대한 처리는 코프로세스를 이용하지 않은 소프트웨어 연산을 처리하라는 의미이다. -fno-builtin 표준 라이브러리와 링크되지 말고 단독으로 링크하라는 의미이다.
이렇게 해서 start.o가 생성되면 지금까지와 역순으로 각각의 명령이 수행된다. (6) 문장을 풀어 쓰면 다음과 같은 의미가 된다.
armv5l-linux-ld -p -X -T ./start-ld-script -o ledtest-elf32 start.o
여기서 링크 스크립트를 지정하는 것이 ./start-ld-script이다. 이렇게 해서 ledtest-elf32가 얻어지면 (7) 문장을 수행한다. (7)의 첫 번째 문장은 출력 포맷을 2진 파일로 만들고(-O binary) .note 섹션의 심볼릭 정보를 제거하고(-R .note) .comment 섹션의 심볼릭 정보를 제거하고(-R .comment) 스트립하라(-S)는 의미가 된다. 이를 풀어 쓰면 다음과 같다.
armv5l-linux-objcopy -O binary -R .note -R .comment -S ledtest-elf32 ledtest-org
원칙상 여기서 수행을 끝내도 되지만 나중을 위해서 한번 더 변화하는 과정을 거치는데 (7)의 두 번째 문장이 1024바이트 단위로 실제 파일을 만든다(이것은 다음 연재에서 설명하겠다). 이렇게 해서 최종 부트코드 ledtest_x5라는 파일이 만들어진다. 이를 플래시 라이터 툴이나 롬 라이터 툴을 이용해 넣으면 된다. EZ-X5에서는 ‘이지플래시’라는 프로그램을 이용해 프린터 포트와 연결된 JTAG 단자를 통해 써넣는다. 마지막으로 전원을 켰을 때LED가 점멸하면 보드의 초기 부팅 조건에 이상이 없음을 알게 된다.
== arm 부트로더에 익숙해지자! 지금까지 살펴본 프로그램은 무척 간단하지만 이 프로그램을 수행시키는 과정은 생각보다 복잡하다. 리눅스를 처음 접하는 독자라면 더욱 어려울 수도 있다. 그러나 이 과정은 임베디드에서 수행되는 부트로더를 만들기 위해서 꼭 거쳐야 하는 첫 번째 관문이다. 앞으로도 많은 관문들이 기다리고 있지만 차근차근 정복하면 그다지 힘들지 않을 것이다. 부디 포기하지 말고 계속 진행하기 바란다. 한번 익숙해지면 그 다음부터는 arm 부트로더를 만드는 작업이 그리 어렵지 않을 것이기 때문이다.
| |