Linux/Linux Kernel

리눅스 커널 소스의 구조 및 objdump 바이너리 유틸리티

Study with Me! 2025. 4. 21. 17:15

리눅스 커널 소스의 구조

  • arch : 아키텍처별로 동작하는 커널 코드
    • arm : 32bit 계열  ARM 아키텍처 코드가 있으며, 라즈비안도 이 하부 디렉터리 코드를 실행함
    • arm64 : 64bit 계열 ARM 아키텍처 코드가 있음
    • x86 : 폴더 이름과 같이 인텔 x86 아키텍처 코드가 있음
  • include : 커널 코드 빌드에 필요한 헤더파일이 있음
  • Documentation : 커널 기술 문서가 있는 디렉터리로, 커널 시스템에 대한 기본 동작을 설명하는 문서를 찾을 수 있음
  • kernel : 커널의 핵심 코드가 있는 디렉터리, 아키텍처와 무관한 커널 공통 코드가 있음
        아키텍처별로 동작하는 메모리 관리 코드는 arch/*/mm/ 아래에 있음
    • irq : 인터럽트 관련 코드
    • sched : 스케줄링 코드
    • power : 커널 파워 매니지먼트 코드
    • locking : 커널 동기화 관련 코드
    • printk : 커널 콘솔 관련 코드
    • trace : ftrace 관련 코드
  • drivers : 모든 시스템의 디바이스 드라이버 코드가 있음, 하부 디렉터리에 드라이버 종류별 소스가 있음
  • fs : 모든 파일 시스템 코드가 있음
        fs 디렉터리의 파일에는 파일 시스템 공통 함수가 있고, 파일 시스템별로 하나씩 세분화된 디렉터리를 볼 수 있음
  • lib : 커널에서 제공하는 라이브러리 코드가 있음
        아키텍처에 종속적인 라이브러리 코드는 arch/*/lib/ 에 있음

objdump 바이너리 유틸리티

아래는 대표적인 바이너리 유틸리티 정리 표이다.

유틸리티 역할
objdump 라이브러리나 ELF 형식의 파일을 어셈블리어로 출력
as 어셈블러
ld 링커
addr2line 주소를 파일과 라인으로 출력
nm 오브젝트 파일의 심벌을 출력
readelf ELF 파일의 내용을 출력

이제 objdump 유틸리티를 사용해보자.

오브젝트 파일로는 리눅스 커널을 빌드하면 생성되는 vmlinux를 활용한다.

 

objdump -x 옵션(파일 헤더 및 섹션 정보 출력)을 사용해보자.

objdump -x vmlinux | more

명령어를 수행하면 아래와 같은 내용을 확인할 수 있다.

vmlinux:     file format elf64-x86-64
vmlinux
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000001000080

Program Header:
    LOAD off    0x0000000000200000 vaddr 0xffffffff81000000 paddr 0x0000000001000000 align 2**21
         filesz 0x0000000001b786bc memsz 0x0000000001b786bc flags r-x
    LOAD off    0x0000000001e00000 vaddr 0xffffffff82c00000 paddr 0x0000000002c00000 align 2**21
         filesz 0x0000000000477000 memsz 0x0000000000477000 flags rw-
    ...

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         01400000  ffffffff81000000  0000000001000000  00200000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       006f5c06  ffffffff82400000  0000000002400000  01600000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
  2 .pci_fixup    00003bb0  ffffffff82af5c10  0000000002af5c10  01cf5c10  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .tracedata    00000078  ffffffff82af97c0  0000000002af97c0  01cf97c0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  ...

내용을 살펴보면 아키텍처 이름(i386:x86-64)과 스타트업 코드의 위치(0x0000000001000080)를 확인할 수 있다.

(이 값들은 시스템에 따라 다를 수 있다.)

스타트업 코드는 이미지가 처음 실행될 때 동작하며, 어셈블리 코드로 구성되어 있다고 한다.

 

이번에는 objdump -d 옵션을 사용해서 vmlinux에서 어셈블리 코드를 출력해보자.

objdump -d vmlinux | more

명령어를 수행하면 아래와 같은 내용을 확인할 수 있다.

vmlinux:     file format elf64-x86-64


Disassembly of section .text:

ffffffff81000000 <_stext>:
ffffffff81000000:	fc                   	cld
ffffffff81000001:	0f 01 15 80 24 19 03 	lgdt   0x3192480(%rip)        # ffffffff84192488 <__init_scratch_end+0x192488>
ffffffff81000008:	b8 10 00 00 00       	mov    $0x10,%eax
ffffffff8100000d:	8e d8                	mov    %eax,%ds

그런데 이렇게 하면 너무 많은 어셈블리 코드가 출력되어 보기가 어렵다.

 

System.map 파일을 사용해 옵션을 지정해서 특정 함수의 어셈블리 코드를 보는 방법을 알아보자.

System.map에서는 심벌 별로 주소를 확인할 수 있다.

리눅스 커널의 schedule() 함수의 주소가 0xffffffff821d0ee0~0xffffffff821d0ff0 임을 확인했다.

이제, 이 값을 사용해 시작 주소와 끝 주소를 지정한 objdump -d 명령을 수행해보자. 

objdump --start-address=0xffffffff821d0ee0 --stop-address=0xffffffff821d0ff0 -d vmlinux
vmlinux:     file format elf64-x86-64


Disassembly of section .text:

ffffffff821d0ee0 <schedule>:
ffffffff821d0ee0:       e8 3b af ed fe          call   ffffffff810abe20 <__fentry__>
ffffffff821d0ee5:       55                      push   %rbp
ffffffff821d0ee6:       48 89 e5                mov    %rsp,%rbp
ffffffff821d0ee9:       41 54                   push   %r12
ffffffff821d0eeb:       53                      push   %rbx
ffffffff821d0eec:       65 48 8b 1c 25 40 43    mov    %gs:0x34340,%rbx
ffffffff821d0ef3:       03 00 
ffffffff821d0ef5:       8b 43 18                mov    0x18(%rbx),%eax

...
ffffffff821d0fdb:       e8 70 dc 70 ff          call   ffffffff818dec50 <__ubsan_handle_load_invalid_value>
ffffffff821d0fe0:       eb d1                   jmp    ffffffff821d0fb3 <schedule+0xd3>
ffffffff821d0fe2:       66 66 2e 0f 1f 84 00    data16 cs nopw 0x0(%rax,%rax,1)
ffffffff821d0fe9:       00 00 00 00 
ffffffff821d0fed:       0f 1f 00                nopl   (%rax)