크로스 컴파일 환경

x86_64 호스트에서 ARM, ARM64, RISC-V 등 다른 아키텍처용 리눅스 커널을 빌드하는 크로스 컴파일 환경의 원리, 구축, 검증, 배포까지 다루는 종합 가이드입니다.

전제 조건: 개발 환경 설정커널 빌드 시스템(Build System) 문서를 먼저 읽으세요. 크로스 컴파일은 네이티브 빌드 과정을 이해한 뒤 진행하는 것이 효율적입니다.
일상 비유: 크로스 컴파일은 한국에서 일본어 책을 인쇄하는 것과 비슷합니다. 인쇄 장비(호스트)는 한국에 있지만, 결과물(바이너리)은 일본어 독자(타겟 아키텍처)가 읽을 수 있어야 합니다. 이를 위해 일본어 활자(크로스 툴체인)가 필요합니다.

핵심 요약

  • 크로스 컴파일(Cross Compilation) — 호스트(Host) 아키텍처와 다른 타겟(Target) 아키텍처용 바이너리를 생성하는 빌드 방식입니다
  • 크로스 툴체인(Cross Toolchain) — 컴파일러(GCC/Clang), 어셈블러(as), 링커(Linker)인 ld, binutils를 포함하는 도구 모음입니다
  • ARCH / CROSS_COMPILE — 커널 빌드 시스템(Kbuild)에 타겟 아키텍처와 툴체인 접두사를 지정하는 두 핵심 변수입니다
  • 타겟 트리플렛(Target Triplet)aarch64-linux-gnu처럼 아키텍처-OS-ABI를 명시하는 명명 규칙입니다
  • QEMU 검증 — 실 하드웨어 없이 크로스 빌드된 커널을 에뮬레이터에서 부팅·테스트할 수 있습니다

단계별 이해

  1. 툴체인 설치 — 배포판 패키지 매니저로 크로스 컴파일러를 설치합니다 (apt install gcc-aarch64-linux-gnu)
  2. 환경 변수 설정ARCH=arm64CROSS_COMPILE=aarch64-linux-gnu-를 지정합니다
  3. defconfig 생성make defconfig로 타겟 아키텍처의 기본 설정을 생성합니다
  4. 커널 빌드make -j$(nproc)으로 크로스 컴파일을 실행합니다
  5. 바이너리 검증file, readelf로 생성된 이미지의 아키텍처를 확인합니다
  6. QEMU 부팅qemu-system-aarch64로 커널을 부팅하여 동작을 검증합니다

개요

크로스 컴파일이란

크로스 컴파일은 프로그램을 실행할 시스템(타겟)과 빌드하는 시스템(호스트)의 CPU 아키텍처가 다를 때 사용하는 빌드 방식입니다. 리눅스 커널 개발에서는 x86_64 워크스테이션에서 ARM, ARM64, RISC-V 등의 임베디드 보드용 커널을 빌드할 때 필수적입니다.

네이티브 빌드 vs 크로스 빌드

네이티브 빌드 (Native Build) 호스트 (x86_64) gcc (네이티브) 소스 코드 .c / .S ELF 호스트 = 타겟 (동일 아키텍처) x86_64 워크스테이션에서 빌드 → x86_64 서버에서 실행 별도 툴체인 설치 불필요, 가장 단순한 빌드 방식 장점: 설정 간단, 호스트에서 바로 실행·디버깅(Debugging) 가능 단점: 타겟이 비력한(underpowered) 장치면 빌드 매우 느림 크로스 빌드 (Cross Build) 호스트 (x86_64) aarch64-linux-gnu-gcc 소스 코드 .c / .S ELF 호스트 ≠ 타겟 (다른 아키텍처) x86_64 워크스테이션에서 빌드 → ARM64 보드에서 실행 크로스 툴체인 필수, ARCH/CROSS_COMPILE 변수 지정 장점: 고성능 호스트 활용, 빠른 빌드, 다중 아키텍처 지원 단점: 툴체인 설정 필요, 호스트에서 직접 실행 불가
그림 1. 네이티브 빌드와 크로스 빌드 비교 — 호스트와 타겟의 아키텍처 관계
구분네이티브 빌드크로스 빌드
호스트-타겟 관계동일 아키텍처다른 아키텍처
컴파일러gcc (시스템 기본)aarch64-linux-gnu-gcc
환경 변수불필요ARCH, CROSS_COMPILE 필수
빌드 산출물 실행호스트에서 직접 실행타겟 보드 또는 QEMU 필요
빌드 속도타겟 성능에 의존고성능 호스트 활용 가능
대표 사용처서버·데스크톱 커널임베디드·IoT·모바일 커널

크로스 컴파일이 필요한 경우

크로스 컴파일 내부 동작 원리

컴파일 4단계

C 소스가 타겟 아키텍처용 바이너리로 변환되는 과정은 네이티브 빌드와 동일한 4단계를 거칩니다. 차이점은 각 단계에서 사용하는 도구가 크로스 툴체인이라는 점입니다.

단계도구입력출력설명
1. 전처리(Preprocessing)cpp.c.i매크로(Macro) 확장, #include 삽입, 조건부 컴파일 처리
2. 컴파일(Compilation)cc1.i.sC 코드를 타겟 아키텍처의 어셈블리(Assembly)로 변환
3. 어셈블(Assembly)as.s.o어셈블리를 타겟 아키텍처의 기계어(Machine Code)(오브젝트 파일)로 변환
4. 링크(Linking)ld.oELF오브젝트 파일들을 연결하여 최종 실행 파일(vmlinux) 생성
# 각 단계를 수동으로 실행하는 예 (ARM64)
# 1) 전처리
aarch64-linux-gnu-gcc -E kernel/fork.c -o fork.i
# 2) 컴파일 (어셈블리 생성)
aarch64-linux-gnu-gcc -S fork.i -o fork.s
# 3) 어셈블 (오브젝트 파일 생성)
aarch64-linux-gnu-as fork.s -o fork.o
# 4) 링크 (실행 파일 생성)
aarch64-linux-gnu-ld fork.o -o fork
코드 설명

ARM64용 크로스 컴파일의 4단계를 개별 명령으로 분리한 예입니다. 실제 커널 빌드에서는 make가 이 과정을 자동으로 수행합니다.

  • -E전처리만 수행합니다. #include, #define 매크로가 전개된 .i 파일을 생성합니다.
  • -S컴파일만 수행하여 AArch64 어셈블리(.s)를 출력합니다. adrp, ldr, str 등 ARM64 명령어가 포함됩니다.
  • as크로스 어셈블러가 ARM64 기계어를 담은 ELF 오브젝트 파일(.o)을 생성합니다.
  • ld크로스 링커가 오브젝트 파일들을 연결합니다. 커널에서는 vmlinux.lds 링커 스크립트를 사용합니다.

Sysroot 개념과 경로 탐색

Sysroot는 크로스 컴파일러가 타겟 아키텍처용 헤더 파일과 라이브러리를 찾는 루트 디렉토리입니다. 커널 빌드에서는 자체 헤더를 사용하므로 sysroot가 필수는 아니지만, 유저스페이스 도구를 크로스 빌드할 때는 중요합니다.

# 크로스 컴파일러의 sysroot 위치 확인
aarch64-linux-gnu-gcc -print-sysroot
# 출력 예: /usr/aarch64-linux-gnu

# sysroot 내부 구조
ls /usr/aarch64-linux-gnu/
# include/  lib/  — 타겟 아키텍처용 헤더와 라이브러리

# 명시적 sysroot 지정 (유저스페이스 빌드 시)
aarch64-linux-gnu-gcc --sysroot=/path/to/sysroot -o hello hello.c

ABI와 호출 규약(Calling Convention)

크로스 컴파일에서 ABI(Application Binary Interface) 일치는 핵심 요소입니다. 특히 ARM 32비트는 여러 ABI 변종이 있어 주의가 필요합니다.

ABI툴체인 접미사부동소수점 처리대표 사용처
EABI soft-floatarm-linux-gnueabi-소프트웨어 에뮬레이션FPU 없는 임베디드 장치
EABI hard-floatarm-linux-gnueabihf-하드웨어 FPU 사용Raspberry Pi, BeagleBone
AArch64aarch64-linux-gnu-하드웨어 FPU (항상)ARM64 서버, 모바일 SoC
RISC-V LP64Driscv64-linux-gnu-Double-precision FPURISC-V 64비트 보드
주의: ARM 32비트에서 gnueabi(soft-float)와 gnueabihf(hard-float) 툴체인을 혼용하면 링크 오류 또는 런타임 크래시가 발생합니다. 타겟 보드의 FPU 지원 여부를 먼저 확인하세요.

엔디언(Endianness) 고려사항

대부분의 ARM64와 RISC-V 시스템은 리틀 엔디언(Little-Endian)을 사용하지만, 일부 네트워크 장비나 레거시 시스템에서는 빅 엔디언(Big-Endian)이 필요합니다.

아키텍처기본 엔디언빅 엔디언 지원설정 방법
ARM리틀 엔디언예 (armeb)CONFIG_CPU_BIG_ENDIAN=y
ARM64리틀 엔디언예 (aarch64_be)CONFIG_CPU_BIG_ENDIAN=y
RISC-V리틀 엔디언아니요리틀 엔디언만 지원
MIPS빅 엔디언기본값mipsel- 접두사로 리틀 엔디언 선택
PowerPC빅 엔디언기본값powerpc64le- 접두사로 리틀 엔디언 선택

툴체인 해부

툴체인 구성 요소

크로스 툴체인 구성 요소 컴파일러 (Compiler) C/C++ → 어셈블리 변환 aarch64-linux-gnu-gcc aarch64-linux-gnu-g++ LLVM: clang --target=aarch64 Binutils 바이너리 조작 도구 모음 as (어셈블러) ld (링커) objcopy, objdump, nm, strip readelf, ar, ranlib, size C 라이브러리 (libc) 타겟 시스템 런타임 glibc (범용) musl (경량) uClibc-ng (임베디드) 커널 빌드 시에는 미사용 커널 헤더 (Kernel Headers) 시스템 콜 인터페이스 정의 include/uapi/ arch/*/include/uapi/ make headers_install로 설치 GDB 디버거 (선택) aarch64-linux-gnu-gdb 또는 gdb-multiarch — 크로스 아키텍처 원격 디버깅 지원 커널 빌드에서의 실제 사용 커널은 자체 헤더와 라이브러리를 사용하므로, 크로스 빌드 시 필수 구성 요소는 컴파일러 + binutils뿐입니다. libc와 커널 헤더는 유저스페이스 도구(BusyBox, systemd 등) 크로스 빌드 시 필요합니다.
그림 2. 크로스 툴체인 구성 요소 — 컴파일러, binutils, C 라이브러리, 커널 헤더
도구GNU 이름LLVM 이름역할
C 컴파일러aarch64-linux-gnu-gccclang --target=aarch64-linux-gnuC 소스를 어셈블리로 변환
어셈블러aarch64-linux-gnu-asclang (통합) / llvm-as어셈블리를 오브젝트 코드로 변환
링커aarch64-linux-gnu-ldld.lld오브젝트 파일을 연결하여 ELF 생성
아카이버aarch64-linux-gnu-arllvm-ar정적 라이브러리(.a) 생성
오브젝트 복사aarch64-linux-gnu-objcopyllvm-objcopyELF → 바이너리 이미지 변환 (vmlinux → Image)
오브젝트 덤프(Dump)aarch64-linux-gnu-objdumpllvm-objdump디스어셈블, 섹션 분석
심볼 목록aarch64-linux-gnu-nmllvm-nm오브젝트 파일의 심볼 테이블(Symbol Table) 출력
ELF 분석aarch64-linux-gnu-readelfllvm-readelfELF 헤더, 섹션, 세그먼트 분석
스트립aarch64-linux-gnu-stripllvm-strip디버그 심볼 제거 (바이너리 크기 축소)

타겟 트리플렛 규칙

타겟 트리플렛(Target Triplet) 구조 aarch64 - linux - gnu 아키텍처 (ARCH) 운영체제 (OS) ABI / libc 주요 트리플렛 예시 arm-linux-gnueabihf ARM 32비트, Linux, GNU EABI hard-float aarch64-linux-gnu ARM 64비트 (AArch64), Linux, GNU libc riscv64-linux-gnu RISC-V 64비트, Linux, GNU libc mips-linux-gnu MIPS 32비트 (빅 엔디언), Linux, GNU libc powerpc64le-linux-gnu PowerPC 64비트 (리틀 엔디언), Linux, GNU libc arm-none-eabi ARM 베어메탈 (OS 없음), Embedded ABI
그림 3. 타겟 트리플렛 구조 — 아키텍처, 운영체제, ABI 세 요소로 구성
팁: 크로스 컴파일러의 -dumpmachine 옵션으로 실제 타겟 트리플렛을 확인할 수 있습니다: aarch64-linux-gnu-gcc -dumpmachineaarch64-linux-gnu

Multilib와 ABI 변종

Multilib는 하나의 툴체인이 여러 ABI 변종을 지원하는 기능입니다. 예를 들어 ARM 툴체인이 Thumb, ARM, Thumb-2 명령어 세트를 모두 지원할 수 있습니다.

# 툴체인이 지원하는 multilib 구성 확인
aarch64-linux-gnu-gcc -print-multi-lib
# 출력 예: .;  (단일 ABI — ARM64는 보통 multilib 불필요)

arm-linux-gnueabihf-gcc -print-multi-lib
# 출력 예:
# .;  
# thumb;@mthumb

# 지원되는 CPU 목록 확인
aarch64-linux-gnu-gcc -mcpu=help 2>&1 | head -20

캐나디안 크로스 빌드

캐나디안 크로스(Canadian Cross)는 세 가지 다른 아키텍처가 관여하는 빌드 방식입니다. 빌드 머신(A)에서 호스트 머신(B)용 컴파일러를 만들고, 그 컴파일러가 타겟 머신(C)용 코드를 생성합니다.

머신역할예시
빌드(Build)컴파일러 자체를 빌드하는 머신x86_64 Linux 서버
호스트(Host)컴파일러가 실행되는 머신Windows x86_64 PC
타겟(Target)컴파일러가 생성하는 코드의 실행 대상ARM64 보드

일반적인 커널 크로스 컴파일에서는 빌드=호스트(x86_64)이고 타겟만 다르므로, 캐나디안 크로스가 아닌 단순 크로스 빌드입니다.

GNU 크로스 툴체인

배포판별 패키지 설치

# Ubuntu/Debian — 가장 간편한 설치 방법
sudo apt install -y \
  gcc-arm-linux-gnueabi \
  gcc-arm-linux-gnueabihf \
  gcc-aarch64-linux-gnu \
  gcc-riscv64-linux-gnu \
  gcc-powerpc64le-linux-gnu \
  gcc-mips-linux-gnu

# binutils도 함께 설치 (별도 설치가 필요한 경우)
sudo apt install -y \
  binutils-aarch64-linux-gnu \
  binutils-riscv64-linux-gnu
# Fedora/RHEL
sudo dnf install -y \
  gcc-arm-linux-gnu \
  gcc-aarch64-linux-gnu \
  gcc-riscv64-linux-gnu \
  gcc-powerpc64le-linux-gnu
# Arch Linux
sudo pacman -S \
  arm-none-eabi-gcc \
  aarch64-linux-gnu-gcc \
  riscv64-linux-gnu-gcc

Linaro ARM 툴체인

Linaro는 ARM 생태계에 최적화된 크로스 툴체인을 제공합니다. 배포판 패키지보다 최신 버전이 필요하거나 특정 최적화 옵션이 필요할 때 유용합니다.

# Linaro ARM64 툴체인 다운로드 및 설치
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-*-x86_64_aarch64-linux-gnu.tar.xz
tar xf gcc-linaro-*-x86_64_aarch64-linux-gnu.tar.xz -C /opt/
export PATH=/opt/gcc-linaro-*/bin:$PATH

# 설치 확인
aarch64-linux-gnu-gcc --version

Bootlin 사전 빌드 툴체인

Bootlin(구 Free Electrons)은 다양한 아키텍처와 libc 조합의 사전 빌드 툴체인을 제공합니다. musl, uClibc-ng 등 경량 libc 기반 툴체인이 필요할 때 유용합니다.

프로젝트지원 아키텍처libc 옵션장점단점
배포판 패키지주요 5~6개glibc설치 간편, 자동 업데이트버전 선택 제한
LinaroARM, ARM64glibcARM 최적화, 안정성 검증ARM 전용
Bootlin20+ 아키텍처glibc, musl, uClibc-ng다양한 조합, 최신 버전수동 설치 필요
crosstool-NG모든 아키텍처모든 libc완전 커스텀 빌드빌드 시간 소요 (30분~2시간)

crosstool-NG로 커스텀 툴체인 빌드

crosstool-NG는 GCC 크로스 툴체인을 처음부터 빌드하는 도구입니다. 특정 GCC 버전, libc, binutils 조합이 필요할 때 사용합니다.

# crosstool-NG 설치
git clone https://github.com/crosstool-ng/crosstool-ng.git
cd crosstool-ng
./bootstrap && ./configure --prefix=/opt/crosstool-ng
make && make install
export PATH=/opt/crosstool-ng/bin:$PATH

# 사전 정의된 샘플 목록 확인
ct-ng list-samples | grep aarch64
# 출력 예: aarch64-unknown-linux-gnu

# ARM64 타겟 선택 및 빌드
ct-ng aarch64-unknown-linux-gnu
ct-ng menuconfig  # 필요 시 GCC 버전, libc 등 커스텀
ct-ng build       # 빌드 시작 (30분~2시간 소요)

툴체인 버전과 커널 버전 호환성

커널 버전최소 GCC최소 Clang최소 binutils비고
5.4 LTS4.910.02.23LLVM 공식 지원 시작
5.10 LTS4.910.02.25RISC-V Clang 지원 개선
5.15 LTS5.111.02.25최소 GCC 버전 상향
6.1 LTS5.113.02.25Clang CFI 지원 확대
6.6 LTS5.113.02.25Rust for Linux 초기 지원
6.12+5.113.02.25LLVM_IAS=1 기본 활성화 추세
확인 방법: 커널 소스의 Documentation/process/changes.rst 파일에서 정확한 최소 버전 요구사항을 확인할 수 있습니다. scripts/min-tool-version.sh로 프로그래밍적으로 조회할 수도 있습니다.

LLVM/Clang 크로스 컴파일

Clang 크로스 빌드 기본

LLVM/Clang은 단일 바이너리로 여러 타겟 아키텍처를 지원하므로, 아키텍처별로 별도의 컴파일러를 설치할 필요가 없습니다.

# Clang으로 ARM64 커널 크로스 빌드
make ARCH=arm64 LLVM=1 defconfig
make ARCH=arm64 LLVM=1 -j$(nproc)

# RISC-V 커널 크로스 빌드
make ARCH=riscv LLVM=1 defconfig
make ARCH=riscv LLVM=1 -j$(nproc)

# 특정 LLVM 도구 경로 지정 (버전별 설치 시)
make ARCH=arm64 LLVM=-18 defconfig
make ARCH=arm64 LLVM=-18 -j$(nproc)
코드 설명

LLVM 기반 크로스 빌드의 핵심은 LLVM=1 변수 하나입니다.

  • LLVM=1CC, LD, AR, NM, OBJCOPY 등 모든 도구를 LLVM 도구체인(clang, ld.lld, llvm-ar 등)으로 전환합니다. CROSS_COMPILE 지정이 불필요합니다.
  • LLVM=-18버전 접미사를 지정하여 clang-18, ld.lld-18 등 특정 버전의 LLVM 도구를 사용합니다.
  • ARCH=arm64Clang이 --target=aarch64-linux-gnu를 자동으로 추가합니다. GNU 크로스 빌드와 달리 CROSS_COMPILE이 불필요합니다.

--target 플래그와 --sysroot

# Clang의 타겟 지정 방식 (수동)
clang --target=aarch64-linux-gnu -c test.c -o test.o
clang --target=riscv64-linux-gnu -c test.c -o test.o

# sysroot와 함께 사용 (유저스페이스 빌드 시)
clang --target=aarch64-linux-gnu --sysroot=/usr/aarch64-linux-gnu -o hello hello.c

# GNU binutils와 혼합 사용 (LLVM linker 대신 GNU ld 사용)
make ARCH=arm64 CC=clang CROSS_COMPILE=aarch64-linux-gnu- defconfig

통합 어셈블러 (LLVM_IAS=1)

LLVM에는 통합 어셈블러(Integrated Assembler)가 내장되어 있어, GNU as 없이도 어셈블리 파일을 처리할 수 있습니다.

# LLVM 통합 어셈블러 사용 (기본값)
make ARCH=arm64 LLVM=1 LLVM_IAS=1 -j$(nproc)

# GNU 어셈블러 강제 사용 (호환성 문제 시)
make ARCH=arm64 LLVM=1 LLVM_IAS=0 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

GCC vs Clang 크로스 컴파일 비교

항목GCC 크로스 빌드Clang 크로스 빌드 (LLVM=1)
설치아키텍처별 별도 패키지단일 clang 패키지
타겟 지정CROSS_COMPILE=aarch64-linux-gnu-ARCH=arm64만으로 충분
링커GNU ld (bfd/gold)ld.lld (더 빠른 링크 속도)
어셈블러GNU as통합 어셈블러 (LLVM_IAS)
정적 분석-Wextra 수준더 엄격한 경고, CFI 지원
빌드 속도보통LTO/ThinLTO로 더 빠를 수 있음
호환성모든 커널 버전 지원5.4+ 공식 지원, 일부 아키텍처 제한
멀티 아키텍처아키텍처별 별도 설치하나의 바이너리로 모든 타겟

툴체인 검증과 진단

사전 점검 루틴

크로스 빌드 실패의 대부분은 소스 문제가 아니라 툴체인 설치 누락이나 환경 변수 불일치에서 발생합니다. 빌드 전 아래 점검을 수행하면 실패 원인을 빠르게 분리할 수 있습니다.

# 1) 툴체인 존재 확인
which aarch64-linux-gnu-gcc || echo "ERROR: 크로스 컴파일러 미설치"

# 2) 타겟 머신 확인
aarch64-linux-gnu-gcc -dumpmachine
# 기대 출력: aarch64-linux-gnu

# 3) 컴파일러 버전 확인
aarch64-linux-gnu-gcc --version | head -1
# 기대 출력: aarch64-linux-gnu-gcc (Ubuntu ...) 12.3.0

# 4) 환경 변수 확인
echo "ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE"

# 5) 커널 최소 버전 요구사항 확인
cat scripts/min-tool-version.sh | grep -A2 gcc

테스트 컴파일로 툴체인 검증

# 최소 테스트 프로그램으로 툴체인 동작 검증
echo 'int main() { return 0; }' > /tmp/test_cross.c
aarch64-linux-gnu-gcc -static /tmp/test_cross.c -o /tmp/test_cross

# 생성된 바이너리의 아키텍처 확인
file /tmp/test_cross
# 기대: ELF 64-bit LSB executable, ARM aarch64, ...

# QEMU 유저모드로 실행 검증 (선택)
qemu-aarch64-static /tmp/test_cross; echo "exit code: $?"
# 기대: exit code: 0

# 정리
rm -f /tmp/test_cross /tmp/test_cross.c

Sysroot 디렉토리 탐색

# 크로스 컴파일러의 검색 경로 확인
aarch64-linux-gnu-gcc -print-search-dirs | head -3

# sysroot 위치
aarch64-linux-gnu-gcc -print-sysroot

# 시스템 포함 경로 (include path) 확인
echo | aarch64-linux-gnu-gcc -E -v - 2>&1 | sed -n '/include.*search/,/End/p'

# 라이브러리 경로 확인
aarch64-linux-gnu-gcc -print-file-name=libgcc.a

환경 변수 관리 전략

아키텍처별 환경 스크립트

# env-arm64.sh — ARM64 크로스 빌드 환경 설정
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export INSTALL_MOD_PATH=$PWD/out/modules
export INSTALL_PATH=$PWD/out/boot
export INSTALL_DTBS_PATH=$PWD/out/dtbs

# 사용 방법
source env-arm64.sh
make defconfig
make -j$(nproc)
# env-riscv.sh — RISC-V 크로스 빌드 환경 설정
export ARCH=riscv
export CROSS_COMPILE=riscv64-linux-gnu-
export INSTALL_MOD_PATH=$PWD/out/modules
export INSTALL_PATH=$PWD/out/boot
주의: 쉘 세션마다 ARCH, CROSS_COMPILE 값이 달라지면 재현이 어렵습니다. 프로젝트별 환경 스크립트로 고정하고, source env-arm64.sh로 일관되게 사용하세요.

Makefile 기본값 설정

# 매번 ARCH/CROSS_COMPILE을 입력하는 대신
# 커널 소스 디렉토리에 GNUmakefile을 만들어 기본값 설정
cat > GNUmakefile <<'EOF'
# GNUmakefile — 크로스 빌드 기본값 (Makefile보다 우선 로드)
ARCH ?= arm64
CROSS_COMPILE ?= aarch64-linux-gnu-
include Makefile
EOF

# 이제 make만 실행하면 ARM64 크로스 빌드 수행
make defconfig
make -j$(nproc)

direnv를 활용한 자동 환경 전환

# direnv 설치
sudo apt install -y direnv

# 커널 소스 디렉토리에 .envrc 생성
cat > .envrc <<'EOF'
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
EOF

# direnv 허용
direnv allow .

# 이후 해당 디렉토리 진입 시 자동으로 환경 변수 설정
cd /path/to/linux
echo $ARCH  # → arm64

Kbuild 크로스 컴파일 로직 분석

최상위 Makefile의 ARCH/CROSS_COMPILE 처리

커널 최상위 Makefile에서 크로스 컴파일 관련 변수가 어떻게 처리되는지 핵심 부분을 분석합니다.

# Makefile (top-level) — ARCH/SUBARCH 해석
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
                                  -e s/sun4u/sparc64/ -e s/aarch64.*/arm64/ \
                                  -e s/riscv.*/riscv/ ...)

# ARCH가 지정되지 않으면 호스트 아키텍처(SUBARCH)를 사용
ARCH     ?= $(SUBARCH)
# SRCARCH는 실제 arch/ 디렉토리 이름으로 매핑(Mapping)
SRCARCH  := $(ARCH)

# 크로스 컴파일러 도구 변수 조합
CC       = $(CROSS_COMPILE)gcc
LD       = $(CROSS_COMPILE)ld
AR       = $(CROSS_COMPILE)ar
OBJCOPY  = $(CROSS_COMPILE)objcopy
OBJDUMP  = $(CROSS_COMPILE)objdump
READELF  = $(CROSS_COMPILE)readelf
코드 설명

커널 최상위 Makefile의 크로스 컴파일 변수 해석 로직입니다.

  • SUBARCHuname -m 출력을 커널 아키텍처 이름으로 정규화합니다. x86_64x86, aarch64arm64 등으로 변환합니다.
  • ARCH ?= $(SUBARCH)ARCH가 명시적으로 지정되지 않으면 호스트 아키텍처를 기본값으로 사용합니다. 크로스 빌드 시 반드시 ARCH=arm64처럼 지정해야 합니다.
  • SRCARCHarch/$(SRCARCH)/ 디렉토리를 결정합니다. 대부분 ARCH와 동일하지만, 일부 매핑(Mapping)이 있습니다 (예: ARCH=arm64SRCARCH=arm64).
  • CC = $(CROSS_COMPILE)gccCROSS_COMPILE=aarch64-linux-gnu-가 설정되면 CC=aarch64-linux-gnu-gcc가 됩니다. 접두사 방식으로 모든 GNU 도구에 일괄 적용됩니다.

LLVM=1 도구 변수 전환

# Makefile — LLVM=1 설정 시 도구 변수 전환
ifdef LLVM
  CC       = clang
  LD       = ld.lld
  AR       = llvm-ar
  NM       = llvm-nm
  OBJCOPY  = llvm-objcopy
  OBJDUMP  = llvm-objdump
  READELF  = llvm-readelf
  STRIP    = llvm-strip
endif

# LLVM 버전 접미사 지원 (예: LLVM=-18)
# LLVM=1이면 접미사 없음, LLVM=-18이면 clang-18, ld.lld-18 등 사용
코드 설명

LLVM=1 설정 시 모든 빌드 도구가 GNU에서 LLVM 도구체인으로 전환되는 로직입니다.

  • CC = clangGCC 대신 Clang을 사용합니다. Clang은 --target 플래그로 타겟 아키텍처를 지정하므로 CROSS_COMPILE이 불필요합니다.
  • LD = ld.lldGNU ld 대신 LLVM 링커(lld)를 사용합니다. 대규모 프로젝트에서 링크 속도가 더 빠릅니다.
  • LLVM=-18시스템에 여러 LLVM 버전이 설치된 경우, 접미사로 특정 버전을 지정할 수 있습니다.

ARCH → SRCARCH → 하위 Makefile 변수 전파

Kbuild ARCH/CROSS_COMPILE 변수 전파 흐름 사용자 명령 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 최상위 Makefile ARCH → SRCARCH 매핑 arch/arm64/Makefile KBUILD_CFLAGS, KBUILD_IMAGE 설정 변수 조합 결과 CC=aarch64-linux-gnu-gcc LD=aarch64-linux-gnu-ld OBJCOPY=aarch64-linux-gnu-objcopy scripts/Makefile.build $(CC) $(KBUILD_CFLAGS) -c source.c -o source.o arch/arm64/boot/Makefile $(OBJCOPY) -O binary vmlinux Image vmlinux (AArch64 ELF) arch/arm64/boot/Image (Raw Binary) objcopy
그림 4. Kbuild 변수 전파 흐름 — 사용자 명령부터 최종 이미지 생성까지

아키텍처별 Makefile 분석

아래 예시는 Linux master 기준 핵심 변수만 발췌하거나, 버전과 설정에 따라 달라지는 부분은 실무적으로 확인할 항목만 요약한 것입니다. 실제 값은 사용하는 커널 버전과 .config에 따라 달라질 수 있습니다.

arch/arm64/Makefile 핵심 변수

# arch/arm64/Makefile — Linux master 기준 핵심 발췌
LDFLAGS_vmlinux  := --no-undefined -X --pic-veneer
KBUILD_CFLAGS    += -mgeneral-regs-only  # FP/SIMD 레지스터(Register) 미사용
KBUILD_CFLAGS    += $(call cc-option,-mabi=lp64)

boot             := arch/arm64/boot
ifeq ($(CONFIG_EFI_ZBOOT),)
KBUILD_IMAGE     := $(boot)/Image.gz
else
KBUILD_IMAGE     := $(boot)/vmlinuz.efi
endif
코드 설명

arch/arm64/Makefile에서 ARM64 크로스 빌드에 영향을 미치는 핵심 변수입니다.

  • LDFLAGS_vmlinuxARM64 전용 vmlinux 링크 옵션입니다. 문서 작성 시점의 상류 커널은 단순 -z norelro만 쓰지 않고 추가 플래그를 함께 사용합니다.
  • -mgeneral-regs-only커널 코드에서 부동소수점/SIMD 레지스터(Register)를 사용하지 않게 만듭니다. 커널은 인터럽트(Interrupt) 컨텍스트에서 FP 레지스터를 보존하지 않으므로 안전을 위해 필요합니다.
  • KBUILD_IMAGE기본 설치 대상 이미지는 CONFIG_EFI_ZBOOT 설정에 따라 달라집니다. 일반적으로는 Image.gz가 기본이지만, arch/arm64/boot/Image 자체도 별도 타겟으로 계속 빌드할 수 있습니다.

arch/arm/Makefile 핵심 변수

# arch/arm/Makefile — ARM 32비트 크로스 빌드 핵심
KBUILD_CFLAGS    += -msoft-float      # 또는 -mfloat-abi=hard (하드웨어 FPU)
KBUILD_CFLAGS    += $(call cc-option,-mno-thumb-interwork)

# CPU 튜닝 (defconfig에 따라 자동 설정)
KBUILD_CFLAGS    += $(call cc-option,-mcpu=cortex-a7)

# 부팅 이미지
KBUILD_IMAGE     := arch/arm/boot/zImage
# zImage: 압축 + 자체 해제 코드 포함
# uImage: U-Boot 헤더 추가 (make uImage)

arch/riscv/Makefile 핵심 변수

# arch/riscv/Makefile — Linux master 기준 핵심 발췌
KBUILD_CFLAGS    += -mabi=lp64
KBUILD_CFLAGS    += -mno-save-restore

boot             := arch/riscv/boot
boot-image-y     := Image
boot-image-$(CONFIG_KERNEL_GZIP) := Image.gz
KBUILD_IMAGE     := $(boot)/$(boot-image-y)

아키텍처별 커널 이미지 형식

커널 이미지 생성 파이프라인 .c / .S 소스 kernel/, mm/, ... CC .o 파일 오브젝트 AR built-in.a 정적 아카이브 LD vmlinux ELF (디버그 심볼) OBJCOPY Image Raw Binary (부팅용) gzip Image.gz / zImage 압축 이미지 아키텍처별 최종 이미지 ARM: zImage 자체 해제 코드 포함 ARM64: Image / Image.gz 로더(Loader)가 압축 해제 RISC-V: Image / Image.gz ARM64와 유사 x86: bzImage setup + 압축 커널
그림 5. 커널 이미지 생성 파이프라인 — 소스부터 부팅 가능한 이미지까지
아키텍처이미지 이름경로형식부트로더(Bootloader)
ARMzImagearch/arm/boot/zImage압축 + 자체 해제 코드U-Boot (bootz)
ARMuImagearch/arm/boot/uImagezImage + U-Boot 헤더U-Boot (bootm)
ARM64Imagearch/arm64/boot/Image비압축 Raw BinaryU-Boot (booti)
ARM64Image.gzarch/arm64/boot/Image.gzgzip 압축U-Boot (booti)
RISC-VImagearch/riscv/boot/Image비압축 Raw BinaryOpenSBI + U-Boot
MIPSvmlinuxvmlinuxELFYAMON, U-Boot
PowerPCzImagearch/powerpc/boot/zImage압축 + 래퍼yaboot, GRUB, U-Boot
x86bzImagearch/x86/boot/bzImagesetup + 압축 커널GRUB, systemd-boot

defconfig 구조와 선택 가이드

아키텍처별 주요 defconfig 목록

아키텍처defconfig대상설명
ARMmulti_v7_defconfigARMv7 SoC 범용Cortex-A7/A9/A15 다중 SoC 지원
ARMbcm2835_defconfigRaspberry Pi 1/ZeroBCM2835 SoC 전용
ARMomap2plus_defconfigTI OMAP / BeagleBoneAM335x, AM57xx 등
ARM64defconfigARM64 범용대부분의 ARM64 플랫폼 지원
RISC-VdefconfigRISC-V 범용64비트 RISC-V 기본 설정
RISC-Vrv32_defconfigRISC-V 32비트32비트 RISC-V 전용
MIPSmalta_defconfigMIPS Malta 보드QEMU MIPS 에뮬레이션 호환
PowerPCpseries_defconfigIBM Power 서버QEMU pSeries 호환
# 특정 아키텍처의 사용 가능한 defconfig 목록 확인
make ARCH=arm64 help | grep defconfig
make ARCH=arm help | grep defconfig | head -20
make ARCH=riscv help | grep defconfig

커스텀 defconfig 생성과 관리

# 1) 기존 defconfig에서 시작
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig

# 2) menuconfig로 커스텀 설정 (필요한 옵션 추가/제거)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

# 3) 커스텀 defconfig 저장
make ARCH=arm64 savedefconfig
cp defconfig arch/arm64/configs/my_board_defconfig

# 4) 이후 사용
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- my_board_defconfig

설정 프래그먼트 활용

# 크로스 빌드 전용 설정 프래그먼트 생성
cat > cross-debug.config <<'EOF'
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF5=y
CONFIG_GDB_SCRIPTS=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
EOF

# defconfig에 프래그먼트 병합
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
scripts/kconfig/merge_config.sh .config cross-debug.config

# 병합 결과 확인 후 빌드
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

아키텍처별 크로스 빌드 가이드

ARM (32-bit) 빌드 상세

# ARM 32비트 크로스 빌드 전체 과정
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# multi_v7_defconfig: 대부분의 ARMv7 SoC 지원
make multi_v7_defconfig
make -j$(nproc)

# 빌드 결과 확인
file arch/arm/boot/zImage
# 출력: Linux kernel ARM boot executable zImage

# DTB 빌드 (Device Tree Blob)
make dtbs
ls arch/arm/boot/dts/*.dtb | head -5

ARM64 (AArch64) 빌드 상세

# ARM64 크로스 빌드 전체 과정
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

make defconfig
make -j$(nproc)

# 빌드 결과 확인
file arch/arm64/boot/Image
# raw kernel image이므로 환경에 따라 단순 data 또는 EFI 관련 형식으로 보일 수 있습니다

# ELF 헤더로 아키텍처 이중 확인
readelf -h vmlinux | grep -E "Machine|Class"
# Machine: AArch64
# Class: ELF64

# DTB 빌드
make dtbs
ls arch/arm64/boot/dts/arm/*.dtb 2>/dev/null
ls arch/arm64/boot/dts/rockchip/*.dtb 2>/dev/null | head -3

RISC-V 빌드 상세

# RISC-V 64비트 크로스 빌드 전체 과정
export ARCH=riscv
export CROSS_COMPILE=riscv64-linux-gnu-

make defconfig
make -j$(nproc)

# 빌드 결과 확인
file arch/riscv/boot/Image
# 출력: RISC-V, ...

readelf -h vmlinux | grep -E "Machine|Class"
# Machine: RISC-V
# Class: ELF64

MIPS 빌드 상세

# MIPS 32비트 크로스 빌드 (Malta 보드 — QEMU 호환)
export ARCH=mips
export CROSS_COMPILE=mips-linux-gnu-

make malta_defconfig
make -j$(nproc)

# MIPS는 vmlinux를 직접 QEMU에 전달
file vmlinux
# 출력: ELF 32-bit MSB executable, MIPS, ...

PowerPC 빌드 상세

# PowerPC 64비트 (리틀 엔디언) 크로스 빌드
export ARCH=powerpc
export CROSS_COMPILE=powerpc64le-linux-gnu-

make pseries_defconfig
make -j$(nproc)

file vmlinux
# 출력: ELF 64-bit LSB executable, 64-bit PowerPC, ...

Device Tree 크로스 컴파일

DTB 빌드 (make dtbs)

# DTB (Device Tree Blob) 빌드
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs

# 특정 DTB만 빌드
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip/rk3399-rock-pi-4a.dtb

# DTB 설치
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_DTBS_PATH=./out/dtbs dtbs_install
Device Tree 크로스 컴파일 흐름 .dts (소스) 보드별 설정 .dtsi (포함파일) SoC 공통 정의 dtc (컴파일러) Device Tree Compiler .dtb (바이너리) 부트로더에 전달 오버레이(Overlay) 흐름 .dts (오버레이) → dtc → .dtbo 기본 .dtb + .dtbo = 최종 DTB 런타임 또는 부트로더에서 병합 fdtoverlay 명령으로 수동 병합 가능
그림 7. Device Tree 크로스 컴파일 흐름 — DTS 소스에서 DTB 바이너리까지

DTB 검증

# DTB 내용 확인 (바이너리 → 텍스트 역변환)
dtc -I dtb -O dts arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4a.dtb | head -30

# DTB 바인딩 검증 (스키마 기반)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dt_binding_check
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs_check

# DTB 파일 크기 확인
ls -lh arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4a.dtb
관련 문서: 디바이스 트리(Device Tree) 문서에서 DTS 문법, 바인딩 규칙, 오버레이(Overlay) 작성법을 자세히 다룹니다.

커널 모듈(Kernel Module) 크로스 컴파일

인트리 모듈 크로스 빌드

# 커널 빌드 시 모듈 함께 빌드
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) modules

# 모듈 설치 (타겟용 디렉토리에)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  INSTALL_MOD_PATH=./out/rootfs modules_install

# 설치 결과 확인
ls ./out/rootfs/lib/modules/$(make kernelrelease)/
# kernel/  modules.dep  modules.alias  ...

아웃오브트리 모듈 크로스 빌드

/* hello.c — 크로스 빌드 테스트 모듈 */
#include <linux/module.h>
#include <linux/init.h>

static int __init hello_init(void)
{
    pr_info("Hello from cross-compiled module!\n");
    return 0;
}

static void __exit hello_exit(void)
{
    pr_info("Goodbye from cross-compiled module!\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cross-compile test module");
# Makefile — 아웃오브트리 모듈 크로스 빌드
obj-m := hello.o

KDIR  ?= /path/to/linux-source
ARCH  ?= arm64
CROSS_COMPILE ?= aarch64-linux-gnu-

all:
	$(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean
코드 설명

아웃오브트리(Out-of-tree) 커널 모듈을 크로스 빌드하는 표준 패턴입니다.

  • obj-m := hello.o빌드할 모듈 오브젝트를 지정합니다. hello.c에서 hello.ko 모듈이 생성됩니다.
  • KDIR크로스 빌드된 커널 소스 디렉토리를 가리킵니다. .config와 빌드 산출물이 있어야 합니다.
  • -C $(KDIR) M=$(PWD)-C로 커널 소스로 이동하고, M=으로 현재 모듈 디렉토리를 지정합니다.
  • ARCH, CROSS_COMPILE커널 빌드와 동일한 값을 전달해야 합니다. 불일치 시 모듈 로드가 실패합니다.
# 모듈 빌드 실행
make KDIR=/path/to/linux-source ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-

# 생성된 모듈 아키텍처 확인
file hello.ko
# 출력: ELF 64-bit LSB relocatable, ARM aarch64, ...

# modinfo로 모듈 정보 확인
modinfo hello.ko

INSTALL_MOD_PATH로 모듈 설치

시나리오명령설치 경로
기본 (호스트에 설치)make modules_install/lib/modules/$(uname -r)/
타겟 rootfs에 설치make INSTALL_MOD_PATH=./rootfs modules_install./rootfs/lib/modules/...
NFS 루트에 설치make INSTALL_MOD_PATH=/srv/nfs/arm64 modules_install/srv/nfs/arm64/lib/modules/...

모듈 버전 불일치 문제와 해결

# 모듈 로드 실패 시 흔한 오류
insmod hello.ko
# insmod: ERROR: could not insert module hello.ko: Invalid module format

# 원인 진단: vermagic 문자열 비교
modinfo hello.ko | grep vermagic
# vermagic: 6.1.0-custom SMP preempt mod_unload aarch64

uname -r
# 6.1.0  (← 커널 버전이 다름!)

# 해결: 동일한 커널 소스·설정으로 모듈 재빌드
# 또는 개발 중에는 CONFIG_MODVERSIONS=n으로 버전 검사 비활성화

루트 파일시스템(Filesystem) 생성

BusyBox 기반 initramfs (최소 구성)

# 1) BusyBox 크로스 빌드
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xf busybox-1.36.1.tar.bz2 && cd busybox-1.36.1

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
# 정적 링크 활성화 (Settings → Build static binary)
sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- CONFIG_PREFIX=../initramfs install

# 2) initramfs 디렉토리 구성
cd ../initramfs
mkdir -p proc sys dev etc tmp

# 3) init 스크립트 생성
cat > init <<'EOF'
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
echo "Boot successful! (cross-compiled ARM64 kernel)"
echo "Minimal initramfs only: SSH server not included"
exec /bin/sh
EOF
chmod +x init

# 4) cpio 아카이브 생성
find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz
코드 설명

QEMU에서 크로스 빌드된 커널을 테스트하기 위한 최소 initramfs 생성 과정입니다.

  • CONFIG_STATIC=yBusyBox를 정적 링크합니다. initramfs에는 공유 라이브러리(Shared Library)가 없으므로 정적 빌드가 필수입니다.
  • CONFIG_PREFIXBusyBox를 설치할 디렉토리를 지정합니다. bin/, sbin/, usr/ 하위에 심볼릭 링크가 생성됩니다.
  • mount -t proc/sysfs/devtmpfs커널 가상 파일시스템(VFS)을 마운트(Mount)합니다. 이것이 없으면 /proc, /dev 접근이 불가합니다.
  • exec /bin/shPID 1을 쉘로 교체합니다. 이것이 init 프로세스(Process)가 됩니다.
  • cpio -o -H newc | gzip커널이 인식하는 newc 형식의 cpio 아카이브를 생성하고 gzip으로 압축합니다.

cpio 아카이브 직접 생성

# gen_init_cpio를 사용한 세밀한 initramfs 구성
cat > initramfs.list <<'EOF'
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
nod /dev/null 0666 0 0 c 1 3
dir /bin 0755 0 0
file /bin/busybox ../busybox/busybox 0755 0 0
slink /bin/sh busybox 0755 0 0
dir /proc 0755 0 0
dir /sys 0755 0 0
file /init ../initramfs/init 0755 0 0
EOF

# gen_init_cpio로 아카이브 생성 (커널 소스에 포함)
usr/gen_init_cpio initramfs.list | gzip > initramfs.cpio.gz

Buildroot로 루트 파일시스템 생성

# Buildroot 다운로드
git clone https://github.com/buildroot/buildroot.git
cd buildroot

# QEMU ARM64 virt 보드 설정
make qemu_aarch64_virt_defconfig

# 빌드 (툴체인 + rootfs 자동 생성)
make -j$(nproc)

# 결과물
ls output/images/
# rootfs.ext4  rootfs.cpio.gz  Image  ...
관련 문서: BusyBox, OpenWrt/Buildroot 문서에서 루트 파일시스템 생성을 자세히 다룹니다.

빌드 산출물 구조와 검증

바이너리 아키텍처 검증

# 1) file 명령 — 빠른 형식 확인
file arch/arm64/boot/Image
# raw kernel image이므로 설정에 따라 단순 data 또는 EFI 관련 형식으로 보일 수 있습니다

file vmlinux
# ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked

# 2) readelf — 상세 ELF 헤더 분석
readelf -h vmlinux
# Class:                             ELF64
# Data:                              2's complement, little endian
# Machine:                           AArch64

# 3) objdump — 디스어셈블 샘플 확인
aarch64-linux-gnu-objdump -d vmlinux | head -30
# ARM64 명령어 확인: adrp, ldr, str, bl, ret 등

# 4) strings — 커널 버전 문자열 확인
strings vmlinux | grep "Linux version"
# Linux version 6.1.0 (user@host) (aarch64-linux-gnu-gcc ...)

# 5) 바이너리 크기 확인
size vmlinux
ls -lh arch/arm64/boot/Image

QEMU 크로스 아키텍처 부팅

ARM64 QEMU 부팅 상세

QEMU 크로스 아키텍처 부팅 구성 호스트 (x86_64) qemu-system-aarch64 -kernel arch/arm64/boot/Image -initrd initramfs.cpio.gz -M virt -cpu cortex-a57 -m 2G 에뮬레이션 가상 머신 (ARM64) vCPU: Cortex-A57 AArch64 명령어 해석·실행 virtio 디바이스 블록, 네트워크, 시리얼 ARM64 Linux 커널 실행 console=ttyAMA0 (시리얼) initramfs → /init 실행 uname -m → aarch64 실 하드웨어 없이 커널 검증 가능
그림 9. QEMU 크로스 아키텍처 부팅 구성 — 호스트에서 타겟 커널을 에뮬레이션
# ARM64 커널 QEMU 부팅 (최소 구성)
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a57 \
  -kernel arch/arm64/boot/Image \
  -append "console=ttyAMA0" \
  -nographic \
  -m 2G

# initramfs 포함 부팅
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a57 \
  -kernel arch/arm64/boot/Image \
  -initrd initramfs.cpio.gz \
  -append "console=ttyAMA0 rdinit=/init" \
  -nographic \
  -m 2G
코드 설명

QEMU ARM64 부팅 옵션의 상세 설명입니다.

  • -M virtQEMU의 가상 머신 타입입니다. virt는 virtio 디바이스 기반의 범용 ARM64 플랫폼으로, 실제 하드웨어를 모방하지 않고 순수 가상화에 최적화되어 있습니다.
  • -cpu cortex-a57에뮬레이션할 CPU 모델입니다. cortex-a57은 ARMv8-A 프로파일의 범용 CPU입니다. max를 지정하면 QEMU가 지원하는 최대 기능을 활성화합니다.
  • -initrdinitramfs(초기 RAM 디스크)를 커널에 전달합니다. 커널은 이것을 루트 파일시스템으로 마운트하고 /init을 실행합니다.
  • rdinit=/initinitramfs의 init 프로세스 경로를 지정합니다. 기본값은 /init이지만 명시적으로 지정하는 것이 안전합니다.
  • -nographic그래픽 창 없이 시리얼 콘솔을 터미널에 직접 연결합니다. 서버 환경이나 CI/CD에서 유용합니다.

ARM QEMU 부팅 상세

# ARM 32비트 QEMU 부팅
qemu-system-arm \
  -M virt \
  -cpu cortex-a15 \
  -kernel arch/arm/boot/zImage \
  -append "console=ttyAMA0" \
  -nographic \
  -m 1G

# DTB 파일 지정이 필요한 경우
qemu-system-arm \
  -M vexpress-a15 \
  -cpu cortex-a15 \
  -kernel arch/arm/boot/zImage \
  -dtb arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dtb \
  -append "console=ttyAMA0" \
  -nographic \
  -m 1G

RISC-V QEMU 부팅 상세

# RISC-V 64비트 QEMU 부팅
qemu-system-riscv64 \
  -M virt \
  -kernel arch/riscv/boot/Image \
  -append "console=ttyS0 earlycon" \
  -nographic \
  -m 2G

# OpenSBI (BIOS) 명시 지정
qemu-system-riscv64 \
  -M virt \
  -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.bin \
  -kernel arch/riscv/boot/Image \
  -initrd initramfs.cpio.gz \
  -append "console=ttyS0 rdinit=/init" \
  -nographic \
  -m 2G

MIPS QEMU 부팅 상세

# MIPS Malta 보드 QEMU 부팅
qemu-system-mips \
  -M malta \
  -kernel vmlinux \
  -append "console=ttyS0" \
  -nographic \
  -m 256M
아키텍처QEMU 바이너리-M 옵션-cpu 옵션콘솔 장치
ARMqemu-system-armvirt / vexpress-a15cortex-a15ttyAMA0
ARM64qemu-system-aarch64virtcortex-a57 / maxttyAMA0
RISC-Vqemu-system-riscv64virt(기본 rv64)ttyS0
MIPSqemu-system-mipsmalta24KfttyS0
PowerPCqemu-system-ppc64pseriesPOWER9hvc0

QEMU 고급 설정

네트워크 설정

# 유저 모드 네트워크 (기본, NAT) — 호스트 포트 포워딩
qemu-system-aarch64 \
  -M virt -cpu cortex-a57 -m 2G -nographic \
  -kernel arch/arm64/boot/Image \
  -initrd initramfs.cpio.gz \
  -append "console=ttyAMA0 rdinit=/init" \
  -netdev user,id=net0,hostfwd=tcp::2222-:22 \
  -device virtio-net-pci,netdev=net0

# 게스트 내부에서 네트워크 확인
# ip addr show   →  eth0에 10.0.2.15 자동 할당
# 이 최소 initramfs에는 SSH 서버가 없으므로, 포트 포워딩만으로는 바로 ssh 접속되지 않습니다

블록 디바이스와 rootfs 마운트

# ext4 rootfs 이미지 생성
dd if=/dev/zero of=rootfs.ext4 bs=1M count=512
mkfs.ext4 rootfs.ext4
sudo mount rootfs.ext4 /mnt
sudo cp -a ./out/rootfs/* /mnt/
sudo umount /mnt

# rootfs 이미지로 QEMU 부팅
qemu-system-aarch64 \
  -M virt -cpu cortex-a57 -m 2G -nographic \
  -kernel arch/arm64/boot/Image \
  -append "console=ttyAMA0 root=/dev/vda rw" \
  -drive file=rootfs.ext4,format=raw,if=virtio

시리얼 콘솔과 로그 캡처

# 부팅 로그를 파일로 캡처
qemu-system-aarch64 \
  -M virt -cpu cortex-a57 -m 2G \
  -kernel arch/arm64/boot/Image \
  -append "console=ttyAMA0 earlycon" \
  -nographic \
  -serial file:boot.log

# 타임아웃 자동 종료 (CI/CD용)
timeout 60 qemu-system-aarch64 \
  -M virt -cpu cortex-a57 -m 2G \
  -kernel arch/arm64/boot/Image \
  -append "console=ttyAMA0" \
  -nographic \
  -no-reboot || true

QEMU 기반 커널 디버깅(Debugging)

QEMU GDB 스텁

GDB 원격 디버깅 아키텍처 호스트 (x86_64) gdb-multiarch vmlinux target remote :1234 TCP :1234 QEMU GDB 스텁 qemu-system-aarch64 -s -S GDB 프로토콜 중개 에뮬레이션 게스트 커널 (ARM64) 중단점, 레지스터 조회 메모리 덤프, 스텝 실행
그림 12. GDB 원격 디버깅 아키텍처 — 호스트 GDB와 QEMU GDB 스텁 연결
# 터미널 1: QEMU 시작 (GDB 대기 모드)
qemu-system-aarch64 \
  -M virt -cpu cortex-a57 -m 2G -nographic \
  -kernel arch/arm64/boot/Image \
  -append "console=ttyAMA0 nokaslr" \
  -s -S
# -s: TCP 포트 1234에서 GDB 연결 대기
# -S: CPU를 중단 상태로 시작 (GDB 연결 후 continue)

# 터미널 2: GDB 연결
gdb-multiarch vmlinux
# (gdb) target remote :1234
# (gdb) break start_kernel
# (gdb) continue
# (gdb) bt
팁: nokaslr 커널 파라미터는 KASLR(Kernel Address Space Layout Randomization)을 비활성화하여 심볼 주소가 vmlinux와 일치하도록 합니다. 디버깅 시 필수입니다.
관련 문서: QEMU 가상화, GDB 커널 디버깅 문서에서 더 상세한 내용을 다룹니다.

실제 하드웨어 배포

SD 카드 배포

# Raspberry Pi 4 (ARM64) 예시
# SD 카드 장치 확인 (예: /dev/sdb)
lsblk

# 부트 파티션에 커널 이미지 복사
sudo mount /dev/sdb1 /mnt/boot
sudo cp arch/arm64/boot/Image /mnt/boot/kernel8.img
sudo cp arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /mnt/boot/

# 루트 파티션에 모듈 설치
sudo mount /dev/sdb2 /mnt/rootfs
sudo make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  INSTALL_MOD_PATH=/mnt/rootfs modules_install

sudo umount /mnt/boot /mnt/rootfs

TFTP 부팅 (U-Boot + tftpd)

# 호스트: TFTP 서버 설정
sudo apt install -y tftpd-hpa
sudo cp arch/arm64/boot/Image /srv/tftp/
sudo cp arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4a.dtb /srv/tftp/

# 타겟 보드 U-Boot 콘솔에서:
# setenv serverip 192.168.1.100
# setenv ipaddr 192.168.1.200
# tftp 0x40080000 Image
# tftp 0x44000000 rk3399-rock-pi-4a.dtb
# booti 0x40080000 - 0x44000000

NFS 루트 파일시스템 부팅

# 호스트: NFS 서버 설정
sudo apt install -y nfs-kernel-server
sudo mkdir -p /srv/nfs/arm64-root

# rootfs와 모듈 복사
sudo cp -a ./out/rootfs/* /srv/nfs/arm64-root/
sudo make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  INSTALL_MOD_PATH=/srv/nfs/arm64-root modules_install

# NFS 내보내기 설정
echo "/srv/nfs/arm64-root 192.168.1.0/24(rw,no_root_squash,no_subtree_check)" | \
  sudo tee -a /etc/exports
sudo exportfs -ra

# 커널 부트 파라미터 (U-Boot 또는 QEMU)
# root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/arm64-root,v3 ip=dhcp

부트로더 연동

U-Boot에서 커널 로드

이미지 형식U-Boot 명령아키텍처설명
Image (raw)bootiARM64, RISC-V비압축 커널 이미지를 로더(Loader)나 펌웨어(Firmware)가 직접 해석해 부팅
zImagebootzARM자체 해제 압축 이미지 부팅
uImagebootmARM, MIPS, PowerPCU-Boot 헤더 포함 이미지
FIT Imagebootm모든 아키텍처커널+DTB+initrd 번들 이미지

FIT 이미지 생성

# FIT (Flattened Image Tree) 이미지 — 커널+DTB+initrd 번들
cat > fit-image.its <<'EOF'
/dts-v1/;

/ {
    description = "ARM64 Kernel + DTB + initramfs";
    images {
        kernel {
            description = "Linux kernel";
            data = /incbin/("arch/arm64/boot/Image.gz");
            type = "kernel";
            arch = "arm64";
            os = "linux";
            compression = "gzip";
            load = <0x40080000>;
            entry = <0x40080000>;
        };
        fdt {
            description = "Device Tree";
            data = /incbin/("arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4a.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
        };
        ramdisk {
            description = "initramfs";
            data = /incbin/("initramfs.cpio.gz");
            type = "ramdisk";
            arch = "arm64";
            os = "linux";
            compression = "none";
        };
    };
    configurations {
        default = "config-1";
        config-1 {
            kernel = "kernel";
            fdt = "fdt";
            ramdisk = "ramdisk";
        };
    };
};
EOF

# FIT 이미지 생성
mkimage -f fit-image.its image.itb

# U-Boot에서 부팅
# tftp 0x40000000 image.itb
# bootm 0x40000000

빌드 성능 최적화

ccache 크로스 빌드 설정

# ccache 설치
sudo apt install -y ccache

# 크로스 빌드에 ccache 적용 (방법 1: CC 오버라이드)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  CC="ccache aarch64-linux-gnu-gcc" -j$(nproc)

# 방법 2: PATH에 ccache 심볼릭 링크 디렉토리 추가
export PATH=/usr/lib/ccache:$PATH
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

# ccache 통계 확인
ccache -s
# cache hit rate: 증분 빌드에서 90%+ 가능

# ccache 캐시 크기 설정
ccache -M 10G

병렬 빌드 전략

# CPU 코어 수 기반 병렬 빌드 (가장 일반적)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

# 메모리 제한이 있는 경우 (코어 수보다 작게 설정)
# 경험칙: 코어당 약 2GB RAM 필요
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4

# 시스템 부하 기반 자동 조절
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) -l$(nproc)

증분 크로스 빌드 팁

# O= 옵션으로 아키텍처별 빌드 디렉토리 분리
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- O=build/arm64 defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- O=build/arm64 -j$(nproc)

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- O=build/riscv defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- O=build/riscv -j$(nproc)

# 소스 트리는 깨끗하게 유지, 빌드 산출물은 O= 하위에 분리
ls build/arm64/arch/arm64/boot/Image
ls build/riscv/arch/riscv/boot/Image

CI/CD 크로스 빌드 통합

GitHub Actions 크로스 빌드 파이프라인

# .github/workflows/cross-build.yml
name: Cross-compile Linux Kernel
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        arch: [arm64, riscv, arm]
        include:
          - arch: arm64
            cross: aarch64-linux-gnu-
            pkg: gcc-aarch64-linux-gnu
            image: arch/arm64/boot/Image
          - arch: riscv
            cross: riscv64-linux-gnu-
            pkg: gcc-riscv64-linux-gnu
            image: arch/riscv/boot/Image
          - arch: arm
            cross: arm-linux-gnueabihf-
            pkg: gcc-arm-linux-gnueabihf
            image: arch/arm/boot/zImage

    steps:
      - uses: actions/checkout@v4
      - name: Install toolchain
        run: sudo apt-get install -y ${{ matrix.pkg }}
      - name: Build kernel
        run: |
          make ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross }} defconfig
          make ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross }} -j$(nproc)
      - name: Verify binary
        run: file ${{ matrix.image }}
코드 설명

GitHub Actions 매트릭스 빌드로 ARM64, RISC-V, ARM 커널을 병렬 크로스 빌드하는 워크플로입니다.

  • strategy.matrix3개 아키텍처에 대해 병렬 빌드 잡을 생성합니다. 각 잡은 독립적인 VM에서 실행됩니다.
  • include아키텍처별 크로스 컴파일러 패키지, 접두사, 이미지 경로를 매핑합니다.
  • file ${{ matrix.image }}빌드 후 생성된 이미지의 아키텍처가 올바른지 검증합니다.

Docker 기반 재현 가능한 크로스 빌드

# Dockerfile.cross-arm64
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
    build-essential bc kmod flex bison libssl-dev \
    gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu \
    libelf-dev ccache
ENV ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
WORKDIR /linux
CMD ["sh", "-lc", "make -j$(nproc)"]

# 빌드 실행
docker build -t cross-arm64 -f Dockerfile.cross-arm64 .
docker run --rm -v $PWD:/linux cross-arm64 \
  sh -c "make defconfig && make -j\$(nproc)"

멀티 아키텍처 동시 빌드

매트릭스 빌드 전략

# 멀티 아키텍처 빌드 스크립트 (build-all.sh)
#!/bin/bash
set -e

ARCHS="arm64:aarch64-linux-gnu- riscv:riscv64-linux-gnu- arm:arm-linux-gnueabihf-"

for entry in $ARCHS; do
    arch=${entry%%:*}
    cross=${entry##*:}
    echo "=== Building $arch ==="
    make ARCH=$arch CROSS_COMPILE=$cross O=build/$arch defconfig
    make ARCH=$arch CROSS_COMPILE=$cross O=build/$arch -j$(nproc)
done

echo "=== All builds complete ==="
for entry in $ARCHS; do
    arch=${entry%%:*}
    echo "$arch:" $(file build/$arch/vmlinux | cut -d: -f2)
done

자주 발생하는 오류와 해결

툴체인 관련 오류

오류 메시지원인해결 방법
aarch64-linux-gnu-gcc: command not found 크로스 컴파일러 미설치 또는 PATH 미설정 sudo apt install gcc-aarch64-linux-gnu
cc1: error: unrecognized command-line option '-mgeneral-regs-only' 호스트 GCC로 빌드 시도 (CROSS_COMPILE 미지정) make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 확인
gcc: error: unrecognized argument in option '-march=...' 오래된 GCC 버전이 ISA 확장을 미지원 GCC 업그레이드 또는 커널 최소 버전 확인

링커 오류

오류 메시지원인해결 방법
undefined reference to '__stack_chk_guard' 스택 보호 심볼 미해결 (libc 미링크) 커널은 CONFIG_STACKPROTECTOR가 자체 구현 — make clean 후 재빌드
ld: incompatible ... when searching for -lgcc 호스트 libgcc와 크로스 libgcc 혼동 CROSS_COMPILE 접미사에 - 포함 확인
error: arch/arm64/... relocation truncated 코드가 주소 범위를 초과 CONFIG_RELOCATABLE 활성화 또는 모듈로 빌드

모듈 로드 실패

오류 메시지원인해결 방법
Invalid module format vermagic 불일치 (커널 버전/설정 차이) 동일 커널 소스·설정으로 모듈 재빌드
disagrees about version of symbol CONFIG_MODVERSIONS CRC 불일치 Module.symvers 파일 일치 확인, 또는 MODVERSIONS=n
Unknown symbol in module 커널에 해당 심볼이 export되지 않음 커널 .config에서 관련 기능 활성화 후 재빌드

QEMU 부팅 실패 원인 분석

증상가능한 원인해결 방법
아무 출력 없이 멈춤 콘솔 장치 불일치 또는 이미지 아키텍처 오류 -append "earlycon" 추가, file Image로 아키텍처 확인
Kernel panic: VFS: unable to mount root fs initrd 미지정 또는 rootfs 부재 -initrd 옵션 추가, 또는 root= 파라미터 확인
Kernel panic: not syncing: Attempted to kill init! init 프로세스가 비정상 종료 initramfs의 /init 스크립트 확인, 정적 링크 여부 확인
부팅 중 "Oops" 출력 커널 버그 또는 설정 문제 CONFIG_DEBUG_INFO=y로 재빌드 후 GDB 디버깅

트러블슈팅 플레이북

환경 진단 스크립트

#!/bin/bash — cross-diag.sh
# 크로스 빌드 환경 진단 스크립트
echo "=== 크로스 빌드 환경 진단 ==="

echo "[1] 환경 변수"
echo "  ARCH=$ARCH"
echo "  CROSS_COMPILE=$CROSS_COMPILE"

echo "[2] 툴체인"
if which ${CROSS_COMPILE}gcc >/dev/null 2>&1; then
    echo "  컴파일러: $(${CROSS_COMPILE}gcc --version | head -1)"
    echo "  타겟: $(${CROSS_COMPILE}gcc -dumpmachine)"
else
    echo "  ERROR: ${CROSS_COMPILE}gcc 미발견!"
fi

echo "[3] 커널 소스"
if [ -f Makefile ]; then
    echo "  버전: $(make kernelversion 2>/dev/null)"
    echo "  .config: $([ -f .config ] && echo '존재' || echo '없음')"
else
    echo "  ERROR: 커널 소스 디렉토리가 아닙니다"
fi

echo "[4] QEMU"
for qemu in qemu-system-aarch64 qemu-system-arm qemu-system-riscv64; do
    if which $qemu >/dev/null 2>&1; then
        echo "  $qemu: $($qemu --version | head -1)"
    fi
done

빌드 로그 분석 기법

# 빌드 로그 캡처
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) 2>&1 | tee build.log

# 오류만 추출
grep -E "error:|Error:|ERROR" build.log

# 경고 통계
grep -c "warning:" build.log

# 실제 컴파일러 호출 확인 (V=1 상세 모드)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- V=1 kernel/fork.o 2>&1 | grep gcc
# aarch64-linux-gnu-gcc가 호출되는지 확인 (호스트 gcc가 아닌지)

크로스 컴파일 검증 체크리스트

크로스 컴파일된 커널이 제대로 빌드되고 동작하는지 체계적으로 검증하는 것이 중요합니다. 아래 체크리스트는 빌드부터 부팅 검증까지 전체 과정을 안내합니다.

크로스 컴파일 검증 체크리스트 1단계: 빌드 전 검증 (arm64 예시) ☐ 툴체인 버전 확인 $ aarch64-linux-gnu-gcc --version (≥ 9.0 권장) ☐ ARCH / CROSS_COMPILE 환경 변수 설정 $ export ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- ☐ make defconfig 실행 2단계: 빌드 검증 (arm64 예시) ☐ 크로스 컴파일 빌드 실행 $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) ☐ 커널 이미지 파일 확인 $ file arch/arm64/boot/Image → raw kernel image 생성 여부 확인 ☐ 빌드 경고 / 오류 없음 확인 3단계: 바이너리 분석 검증 ☐ ELF 헤더 확인 (readelf) $ readelf -h vmlinux → Machine: AArch64, Class: ELF64 ☐ 심볼 테이블 확인 (nm) ☐ 디스어셈블 샘플 확인 $ aarch64-linux-gnu-objdump -d vmlinux | head → ARM64 명령어 (adrp, ldr, str 등) ☐ 커널 바이너리 크기 확인 ☐ 커널 버전 문자열 검사 $ strings vmlinux | grep "Linux version" → ARCH=arm64 포함 여부 확인 ☐ 모듈 빌드 확인 (선택) 4단계: QEMU 부팅 테스트 ☐ qemu-system-aarch64 으로 부팅 실행 $ qemu-system-aarch64 -M virt -cpu cortex-a57 -kernel arch/arm64/boot/Image ☐ 콘솔에서 부팅 메시지 확인 → "Linux version ... SMP ... ARM64 ..." ☐ 커널 패닉 없이 부팅 완료 ☐ init 프로세스 실행 확인 ☐ 기본 명령어 동작 확인 (ls, cat, uname) $ uname -m → aarch64 5단계: 런타임 동작 검증 ☐ /proc/cpuinfo 아키텍처 확인 $ cat /proc/cpuinfo | grep "CPU architecture" → CPU architecture : 8 (AArch64) ☐ /proc/version 커널 정보 확인 ☐ 커널 모듈 로드 테스트 $ insmod hello.ko && rmmod hello → dmesg 로 로드 메시지 확인 ☐ 네트워크 / 블록 디바이스 동작 확인 ☐ 스트레스 테스트 (선택) $ stress-ng --cpu 4 --timeout 60s → 시스템 안정성 검증 ☐ 커널 로그 이상 없음 확인 (dmesg)
그림 15. 크로스 컴파일 검증 체크리스트 — 빌드부터 런타임까지 5단계 검증
아키텍처 ARCH 변수 툴체인 접두사 QEMU 시스템 커널 이미지 경로 콘솔
ARM (32-bit) arm arm-linux-gnueabihf- qemu-system-arm arch/arm/boot/zImage ttyAMA0
ARM64 (64-bit) arm64 aarch64-linux-gnu- qemu-system-aarch64 arch/arm64/boot/Image ttyAMA0
RISC-V (64-bit) riscv riscv64-linux-gnu- qemu-system-riscv64 arch/riscv/boot/Image ttyS0
PowerPC (64-bit) powerpc powerpc64le-linux-gnu- qemu-system-ppc64 arch/powerpc/boot/zImage hvc0
MIPS (32-bit) mips mips-linux-gnu- qemu-system-mips vmlinux ttyS0

참고 자료

공식 커널 문서

툴체인 프로젝트

커뮤니티 리소스