Linux 크로스 컴파일
LinuxEmbedded
크로스 컴파일은 호스트(x86)에서 타겟(ARM 등) 바이너리를 빌드하는 것이다. 임베디드 Linux 개발에서 커널, 모듈, 앱 모두 크로스 컴파일이 필요하다.
툴체인
| 타겟 | 툴체인 접두어 | 설치 (Ubuntu/Debian) |
|---|---|---|
| ARM 32-bit (hard-float) | arm-linux-gnueabihf- | gcc-arm-linux-gnueabihf |
| ARM 64-bit (AArch64) | aarch64-linux-gnu- | gcc-aarch64-linux-gnu |
| ARM bare-metal | arm-none-eabi- | gcc-arm-none-eabi |
# 설치
sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
# 확인
arm-linux-gnueabihf-gcc --version
# 테스트
arm-linux-gnueabihf-gcc -o hello hello.c
file hello
# hello: ELF 32-bit LSB executable, ARM, ...
ARM 공식 툴체인을 직접 다운로드할 수도 있다:
wget https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-linux-gnueabihf.tar.xz
tar xf arm-gnu-toolchain-*.tar.xz
export PATH=$PATH:$PWD/arm-gnu-toolchain-*/bin
커널 크로스 컴파일
두 환경 변수가 핵심이다: ARCH와 CROSS_COMPILE.
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# defconfig 선택 (타겟 보드에 맞게)
make vexpress_defconfig # QEMU Vexpress-A9
# ls arch/arm/configs/ # 사용 가능한 설정 목록
# 커스터마이즈 (선택)
make menuconfig
# 빌드
make -j$(nproc) zImage # 커널 이미지
make -j$(nproc) dtbs # Device Tree
make -j$(nproc) modules # 커널 모듈
# 결과물
# arch/arm/boot/zImage
# arch/arm/boot/dts/*.dtb
AArch64 커널:
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make defconfig
make -j$(nproc) Image dtbs modules
모듈 크로스 컴파일 (out-of-tree)
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export KDIR=/path/to/kernel/source
make -C $KDIR M=$(pwd) modules
# 타겟 rootfs에 설치
make -C $KDIR M=$(pwd) INSTALL_MOD_PATH=/path/to/rootfs modules_install
Rootfs 구성
BusyBox 최소 rootfs
# BusyBox 빌드
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xf busybox-*.tar.bz2 && cd busybox-*/
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
make defconfig
make menuconfig # Settings → Build static binary [*]
make -j$(nproc)
make install # _install/ 에 생성
# rootfs 디렉토리 구성
mkdir -p rootfs/{proc,sys,dev,etc,lib}
cp -a _install/* rootfs/
# init 스크립트
cat > rootfs/init << 'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
exec /bin/sh
EOF
chmod +x rootfs/init
# initramfs 생성
cd rootfs
find . | cpio -o -H newc | gzip > ../rootfs.cpio.gz
Buildroot (자동화)
git clone https://github.com/buildroot/buildroot.git
cd buildroot
make qemu_arm_vexpress_defconfig
make menuconfig # 필요한 패키지 추가
make -j$(nproc)
# output/images/ 에 zImage, rootfs.ext2, dtb 생성
Buildroot는 툴체인 + 커널 + rootfs를 한 번에 빌드한다. 패키지 추가도 menuconfig에서 선택하면 된다.
QEMU 테스트
물리 보드 없이 커널과 rootfs를 테스트한다.
# ARM32 (Vexpress-A9)
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel arch/arm/boot/zImage \
-dtb arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb \
-initrd rootfs.cpio.gz \
-append "root=/dev/ram rdinit=/init console=ttyAMA0" \
-nographic
# ARM64
qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-m 1024M \
-kernel Image \
-initrd rootfs.cpio.gz \
-append "root=/dev/ram rdinit=/init console=ttyAMA0" \
-nographic
# 종료: Ctrl+A → X
GDB 원격 디버깅
# 터미널 1: QEMU에 GDB 서버 활성화
qemu-system-arm -M vexpress-a9 ... -s -S
# -s: gdbserver on :1234
# -S: 시작 시 정지
# 터미널 2: GDB 연결
arm-linux-gnueabihf-gdb vmlinux
(gdb) target remote :1234
(gdb) break start_kernel
(gdb) continue
메모
CROSS_COMPILE은 접두어다arm-linux-gnueabihf-처럼 하이픈으로 끝난다. 빌드 시스템이 뒤에gcc,ld,objcopy등을 붙인다
- gnueabihf vs gnueabi
hf= hard-float. FPU가 있는 칩에서 사용. Cortex-A 시리즈는 거의 다 hfgnueabi(soft-float)는 FPU가 없는 칩용
- 커널 소스 버전과 모듈 호환
- out-of-tree 모듈은 빌드할 때 사용한 커널 소스 버전과 타겟 커널 버전이 정확히 일치해야 한다
vermagic문자열이 다르면insmod시 거부된다
- static vs dynamic BusyBox
- static 빌드면 공유 라이브러리가 필요 없어서 rootfs가 단순해진다
- dynamic이면
lib/에 크로스 컴파일된 libc를 복사해야 한다
- QEMU와 실제 하드웨어의 차이
- QEMU는 타이밍, DMA, 인터럽트 레이턴시를 정확히 에뮬레이션하지 않는다
- 기능 테스트에는 충분하지만, 성능 테스트는 실제 보드에서 해야 한다