Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

1148
469
422441


[라즈베리파이] 커널 빌드 & 컴파일 환경 설정 5. 인터럽트

라즈베리파이 리눅스 커널 컴파일 명령어 출처
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(워크큐)
워크큐(Workqueue) Overview


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2




덧글

댓글 입력 영역