커널 스터디(iamroot 18기) 7주차 내용 정리 #2, 커널 이미지 분석 및 매핑 방법

문연수·2021년 10월 20일
1

iamroot (ARM)

목록 보기
2/2

1. Kernel image

아래의 내용은 대부분 문c 블로그에서 가져온 내용입니다. 더 자세한 내용은 문영일 선생님의 블로그를 참고하시기 바랍니다. 어디까지나 공부 목적으로 용어 해설을 첨부하여 정리한 글입니다.

1.1 What is Image

컴퓨터에서 이미지(image) 는 기록 미디어 안에 있는 내용이 저장된 파일을 가리킨다. 이러한 디스크 이미지는 일부 압축 프로그램을 사용하여 압축 파일을 풀듯이 풀 수도 있고, 별도의 가상 CD/DVD 소프트웨어를 이용하여 마치 실제 CD/DVD 미디어를 사용하는 것처럼 에뮬레이트하는 데 쓸 수도 있다.

Boot Image

부트 이미지는 디스트 이미지(저장 매체에 대한 완전한 구조와 내용들을 포함하는 컴퓨터 파일)의 일종이다. 이것이 부트 디바이스(boot device) 로 옮겨지면 관련된 하드웨어가 부팅을 진행할 수 있다.
부트 이미지는 일반적으로 운영체제, 유틸리티, 진단장비 뿐만 아니라 부트와 데이터 복구 정보 또한 포함하고 있다.

Boot device

부트 디바이스(시동 장치, 부팅 장치)란 운영체제가 로드되는 장치이다. 현대 PC 의 UEFI 혹은 BIOS 펌웨어는 다양한 장치를 통한 부팅을 지원한다. 이러한 장치에는 대표적으로 GPT 혹은 마스터 부트 레코드 (MBR) 를 통한 솔리드 스테이트 드라이브(SSD) 와 하드 디스크 드라이브(HDD) 가 있다. 뭐 기타 등등 더 있는데 중략.

여기서 UEFI, BIOS, GPT, MBR, HDD, SSD 등은 생략... 여기까지 설명하면 너무 길어진다.

bootloader

위에서 설명한 UEFIBIOS 와 같은 펌웨어를 부트로더라 부르며 이러한 소프트웨어는 AArch64 linux 에서 보통 아래와 같은 기능을 수행한다:

  1. RAM 초기화 및 설정 (필수)

  2. 디바이스 트리 설정 (필수)
    디바이스 트리 블롭(DTB, Device tree Blob) 은 반드시 8-byte 경계를 기점으로 배치되어야 하고, 반드시 2 MiB 를 넘겨선 안된다. x0 레지스터에 DTB 의 물리주소가 저장된다.

  3. 커널 이미지 압축 해제 (선택)
    AArch64 커널은 현재 더 이상 압축 해제자를 제공하지 않으므로, 만일 압축 해제된 이미지 타기싱 이용된다면, 부트로더에 의해 압축 해제가 수행되는 것을 필요로 한다. 이러한 구현이 필수사항으로 들어가지 않는 부트로더의 경우, 압축되어지지 않은 이미지 타겟이 필요로 되어진다.

  4. 커널 이미지 호출 (필수)
    커널 이미지의 첫 주소로 분기하여 커널의 head.S 루틴을 시작한다.

2. Image Types

  • Image: 압축하지 않고 사용하는 이미지
  • zImage: 압축하여 사용, 자체 압축 해제 루틴이 포함되어 있음 (ARM32)
  • Image.gz: 압축하여 사용하는 이미지, 해제 루틴 x (ARM64)
    (e.g. gz, bz2, lz4, lzma, lzo)
  • uImage: u-Boot 부트로더가 zImage 를 변형(64 바이트 헤더 추가)하여 사용하는 이미지.
  • bzImage: zImage 를 확장시켜 PC 에서 사용하는 포맷
  • xipImage: eXecute In Place Image, 압축하지 않고 바로 플래시 ROM 에서 커널이 동작하는 이미지. Flash 에서 읽고, 압축 해제하고, RAM 으로 복사하는 과정이 없음.

3. Kernel image start location

`

종류초기 위치부트로더가 이미지를
SDRAM 에 로드 하는가?
실행 위치SDRAM 으로 커널 copy커널 실행 메모리
ImageROM/FlashYSDRAMYSDRAM
Image with ZBOOTROM/FlashNROM/FlashYSDRAM
zImageROM/FlashYSDRAMY + DecompressSDRAM
zImage with ZBOOTROM/FlashNROM/FlashY + DecompressSDRAM
xipImageROM/FLASHNROM/FlashN But...ROM/Flash
  • SDRAM 에서 코드를 동작시키면 ROM/Flash 보다 수행 속도는 빠르지만 ROM/Flash 에서 SDRAM 으로 로딩하는 절차가 추가된다.
  • ROM/Fash 에서 zImage 를 직접 실행하게 되면 Decompres 결과를 SDRAM 으로 바로 쏴버린다.

ZBOOT 란...

CONFIG_ZBOOT_ROM 옵션으로 ImagezImage(uImage)SDRAM 으로 옮기지 않고 ROM/Flash 에서 곧바로 동작 시킬 때 사용하는 옵션.

xipImage : SDRAM 으로의 copy

커널 코드는 SDRAM 으로 옮기지 않지만, Data 공간은 SDRAM 에 옮긴다.

ROM

말 그대로, Ready Only Memory

휘발성 RAM

  • RAM: Random Access Memory, 임의의 영역에 접근하여 읽고 쓰기가 가능한 주기억 장치.
  • SRAM: Static RAM, 전원이 공급되는 동안 데이터가 유지되는 RAM
  • DRAM: Dynamic RAM, 데이터를 유지하기 위해 일정 시간마다 재생(refresh) 해주어야 하는 램
  • SDRAM: Synchronous DRAM, DRAM 의 발전된 형태. 보통의 DRAM 과 달리 제어 장치 입력을 클락 신호와 동시에 일어나도록 한 동기화 방식의 DRAM.

비휘발성 RAM

  • Flash memory: 값이 싸고 집적도는 높지만 느리다.
  • FRAM: Ferroelectric RAM, DRAM 과 비슷하지만 데이터를 유지하기 위해 재생할 필요 없음.
  • PRAM: Phase-change RAM, 상변태를 하는 물질을 이용하여 저항차이로 데이터를 저장, 전력소모 매우 적음.

4 zImage 물리 시작 주소 with ARM32

zImage 가 실행되는 물리 주소는 시스템마다 다르며 이를 커널에 알리기 위해 TEXT_OFFSET 을 정의하여 사용한다.
Kernel 4.0 이전에는 시스템 오프셋을 시스템마다 미리 지정해서 물리 메모리에서 최하단(TEXT_BASE) 에서 어느 정도 거리를 띄워서 사용했다.

5. ZBOOT 와 xipImage 차이점

ZBOOT 의 경우 압축된 커널 이미지를 ROM/Flash 에서 동작시켜 SDRAM 에 압축을 풀어, 실제 커널은 SDRAM 에서 실행된다.
xipImageROM/Flash 에서 바로 실행되지만, Kernel DataSDRAM 으로 로드하여 실행한다.

6. ARM 의 Image.gz 생성 과정

  1. vmlinux (ELF)
    리눅스 커널 코드를 컴파일하여 생성된 *.o 파일과 *.a 파일들을 링크하여 하나의 vmlinux 파일을 생성한다.

  2. Image (bin)

    objcopy -O binary -R .note -R \
    .note.gnu.build-id -R .comment \
    -S vmlinux {arch}/Image

    생성된 vmlinux 파일에 대해 objcopy 를 진행한다. 이 과정에서 일부 Section 정보를 지운다. 결과물로 Image 파일이 생성된다.

아래부턴 ISA 별로 갈린다.

ARM32

  1. piggy.gz
    gzip 을 통해 piggy.gz 이라는 파일을 생성한다.

  2. piggy.o
    생성된 piggy.gz 파일에서 디버깅 정보 등을 삭제하여 piggy.o 를 생성한다.

  3. zImage or bzImage
    마지막으로 커널의 압축을 해제시켜주는 코드를 커널 앞부분에 덧붙여서 링커를 통해 zImage 혹은 bzImage 파일을 생성한다.
    물리 메모리가 1MiB 위치에 로드될 수 있는 작은 크기의 커널은 zImage 가 생성되고, 그렇지 못한 경우엔 bzImage (big zImage) 가 생성된다.

ARM64

  1. Image.gz (bin)
    cat arch/arm64/boot/Image | \
    gzip -n -f -9 > arch/arm64/boot/Image.gz
    일부 섹션 정보를 지운 이미지 파일을 gzip 으로 압축해서 동일한 디렉토리에 Image.gz 라는 이름으로 저장한다.

7. 커널 이미지(vmlinux) 분석

7.1 file 명령어 실행 결과

vmlinux 파일의 구조를 file 명령어를 통해 확인한 결과이다. ELF 64-bit, ARM aarch64, not stripped 로 확인된다.

7.2 readelf 실행 결과

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           AArch64
  Version:                           0x1
  Entry point address:               0xffff800010000000
  Start of program headers:          64 (bytes into file)
  Start of section headers:          329082624 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         3
  Size of section headers:           64 (bytes)
  Number of section headers:         40
  Section header string table index: 39

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .head.text        PROGBITS         ffff800010000000  00010000
       0000000000010000  0000000000000000  AX       0     0     65536
  [ 2] .text             PROGBITS         ffff800010010000  00020000
       0000000000dfb178  0000000000000008  AX       0     0     2048
  [ 3] .got.plt          PROGBITS         ffff800010e0b178  00e1b178
       0000000000000018  0000000000000008  WA       0     0     8
  [ 4] .rodata           PROGBITS         ffff800010e10000  00e20000
       00000000006f5f80  0000000000000000  WA       0     0     4096
  [ 5] .pci_fixup        PROGBITS         ffff800011505f80  01515f80
       00000000000025e0  0000000000000000   A       0     0     16
  [ 6] __ksymtab         PROGBITS         ffff800011508560  01518560
       000000000000f570  0000000000000000   A       0     0     4
  [ 7] __ksymtab_gpl     PROGBITS         ffff800011517ad0  01527ad0
       0000000000013ea8  0000000000000000   A       0     0     4
  [ 8] __ksymtab_strings PROGBITS         ffff80001152b978  0153b978
       0000000000038dc7  0000000000000001 AMS       0     0     1
  [ 9] __param           PROGBITS         ffff800011564740  01574740
       0000000000004178  0000000000000000   A       0     0     8
  [10] __modver          PROGBITS         ffff8000115688b8  015788b8
       00000000000000f0  0000000000000000   A       0     0     8
  [11] __ex_table        PROGBITS         ffff8000115689a8  015789a8
       0000000000002068  0000000000000000   A       0     0     8
  [12] .notes            NOTE             ffff80001156aa10  0157aa10
       000000000000003c  0000000000000000   A       0     0     4
  [13] .init.text        PROGBITS         ffff800011570000  01580000
       00000000000718b4  0000000000000000  AX       0     0     4
  [14] .exit.text        PROGBITS         ffff8000115e18b4  015f18b4
       00000000000076f4  0000000000000000  AX       0     0     4
  [15] .altinstructions  PROGBITS         ffff8000115e8fa8  015f8fa8
       000000000003570c  0000000000000000   A       0     0     1
  [16] .init.data        PROGBITS         ffff800011620000  01630000
       0000000000093c8e  0000000000000000  WA       0     0     256
  [17] .data..percpu     PROGBITS         ffff8000116b4000  016c4000
       000000000000db98  0000000000000000  WA       0     0     64
  [18] .hyp.data..percpu PROGBITS         ffff8000116c2000  016d2000
       0000000000000e20  0000000000000000  WA       0     0     16
  [19] .rela.dyn         RELA             ffff8000116c2e20  016d2e20
       000000000045f8e8  0000000000000018   A       0     0     8
  [20] .data             PROGBITS         ffff800011b30000  01b40000
       00000000002a4520  0000000000000000  WA       0     0     4096
  [21] __bug_table       PROGBITS         ffff800011dd4520  01de4520
       0000000000015e88  0000000000000000  WA       0     0     4
  [22] .mmuoff.data[...] PROGBITS         ffff800011dea800  01dfa800
       0000000000000018  0000000000000000  WA       0     0     2048
  [23] .mmuoff.data.read PROGBITS         ffff800011deb000  01dfb000
       0000000000000008  0000000000000000  WA       0     0     8
  [24] .pecoff_edat[...] PROGBITS         ffff800011deb008  01dfb008
       00000000000001f8  0000000000000000  WA       0     0     1
  [25] .bss              NOBITS           ffff800011dec000  01dfb200
       0000000000080d34  0000000000000000  WA       0     0     4096
  [26] .debug_aranges    PROGBITS         0000000000000000  01dfb200
       0000000000036d50  0000000000000000           0     0     16
  [27] .debug_info       PROGBITS         0000000000000000  01e31f50
       000000000d24e71f  0000000000000000           0     0     1
  [28] .debug_abbrev     PROGBITS         0000000000000000  0f08066f
       00000000006da629  0000000000000000           0     0     1
  [29] .debug_line       PROGBITS         0000000000000000  0f75ac98
       00000000014a1c53  0000000000000000           0     0     1
  [30] .debug_frame      PROGBITS         0000000000000000  10bfc8f0
       0000000000359b88  0000000000000000           0     0     8
  [31] .debug_str        PROGBITS         0000000000000000  10f56478
       0000000000410681  0000000000000001  MS       0     0     1
  [32] .debug_ranges     PROGBITS         0000000000000000  11366b00
       0000000000000220  0000000000000000           0     0     16
  [33] .comment          PROGBITS         0000000000000000  11366d20
       0000000000000034  0000000000000001  MS       0     0     1
  [34] .debug_loclists   PROGBITS         0000000000000000  11366d54
       0000000001bcb113  0000000000000000           0     0     1
  [35] .debug_rnglists   PROGBITS         0000000000000000  12f31e67
       00000000003b5cb0  0000000000000000           0     0     1
  [36] .debug_line_str   PROGBITS         0000000000000000  132e7b17
       0000000000036636  0000000000000001  MS       0     0     1
  [37] .symtab           SYMTAB           0000000000000000  1331e150
       000000000043f620  0000000000000018          38   160120     8
  [38] .strtab           STRTAB           0000000000000000  1375d770
       0000000000278dce  0000000000000000           0     0     1
  [39] .shstrtab         STRTAB           0000000000000000  139d653e
       00000000000001bd  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000010000 0xffff800010000000 0xffff800010000000
                 0x0000000001deb200 0x0000000001e6cd34  RWE    0x10000
  NOTE           0x000000000157aa10 0xffff80001156aa10 0xffff80001156aa10
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10

 Section to Segment mapping:
  Segment Sections...
   00     .head.text .text .got.plt .rodata .pci_fixup __ksymtab __ksymtab_gpl __ksymtab_strings __param __modver __ex_table .notes .init.text .exit.text .altinstructions .init.data .data..percpu .hyp.data..percpu .rela.dyn .data __bug_table .mmuoff.data.write .mmuoff.data.read .pecoff_edata_padding .bss 
   01     .notes 
   02     

커널 이미지 (vmlinux) 파일을

readelf --headers vmlinux

명령어로 읽은 결과를 출력한 것이다. 여러 섹션이 존재하는데 앞서 말했던 일부 정보들은 Image.gz 생성 중 삭제된다.

삭제되는 섹션

  • 12 번 섹션(.notes)
  • 33 번 섹션(.comment)

커널 코드 섹션

  • 1 번 섹션(.head.text)
  • 2 번 섹션(.text)
  • 13 번 섹션(.init.text)
  • 14 번 섹션 (.exit.text)

커널 데이터 섹션

  • 4 번 섹션(.rodata)
  • 16 번 섹션(.init.data)
  • 17 번 섹션(.data..percpu)
  • 18 번 섹션(.hyp.data..percpu)
  • 20 번 섹션(.data)
  • 25 번 섹션(.bss)

8. 커널의 시작 주소는 어디일까?

앞서 얘기했던 것처럼 부트로더가 커널을 압축 해제하고 커널 이미지를 호출하는데 이 시작 주소는 물리 DRAM2 MiB 단위로 정렬된 주소라면 어디에서든 동작할 수 있는 position independent 코드로 구성된다. 이 이미지의 시작 주소는 시스템마다 모두 다르다.
요약) 물리주소는 시스템마다 가변적. but, 2 MiB 로 정렬된 위치에 존재함. adrp 명령으로 확인 가능함.

8.1 TEXT_OFFSET

커널 v4.6 이전

리눅스 커널 v4.6 이전까진 AArch64 의 실제 커널 시작 코드는 보안 문제로 TEXT_OFFSET 만큼 떨어져 있었다 . 0x...0000 이 시작주소가 아니고 여기에서 TEXT_OFFSET 만큼 더해야 실제 커널의 시작 주소로 접근이 가능하다.

커널 v4.6 이후

KASLR (Kernel Address Sanitizer Location Randomization) 의 relocatable kernel 개념이 도입되면서 TEXT_OFFSET 에 의미가 사라졌고, v5.8-rc2 에서 완전히 제거되었다.

8.2 KASLR (Kernel Address Sanitizer Location Randomization)

Kernel Adress Sanitizer Location Randomization, (커널 주소 살균제 주소 무작위화???) 의 약자로 보안을 위해 커널 가상 주소 공간에서 커널 이미지 및 커널 모듈이 위치해 있는 곳을 알 수 없게 런타임 에 랜덤 배치한다.

KASANKernel Address SANitizer 의 약자이다. 이후 KASAN 이란 용어가 나오면 이를 말하는 것이다.

9. Static 페이지 테이블

커널이 컴파일 될 때 5 개의 페이지 테이블이 미리 생성된다. 기존에는 두 가지 페이지 테이블만 존재했으나 보안 목적으로 여러 테이블로 분리했다.

9.1 init_pg_dir

초기 부팅 시 사용되며 사용할 페이지 테이블의 단계와 개수를 컴파일 타임에 계산. 정규 맵핑(swapper_pg_dir 사용) 이전에 잠깐 만들어서 사용. paging_init() 후에 swapper_pg_dir 로 전환한 후 init_pg_dir 는 할당 해제한다.

9.2 swapper_pg_dir

보안을 위해 read-only 로 맵핑하여 사용하고, 커널의 정규 루틴만을 사용하여 변경 가능하도록 막아 놓았다. 따라서 외부에서 커널이 노출되더라도 쉽게 접근하여 사용이 불가능함.

10. Identity 매핑

물리 주소 공간과 유저 가상 주소 공간 동일하도록 1대 1 로 매핑하는 것을 의미한다. MMU (Memory Management Unit) 가 켜지는 순간, 물리 주소가 아닌 가상 주소를 사용하게 되는데 이 경우 문제가 발생할 수 있으므로, 동일한 주소에 1대 1 로 매핑하여 커널 코드가 이어서 정상적으로 동작할 수 있도록 만든다.

만일 DRAM 이 너무 높은 주소 공간한다면?

만일 DRAM 의 주소(예를 들어, 52-bit) 가 너무 높고 유저 가상 주소가 너무 작은(예를 들어, 48-bit) 경우에는 어떻게 할까? 이 경우에는 어쩔 수 없이 유저 가상 주소 공간(DRAM 의 가상 주소 공간 크기인 52-bit 로)을 키운다.

아주 기본적인 배경지식이고 더 정확한 내용은 이후에 자세히 정리한다.

출처

[사이트] http://jake.dothome.co.kr/image1/
[사이트] https://ko.wikipedia.org/wiki/랜덤_액세스_메모리
[사이트] https://ko.wikipedia.org/wiki/SDRAM
[사이트] https://elinux.org/Kernel_XIP
[사이트] https://ko.wikipedia.org/wiki/디스크_이미지
[사이트] https://en.wikipedia.org/wiki/Boot_image
[사이트] https://en.wikipedia.org/wiki/Booting#Boot_device
[사이트] https://www.kernel.org/doc/html/latest/arm64/booting.html
[사이트] https://github.com/torvalds/linux/commit/a7f8de168ace487fa7b88cb154e413cf40e87fc6
[사이트] http://jake.dothome.co.kr/head-64-510/
[사이트] https://github.com/torvalds/linux/commit/120dc60d0bdbadcad7460222f74c9ed15cdeb73e
[책] 리눅스 커널 내부구조 (저자: 백승제, 최종무 저)

profile
2000.11.30

0개의 댓글