라즈베리파이 리눅스 커널 컴파일 명령어 출처
https://wikidocs.net/3243
https://www.raspberrypi.org/documentation/linux/kernel/building.md
소스 코드 다운로드 및 빌드 스크립트 작성
아래와 같이 폴더를 하나 생성합니다.
/home001/austindh.kim/src/raspberry_kernel
소스 코드는 아래 명령어로 다운로드 받습니다.
git clone --depth=1 https://github.com/raspberrypi/linux
소스 코드를 다 받으면 linux란 폴더가 생깁니다.
austindh.kim~/src/raspberry_kernel$ ls -l
total 4
drwxr-xr-x 25 austindh.kim home001 4096 Feb 12 08:35 linux
크로스컴파일 환경 설정
아래 명령어로 라즈베리 파이 크로스컴파일 툴 체인을 설치합니다. 제가 주로 자주 쓰는 프로그램은 ~/bin/ 폴더에 관리하므로 ~/bin/raspberry_tools에 설치합니다.
git clone https://github.com/raspberrypi/tools ~/bin/raspberry_tools
이제 리눅스 빌드 서버에서 해당 크로스 컴파일 프로그램이 실행되도록 설정을 해줘야 합니다.
유저 루트 디렉토리로 이동해서 .bashrc를 수정하면 컴파일 하기 전에 귀찮게 PATH 설정할 필요가 없습니다.
.bashrc 파일을 열고 가장 하단에 아래 코드 두 줄을 추가합니다.
austindh.kim@~$ vi .bashrc
RASPBERRY_CROSS_TOOLS=~/bin/raspberry_tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
PATH=$PATH:$RASPBERRY_CROSS_TOOLS
리눅스 커널 코드를 밥 먹듯이 수정할텐데, 일일히 커널 빌드 명령어 치기 참 귀찮죠.
그래서 아래와 같은 스크립트를 작성해서 build_kernel.sh 파일을 linux 폴더에 저장합니다.
물론 라즈베리 리눅스 커널 컴파일 명령어는 https://wikidocs.net/3243 을 참고했습니다.
#!/bin/sh
# This file is desinged to build Raspberry pi Linux Kernel
#
OUTPUT="/home001/austindh.kim/src/raspberry_kernel/out"
KERNEL=kernel7
#make config
make ARCH=arm O=$OUTPUT CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
#compile
make ARCH=arm O=$OUTPUT CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs -j3
컴파일
이미 작성한 빌드 스크립트만 실행하면 바로 커널 빌드를 시작합니다.
austindh.kim@~/src/raspberry_kernel/linux$ ./build_kernel.sh
make[1]: Entering directory `/home001/austindh.kim/src/raspberry_kernel/out'
GEN ./Makefile
#
# configuration written to .config
#
make[1]: Leaving directory `/home001/austindh.kim/src/raspberry_kernel/out'
make[1]: Entering directory `/home001/austindh.kim/src/raspberry_kernel/out'
GEN ./Makefile
scripts/kconfig/conf --silentoldconfig Kconfig
make[1]: Leaving directory `/home001/austindh.kim/src/raspberry_kernel/out'
make[1]: Entering directory `/home001/austindh.kim/src/raspberry_kernel/out'
CHK include/config/kernel.release
GEN ./Makefile
CHK include/generated/uapi/linux/version.h
Using /home001/austindh.kim/src/raspberry_kernel/linux as source for kernel
CHK include/generated/utsrelease.h
CHK include/generated/timeconst.h
CHK include/generated/bounds.h
CHK include/generated/asm-offsets.h
CALL /home001/austindh.kim/src/raspberry_kernel/linux/scripts/checksyscalls.sh
CHK include/generated/compile.h
CC arch/arm/mm/dma-mapping.o
CC arch/arm/common/firmware.o
.. 생략 ..
H16TOFW firmware/edgeport/boot2.fw
H16TOFW firmware/edgeport/down.fw
H16TOFW firmware/edgeport/down2.fw
IHEX2FW firmware/whiteheat_loader.fw
IHEX2FW firmware/keyspan_pda/keyspan_pda.fw
IHEX2FW firmware/whiteheat.fw
IHEX2FW firmware/keyspan_pda/xircom_pgs.fw
make[1]: Leaving directory `/home001/austindh.kim/src/raspberry_kernel/out'
austindh.kim@LGEARND7B16:~/src/raspberry_kernel/linux$
아래 폴더에 가면 제대로 컴파일이 됐음을 확인할 수 있습니다.
austindh.kim@~/src/raspberry_kernel/out$ ls
arch crypto fs ipc Makefile modules.order scripts source virt
block drivers include kernel mm Module.symvers security System.map vmlinux
certs firmware init lib modules.builtin net sound usr vmlinux.o
전처리 파일 추출
리눅스 커널 소스를 좀 더 상세히 분석하려면 전처리 파일이 필요합니다. 아래 패치를 적용합니다.
austindh.kim@~/src/raspberry_kernel/linux$ git diff
diff --git a/Makefile b/Makefile
index 9550b69..b9e53f3 100644
--- a/Makefile
+++ b/Makefile
@@ -395,6 +395,7 @@ KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
-Wno-format-security \
+ -save-temps=obj \
-std=gnu89
KBUILD_CPPFLAGS := -D__KERNEL__
KBUILD_AFLAGS_KERNEL :=
그럼 제대로 전처리 파일이 추출됐는지 확인해볼까요.
아래 work_on_cpu 함수의 INIT_WORK_ONSTACK 매크로를 전처리 파일에서 확인하겠습니다.
[kernel/workqueue.c]
long work_on_cpu(int cpu, long (*fn)(void *), void *arg)
{
struct work_for_cpu wfc = { .fn = fn, .arg = arg };
INIT_WORK_ONSTACK(&wfc.work, work_for_cpu_fn);
schedule_work_on(cpu, &wfc.work);
flush_work(&wfc.work);
destroy_work_on_stack(&wfc.work);
return wfc.ret;
}
workqueue.c 에 대한 전처리 파일을 out 파일에서 찾아보니 아래와 같이 보이네요.
austindh.kim@~/src/raspberry_kernel/out$ find . -name *workqueue*.i
./kernel/.tmp_workqueue.i
전처리 파일에서 work_on_cpu 함수는 아래와 같습니다.
long work_on_cpu(int cpu, long (*fn)(void *), void *arg)
{
struct work_for_cpu wfc = { .fn = fn, .arg = arg };
do { __init_work(((&wfc.work)), 1); ((&wfc.work))->data = (atomic_long_t) { (WORK_STRUCT_NO_POOL) }; INIT_LIST_HEAD(&((&wfc.work)
schedule_work_on(cpu, &wfc.work);
flush_work(&wfc.work);
destroy_work_on_stack(&wfc.work);
return wfc.ret;
}
INIT_WORK_ONSTACK 매크로의 실제 구현부를 확인할 수 있어 좋습니다.
INIT_WORK_ONSTACK(&wfc.work, work_for_cpu_fn);
->
do { __init_work(((&wfc.work)), 1); ((&wfc.work))->data = (atomic_long_t) { (WORK_STRUCT_NO_POOL) }; INIT_LIST_HEAD(&((&wfc.work)
vmlinux 소스 코드 정보 추가
리눅스 커널 코드를 빌드하면 vmlinux란 파일이 추출됩니다.
바이너리 유틸리티를 활용하면 vmlinux에서 여러 디버깅 정보 확인을 할 수 있는데요.
아래 컨피그를 추가하면 vmlinux 심볼 테이블에 소스 코드 정보가 추가됩니다.
diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig
index 96fbcc6..961829b 100644
--- a/arch/arm/configs/bcm2709_defconfig
+++ b/arch/arm/configs/bcm2709_defconfig
@@ -31,6 +31,7 @@ CONFIG_KPROBES=y
CONFIG_JUMP_LABEL=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
+CONFIG_DEBUG_INFO=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_BLK_DEV_THROTTLING=y
위와 같이 컨피그 코드 수정 후 build_kernel.sh 파일을 실행시켜 생긴 .config 파일을 열어보면 CONFIG_DEBUG_INFO=y 컨피그가 실제 켜져 있네요.
austindh.kim@~/src/raspberry_kernel/out$ vi .config
5459 # Compile-time checks and compiler options
5460 #
5461 CONFIG_DEBUG_INFO=y
5462 # CONFIG_DEBUG_INFO_REDUCED is not set
그런데 컴파일 도중 아래와 같은 에러 메세지와 함께 빌드가 중지됩니다.
lib/raid6/neon8.c: In function ‘raid6_neon8_gen_syndrome_real’:
lib/raid6/neon8.c:158:1: internal compiler error: in dwarf2out_frame_debug_adjust_cfa, at dwarf2cfi.c:1078
}
^
Please submit a full bug report,
with preprocessed source if appropriate.
See <https://bugs.launchpad.net/gcc-linaro> for instructions.
make[3]: *** [lib/raid6/neon8.o] Error 1
make[3]: *** Waiting for unfinished jobs....
lib/raid6/neon4.c: In function ‘raid6_neon4_gen_syndrome_real’:
lib/raid6/neon4.c:114:1: internal compiler error: in dwarf2out_frame_debug_adjust_cfa, at dwarf2cfi.c:1078
}
^
소스 정보가 포함된 vmlinux 파일을 얻는 게 목적이므로 아래 패치를 적용하고 다시 빌드합니다.
diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile
index 3057011..69f563e 100644
--- a/lib/raid6/Makefile
+++ b/lib/raid6/Makefile
@@ -5,7 +5,7 @@ raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \
raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o avx512.o recov_avx512.o
raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o
-raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o
+raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o
raid6_pq-$(CONFIG_TILEGX) += tilegx8.o
raid6_pq-$(CONFIG_S390) += s390vx8.o recov_s390xc.o
위 패치를 적용하고 난 후 out 폴더에 vmlinux가 제대로 생성됩니다.
바이너리 유틸리티 사용
raspberry_tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-objdump 폴더에 위치한 arm-linux-gnueabihf-objdump 파일을 out 폴더에 복사합니다.
austin.kim@~/src/raspberry_kernel/out$ cp ~/bin/raspberry_tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-objdump .
austin.kim@LGEARND7B16:~/src/raspberry_kernel/out$
아래 명령어를 치면 arm-linux-gnueabihf-objdump 바이너리 유틸리티 옵션을 확인할 수 있습니다.
austindh.kim@~/src/raspberry_kernel/out$ ./arm-linux-gnueabihf-objdump
Usage: ./arm-linux-gnueabihf-objdump <option(s)> <file(s)>
Display information from object <file(s)>.
At least one of the following switches must be given:
-a, --archive-headers Display archive header information
-f, --file-headers Display the contents of the overall file header
-p, --private-headers Display object format specific file header contents
-P, --private=OPT,OPT... Display object format specific contents
-h, --[section-]headers Display the contents of the section headers
-x, --all-headers Display the contents of all headers
-d, --disassemble Display assembler contents of executable sections
-D, --disassemble-all Display assembler contents of all sections
-S, --source Intermix source code with disassembly
-s, --full-contents Display the full contents of all sections requested
-g, --debugging Display debug information in object file
-e, --debugging-tags Display debug information using ctags style
-G, --stabs Display (in raw form) any STABS info in the file
-W[lLiaprmfFsoRt] or
--dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
=frames-interp,=str,=loc,=Ranges,=pubtypes,
=gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
=addr,=cu_index]
Display DWARF info in the file
-t, --syms Display the contents of the symbol table(s)
-T, --dynamic-syms Display the contents of the dynamic symbol table
-r, --reloc Display the relocation entries in the file
-R, --dynamic-reloc Display the dynamic relocation entries in the file
@<file> Read options from <file>
-v, --version Display this program's version number
-i, --info List object formats and architectures supported
-H, --help Display this information
아래 리다이렉트 명령어로 결과 파일을 dump_kernel_code.c에 저장합니다.
austindh.kim@~/src/raspberry_kernel/out$ ./arm-linux-gnueabihf-objdump -d -S vmlinux > dump_kernel_code.c
이제 dump_kernel_code.c 파일을 열어서 start_kernel 코드를 검색하면 어셈블리와 소스코드를 함께 볼 수 있습니다.
80b00970 <start_kernel>:
ioremap_huge_init();
kaiser_init();
}
asmlinkage __visible void __init start_kernel(void)
{
80b00970: e1a0c00d mov ip, sp
80b00974: e92ddff0 .word 0xe92ddff0
80b00978: e24cb004 sub fp, ip, #4
80b0097c: e24dd01c sub sp, sp, #28
char *command_line;
char *after_dashes;
set_task_stack_end_magic(&init_task);
80b00980: e59f0344 .word 0xe59f0344
80b00984: ebd8655d bl 80119f00 <set_task_stack_end_magic>
smp_setup_processor_id();
80b00988: eb000c3b .word 0xeb000c3b
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();
cgroup_init_early();
"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.
그리고 혹시 궁금점이 있으면 댓글로 질문 남겨주세요. 상세한 답글 올려드리겠습니다!"
# Reference (리눅스 커널 매크로 분석)
#Reference 시스템 콜
Reference(프로세스)
Reference(워크큐)
워크큐(Workqueue) Overview
# Reference: For more information on 'Linux Kernel';
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2

최근 덧글