← Documents Documentation/process/debugging/gdb-kernel-debugging.rst GitHub 원문 ↗

Linux 6.18.37 · Debugging

GDB로 kernel과 module debugging

Debug symbol kernel build, vmlinux와 target 연결, KASLR 해제, Linux GDB helper로 task·module·list를 조사하는 방법을 설명합니다.

Source pathDocumentation/process/debugging/gdb-kernel-debugging.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

Debug symbol과 Python 지원 GDB

gdb-kernel-debugging.rst:3-21

CONFIG_GDB_SCRIPTS와 debug information을 포함해 kernel을 build하고 strip되지 않은 vmlinux를 사용한다. GDB는 Python support가 필요하며 target kernel과 source·vmlinux가 정확히 같은 build여야 type과 address가 일치한다.

QEMU 또는 KGDB target 연결

gdb-kernel-debugging.rst:22-70

QEMU는 -s -S로 gdb stub을 열고 boot 전 CPU를 멈출 수 있다. GDB에서 vmlinux를 열고 target remote :1234로 연결한다. Real hardware는 KGDB transport를 사용한다. Address가 runtime randomization과 어긋나지 않도록 debugging boot에서 nokaslr을 사용하거나 relocation offset을 반영한다.

gdb vmlinux
(gdb) target remote :1234
(gdb) lx-symbols
(gdb) break start_kernel
(gdb) continue

Linux GDB helper 사용

gdb-kernel-debugging.rst:71-157

scripts/gdb가 제공하는 lx-symbols는 load된 module symbol을 찾아 추가하고, lx-dmesg는 kernel log buffer를 읽는다. lx-ps와 task helper로 process를 순회하고 list·hlist macro를 Python command로 안전하게 따라갈 수 있다.

Module이 load·unload되면 symbol address가 바뀌므로 breakpoint 전에 lx-symbols를 다시 실행한다. Optimized-out local과 inline frame은 compiler 결과이므로 disassembly와 register를 source와 함께 읽는다.

Helper command 범주

gdb-kernel-debugging.rst:158-176
  • lx-symbols와 module symbol loading
  • lx-dmesg log extraction
  • lx-ps와 task lookup
  • Kernel list, hlist와 rb-tree traversal
  • per-CPU variable와 current task 접근
  • Kernel configuration과 timer inspection

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. highlight:: none
2
3 Debugging kernel and modules via gdb
4 ====================================
5
6 The kernel debugger kgdb, hypervisors like QEMU or JTAG-based hardware
7 interfaces allow to debug the Linux kernel and its modules during runtime
8 using gdb. Gdb comes with a powerful scripting interface for python. The
9 kernel provides a collection of helper scripts that can simplify typical
10 kernel debugging steps. This is a short tutorial about how to enable and use
11 them. It focuses on QEMU/KVM virtual machines as target, but the examples can
12 be transferred to the other gdb stubs as well.
13
14
15 Requirements
16 ------------
17
18 - gdb 7.2+ (recommended: 7.4+) with python support enabled (typically true
19 for distributions)
20
21
22 Setup
23 -----
24
25 - Create a virtual Linux machine for QEMU/KVM (see www.linux-kvm.org and
26 www.qemu.org for more details). For cross-development,
27 https://landley.net/aboriginal/bin keeps a pool of machine images and
28 toolchains that can be helpful to start from.
29
30 - Build the kernel with CONFIG_GDB_SCRIPTS enabled, but leave
31 CONFIG_DEBUG_INFO_REDUCED off. If your architecture supports
32 CONFIG_FRAME_POINTER, keep it enabled.
33
34 - Install that kernel on the guest, turn off KASLR if necessary by adding
35 "nokaslr" to the kernel command line.
36 Alternatively, QEMU allows to boot the kernel directly using -kernel,
37 -append, -initrd command line switches. This is generally only useful if
38 you do not depend on modules. See QEMU documentation for more details on
39 this mode. In this case, you should build the kernel with
40 CONFIG_RANDOMIZE_BASE disabled if the architecture supports KASLR.
41
42 - Build the gdb scripts (required on kernels v5.1 and above)::
43
44 make scripts_gdb
45
46 - Enable the gdb stub of QEMU/KVM, either
47
48 - at VM startup time by appending "-s" to the QEMU command line
49
50 or
51
52 - during runtime by issuing "gdbserver" from the QEMU monitor
53 console
54
55 - cd /path/to/linux-build
56
57 - Start gdb: gdb vmlinux
58
59 Note: Some distros may restrict auto-loading of gdb scripts to known safe
60 directories. In case gdb reports to refuse loading vmlinux-gdb.py, add::
61
62 add-auto-load-safe-path /path/to/linux-build
63
64 to ~/.gdbinit. See gdb help for more details.
65
66 - Attach to the booted guest::
67
68 (gdb) target remote :1234
69
70
71 Examples of using the Linux-provided gdb helpers
72 ------------------------------------------------
73
74 - Load module (and main kernel) symbols::
75
76 (gdb) lx-symbols
77 loading vmlinux
78 scanning for modules in /home/user/linux/build
79 loading @0xffffffffa0020000: /home/user/linux/build/net/netfilter/xt_tcpudp.ko
80 loading @0xffffffffa0016000: /home/user/linux/build/net/netfilter/xt_pkttype.ko
81 loading @0xffffffffa0002000: /home/user/linux/build/net/netfilter/xt_limit.ko
82 loading @0xffffffffa00ca000: /home/user/linux/build/net/packet/af_packet.ko
83 loading @0xffffffffa003c000: /home/user/linux/build/fs/fuse/fuse.ko
84 ...
85 loading @0xffffffffa0000000: /home/user/linux/build/drivers/ata/ata_generic.ko
86
87 - Set a breakpoint on some not yet loaded module function, e.g.::
88
89 (gdb) b btrfs_init_sysfs
90 Function "btrfs_init_sysfs" not defined.
91 Make breakpoint pending on future shared library load? (y or [n]) y
92 Breakpoint 1 (btrfs_init_sysfs) pending.
93
94 - Continue the target::
95
96 (gdb) c
97
98 - Load the module on the target and watch the symbols being loaded as well as
99 the breakpoint hit::
100
101 loading @0xffffffffa0034000: /home/user/linux/build/lib/libcrc32c.ko
102 loading @0xffffffffa0050000: /home/user/linux/build/lib/lzo/lzo_compress.ko
103 loading @0xffffffffa006e000: /home/user/linux/build/lib/zlib_deflate/zlib_deflate.ko
104 loading @0xffffffffa01b1000: /home/user/linux/build/fs/btrfs/btrfs.ko
105
106 Breakpoint 1, btrfs_init_sysfs () at /home/user/linux/fs/btrfs/sysfs.c:36
107 36 btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj);
108
109 - Dump the log buffer of the target kernel::
110
111 (gdb) lx-dmesg
112 [ 0.000000] Initializing cgroup subsys cpuset
113 [ 0.000000] Initializing cgroup subsys cpu
114 [ 0.000000] Linux version 3.8.0-rc4-dbg+ (...
115 [ 0.000000] Command line: root=/dev/sda2 resume=/dev/sda1 vga=0x314
116 [ 0.000000] e820: BIOS-provided physical RAM map:
117 [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
118 [ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
119 ....
120
121 - Examine fields of the current task struct(supported by x86 and arm64 only)::
122
123 (gdb) p $lx_current().pid
124 $1 = 4998
125 (gdb) p $lx_current().comm
126 $2 = "modprobe\000\000\000\000\000\000\000"
127
128 - Make use of the per-cpu function for the current or a specified CPU::
129
130 (gdb) p $lx_per_cpu(runqueues).nr_running
131 $3 = 1
132 (gdb) p $lx_per_cpu(runqueues, 2).nr_running
133 $4 = 0
134
135 - Dig into hrtimers using the container_of helper::
136
137 (gdb) set $leftmost = $lx_per_cpu(hrtimer_bases).clock_base[0].active.rb_root.rb_leftmost
138 (gdb) p *$container_of($leftmost, "struct hrtimer", "node")
139 $5 = {
140 node = {
141 node = {
142 __rb_parent_color = 18446612686384860673,
143 rb_right = 0xffff888231da8b00,
144 rb_left = 0x0
145 },
146 expires = 1228461000000
147 },
148 _softexpires = 1228461000000,
149 function = 0xffffffff8137ab20 <tick_nohz_handler>,
150 base = 0xffff888231d9b4c0,
151 state = 1 '\001',
152 is_rel = 0 '\000',
153 is_soft = 0 '\000',
154 is_hard = 1 '\001'
155 }
156
157
158 List of commands and functions
159 ------------------------------
160
161 The number of commands and convenience functions may evolve over the time,
162 this is just a snapshot of the initial version::
163
164 (gdb) apropos lx
165 function lx_current -- Return current task
166 function lx_module -- Find module by name and return the module variable
167 function lx_per_cpu -- Return per-cpu variable
168 function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
169 function lx_thread_info -- Calculate Linux thread_info from task variable
170 lx-dmesg -- Print Linux kernel log buffer
171 lx-lsmod -- List currently loaded modules
172 lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules
173
174 Detailed help can be obtained via "help <command-name>" for commands and "help
175 function <function-name>" for convenience functions.
176

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

GDB로 kernel과 module debug하기

1-19

kgdb, QEMU 같은 hypervisor, JTAG 기반 hardware interface가 제공하는 GDB stub을 이용하면 running Linux kernel과 module을 GDB로 debug할 수 있다. GDB에는 강력한 Python scripting interface가 있으며 kernel source는 반복적인 kernel debugging 절차를 단순화하는 helper script를 제공한다.

이 문서는 QEMU/KVM virtual machine을 target으로 helper를 enable하고 사용하는 짧은 tutorial이지만 같은 예를 다른 GDB stub에도 적용할 수 있다. Python support를 enable한 GDB 7.2 이상이 필요하고 7.4 이상을 권장한다.

Target과 kernel build 준비

22-45
  • QEMU/KVM용 Linux virtual machine을 만든다. 자세한 내용은 www.linux-kvm.org와 www.qemu.org를 참고한다. Cross development의 시작 image와 toolchain은 https://landley.net/aboriginal/bin 에서 구할 수 있다.
  • Kernel을 CONFIG_GDB_SCRIPTS=y로 build하고 CONFIG_DEBUG_INFO_REDUCED는 끈다. Architecture가 CONFIG_FRAME_POINTER를 지원하면 enable한다.
  • Guest에 kernel을 설치하고 필요하면 kernel command line에 nokaslr를 추가해 KASLR을 끈다.
  • Module에 의존하지 않는 경우 QEMU의 -kernel, -append, -initrd option으로 kernel을 직접 boot할 수도 있다. 이 방식에서 architecture가 KASLR을 지원한다면 CONFIG_RANDOMIZE_BASE를 disable한다.
  • Linux v5.1 이상에서는 make scripts_gdb로 GDB helper script를 build한다.
make scripts_gdb

QEMU GDB stub과 연결

46-69

VM을 시작할 때 QEMU command line에 -s를 추가하거나 running VM의 QEMU monitor console에서 gdbserver를 실행해 GDB stub을 enable한다.

cd /path/to/linux-build
gdb vmlinux

(gdb) target remote :1234

일부 distribution은 안전하다고 등록된 directory에서만 GDB script auto-loading을 허용한다. GDB가 vmlinux-gdb.py load를 거부하면 ~/.gdbinit에 다음 설정을 추가한다.

add-auto-load-safe-path /path/to/linux-build

Kernel과 module symbol load

71-86
(gdb) lx-symbols
loading vmlinux
scanning for modules in /home/user/linux/build
loading @0xffffffffa0020000: /home/user/linux/build/net/netfilter/xt_tcpudp.ko
loading @0xffffffffa0016000: /home/user/linux/build/net/netfilter/xt_pkttype.ko
loading @0xffffffffa0002000: /home/user/linux/build/net/netfilter/xt_limit.ko
loading @0xffffffffa00ca000: /home/user/linux/build/net/packet/af_packet.ko
loading @0xffffffffa003c000: /home/user/linux/build/fs/fuse/fuse.ko
...
loading @0xffffffffa0000000: /home/user/linux/build/drivers/ata/ata_generic.ko

lx-symbols는 vmlinux symbol을 load하고 build directory를 scan해 target에서 현재 load된 module의 .ko file을 실제 load address에 연결한다.

아직 load되지 않은 module에 breakpoint 걸기

87-107
(gdb) b btrfs_init_sysfs
Function "btrfs_init_sysfs" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (btrfs_init_sysfs) pending.

(gdb) c

loading @0xffffffffa0034000: /home/user/linux/build/lib/libcrc32c.ko
loading @0xffffffffa0050000: /home/user/linux/build/lib/lzo/lzo_compress.ko
loading @0xffffffffa006e000: /home/user/linux/build/lib/zlib_deflate/zlib_deflate.ko
loading @0xffffffffa01b1000: /home/user/linux/build/fs/btrfs/btrfs.ko

Breakpoint 1, btrfs_init_sysfs () at /home/user/linux/fs/btrfs/sysfs.c:36
36    btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj);

GDB가 아직 symbol을 모르는 module function에 pending breakpoint를 만들고 target을 계속 실행하면 module load 시 lx-symbols가 symbol을 연결한 뒤 해당 source line에서 멈출 수 있다.

Kernel log와 current task 확인

109-127
(gdb) lx-dmesg
[     0.000000] Initializing cgroup subsys cpuset
[     0.000000] Initializing cgroup subsys cpu
[     0.000000] Linux version 3.8.0-rc4-dbg+ (...
[     0.000000] Command line: root=/dev/sda2 resume=/dev/sda1 vga=0x314
[     0.000000] e820: BIOS-provided physical RAM map:
...

(gdb) p $lx_current().pid
$1 = 4998
(gdb) p $lx_current().comm
$2 = "modprobe\000\000\000\000\000\000\000"

lx-dmesg는 target kernel의 log buffer를 출력한다. x86과 arm64에서는 $lx_current()로 현재 task_struct를 얻어 pid와 comm 같은 field를 조사할 수 있다.

Per-CPU 자료와 container_of helper

128-155
(gdb) p $lx_per_cpu(runqueues).nr_running
$3 = 1
(gdb) p $lx_per_cpu(runqueues, 2).nr_running
$4 = 0

(gdb) set $leftmost = $lx_per_cpu(hrtimer_bases).clock_base[0].active.rb_root.rb_leftmost
(gdb) p *$container_of($leftmost, "struct hrtimer", "node")

$lx_per_cpu(symbol)는 current CPU의 per-CPU variable을 반환하고 두 번째 인자로 CPU 번호를 주면 특정 CPU 값을 반환한다. 예시는 runqueue의 nr_running을 읽는다.

Hrtimer 예시는 per-CPU hrtimer_bases에서 active rbtree의 leftmost node를 얻은 뒤 $container_of()로 struct hrtimer를 복원한다. 출력에서는 rb node pointer, expires와 _softexpires, callback function tick_nohz_handler, base, state, relative/soft/hard flag를 직접 확인할 수 있다.

제공되는 command와 convenience function

158-175
이름기능
lx_currentcurrent task를 반환한다
lx_module이름으로 module을 찾아 module variable을 반환한다
lx_per_cpuper-CPU variable을 반환한다
lx_task_by_pidPID로 task를 찾아 task_struct variable을 반환한다
lx_thread_infotask variable에서 Linux thread_info를 계산한다
lx-dmesgLinux kernel log buffer를 출력한다
lx-lsmod현재 load된 module을 나열한다
lx-symbolskernel과 현재 load된 module symbol을 다시 load한다

Command의 세부 도움말은 help <command-name>, convenience function의 도움말은 help function <function-name>으로 확인한다. Helper 수와 기능은 kernel version에 따라 달라질 수 있으며 이 목록은 문서 작성 당시의 snapshot이다.