커널 이미지 빌드 과정 (Kernel Image Build Process)
Linux Kernel 코드 분석은 기나긴 여행이다.
Linux Kernel 분석 과정은 상당한 끈기와 노력 및 정신적 무장을 필요로 한다.
그래서, 누구나 시작은 할 수 있으나 목표한 과정까지 완주하여 달콤한 열매를 맛보는 이는 그리 많지 않은 것 같다.
나 또한 매주 주말 중 하루를 반납하고 스터디 모임에 꼬박꼬박 참여하고 있으며, 평일 또한 업무 이외의 시간 중 상당수를 Kernel 분석에 투자하고 있는 중이다.
이러한 노력이 수년 후 엄청난 결실로 돌아오리라는 것을 믿어 의심치 않고 있다.
다만, 이러한 노력은 어디까지나 의무감이 아닌 내 자신의 호기심과 재미에 바탕을 두고 있다는 것이 중요하다. 재미없는 것을 어떻게 수년씩 꾸준히 해나갈 수 있겠는가 ?
그래서, 분석할 Architecture 도 내가 주로 업무를 수행하는 x86 환경이 아닌 Embedded 용으로 사용되는 ARM Architecture 를 선택하였다. (Embedded 는 평소 내가 항상 해보고 싶던 분야였다.)
Linux Kernel 은 아주 다양한 Architecture 를 지원하기 때문에 특정 Architecture 와 SoC(System On Chip)를 결정하고 분석을 해야 일관된 분석 작업이 가능하다.
이러한 결정없이 분석을 하게 되면 불필요한 코드까지 분석을 해야하고, 코드의 흐름을 따라가기가 무척 어려워지게 된다.
현재 내가 분석하고 있는 환경이다.
- Kernel Version : 3.12.20
- 개발보드 : Exynos 5420
이러한 Reference 들은 그때그때 포스팅에 올릴 것이다.
대부분 Kernel 코드 분석의 시작을 Bootloader 또는 head.S 라는 파일에서부터 시작하는데, 이 head.S 파일을 분석하기 위해서는 기본적인 ARM Architecture 와 더불어 Assembly 의 해독능력도 필요하다.
head.S 뿐 아니라 커널의 C 소스들을 분석하는 중간중간에도 Assembly 코드를 분석해야하는 상황들은 자주 만난다.
그렇다고 커널분석을 하기전부터 이미 Assembly 의 대가일 필요는 없다.
ARM System Developers' Guide 라는 책을 먼저 정독하길 추천한다.
ARM Architecture 및 Assembly 의 기본 명령어에 대한 어느정도 선행적 이해는 있어야 한다.
고급 명령어나 Operand Register 의 Bit 별 쓰임새 등은 그때그때 찾아보면 된다.
자... 그럼 이제부터 망설이지 말고 Linux Kernel 의 바다속으로 모두 뛰어들어보자.
Linux Kernel 이라는 것은 결국 하나의 Binary Image 에 불과하다.
그 어렵고 방대한 Linux Kernel 이 빌드과정을 거치면 겉보기에는 보잘 것 없는 2~3 MB 짜리 파일하나(zImage)만 덩그러니 남는 것이다.
하지만, 이 파일 하나를 우습게 보지 마시라.
이 파일 하나가 현재 인류가 생산해내고 있는 가장 높은 차원의 지적/논리적 산물이며, 인류의 미래를 긍정적으로 변화시키거나 또는 파국으로 몰고갈 수 있는 엄청난 내공이 깃들여 있는 작품인 것이다.
인류가 만들어낸 드래곤볼이라고나 할까 ? ^^
먼저 ARM 용 Kernel Image 를 빌드하려면 Tool Chain 을 설치하여 Cross Compile 환경을 꾸며야 한다.
아래와 같은 과정을 수행하면 Kernel 분석에 필요한 .config 파일이나 이미지들이 생성될 것이다.
반드시 아래의 환경으로만 해야하는 것은 아니므로 각자 다른 환경에서 빌드하는 것도 상관없다.
- VirtualBox 설치
- 우분투 12.04 LTS 버전 설치
- https://www.kernel.org/ 에서 3.12.20 Stable 버전 Kernel 소스 Download
- KERNEL_ROOT 디렉토리에서 아래와 같이 zImage 빌드
-
- sudo apt-get install gcc-arm-linux-gnueabihf
- export ARCH=arm
- export CROSS_COMPILE=arm-linux-gnueabihf-
- make ARCH=arm exynos_defconfig
- You can see .config file.
- make ARCH=arm menuconfig
- System Type -> Enter
- SAMSUNG EXYNOS SoCs Support -> Enter
- [*]SAMSUNG EXYNOS5 -> Select using spacebar
- [ ]SAMSUNG EXYNOS4 -> Deselect
- Save & Exit - make -j5
위의 과정을 수행하고 나면 여러분은 다음과 같은 핵심 파일들이 생성되는 것을 볼 수 있을 것이다.
linux-3.12.20 디렉토리가 KERNEL_ROOT 디렉토리라고 보면 된다.
- linux-3.12.20/.config
- linux-3.12.20/vmlinux
- linux-3.12.20/arch/arm/boot/Image
- linux-3.12.20/arch/arm/boot/compressed/vmlinux
- linux-3.12.20/arch/arm/boot/zImage
Kernel 빌드에 대한 것은 아래 그림만 이해하고 있으면 코드 분석에 큰 무리는 없는 듯하다.
(실제 빌드 과정에서 아래의 과정을 확인하고 싶다면 make 시 V=1 옵션을 주면 된다.)
맨 처음 생기는 Kernel 빌드의 부산물은 KERNEL_ROOT 디렉토리에 생기는 vmlinux 이다.
이는 file 명령을 통해 확인해 보면 ELF(Excutable Linking Format) 파일이라는 것을 확인할 수 있다. 크기는 약 50 M 정도 된다.
$ file vmlinux vmlinux.ARM11A: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, BuildID[sha1]=0x5f29cda7984375a0eaac1f17849c97b0e5ea2a15, not stripped $ ls -al vmlinux -rwxrwxr-x 1 dplee dplee 59133322 6월 22 21:40 vmlinux.ARM11A
이 파일에서 (1) 의 과정을 통해 .comment, symbol table, relocation 정보 등을 제거한 파일이 arch/arm/boot/Image 파일이다. 이 파일은 ELF 형식이 아니고 그냥 Data Binary File 이다.
쉽게 얘기하면 순수한 Kernel 의 코드와 데이터만을 빼놓은 이미지라고 보면 되겠다.
Image 파일을 (2) 의 과정을 통해 gzip 으로 압축한 파일이 piggy.gzip 파일이다.
따라서, piggy.gzip 파일은 단순히 Data Binary File 을 압축만 한 파일이라고 할 수 있다.
이러한 압축 파일을 통째로 piggy.gzip.S 에 포함하여 Assembling 하면 piggy.gzip.o 파일이 만들어진다.
이렇게 하는 이유는 본격적인 Kernel 의 시작(start_kernel)을 하기 전에 초기화 작업을 하고 압축을 해제하는 코드인 head.o 와 misc.o 를 묶어서 다시 Linking 하기 위한 것이다.
piggy.gzip.o, head.o, misc.o 파일 3 개를 Link 하여 새로이 만들어낸 파일이 arch/arm/boot/compressed/vmlinx 파일이며 이 파일은 새로이 Linking 하여 만들어낸 파일이므로 역시 ELF 파일이 된다. KERNEL_ROOT 에 맨처음 생성되었던 파일과 다르다는 것을 알아야 한다.
마지막으로, Linking 과정에서 .comment, symbol, relocation 정보들이 또 들어가 있을 것이므로 이를 objcopy 를 통해 다시 제거하면 우리가 얻고자 하는 최종 zImage 파일이 만들어진다.
크기는 약 2~3 M 정도 된다.
이 파일이 바로 드래곤볼의 위력을 가진 Kernel Image 인 것이다.
Computer 를 Booting 하면 Boot Loader 는 필요한 하드웨어 초기화 작업을 수행한 후, 이 zImage 를 특정한 메모리 영역에 로드하고 PC(Program Counter)를 해당 zImage 의 첫번째 명령어(Instruction)로 셋팅해 줌으로서 zImage 가 시작되도록 한다.
그 첫 시작 지점은 위에서 실질적인 Kernel 이미지와 함께 Linking 되었던 head.o 파일의 코드가 된다.
아직은 진정한 Kernel 코드의 시작은 아니다.
head.o 에서는 진정한 Kernel 시작 전에 적절한 초기화 작업을 수행한 후,
misc.o 의 코드들을 호출하여 piggy.gzip.o, 즉 압축된 원래의 Kernel Image 의 압축을 해제하여 또 다른 특정 메모리 영역에 복제한 후, 최종적으로 PC 를 그 Kernel Image 의 첫 부분(start_kernel 함수)으로 분기하도록 해준다.
우리는 커널 코드의 분석을 head.o (head.S)부터 시작할 것이다.
zImage 를 메모리의 어느 위치에 로드하고 압축을 해제하여 어느 영역에 위치시키며, kernel 초기화 작업은 어떤 것들을 수행하는지 면밀히 살펴볼 것이다.
안녕하세요, 우연히 보게 되었는데 정확히 모르던 것들을 확실하게 이해할 수 있게 되었습니다. 감사합니다!
감사합니다~^^