툴체인은 임베디드 리눅스의 첫 번째 요소이자 프로젝트의 시작점으로 장치에서 실행될 모든 코드는 툴체인을 통해 컴파일된다.
초기 단계에서의 선택이 최종 산출물에 엄청난 영향을 끼칠 수도 있다.
● 툴체인 요건
- 프로젝트에 필요한 언어를 지원해야 한다.
- POSIX (Portable Operatin System Inerface) 및 기타 시스템 인터페이스에 대한 구현을 갖고 있어야 한다.
- 보안 결함이나 버그가 발견되면 업데이트가 되어야 한다.
- 프로젝트 내내 바뀌지 않아야 한다. (프로젝트 도중 컴파일러와 개발 라이브러리를 바꾸면 버그가 생기기 쉽다.)
● 툴체인 얻는 방법
- 파일을 다운로드해서 설치
- crosstool-NG 도구를 통해 소스 코드로부터 툴체인 빌드
- 빌드 시스템이 생성한 툴체인
[ 툴체인 소개 ]
- 소스 코드를 타깃 장치에서 실행할 수 있는 실행파일로 컴파일러, 링커, 런타임 라이브러리를 포함하는 컴파일 도구의 집합이다.
- 처음에는 부트로더, 커널, 루트 파일시스템을 빌드하기 위해 툴체인이 필요하다.
- 기본 오픈 소스 패키지들이 어셈블리, C, C++를 사용하기 때문에 툴체인은 이 언어로 작성된 코드를 컴파일할 수 있어야 한다.
- 흔히 리눅스용 툴체인은 GNU 프로젝트에서 만들어진 요소에 기반을 두고 있다.
- LLVM (Low Level Virtual Machine) : BSD 라이선스, Clang 컴파일러, 좀 더 빠른 컴파일, 나은 진단 기능
- GNU : GPL 라이선스, 기존 코드와의 호환성 및 광범위한 아키텍처와 운영체제 지원
● 표준 GNU 툴체인 세 가지 요소
- Binutils : 어셈블러와 링커를 포함하는 바이너리 유틸리티의 집합
- GCC (GNU Compiler Collection) : C와 여러 언어를 위한 컴파일러
- C 라이브러리 : POSIX 규격에 기반을 둔 표준 API. 응용 프로그램에서 운영체제 커널로 연결되는 주요 인터페이스
그 밖에도 커널에 직접 접근할 때 필요한 정의와 상수를 담고 있는 리눅스 커널 헤더가 필요하다.
C 라이브러리를 컴파일하기 위해서도 필요하지만 나중에 리눅스 프레임 버퍼 드라이버를 통해 그래픽을 보여주는 라이브러리를 컴파일할 때도 필요하다.
대부분의 사람들이 GDB(GNU 디버거)를 툴체인의 일부로 생각한다.
● 툴체인 종류
- 네이티브 : 툴체인이 만들어내는 프로그램과 같은 종류의 시스템. ARM용 데비안을 실행하는 라즈베리 파이는 자체적으로 네이티브 컴파일러를 갖고 있다.
- 크로스 : 툴체인이 Target Machine과 다른 종류의 시스템에서 실행. 빠른 데스크톱 PC에서 개발 후, 임베디드 장치에 로드하여 테스트할 수 있다.
거의 모든 임베디드 리눅스 개발은 크로스 개발 툴체인을 이용한다.
임베디드 장치가 연산 능력, 메모리, 저장 공간이 부족해서 개발에 적합하지 않고 호스트와 Target 환경을 분리할 수 있기 때문이다.
호스트와 Target이 같은 아키텍처를 사용할 때, Host PC에서 네이티브로 컴파일하고 Target에 바이너리를 복사할 때 문제가 생길 수 있다.
Host 배포판은 Target보다 더 자주 업데이트 되고, Target용 라이브러리와 Host 개발 라이브러리가 약간 다른 버전을 갖게 될 수 있기 때문이다.
● CPU 아키텍처
툴체인은 Target CPU 특징에 맞게 빌드되어야 한다.
- CPU 아키텍처 : ARM, MIPS, x86_64 등
- 빅엔디안 또는 리틀엔디안 : 어떤 CPU는 두 가지 모드로 동작할 수 있지만 기계어 코드가 각각 다름
- 부동소수점 지원 : 모든 버전의 임베디드 프로세서가 하드웨어 부동소수점 장치를 구현하지 않음. 이 경우 툴체인은 소프트웨어 부동소수점 라이브러리를 부르도록 설정할 수 있음
- ABI (Application Binary Interace) : 함수 호출 간에 인자를 넘기는 호출 규칙
여러 아키텍처에서 ABI는 같은 계열의 프로세서에서 동일하다. 예외는 ARM이다.
2000년대 후반부터 EABI(Extended Application Binary Interface)가 쓰이며, EABI는 부동소수점 인자 전달 방식에 따라 둘로 나뉜다.
EABI는 범용(정수) 레지스터를 사용하지만 새로운 EABIHF(Extended Application Binary Interface Hard-Float)는 부동소수점 레지스터를 사용한다.
EABIHF는 정수 레지스터와 부동소수점 레지스터 간 복사가 필요 없기 때문에 부동소수점 연산이 훨씬 빠르지만, 부동소수점 장치가 없는 CPU와 호환되지 않는다.
따라서 두 ABI 사이에서 선택해야 하고, 둘을 섞어 쓸 수 없으므로 무엇을 쓸지 선택해야 한다.
GNU 접두어
GNU는 대시로 구분된 3~4개의 요소로 이루어진 접두어 붙인다.
- CPU : ARM, MIPS, x86_64 같은 CPU 아키텍처
- 리틀엔디안 el, 빅엔디안 eb
- 리틀엔디안 MIPS인 mipsel과 빅엔디안 ARM인 armeb이 있다.
- 벤더 : 툴체인 공급자를 나타냄
- buildroot, poky, unknown이 있으며 생략하기도 한다.
- 커널 : 여기서는 모두 Linux.
- 운영체제 : 사용자 공간 요소의 이름
- gnu나 musl일 수 있으며, 여기에 ABI를 붙이기도 한다.
- ARM 툴체인의 경우, gnueabi, gnueabihf, musleabi, musleabihf 등이 될 수 있다.
gcc의 -dumpmachine 옵션
gcc의 -dumpmachine 옵션을 사용하면 툴체인을 빌드할 때 쓰인 조합을 알 수 있다.
호스트 컴퓨터에서는 다음과 같은 결과가 나타날 수 있다.
$ gcc -dumpmachine
x86_64-linux-gnu
● C 라이브러리 고르기
유닉스 운영체제의 프로그래밍 인터페이스는 C언어로 정의되어 있는데, 지금은 POSIX 표준으로 정의되어 있다.
C 라이브러리는 그 인터페이스의 구현으로 키눅스 프로그램에서 커널로 통하는 관문이다.
자바나 파이썬 같은 언어로 프로그램을 작성하더라도, 각 런타임 지원 라이브러리는 결국 C라이브러리를 불러야 한다.
C 라이브러리는 커널의 서비스가 필요할 때마다 System Call 인터페이스를 통해 User Space와 Kernel Space를 전환한다.
라이브러리 종류
- glibc
- 표준 GNU C 라이브러리이다.
- 크기가 크다.
- 최근까지 구성 변경이 용이하지 않지만 POSIX API의 가장 완전한 구현이다.
- 라이선스는 LGPL 2.1이다.
- musl libc
- 비교적 최근에 등장했다.
- 작고 표준을 준수하는 GNU libc의 대안으로 많은 관심을 끌고 있다.
- 램과 저장 공간의 크기가 제한된 시스템을 위한 라이브러리이다.
- MIT 라이선스를 따른다.
- uClibc-ng
- 마이크로 컨트롤러 C 라이브러리이다.
- 처음에는 uClinux(MMU 없는 CPU)용으로 개발했지만, 그 후 완전한 리눅스에서 쓸 수 있도록 개조됐다.
- 라이선스는 LGPL 2.1이다.
- eglibc
- glibc를 임베디드용으로 변경한 라이브러리이다.
- glibc가 다루지 않는 아키텍처 (특히 파워 PC e500 CPU 코어)를 지원하는 구성 옵션이 추가되었다.
- glibc 버전 2.2에 병합되었으며, 더 이상 유지보수되지 않는다.
그래서 어떠한 툴체인을 써야 하냐 하면 uClinux를 사용한다면 uClibc-ng를 선택할 수 있으며, 저장소나 램의 크기가 제한이 되어 있다면 musl linc를 선택하는 것이 좋다. 그렇지 않다면 glibc를 사용하면 좋다.
[ 툴체인 찾기 ]
- 미리 빌드된 툴체인 중에 필요사항을 만족하는 것 찾기
- 임베디드 빌드 도구를 통해 생성된 것 사용하기 (6장)
- 직접 만들기 (2장)
미리 빌드된 크로스 툴체인을 선택하고 싶을 것이다. 하지만 특정 툴체인의 구성에 제한되고 해당 툴체인을 제공한 사람이나 조직에 의존하게 된다.
그렇기 때문에 해당 툴체인이 요구사항을 만족하는지 판단해야 한다.
하지만 툴체인 빌드는 쉬운 일이 아니며, 전체 과정을 직접 하고 싶다면, Cross Linux From Scratch를 참고하면 된다.
좀 더 쉬운 방법은 crosstool-NG를 사용하는 것이다. 하지만 여전히 상당한 수준의 지식이 필요하긴 하다.
훨씬 더 쉬운 방법은 Buildroot나 Yocto 프로젝트 같은 빌드 시스템을 쓰는 것이다.
빌드 과정의 일부로 툴체인을 만들어주기 때문이다. 가장 선호하는 방법이다.