임베디드 스터디 - 커널 빌드 과정
임베디드 스터디 - 커널 빌드 과정
커널 빌드 개요
- 리눅스 커널은 수천 개의 소스 파일로 구성되어 있어 단순한
gcc명령으로 빌드 불가 - 이를 관리하기 위해 Kbuild 시스템을 사용함
- 계층적 Makefile 구조로 각 서브디렉토리를 독립적으로 관리
ARCH,CROSS_COMPILE환경변수로 타겟 플랫폼을 유연하게 전환- Kconfig 시스템으로 포함할 기능을 선택적으로 구성
ARCH · CROSS_COMPILE 환경변수
- 임베디드 커널 빌드는 호스트(x86)에서 타겟(ARM 등)용 바이너리를 만드는 크로스 컴파일 환경
ARCH: 빌드 시 참조할arch/하위 디렉토리를 결정함
1
2
3
ARCH=arm → arch/arm/ 참조 (ARMv7, Cortex-A/R)
ARCH=arm64 → arch/arm64/ 참조 (ARMv8, AArch64)
ARCH=x86 → arch/x86/ 참조
CROSS_COMPILE: 크로스 컴파일러 툴체인의 접두사를 지정함- 접두사 뒤에
gcc,ld,objcopy등을 붙여 툴체인 전체를 자동 조합
- 접두사 뒤에
1
2
3
4
5
6
7
8
9
# ARM64 타겟 커널 빌드 예시
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
# → CC = aarch64-linux-gnu-gcc
# → LD = aarch64-linux-gnu-ld
# → OBJCOPY = aarch64-linux-gnu-objcopy
# ARMv7 타겟 커널 빌드 예시
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
# → CC = arm-linux-gnueabihf-gcc
- Makefile에 컴파일러를 하드코딩하지 않고 환경변수로 분리하여 타겟 전환 시 Makefile 수정 불필요
Kbuild 시스템 — 계층적 Makefile 구조
- 최상위 Makefile이 모든 파일을 직접 관리하면 유지보수가 불가능함
- Kbuild는 각 서브디렉토리에 독립적인 Makefile을 두는 계층 구조로 이를 해결함
- CMake의
add_subdirectory()와 동일한 개념
- CMake의
1
2
3
4
5
6
7
8
9
10
linux/
├── Makefile ← 최상위: ARCH/CROSS_COMPILE 처리, 전체 조율
├── drivers/
│ ├── Makefile ← 하위 디렉토리 나열
│ └── tty/
│ └── serial/
│ └── Makefile ← 빌드 대상 파일 선언
└── arch/
└── arm64/
└── Makefile ← ARM64 전용 빌드 규칙
- 각 하위 Makefile은
obj-y/obj-m으로 빌드 대상만 선언
1
2
3
4
# drivers/tty/serial/Makefile 예시
obj-y += pl011.o # 항상 커널에 포함
obj-m += some_drv.o # 커널 모듈(.ko)로 빌드
obj-$(CONFIG_SERIAL_PL011) += pl011.o # Kconfig 설정에 따라 조건부 포함
Kconfig · menuconfig · defconfig · .config
CONFIG_변수는 Kconfig 시스템에서 생성됨- 각 디렉토리의
Kconfig파일이 설정 항목(드라이버 포함 여부 등)을 정의하고, 개발자가 선택한 결과가.config에 저장됨
| 명령/파일 | 역할 |
|---|---|
Kconfig | 각 디렉토리의 설정 항목 정의 파일 |
make menuconfig | Kconfig를 읽어 TUI로 Y/N/M 선택 |
make defconfig | 특정 보드용 기본 설정 로드 (예: make bcm2711_defconfig) |
.config | 선택 결과 저장 파일 — 빌드 시 참조 |
1
2
3
4
# .config 내용 예시
CONFIG_SERIAL_PL011=y # 커널에 정적 포함 → obj-y
CONFIG_USB_SERIAL=m # 모듈로 빌드 → obj-m
# CONFIG_INFINIBAND is not set # 제외
obj-$(CONFIG_SERIAL_PL011)은.config값이 대입되어obj-y또는obj-m이 됨
1
2
3
4
5
6
7
Kconfig 정의
↓
make menuconfig / defconfig ← 개발자 선택
↓
.config 생성
↓
Kbuild가 .config 참조 → obj-y / obj-m 결정 → 빌드
빌드 결과물 — vmlinux · zImage · uImage
- 커널 빌드 결과물은 사용 목적에 따라 세 가지 형태로 변환됨
1
2
3
4
5
6
7
vmlinux
↓ ELF 헤더 제거 + objcopy
Image (순수 바이너리)
↓ gzip 압축 + 자가 압축 해제 코드 추가
zImage
↓ mkimage로 U-Boot 헤더(64바이트) 추가
uImage
| 결과물 | 형식 | 특징 | 주요 용도 |
|---|---|---|---|
vmlinux | ELF | 심볼·디버그 정보 포함, 미압축 | 커널 디버깅 (gdb vmlinux) |
zImage | 압축 바이너리 | gzip 압축, 자가 압축 해제 코드 내장 | ARM 임베디드 표준 부팅 |
uImage | U-Boot 래핑 | 64바이트 헤더(CRC·로드주소·진입점) 추가 | U-Boot bootm 명령으로 부팅 |
vmlinux의vm은 virtual memory의 약자
전체 빌드 흐름
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. make bcm2711_defconfig ← 타겟 보드 기본 설정 로드
↓
2. make menuconfig ← 필요 시 추가 설정 조정
↓
3. .config 생성
↓
4. make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
↓
Kbuild가 계층적 Makefile 순회
.config 참조 → obj-y/obj-m 결정
aarch64-linux-gnu-gcc 로 각 .o 컴파일
↓
5. 링크 → vmlinux (ELF)
↓
6. objcopy → Image → zImage / mkimage → uImage
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.