Clang/LLVM로 Linux 커널 빌드 하기

이 글은 Ubuntu 환경에서 Clang/LLVM 툴체인을 이용해 Linux 커널을 실제로 빌드하는 방법을 단계별로 정리한다.

Clang/LLVM 툴체인 설치는 아래 글을 먼저 확인한다.

Ubuntu 커널 빌드에 대한 건 아래 글을 참고한다.

전체 빌드 흐름 한눈에 보기

커널 소스
  |
.config 생성 (defconfig / menuconfig)
  |
make LLVM=1
  |
clang -> LLVM IR -> ld.lld
  |
vmlinux / Image / modules

핵심은 LLVM=1 하나로 컴파일러, 링커, binutils 계열을 모두 LLVM 쪽으로 전환한다는 점이다.

커널 소스 준비

소스 받기

준비된 커널 소스가 없다면 아래처럼 받는다.

git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux

실무에서는 다음 중 하나를 써도 무방하다.

  • LTS 커널
  • 배포판 커널 소스
  • SoC 벤더 커널 트리

빌드 방법 자체는 동일하다.

Out-of-tree 빌드 디렉터리 구성

커널 빌드는 산출물이 많기 때문에 O= 옵션을 쓰는 것이 정석이다.

mkdir -p out

이후 모든 make 명령에 O=out을 붙인다.

x86_64 (native) Clang/LLVM 커널 빌드

기본 config 생성

make O=out defconfig

이 단계에서 .config가 생성된다. 필자의 경우는 기존에 빌드해 둔 소스가 있어서 mrproper 후 진행했다.

$ make mrproper
$ make O=out defconfig

Clang/LLVM 빌드 실행

make O=out LLVM=1 -j$(nproc)

이 한 줄이 의미하는 바는 다음과 같다.

항목의미
LLVM=1clang + ld.lld + llvm-binutils 사용
-j$(nproc)병렬 빌드

중간에 LTO, CFI 등 config 선택이 나오면 여기서는 Enter로 넘어간다.

컴파일하다 보면 GCC로 빌드할 때와 달리 아래처럼 error가 발생하는 경우가 있다. Clang이 기존 GCC에서는 warning으로 넘어가던 것들을 더 엄격한 기준으로 error 처리하기 때문이다.

../net/netfilter/nf_conntrack_netlink.c:677:41: error: variable 'len' is uninitialized when used here [-Werror,-Wuninitialized]
  677 |                + nla_total_size(sizeof(char) * len); /* CTA_SECCTX_NAME */
      |                                                ^~~
../net/netfilter/nf_conntrack_netlink.c:670:9: note: initialize the variable 'len' to silence this warning
  670 |         int len, ret;
      |                ^
      |                 = 0
1 error generated.
make[5]: *** [../scripts/Makefile.build:243: net/netfilter/nf_conntrack_netlink.o] Error 1
make[4]: *** [../scripts/Makefile.build:481: net/netfilter] Error 2
make[4]: *** Waiting for unfinished jobs....
...

이 에러는 len이 초기화 없이 사용된 경우로, 컴파일러가 len = 0으로 초기화할 것을 제안하고 있다. 이런 식으로 발생하는 error를 하나씩 확인하고 수정해서 컴파일이 끝까지 완료되도록 한다.

arm64 크로스 컴파일 (Clang/LLVM)

arm64에서는 ARCH 지정이 필수다.

arm64 defconfig 설정

make O=out ARCH=arm64 defconfig

Clang + GNU cross 혼합 빌드 (가장 흔한 패턴)

make O=out ARCH=arm64 LLVM=1 \
  CROSS_COMPILE=aarch64-linux-gnu- \
  -j$(nproc)

이 조합의 의미는 다음과 같다.

  • 컴파일러/링커: Clang + LLD
  • 일부 도구/asm fallback: GNU binutils

실무에서 가장 안정적인 조합이다.

Integrated Assembler 문제 발생 시

특정 .S 파일에서 에러가 나면 통합 어셈블러를 끈다.

make O=out ARCH=arm64 LLVM=1 LLVM_IAS=0 \
  CROSS_COMPILE=aarch64-linux-gnu- \
  -j$(nproc)

빌드 산출물 확인

주요 결과물

파일의미
vmlinuxELF 커널 이미지
arch/x86/boot/bzImagex86 부트 이미지
arch/arm64/boot/Imagearm64 커널 이미지
modules/커널 모듈

빌드 성공 여부 빠른 체크

file out/vmlinux

아래처럼 “ELF 64-bit LSB pie executable, ARM aarch64” 정도가 확인되면 정상적으로 빌드됐다고 볼 수 있다.

$ file out/vmlinux
out/vmlinux: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), statically linked, BuildID[sha1]=b21eee087608e453d874e6e8ce457056496c39ac, with debug_info, not stripped

GCC 빌드와의 차이 요약

항목GCCClang/LLVM
빌드 플래그기본LLVM=1
링커ld.bfdld.lld
BTF 연계보통매우 좋음
분석 도구제한적sanitizer, analyzer 풍부

참고 사이트

답글 남기기