Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

16312
2027
233849


[라즈베리파이] 라즈비안(Raspbian) 리눅스 커널(LinuxKernel) 전처리 파일 생성하기 2. 라즈베리 파이 설정

이번 절에서는 리눅스 커널을 빌드하는 과정에서 전처리 코드를 생성하는 방법을 소개합니다.

리눅스 커널 소스코드를 분석하다 보면 수많은 매크로를 만납니다. 리눅스 커널을 캡슐화나 다형성과 같은 객체지향 방식으로 구현하다 보니 매크로로 구현된 코드가 많습니다. 그런데 이 매크로가 소스 분석의 걸림돌 중 하나로 작용합니다. 

전처리 코드는 이러한 매크로를 모두 풀어서 표현합니다. 따라서 훨씬 편하게 소스코드를 분석할 수 있으며, 리눅스 커널 코드를 분석할 때는 전처리 코드를 함께 보시기를 바랍니다.

전처리 코드는 GCC 컴파일 오브젝트를 생성하는 과정에서 추출됩니다. 커널에서 전처리 코드를 추출하는 방법은 크게 두 가지가 있습니다. 

전체 전처리 파일을 추출
특정 전처리 파일을 추출

전체 전처리 파일을 추출하는 방법

먼저 리눅스 커널의 전체 소스코드를 전처리 파일로 추출하는 방법은 다음과 같습니다.

01 diff --git a/Makefile b/Makefile
02 index 3da5790..0414cb2 100644
03  --- a/Makefile
04 +++ b/Makefile
05 @@ -419,6 +419,7 @@ KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
06            -fno-strict-aliasing -fno-common -fshort-wchar \
07            -Werror-implicit-function-declaration \
08            -Wno-format-security \
09 +          -save-temps=obj \
10            -std=gnu89
11  KBUILD_CPPFLAGS := -D__KERNEL__
12  KBUILD_AFLAGS_KERNEL :=

위 패치 코드에서 어떤 옵션이 전처리 코드를 추출하는 역할을 수행할까요? 바로 다음 09번째 줄의 '-save-temps=obj \' 구문입니다. 

09 +          -save-temps=obj \

패치 코드를 입력하는 방식의 이해를 돕기 위해 패치 코드를 반영하기 전 코드를 살펴보겠습니다.

Makefile
408 # Use LINUXINCLUDE when you must reference the include/ directory.
409 # Needed to be compatible with the O= option
410 LINUXINCLUDE    := \
411         -I$(srctree)/arch/$(hdr-arch)/include \
412         -I$(objtree)/arch/$(hdr-arch)/include/generated \
413         $(if $(KBUILD_SRC), -I$(srctree)/include) \
414         -I$(objtree)/include \
415         $(USERINCLUDE)
416
417 KBUILD_AFLAGS   := -D__ASSEMBLY__
418 KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
419            -fno-strict-aliasing -fno-common -fshort-wchar \
420            -Werror-implicit-function-declaration \
421            -Wno-format-security \
422            -std=gnu89
423 KBUILD_CPPFLAGS := -D__KERNEL__
424 KBUILD_AFLAGS_KERNEL :=
425 KBUILD_CFLAGS_KERNEL :=
426 KBUILD_AFLAGS_MODULE  := -DMODULE

위 Makefile 파일의 421번째 줄 다음에 다음 코드를 입력하면 됩니다.

-save-temps=obj \

-save-temps=obj \를 입력한 후의 Makefile은 다음과 같습니다. 

417 KBUILD_AFLAGS   := -D__ASSEMBLY__
418 KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
419            -fno-strict-aliasing -fno-common -fshort-wchar \
420            -Werror-implicit-function-declaration \
421            -Wno-format-security \
422            -save-temps=obj \
423            -std=gnu89
424 KBUILD_CPPFLAGS := -D__KERNEL__
425 KBUILD_AFLAGS_KERNEL :=
426 KBUILD_CFLAGS_KERNEL :=
427 KBUILD_AFLAGS_MODULE  := -DMODULE
 
이 같은 방식으로 Makefile 을 수정한 다음, 이전 절에 소개한 build_rpi_kernel.sh 커널 빌드 스크립트를 실행합시다. 그럼 전처리 코드가 out 폴더에 생성됩니다. 전처리 코드가 어떻게 생성됐는지 확인해 보겠습니다.

먼저 커널 스케줄링과 관련된 공통 코드가 담긴 kernel/sched/core.c 파일에 대한 전처리 코드를 찾아보겠습니다.

root@raspberrypi:/home/pi/rpi_kernel_src/out/kernel/sched# ls -al
total 33692
...
-rw-r--r--  1 root home  613852 Mar 19 09:35 .tmp_completion.i
-rw-r--r--  1 root home 20953 Mar 19 09:35 .tmp_completion.s
-rw-r--r--  1 root home 2800883 Mar 19 09:57 .tmp_core.i
-rw-r--r--  1 root home 368699 Mar 19 09:57 .tmp_core.s
-rw-r--r--  1 root home 1262723 Mar 19 09:35 .tmp_cpuacct.i
-rw-r--r--  1 root home 17772 Mar 19 09:35 .tmp_cpuacct.s

C 언어로 작성된 리눅스 커널 소스 파일은 다음 위치에서 전처리 파일로 생성됩니다. 

linux/kernel/sched/core.c
out/kernel/sched/.tmp_core.i

소스 파일명 앞에 ".tmp_"란 접두사와 맨 끝에 “i”가 붙습니다.

그럼 다른 리눅스 커널 소스코드는 어떻게 전처리 파일로 생성될까요? linux/init 폴더에 있는 아래 파일을 예로 들겠습니다.

calibrate.c  do_mounts.c do_mounts_initrd.c

위 파일들은 전처리 과정에서 out/init 폴더에 다음과 같은 이름으로 생성됩니다.

.tmp_calibrate.i  .tmp_do_mounts.i  .tmp_do_mounts_initrd.i

root@raspberrypi:/home/pi/rpi_kernel_src/out/init# ls -al
total 12912
-rw-r--r--  1 312538 Mar 19 09:34 .tmp_calibrate.i
-rw-r--r--  1 10928 Mar 19 09:34 .tmp_calibrate.s
-rw-r--r--  1 2830878 Mar 19 09:34 .tmp_do_mounts.i
-rw-r--r--  1 1616189 Mar 19 09:34 .tmp_do_mounts_initrd.i
-rw-r--r--  1 7311 Mar 19 09:34 .tmp_do_mounts_initrd.s
-rw-r--r--  1 1621237 Mar 19 09:34 .tmp_do_mounts_rd.i
-rw-r--r--  1 14425 Mar 19 09:34 .tmp_do_mounts_rd.s

위와 같이 커널 Makefile을 수정해 모든 리눅스 커널 소스 파일을 전처리 코드가 담긴 *.i 파일로 변환할 수 있습니다. 문제는 "*.i"뿐만 아니라 "*.s" 파일로 생성돼 용량이 5GB까지 늘어난다는 점입니다. 

특정 처리 파일을 추출하는 방법

대부분 특정 커널 소스 파일의 전처리 파일을 열어 보고 싶을 때가 많을 것입니다. 이번에는 특정 소스 파일만 전처리 파일로 생성하는 방법을 알아보겠습니다. 먼저 다음과 같은 셸 스크립트 코드를 실행해야 합니다.

build_preprocess_RPi_kernel.sh
01 #!/bin/bash
02
03 echo "configure build output path"
04 
05 KERNEL_TOP_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
06 OUTPUT="$KERNEL_TOP_PATH/out"
07 echo "$OUTPUT"
08 
09 KERNEL=kernel7
10 BUILD_LOG="$KERNEL_TOP_PATH/rpi_preproccess_build_log.txt"
11 
12 PREPROCESS_FILE=$1
13 echo "build preprocessed file: $PREPROCESS_FILE"
14
15 echo "move kernel source"
16 cd linux
17
18 echo "make defconfig"
19 make O=$OUTPUT bcm2709_defconfig
20
21 echo "kernel build"
22 make $PREPROCESS_FILE O=$OUTPUT zImage modules dtbs -j4 2>&1 | tee $BUILD_LOG

위와 같은 코드를 입력한 후 build_preprocess_rpi_kernel.sh라는 이름으로 저장합니다. 파일을 저장한 후 다음 명령어로 실행 권한을 줍니다.

root@raspberrypi:/home/pi/rpi_kernel_src# chmod +x build_preprocess_RPi_kernel.sh

앞에서 소개한 build_rpi_kernel.sh 빌드 스크립트에 세 줄 정도를 추가했는데, 아래의 12~13번째 줄은 셸 스크립트를 실행할 때 전달하는 소스코드의 이름입니다.

12 PREPROCESS_FILE=$1
13 echo "build preprocessed file: $PREPROCESS_FILE"

다음으로 22번째 줄의 코드를 보겠습니다.

22 make $PREPROCESS_FILE O=$OUTPUT zImage modules dtbs -j4 2>&1 | tee $BUILD_LOG

커널 코드를 빌드하는 명령어에 "$PREPROCESS_FILE"이라는 구문이 추가됐습니다. 이는 지정한 파일만 전처리 파일로 추출하라는 뜻입니다.

이번에는 build_preprocess_rpi_kernel.sh 셸 스크립트를 실행하는 방법을 알아보겠습니다. build_preprocess_rpi_kernel.sh 셸 스크립트를 실행할 때는 디렉터리를 포함한 파일 이름을 지정해야 합니다. 

build_preprocess_rpi_kernel.sh [파일이름.i]

예를 들어, linux/sched/core.c 파일을 전처리 코드로 추출하려면 다음 형식으로 셸 스크립트를 실행하면 됩니다.

build_preprocess_rpi_kernel.sh kernel/sched/core.i

이번에는 라즈베리 파이에서 다음 명령어로 실행해 봅시다.

root@raspberrypi:/home/pi/Rpi_kernel_src# build_preprocess_rpi_kernel.sh kernel/sched/core.i
configure build output path
build preprocessed file: kernel/sched/core.i
make[1]: Entering directory ' root@raspberrypi:/home/pi/rpi_kernel_src/out '
  GEN     ./Makefile
#
# configuration written to .config
#
make[1]: Leaving directory '/home/pi/rpi_kernel_src/out'
make[1]: Entering directory '/home/pi/rpi_kernel_src/out'
  GEN     ./Makefile
scripts/kconfig/conf  --silentoldconfig Kconfig
  CHK     include/config/kernel.release
  GEN     ./Makefile
  CHK     include/generated/uapi/linux/version.h
  Using  /home/pi/Rpi_kernel_src/linux as source for kernel
  CHK     include/generated/utsrelease.h
  CHK     scripts/mod/devicetable-offsets.h
  CHK     include/generated/timeconst.h
  CHK     include/generated/bounds.h
  CHK     include/generated/asm-offsets.h
  CALL    /home/pi/rpi_kernel_src/linux/scripts/checksyscalls.sh
  CHK     include/generated/compile.h
  CPP     kernel/sched/core.i
  GZIP    kernel/config_data.gz
  CHK     kernel/config_data.h
  Kernel: arch/arm/boot/Image is ready
  Building modules, stage 2.
  Kernel: arch/arm/boot/zImage is ready
  MODPOST 1506 modules
make[1]: Leaving directory '/home/pi/rpi_kernel_src/out'

전처리 파일을 문제 없이 생성하면 위와 같은 빌드 메시지를 볼 수 있습니다. out/kernel/sched 디렉터리로 가면 core.i 파일만 전처리 코드로 생성된 것을 확인할 수 있습니다. 

root@raspberrypi:/home/pi/rpi_kernel_src/out/kernel/sched# ls
autogroup.o  clock.o       core.i  cpuacct.o      cpufreq.o            cpupri.o

하지만 소스코드 디렉터리를 잘못 지정하면 다음과 같은 에러 메시지와 함께 빌드가 중단됩니다.

root@raspberrypi:/home/pi/rpi_kernel_src# ./build_preprocess_rpi_kernel.sh  sched/core.i
configure build output path
build preprocessed file: sched/core.i
make[1]: Entering directory '/home001/austin.kim/src/book_RPi_kernel/out'
  GEN     ./Makefile
#
# configuration written to .config
#
make[1]: Leaving directory '/home/pi/rpi_kernel_src/out'
make[1]: Entering directory '/home/pi/rpi_kernel_src/out'
  GEN     ./Makefile
scripts/kconfig/conf  --silentoldconfig Kconfig
make[1]: *** No rule to make target 'sched/core.i'.  Stop.
make[1]: Leaving directory '/home/pi/rpi_kernel_src/out'
Makefile:146: recipe for target 'sub-make' failed
make: *** [sub-make] Error 2

따라서 이 셸 스크립트를 실행할 때는 디렉터리와 소스 파일을 이름을 정확히 지정해야 합니다.

* 유튜브 강의 동영상도 있으니 같이 들으시면 더 많은 걸 배울 수 있습니다. 






라즈베리 파이 설정 

라즈베리 파이 설치하기
라즈베리 파이 기본 설정하기 
라즈비안 리눅스 커널 빌드

"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)


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

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

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


Thanks,
Austin Kim














핑백

덧글

  • 커널커널 2021/02/26 23:05 # 삭제 답글

    안녕하세요. 책을 구매해서 이 부분을 실습하고 있는데 빌드 중에 오류가 발생해서 질문드립니다.
    전체 전처리 파일을 추출하는 부분입니다.

    로그 마지막 부분이 다음과 같은데요

    CC [M] drivers/md/dm-table.o
    CC [M] drivers/media/dvb-frontends/stb0899_drv.o
    /home/pi/rpi_kernel_src/linux/drivers/md/dm-table.c:24: fatal error: closing dependency file drivers/md/.dm-table.o.d: 장치에 남은 공간이 없음


    compilation terminated.
    make[3]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: drivers/md/dm-table.o] 오류 1
    make[2]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:544: drivers/md] 오류 2
    make[2]: *** 끝나지 않은 작업을 기다리고 있습니다....
    CC [M] fs/xfs/libxfs/xfs_sb.o
    /home/pi/rpi_kernel_src/linux/drivers/media/dvb-frontends/stb0899_drv.c:35: fatal error: closing dependency file drivers/media/dvb-frontends/.stb0899_drv.o.d: 장치에 남은 공간이 없음


    compilation terminated.
    make[4]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: drivers/media/dvb-frontends/stb0899_drv.o] 오류 1
    make[3]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:544: drivers/media/dvb-frontends] 오류 2
    make[2]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:544: drivers/media] 오류 2
    make[1]: *** [/home/pi/rpi_kernel_src/linux/Makefile:1059: drivers] 오류 2
    make[1]: *** 끝나지 않은 작업을 기다리고 있습니다....
    CC [M] fs/xfs/libxfs/xfs_symlink_remote.o
    /home/pi/rpi_kernel_src/linux/fs/xfs/libxfs/xfs_sb.c:33: fatal error: closing dependency file fs/xfs/libxfs/.xfs_sb.o.d: 장치에 남은 공간이 없음


    compilation terminated.
    make[3]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: fs/xfs/libxfs/xfs_sb.o] 오류 1
    make[3]: *** 끝나지 않은 작업을 기다리고 있습니다....
    CC fs/fcntl.o
    /home/pi/rpi_kernel_src/linux/fs/xfs/libxfs/xfs_symlink_remote.c:23: fatal error: closing dependency file fs/xfs/libxfs/.xfs_symlink_remote.o.d: 장치에 남은 공간이 없음


    compilation terminated.
    make[3]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: fs/xfs/libxfs/xfs_symlink_remote.o] 오류 1
    CC fs/ioctl.o
    /home/pi/rpi_kernel_src/linux/fs/fcntl.c:31: fatal error: closing dependency file fs/.fcntl.o.d: 장치에 남은 공간이 없음
    #include <linux/uaccess.h>

    compilation terminated.
    make[2]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: fs/fcntl.o] 오류 1
    make[2]: *** 끝나지 않은 작업을 기다리고 있습니다....
    /home/pi/rpi_kernel_src/linux/fs/xfs/libxfs/xfs_refcount_btree.c:435:1: fatal error: error writing to fs/xfs/libxfs/.tmp_xfs_refcount_btree.s: 장치에 남은 공간이 없음
    }
    ^
    compilation terminated.
    make[3]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: fs/xfs/libxfs/xfs_refcount_btree.o] 오류 1
    /home/pi/rpi_kernel_src/linux/fs/ioctl.c:24: fatal error: closing dependency file fs/.ioctl.o.d: 장치에 남은 공간이 없음


    compilation terminated.
    make[2]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: fs/ioctl.o] 오류 1
    /home/pi/rpi_kernel_src/linux/fs/xfs/libxfs/xfs_refcount.c:1729:1: fatal error: error writing to fs/xfs/libxfs/.tmp_xfs_refcount.s: 장치에 남은 공간이 없음
    }
    ^
    compilation terminated.
    make[3]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:303: fs/xfs/libxfs/xfs_refcount.o] 오류 1
    make[2]: *** [/home/pi/rpi_kernel_src/linux/scripts/Makefile.build:544: fs/xfs] 오류 2
    make[1]: *** [/home/pi/rpi_kernel_src/linux/Makefile:1059: fs] 오류 2
    make[1]: 디렉터리 '/home/pi/rpi_kernel_src/out' 나감
    make: *** [Makefile:146: sub-make] 오류 2

    이러면 SD카드 용량이 부족한 건가요?
    그냥 빌드하면 되는데 이 부분은 빌드가 안되네요;;
  • AustinKim 2021/02/27 18:15 #

    정신없이 공부하다보니 이제야 답을 드립니다.

    에러 로그를 보니 시스템에 남은 저장 공간이 부족해 발생하는 에러로 보입니다.
    지금 사용하시는 마이크로 SD카드의 용량이 얼마나 되나요? 용량 최소 16GB 정도는 돼야 전체 전처리 파일을 확보할 수 있습니다.

    62~65페이지를 보시면 특정 한 개 파일만 전처리 파일로 추출할 수 있는 방법을 확인하실 수 있으니, 이 방법을 사용하시길 권장드립니다.

    감사합니다.
  • 커널커널 2021/02/28 01:14 # 삭제

    답변 감사합니다. SD카드는 16기가인데 왜 그럴까요...

    그런데 혹시 이 파일들을 뒤에 실습할때 참조하나요?

    그리고 책을 보다보면 a/blah~ b/blah~ 이런식으로 나오는 부분들이 있는데 그냥 이건 비교 하시기 위해
    가지고 계신 폴더라고 생각하면 되나요? 책에서는 따로 언급이 없으셨던거 같아서요!
  • AustinKim 2021/02/28 12:29 #

    이 파일들은 나중에 실습할 때 참고하지 않습니다. 그냥 이런 내용이 있다라는 정도로 파악하신 후 넘어가시면 되겠습니다. 대신 나중에 실전 개발을 하실 때 참고하시면 좋은 파일입니다.

    그리고, 'a/Makefile'는 수정하기 전, 'b/Makefile'을 뜻하는 구문입니다.

    03 --- a/Makefile
    04 +++ b/Makefile

    리눅스에서는 이 방식으로 파일 간의 차이점을 표기하는데요.
    나중에 이 내용을 추가하도록 하겠습니다.

    책을 읽다가 궁금한 점이 있으면 언제든 댓글로 남겨주세요.
    최대한 자세히 설명드리겠습니다. 또한 유튜브 강의 동영상도 있으니 참고하세요.
    https://www.youtube.com/user/schezokim/videos

    감사합니다.
  • 커널커널 2021/02/28 20:49 # 삭제

    자세한 답변 감사합니다!
  • haru 2021/02/28 14:11 # 삭제 답글

    안녕하세요.

    책을 읽다가 막히는 부분이 있어 구글링 하다가 저자님의 블로그를 보게 되었네요.
    전처리 파일을 생성할 때, 전체 파일에 대해 전처리 파일을 생성하려면 linux/Makefile에 '-save-temps=obj '를 추가해서 빌드하면 된다고 하는데요, 이는 out 디렉터리에 엄청난 크기의 파일들이 추가된다고 했는데요,
    특정 파일의 전처리 파일을 생성할 경우, linux/Makefile에 추가한 '-save-temps=obj '를 제거한 후, linux 상위 디렉터리에서 ./build_preprocess_rpi_kernel.sh kernel/sched/core.i 를 실행해주면 되는 것인가요?

    궁금한 점을 정리하면, 전체 전처리 파일 생성 시 linux/Makefile에 -save-temps=obj 를 추가했는데, 특정 파일의 전처리 파일을 생성할 때는 linux/Makefile의 추가한 부분을 제거해야 하는지 아니면 그대로 두어도 상관 없는지가 궁금합니다.

    감사합니다.
  • haru 2021/02/28 14:25 # 삭제

    아..
    문제 해결되었습니다.

    특정 파일만 전처리 파일 생성시, linux/Makefile의 -save-temps=obj 부분을 제거해야 하는 듯 하네요..
    제 경우, 특정 파일만 전처리 파일 생성 시에 위에 문의하신 분처럼 16GB SD 카드를 사용했는데, 사용 공간이 부족하다는 메시지를 보이고 빌드가 중단되어서 혹시나 해서 문의 드린 것인데요, out 디렉터리에 9G 이상의 파일이 생성되어 용량이 부족하다고 중단되더라고요..

    linux/Makefile에 '-save-temps=obj를 추가하지 않은 상태에서 ./buile_rpi_kernel.sh로 커널 빌드를 한 후, 추가로 ./build_preprocess_rpi_kernel.sh를 진행하니 빌드가 금방 끝나고 kernel/sche/core.i 파일을 확인할 수 있었습니다.

    linux/Makefile에 '-save-temps=obj'를 추가하면, ./buld_rpi_kernel.sh로 커널 빌드를 하더라도 ./build_preprocess_rpi_kerenl.sh를 실행하면, 전체 파일에 대해 *.i 파일을 생성하기 위해서 다시 빌드를 하는 모양입니다. 엄청 시간이 걸리네요..

    다시 진도를 나갈 수 있을 것 같습니다. ^^

    추가로,
    책 본문과 블로그 본문에 특정 파일 전처리 관련 예로 오타가 있어 말씀드립니다.
    build_preprocess_rpi_kernel.sh linux/sched/core.i ==> build_preprocess_rpi_kernel.sh kernel/sched/core.i 로 바뀌어야 할 듯 보입니다.
    저같은 초보분들은 책에 오타 하나도 따라 치기 때문에... 수정이 필요할 듯 보입니다..
    동작이 제대로 안되면 멘붕이.. 하지만, 공부는 더 되는 듯 보입니다. ^^

    좋은 글 감사합니다.
  • AustinKim 2021/02/28 18:22 #

    아, 제대로 빌드가 되니 다행이네요.
    알려주신 내용은 다음 2쇄에 반영토록 하겠습니다.

    감사합니다.
댓글 입력 영역