본문 바로가기

Hacked Brain/embeddedland.net

보드를 살려보자-2

자 : 유영창
출판일 : 2003년11월호

== 크로스 컴파일러와 gcc 소스 패키지
gcc에서 i386 이외의 실행 파일을 만들기 위해서는 리눅스에 이미 설치된 gcc란 컴파일러는 소용이 없다. 몇 가지 옵션만 바꾸어서 gcc를 실행한다고 i386 이외의 프로세서를 지원하는 코드가 생성되는 것은 아니다. 애초에 다른 프로세서에서 동작될 수 있는 실행 파일을 만들 수 있도록 gcc를 만들어야 하는 것이다.
그렇다고 기존에 i386에서 동작하는 gcc를 아예 다른 프로세서에서 동작하는 실행 파일을 만드는 gcc로 바꿔버리면 문제가 된다. 그 뒤로는 i386에서 동작하는 어떤 프로그램도 만들 수 없기 때문이다. 그래서 보통은 gcc의 이름을 조금 바꾼다. arm 계열의 프로세서에서 동작하는 실행 프로그램을 만들기 위해서 사용하는 gcc는 arm-linux-gcc라는 이름을 가진다. 이렇게 이름이 다른 두 컴파일러는 전혀 다른 컴파일러가 아닌 똑같은 gcc인 것이다. 단지 gcc는 i386 프로세서에서 수행되는 코드를 만들어 내는 컴파일러이고 arm-linux-gcc라는 것은 arm 프로세서에서 동작되는 코드를 만들어 내는 컴파일러일 뿐이다(관행적으로 네이티브용 컴파일러에는 보통 gcc가 되고 크로스 컴파일러에는 gcc 앞에 접두사를 붙인다). 만약 알파 프로세서로 동작하는 gcc에서 i386 계열의 실행 코드를 만든다면 크로스 컴파일러의 이름이 아마도 i386-linux-gcc가 되지 않을까.
어찌 되었든 둘 다 동일하게 gcc란 패키지에서 파생한 컴파일러인 것이다. 필자가 자꾸 gcc 패키지라는 말을 쓰고 있다. 그런데 이 gcc 패키지라는 것이 무엇일까?
예를 들어 윈도우에서 델파이라는 개발 툴을 구매해서 설치하면 단순하게 델파이 컴파일러만 설치되는 것이 아니다. 개발을 위한 여러 파일들이 같이 설치된다. 이와 동일한 개념으로 gcc 패키지란 컴파일을 하기 위한 이런 저런 파일들이 모두 담긴 것을 말한다. 다만 윈도우에서와 달리 리눅스에서는 패키지들이 소스로 구성되어 있다. 그래서 gcc의 경우는 ‘gcc 소스 패키지’라고 말하기도 한다.
또 많은 개발자들이 착각하는 것 중의 하나가 gcc를 단순하게 C 컴파일러라고 알고 있다는 점이다. 정확하게 말하자면 gcc는 C 컴파일러가 아니다. C++ 소스를 컴파일할 때도 역시 gcc를 사용하고 어셈블러를 컴파일할 때도 gcc를 사용할 수 있다. 그래서 gcc 소스 패키지에는 C 이외의 몇 가지 컴파일러들이 더 들어 있다. 어찌 되었든 gcc 소스 패키지란 gcc 컴파일러를 만들 수 있는 소스로 구성된 것을 말한다.
이 gcc 소스 패키지를 이용하여 원하는 프로세서를 지원하는 크로스(또는 네이티브) 컴파일러를 만드는 것이다. 그런데 아주 모순된 것 중 하나는 gcc를 만들기 위해서는 gcc가 필요하다는 점이다. 왜냐하면 gcc는 소스 패키지이기 때문이다. 하지만 이 점은 별로 문제가 되지 않는다. 대부분의 리눅스에는 gcc 컴파일러가 이미 설치되어 있기 때문이다.

[ gcc 3.X대의 속설(?) ]
gcc를 이미 사용하고 있는 많은 사람들이 gcc의 3.X 대에 문제가 있다고 알고 있다. 때문에 이 버전의 gcc 사용을 회피하면서 예전 버전을 고집하는 경향이 있다. 그런데 이 부분은 조금 오해가 있는 것 같다. 문제가 되는 버전은 gcc 3.0이다. 문제가 발생했으니 당연히 해당 버전을 고쳤다. 따라서 3.1대 이후라면 별 문제는 없다. 가장 최근의 버전을 사용하면 이전에 발견된 문제는 지속적으로 해소되는 것이다. 더구나 최근에 사용되는 프로세서를 쓰기 위해서는 예전 버전으로는 한계가 있다. 최신 버전을 써야 지원되는 프로세서가 있기 때문이다. 그러므로 겁먹지 말고 3.X대를 사용하기를 강력하게 권유한다.


== 크로스 컴파일러 툴 체인
gcc는 컴파일러이다. 당연히 소스를 컴파일할 수 있다. 그런데 gcc만 가지고서는 아무 일도 못한다. gcc는 단순하게 C 프로그램이나 C++ 프로그램을 어셈블러 파일로 만들어줄 뿐이기 때문이다. gcc의 자세한 동작 원리를 알고 싶다면 kldp.org에서 관련 문서를 찾아보기 바란다(지면 관계상 이 원고에서 자세하기 설명하기는 불가능하므로…). 어쨌든 gcc 이외에 필요한 프로그램과 데이터(?)들이 있다. 이런 모든 요소들을 모아 놓은 것을 ‘크로스 컴파일러 툴 체인’이라고 한다. 컴파일러 툴이라면 될 텐데 굳이 체인이라고 붙는 이유는 필자 개인적으로 각 패키지 간에 서로 의존적이라기 때문일 것으로 추측한다. 다음은 크로스 컴파일러 툴 체인의 목록이다.

◆ binutils : 어셈블러, 링커, 그리고 라이브러리 관련 실행 파일들 모음
◆ kernel : 리눅스 커널(헤더 파일 때문에 필요함)
◆ gcc : 컴파일러
◆ glibc : 라이브러리와 헤더 파일
◆ gdb : 디버거 (툴 체인에 포함시키지 않을 때도 있음)
이 목록의 패키지들은 각각의 버전이 있다. 이 버전은 제각각인데 아쉽게도 이 버전이 다를 때는 상호 호환이 완벽하게 되지 않는다. 이유는 제작자가 모두 다르기 때문이다. 그래서 이 호환되는 버전별로 검증된 것을 모두 골라 놓은 것을 툴 체인이라고 한다.

binutils
바이너리 유틸 패키지이다. 여러 가지 프로그램이 포함되어 있는데 포함되는 내용은 다음과 같다. 주 목적은 실행 파일을 최종적으로 만들고 이에 대한 정보를 제공하는 유틸리티이다.

◆ addr2line : 실행 파일의 어드레스에 대한 소스 파일과 라인 번호를 표현해 주는 프로그램이다.
◆ ar : 라이브러리를 관리하는 프로그램이다.
◆ as : 어셈블러이다.
◆ gasp : 어셈블러 매크로 해석기이다.
◆ ld : 실행 파일을 만들어 주는 링커이다.
◆ nm : 오브젝트 안의 심볼릭을 표시해 주는 프로그램이다.
◆ objcopy : 오브젝트 파일을 컨버팅해 주는 프로그램이다.
◆ objdump : 오브젝트 파일의 정보를 표시해 준다.
◆ ranlib : 라이브러리의 인덱스 파일을 생성한다.
◆ readelf : elf 포맷의 파일 헤더 정보를 해석해 준다.
◆ size : 오브젝트 파일의 섹션 크기와 포함된 오브젝트의 총 크기를 표시해 준다.
◆ strings : 프로그램 내부에 사용되는 초기화 문자열들을 골라 표시해 준다.
◆ strip : 오브젝트나 실행 파일의 정보를 선택적으로 제거해 준다.

kernel
크로스 컴파일러를 구성하는 요소 중 glibc를 만들 때 필요한 요소이다. 시스템과 연관된 데이터 타입이나 시스템 콜을 참조하기 위해서 필요한 헤더 파일을 참조하기 때문에 툴 체인에 들어간다. 엄밀히 말하면 kernel의 헤더 파일만 필요하다.

gcc
이것은 실제 컴파일러 패키지이다. 필자가 지금 설명하는 그 프로그램이다. 다음은 패키지에 포함된 대표적인 컴파일러이다

◆ gcc
◆ cpp
◆ g++
◆ c++
◆ gccbug,gcov

glibc
이 패키지는 커널을 컴파일하거나 부트로더를 컴파일할 때는 필요 없다. 하지만 응용 프로그램을 작성하기 위해서 사용한다면 당연히 이것이 있어야 한다. 이 패키지의 역할은 응용 프로그램에서 사용하는 대부분의 라이브러리와 헤더 파일을 지원하는 것이다. 이 glibc와 비슷한 것 중에 newlib이라는 것이 있다. 이 둘을 구별하자면 glibc는 GNU 라이브러리이다. PC에서 사용하는 대부분의 기본적인 라이브러리를 모두 지원하기 때문에 크기가 매우 크다.
이에 반해 임베디드에서 자주 사용하는 필요한 라이브러리만 모아놓은 것이 newlib이다. 그래서 크기가 매우 작다. 그러나 특별한 경우가 아니라면 필자는 newlib를 별로 추천하지 않는다. 가끔 리눅스용 프로그램을 임베디드 제품에 포팅하다 보면 없는 함수가 있어서 곤란을 겪기 때문이다. 임베디드 장비에서 램이나 플래시 시스템이 16MB 이상이고 여유가 있다면 가급적 glibc를 쓰기를 권유한다.

gdb
이것은 디버거이다. 즉 프로그램을 디버깅할 때 유용한 도구이다. 하지만 윈도우 디버거를 사용하던 사람이라면 매우 불편함을 느낄 수 있다. 왜냐하면 명령형 디버거이기 때문이다. 하지만 없는 것보다는 있는 것이 좋다. printf만 가지고 디버깅하면 매우 힘들기 때문이다. 더욱이 익숙해지면 의외로 막강한 기능에 놀라게 된다. 이 gdb를 굳이 툴 체인에 포함시키지 않는 것은 상호의존적인 관계가 별로 없기 때문이다.

== 크로스 컴파일 환경 구축 방법
이제 본격적으로 시스템에 사용될 크로스 컴파일러 환경을 구축해 보자. 그런데 여기서 필자의 고민이 생겼다. 크로스 컴파일 환경을 구축하는 방법에는 레드햇 배포판 계열이라면 RPM 패키지를 이용하는 방법이 있고, 그냥 소스 패키지를 이용하는 방법이 있기 때문이다. 초기의 RPM은 그렇지 않았는데 요즘에 들어서 RPM이 배포판을 가리는(?) 경향이 있기 때문이다. 또한 arm용 RPM은 최신 버전으로 구하기가 그리 쉬운 것도 아니다. 하지만 크로스 컴파일러를 소스로 설치하는 것은 다른 기회에 살펴보기로 하고 여기서는 RPM으로 설치하는 것만 다뤄 보겠다. 혹시 소스 설치에 관심이 있다면 www.kelp. or.kr에서 필자가 쓴 문서가 있으므로 한번 찾아보기 바란다.
우선 RPM을 설치하기 위해서는 크로스 컴파일 툴 체인의 RPM 파일을 받아야 한다. 독자들이 이것을 직접 구하기는 그리 쉽지 않으므로 필자가 근무하는 회사의 사이트(www.falinux.com)에서 받자. 자료실의 EZ-X5 디렉토리에 두 가지 종류가 있는데, 와우 리눅스 7.1용과 7.3용이 있다. 그외 배포판은 곧 지원할 예정이지만 최근의 배포판이라면 7.3용을 받으면 될 것이다. 다만 여기서는 7.1을 기준으로 하겠다. 자료실에 포함된 파일의 크로스 컴파일 환경에 포함되는 것은 다음과 같다.

어셈블러 및 로더 기타 툴 : armv5l-linux-binutils-2.13.90.0.16-ez1.i386.rpm
컴파일러 : armv5l-linux-gcc-3.2.1-ez1.i386.rpm
크로스 컴파일 구축을 위한 라이브러리 및 일반 라이브러리 : armv5l-linux-glibc-2.3.1-ez1.i386.rpm

실제 설치에 관련된 문서도 있으므로 설치에 관련된 자세한 방법은 문서를 참조하기로 하고 여기서는 간단하게 설명하겠다. 이후의 작업은 root로 작업을 해야만 한다. 따라서 설치시 반드시 순서를 지켜야 한다.

# rpm -Uvh --nodeps --force armv5l-linux-binutils-2.13.90.0.16-ez1.i386.rpm
# rpm -Uvh --nodeps --force armv5l-linux-gcc-3.2.1-ez1.i386.rpm
# rpm -Uvh --nodeps --force armv5l-linux-glibc-2.3.1-ez1.i386.rpm

RPM을 풀면 /usr/armv5l-linux 디렉토리 밑에 크로스 컴파일 환경이 설정된다. 이후 실행환경 경로를 잡아줘야 하는데, 설정할 내용은 PATH=$PATH:/usr/armv5l-linux/bin이다. /root의 .bash_profile 파일에 이 내용을 추가한 후 일단 로그아웃한 다음 다시 root로 로그인하면 된다. 아니면 직접 콘솔에 입력하고 export할 수도 있다.
이렇게 크로스 컴파일 환경을 비롯한 개발 환경을 모두 구축하였다면 이제 본격적인 시험용 부트코드를 만들어야 한다. 그런데 이 부트 코드를 만들기 위해 프로그래머는 하드웨어의 핀 동작보다는 내부 동작에 대한 이해를 하고 있어야 한다. 문제는 이 내부 동작에 대한 이해가 프로그래머를 무척 괴롭히는 과정이라는 것이다. 독자들도 부트로더를 만들기 위해서는 전체는 아니겠지만 조금은 알아 두어야 할 것이다.

== PXA255 프로세서
PXA255는 ARM의 변종이기는 하지만 프로그래머 입장에서 보면 ARM으로 봐도 무방하며 최근 프로세서의 전형적인 모습을 가지고 있다. 즉 XScale 마이크로 아키텍처 외부에 여러 가지의 주변 디바이스가 통합된 프로세서이다. PXA255의 대략적인 내부 구조는 <그림 2>와 같다. PXA255에서 지원하는 내용을 살펴보면 다음과 같다.

사용자 삽입 이미지

<그림 2> PXA255 프로세서의 내부 블럭도

◆ XScale Microarchitecture 100~400MHz Core Clock
◆ System memory interface 100MHz SDRAM 4MB to 256MB of SDRAM 16, 64, 128, 256MB DRAM 4 Banks of SDRAM(64MB) SDRAM interface into self refresh

◆ 6 static memory(SRAM, Flash, VLIO)
◆ PCMCIA/Compact Flash card Interface
◆ LCD Controller
◆ Full Function UART
◆ Bluetooth UART
◆ Standard UART
◆ MMC Controller
◆ SSP
◆ USB Client Controller
◆ AC’97 Controller
◆ I2C Controller
◆ GPIOs
◆ Integrated JTAG support

PXA255의 영문 매뉴얼은 책 한권이다. 따라서 이 모든 내용을 다룰 수는 없으므로 여기서는 일단 넘어가자. 자세한 내용이나 설명이 필요한 부분은 그때그때 알아보기로 하고 이제 이지부트가 동작하는 타겟 보드에 대해서 잠깐 살펴보자.