GDB Linux Kernel Awareness
文章目录
1、GDB Kernel Awareness介绍
GDB Linux Kernel Awareness称为GDB内核感知,它属于GDB调试的扩展部分,它有什么作用呢?
我们平常使用GDB调试的时候,一般都是查看某些寄存器,打一些断点,看某些内存空间,一步一步调试驱动或者应用程序;
那么对于Linux这么庞大的系统,我们调试时,很多数据结构相互包含,我们调试的时候,GDB并不知道我们调试的是什么,不知道是在调试线程,还是在调试时钟等,这就是GDB Linux Kernel Awareness的作用。
举个例子:
比如我们在使用gdb的时候,想查看当前运行的所有进程,该怎么办?
带着问题往下看吧。
2、内核配置
为了构建能够调试的内核,我们需要配置以下几个参数
- CONFIG_DEBUG_INFO:包含内核调试信息,这个选项在幕后为gcc使用的编译器参数增加了-g选项。
- CONFIG_GDB_SCRIPTS:提供GDB Scripts用于调试内核
# 开启kernel debug info
Kernel hacking --->
[*] Kernel debugging
Compile-time checks and compiler options --->
[*] Compile the kernel with debug info
[*] Provide GDB scripts for kernel debuggin
打开相应配置后,编译内核,编译结束后会在内核根目录下生成vmlinux-gdb.py文件。
3、连接GDB
我使用的是Jlink,直接与开发板连接
# Ubuntu的一个终端用于连接开发板
JLinkGDBServer -device Cortex-A7 -endian little -if SWD
# 另一个终端用于调试
arm-linux-gnueabihf-gdb vmlinux
add-auto-load-safe-path ./ # 设置内核输出路径
source ./vmlinux-gdb.py # 一定要指定./ 当前目录,否则可能会报错
target remote :2331
问题一:
注意:某些发行版可能会将gdb脚本的自动加载限制在已知的安全目录中。 如果gdb报告拒绝加载vmlinux-gdb.py(相关命令找不到),请将:
add-auto-load-safe-path /path/to/linux-build
问题二:
加载时报错:找不到linux.utils模块
(gdb) source vmlinux-gdb.py
Traceback (most recent call last):
File "vmlinux-gdb.py", line 25, in <module>
import linux.utils
ImportError: No module named linux.utils
需要加上当前路径:source ./vmlinux-gdb.py
这里使用的gdb script脚本,就是scripts/gdb/下面的脚本。
3、内核调试
如果前面都没有报错,我们就能够可以调试内核了,通过apropos lx查看目前所支持的调试命令。
apropos lx # 查看支持的扩展命令
function lx_current -- Return current task
function lx_module -- Find module by name and return the module variable
function lx_per_cpu -- Return per-cpu variable
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
function lx_thread_info -- Calculate Linux thread_info from task variable
function lx_thread_info_by_pid -- Calculate Linux thread_info from task variable found by pid
lx-cmdline -- Report the Linux Commandline used in the current kernel
lx-cpus -- List CPU status arrays
lx-dmesg -- Print Linux kernel log buffer
lx-fdtdump -- Output Flattened Device Tree header and dump FDT blob to the filename
lx-iomem -- Identify the IO memory resource locations defined by the kernel
lx-ioports -- Identify the IO port resource locations defined by the kernel
lx-list-check -- Verify a list consistency
lx-lsmod -- List currently loaded modules
lx-mounts -- Report the VFS mounts of the current process namespace
lx-ps -- Dump Linux tasks
lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules
lx-version -- Report the Linux Version of the current kernel
3.1 lx-cmdline
lx-cmdline:查看传入内核的参数
(gdb) lx-cmdline
loglevel=8 console=ttyS1,115200 rootwait root=/dev/mmcblk0p1 rootfstype=ext4 boottype=spinor spiafficpu=2 mtdparts=spi0.0:192k(boot)ro,4416k(system)ro,4m(rootfs),20m(firmware),3456k(data),512k(rp),128k(pstore) uboot_msg=2020.10-rc1-g40c17dce6d-dirty(01/12/2024-11:45:56)
3.2 lx-cpus
lx-cpus:查看cpu状态
(gdb) lx-cpus
Possible CPUs : [0, 1, 2]
Present CPUs : [0, 1, 2]
Online CPUs : [0, 1, 2]
Active CPUs : [0, 1, 2]
3.3 lx-dmesg
lx-dmesg:查看linux内核log
3.4 lx-fdtdump
lx-fdtdump:查看设备树,并dump出来
(gdb) lx-fdtdump
fdt_magic: 0xD00DFEED
fdt_totalsize: 0xC000
off_dt_struct: 0x38
off_dt_strings: 0xA764
off_mem_rsvmap: 0x28
version: 17
last_comp_version: 16
Dumped fdt blob to fdtdump.dtb
3.5 lx-iomem
lx-iomem:IO内存资源分配
(gdb) lx-iomem
10000000-17ffffff : System RAM
10008000-109fffff : Kernel code
10b00000-10baefc7 : Kernel data
40002000-4000201f : serial
40006000-4000601f : serial
3.6 lx-lsmod
lx-lsmod:查看加载模块
3.7 lx-mounts
lx-mounts:查看所有mount的文件系统
(gdb) lx-mounts
rootfs / rootfs rw 0 0
/dev/root / ext4 rw,relatime 0 0
devtmpfs /dev devtmpfs rw,relatime 0 0
proc /proc proc rw,nodiratime,relatime 0 0
devpts /dev/pts devpts rw,relatime 0 0
sysfs /sys sysfs rw,relatime 0 0
3.8 lx-ps
lx-ps:查看各个进程
(gdb) lx-ps
0xc0b07fc0 <init_task> 0 swapper/0
0xc78e0000 1 init
0xc78e0600 2 kthreadd
0xc78e0c00 3 rcu_gp
3.9 lx-symbols
lx-symbols:当前加载的模块
3.10 lx-version
lx-version:当前版本
除了直接调用命令外,一些提供的function函数也能直接使用。
支持的函数如下:
function lx_current -- Return current task
function lx_module -- Find module by name and return the module variable
function lx_per_cpu -- Return per-cpu variable
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
function lx_thread_info -- Calculate Linux thread_info from task variable
function lx_thread_info_by_pid -- Calculate Linux thread_info from task variable found by pid
3.11 lx_task_by_pid
lx_task_by_pid:根据PID查找每个task的状态,其结构体类型为struct task_struct
我们通过lx-ps可以查看到每个进程的pid,然后直接调用该函数,能够获取指定进程的各个字段信息。
调用指令:p $lx_task_by_pid(pid编号)
(gdb) lx-ps
0xc0b07fc0 <init_task> 0 swapper/0
0xc78e0000 1 init
0xc78e0600 2 kthreadd
0xc78e0c00 3 rcu_gp
0xc78e1200 4 rcu_par_gp
0xc78e1800 5 kworker/0:0
0xc78e2a00 8 mm_percpu_wq
0xc78e3000 9 ksoftirqd/0
0xc78e3600 10 rcu_preempt
0xc78e3c00 11 rcu_sched
0xc78e4200 12 rcu_bh
0xc78e4800 13 migration/0
......
0xc7a8d400 105 syslogd
0xc7a8e000 109 klogd
.......
(gdb) p $lx_task_by_pid(109)
$6 = {state = 1, stack = 0xc6a48000, usage = {counter = 2}, flags = 4194560, ptrace = 0, wake_entry = {next = 0x0}, on_cpu = 0, wakee_flips = 144, wakee_flip_decay_ts = 0,
last_wakee = 0xc7a8d400, recent_used_cpu = 0, wake_cpu = 0, on_rq = 0, prio = 120, static_prio = 120, normal_prio = 120, rt_priority = 0, sched_class = 0xc070225c <fair_sched_class>,
se = {load = {weight = 1024, inv_weight = 4194304}, runnable_weight = 1024, run_node = {__rb_parent_color = 1, rb_right = 0x0, rb_left = 0x0}, group_node = {next = 0xc7a8e098,
prev = 0xc7a8e098}, on_rq = 0, exec_start = 5726624435, sum_exec_runtime = 19151794, vruntime = 668774412, prev_sum_exec_runtime = 18865627, nr_migrations = 6,
statistics = {<No data fields>}, avg = {last_update_time = 5726623744, load_sum = 327, runnable_load_sum = 327, util_sum = 285696, period_contrib = 342, load_avg = 0,
runnable_load_avg = 0, util_avg = 0, util_est = {enqueued = 1, ewma = 13}}}, rt = {run_list = {next = 0xc7a8e140, prev = 0xc7a8e140}, timeout = 0, watchdog_stamp = 0,
time_slice = 25, on_rq = 0, on_list = 0, back = 0x0}, dl = {rb_node = {__rb_parent_color = 3349733728, rb_right = 0x0, rb_left = 0x0}, dl_runtime = 0, dl_deadline = 0, dl_period = 0,
dl_bw = 0, dl_density = 0, runtime = 0, deadline = 0, flags = 0, dl_throttled = 0, dl_boosted = 0, dl_yielded = 0, dl_non_contending = 0, dl_overrun = 0, dl_timer = {node = {node = {
__rb_parent_color = 3349733808, rb_right = 0x0, rb_left = 0x0}, expires = 0}, _softexpires = 0, function = 0xc0156688 <dl_task_timer>, base = 0xc7ed0800, state = 0 '\000',
is_rel = 0 '\000', is_soft = 0 '\000'}, inactive_timer = {node = {node = {__rb_parent_color = 3349733856, rb_right = 0x0, rb_left = 0x0}, expires = 0}, _softexpires = 0,
function = 0xc0157e88 <inactive_task_timer>, base = 0xc7ed0800, state = 0 '\000', is_rel = 0 '\000', is_soft = 0 '\000'}}, policy = 0, nr_cpus_allowed = 3, cpus_allowed = {bits = {
7}}, rcu_read_lock_nesting = 0, rcu_read_unlock_special = {b = {blocked = 0 '\000', need_qs = 0 '\000', exp_need_qs = 0 '\000', pad = 0 '\000'}, s = 0}, rcu_node_entry = {
next = 0xc7a8e224, prev = 0xc7a8e224}, rcu_blocked_node = 0x0, rcu_tasks_nvcsw = 0, rcu_tasks_holdout = 0 '\000', rcu_tasks_idx = 0 '\000', rcu_tasks_idle_cpu = -1,
rcu_tasks_holdout_list = {next = 0xc7a8e23c, prev = 0xc7a8e23c}, sched_info = {<No data fields>}, tasks = {next = 0xc6802044, prev = 0xc7a8d644}, pushable_tasks = {prio = 140,
prio_list = {next = 0xc7a8e250, prev = 0xc7a8e250}, node_list = {next = 0xc7a8e258, prev = 0xc7a8e258}}, pushable_dl_tasks = {__rb_parent_color = 3349733984, rb_right = 0x0,
rb_left = 0x0}, mm = 0xc6a24600, active_mm = 0xc6a24600, vmacache = {seqnum = 6, vmas = {0x0, 0x0, 0xc699bea0, 0xc699b9c0}}, exit_state = 0, exit_code = 0, exit_signal = 17,
pdeath_signal = 0, jobctl = 0, personality = 8388608, sched_reset_on_fork = 0, sched_contributes_to_load = 0, sched_migrated = 0, sched_remote_wakeup = 0, in_execve = 0, in_iowait = 0,
brk_randomized = 0, atomic_flags = 0, restart_block = {fn = 0xc012f254 <do_no_restart_syscall>, {futex = {uaddr = 0x0, val = 0, flags = 0, bitset = 0, time = 0, uaddr2 = 0x0},
nanosleep = {clockid = 0, type = TT_NONE, {rmtp = 0x0, compat_rmtp = 0x0}, expires = 0}, poll = {ufds = 0x0, nfds = 0, has_timeout = 0, tv_sec = 0, tv_nsec = 0}}}, pid = 109,
tgid = 109, stack_canary = 3015467484, real_parent = 0xc78e0000, parent = 0xc78e0000, children = {next = 0xc7a8e2f4, prev = 0xc7a8e2f4}, sibling = {next = 0xc795e8fc,
prev = 0xc7a8d6fc}, group_leader = 0xc7a8e000, ptraced = {next = 0xc7a8e308, prev = 0xc7a8e308}, ptrace_entry = {next = 0xc7a8e310, prev = 0xc7a8e310}, thread_pid = 0xc7a91680,
pid_links = {{next = 0x0, pprev = 0xc7a91688}, {next = 0x0, pprev = 0xc7a9168c}, {next = 0x0, pprev = 0xc7960390}, {next = 0x0, pprev = 0xc7960394}}, thread_group = {next = 0xc7a8e33c,
prev = 0xc7a8e33c}, thread_node = {next = 0xc7bf4f0c, prev = 0xc7bf4f0c}, vfork_done = 0x0, set_child_tid = 0x0, clear_child_tid = 0x0, utime = 12000000, stime = 4000000, gtime = 0,
prev_cputime = {utime = 0, stime = 0, lock = {raw_lock = {{slock = 0, tickets = {owner = 0, next = 0}}}}}, nvcsw = 13, nivcsw = 4, start_time = 2611642126,
real_start_time = 2611643042, min_flt = 47, maj_flt = 0, cputime_expires = {utime = 0, stime = 0, sum_exec_runtime = 0}, cpu_timers = {{next = 0xc7a8e3c0, prev = 0xc7a8e3c0}, {
next = 0xc7a8e3c8, prev = 0xc7a8e3c8}, {next = 0xc7a8e3d0, prev = 0xc7a8e3d0}}, ptracer_cred = 0x0, real_cred = 0xc7953900, cred = 0xc7953900, comm = "klogd\000stop-daem",
nameidata = 0x0, sysvsem = {undo_list = 0x0}, sysvshm = {shm_clist = {next = 0xc7a8e3fc, prev = 0xc7a8e3fc}}, last_switch_count = 0, last_switch_time = 0, fs = 0xc7a91640,
files = 0xc69a3700, nsproxy = 0xc0b10e38 <init_nsproxy>, signal = 0xc7bf4f00, sighand = 0xc6a3ca80, blocked = {sig = {0, 0}}, real_blocked = {sig = {0, 0}}, saved_sigmask = {sig = {0,
0}}, pending = {list = {next = 0xc7a8e438, prev = 0xc7a8e438}, signal = {sig = {0, 0}}}, sas_ss_sp = 0, sas_ss_size = 0, sas_ss_flags = 2, task_works = 0x0, audit_context = 0x0,
seccomp = {<No data fields>}, parent_exec_id = 4, self_exec_id = 5, alloc_lock = {{rlock = {raw_lock = {{slock = 917518, tickets = {owner = 14, next = 14}}}}}}, pi_lock = {raw_lock = {{
slock = 3407924, tickets = {owner = 52, next = 52}}}}, wake_q = {next = 0x0}, pi_waiters = {rb_root = {rb_node = 0x0}, rb_leftmost = 0x0}, pi_top_task = 0x0, pi_blocked_on = 0x0,
journal_info = 0x0, bio_list = 0x0, plug = 0x0, reclaim_state = 0x0, backing_dev_info = 0x0, io_context = 0x0, ptrace_message = 0, last_siginfo = 0x0, ioac = {<No data fields>},
robust_list = 0x0, pi_state_list = {next = 0xc7a8e4b0, prev = 0xc7a8e4b0}, pi_state_cache = 0x0, perf_event_ctxp = {0x0, 0x0}, perf_event_mutex = {owner = {counter = 0}, wait_lock = {{
rlock = {raw_lock = {{slock = 0, tickets = {owner = 0, next = 0}}}}}}, osq = {tail = {counter = 0}}, wait_list = {next = 0xc7a8e4d0, prev = 0xc7a8e4d0}}, perf_event_list = {
next = 0xc7a8e4d8, prev = 0xc7a8e4d8}, rseq = 0x0, rseq_len = 0, rseq_sig = 0, rseq_event_mask = 5, tlb_ubc = {<No data fields>}, rcu = {next = 0x0, func = 0x0}, splice_pipe = 0x0,
task_frag = {page = 0x0, offset = 0, size = 0}, nr_dirtied = 0, nr_dirtied_pause = 32, dirty_paused_when = 0, timer_slack_ns = 50000, default_timer_slack_ns = 50000,
curr_ret_stack = -1, curr_ret_depth = -1, ret_stack = 0x0, ftrace_timestamp = 0, trace_overrun = {counter = 0}, tracing_graph_pause = {counter = 0}, trace = 0, trace_recursion = 0,
utask = 0x0, pagefault_disabled = 0, oom_reaper_list = 0x0, thread = {address = 0, trap_no = 0, error_code = 0, debug = {hbp = {0x0 <repeats 32 times>}}}}
要想获取单独字段,也支持p $lx_task_by_pid(PID).segment,如下:
(gdb) p $lx_task_by_pid(109).state
$15 = 1
3.12 lx_thread_info
lx_thread_info:根据线程symbol,来获取对应的线程信息
(gdb) p $lx_thread_info(init_task)
$1 = {flags = 3232760088, preempt_count = -962472448, addr_limit = 3232759868,
task = 0xc0b00018 <init_thread_info+24>, cpu = 3222359180, cpu_domain = 3222359880, cpu_context = {r4 = 3354095296,
r5 = 3232792512, r6 = 3221241856, r7 = 2147483653, r8 = 0, r9 = 3232760088, sl = 3232759900, fp = 3232759872,
sp = 3222359388, pc = 3222359056, extra = {5, 3232779272}}, syscall = 3780247554,
used_cp = "\030\001\260\300\024\001\260\300`\000\260\300\264N\021\300", tp_value = {3222359224, 1879031663},
fpstate = {hard = {save = {0 <repeats 33 times>, 3230004071, 0}}, soft = {save = {0 <repeats 33 times>, 3230004071,
0}}}, vfpstate = {hard = {fpregs = {18446744072099725747, 18446744072647344460, 13884598857006842236,
13839929358871960484, 18446744072647344512, 16236039623716376576, 3232760192, 2305839092203543999, 4294967295,
13884599283270811653, 13884599200604225920, 16236039618442095632, 18446744072099725747, 13763000461244235857,
3232779272, 13884599303683441060, 13864590854932982872, 13872761850891862016, 16257997536730546176,
13873206006344843264, 13884599475482132916, 13864591164170628308, 13884599544201609724, 13864591713921042068,
0, 0, 0, 0, 13884600282932981419, 13884770600926314496, 24707596288, 13884599853439255060},
fpexc = 3222702248, fpscr = 3222701308, fpinst = 3229757099, fpinst2 = 3232760420, cpu = 3232760380}}}
3.13 lx_thread_info_by_pid
lx_thread_info_by_pid:根据指定的PID,查找每个线程的状态,其结构体为struct thread_info
(gdb) p $lx_thread_info_by_pid(109)
$12 = {flags = 0, preempt_count = 2, addr_limit = 3204448256, task = 0xc7a8e000, cpu = 0, cpu_domain = 0, cpu_context = {r4 = 3354095296, r5 = 3349733376, r6 = 3349730304,
r7 = 3332523520, r8 = 3332523008, r9 = 3228196620, sl = 3228574208, fp = 3332677396, sp = 3332677312, pc = 3228196228, extra = {0, 0}}, syscall = 0,
used_cp = "\000\000\000\000\000\000\000\000\000\000\001\001\000\000\000", tp_value = {3069935712, 0}, fpstate = {hard = {save = {0 <repeats 35 times>}}, soft = {save = {
0 <repeats 35 times>}}}, vfpstate = {hard = {fpregs = {7021710432085763445, 7598814389921932660, 3473436823868759924, 8443739334891561264, 7598245610908836467,
8318823019731641718, 2320822906498477929, 4991486266103057747, 0 <repeats 24 times>}, fpexc = 1073741824, fpscr = 0, fpinst = 3233240588, fpinst2 = 3332677208, cpu = 0}}}
3.14 current_task
current_task:该命令仅arm64和x86架构支持,使用方法如下:
(gdb) p $lx_current().pid
$1 = 4998
(gdb) p $lx_current().comm
$2 = "modprobe\000\000\000\000\000\000\000"
3.15 lx_module
lx_module:查找指定模块,并返回struct module类型变量。
使用方法: p $lx_module(“module_name”)
例子如下,通过lx-lsmod获取所有模块名称,然后调用lx_module来获取该模块信息。
(gdb) lx-lsmod
Address Module Size Used by
0xbf026000 arobot_udc 28672 0
0xbf011000 udc_core 53248 1 arobot_udc
0xbf00a000 usb_common 16384 1 udc_core
0xbf000000 gpio_keys 20480 0
(gdb) p $lx_module("gpio_keys")
$19 = {state = MODULE_STATE_LIVE, list = {next = 0xc0b19144 <modules>, prev = 0xbf00c004}, name = "gpio_keys", '\000' <repeats 50 times>, mkobj = {kobj = {name = 0xc6a9d5c0 "gpio_keys",
entry = {next = 0xbf00c04c, prev = 0xc7a3e104}, parent = 0xc7a3710c, kset = 0xc7a37100, ktype = 0xc0b10d30 <module_ktype>, sd = 0xc6918180, kref = {refcount = {refs = {
counter = 4}}}, state_initialized = 1, state_in_sysfs = 1, state_add_uevent_sent = 1, state_remove_uevent_sent = 0, uevent_suppress = 0}, mod = 0xbf0030c0,
drivers_dir = 0xc698bfc0, mp = 0x0, kobj_completion = 0x0}, modinfo_attrs = 0xc6b82c00, version = 0x0, srcversion = 0x0, holders_dir = 0xc6a9d640, syms = 0x0, crcs = 0x0,
num_syms = 0, param_lock = {owner = {counter = 0}, wait_lock = {{rlock = {raw_lock = {{slock = 0, tickets = {owner = 0, next = 0}}}}}}, osq = {tail = {counter = 0}}, wait_list = {
next = 0xbf003164, prev = 0xbf003164}}, kp = 0x0, num_kp = 0, num_gpl_syms = 0, gpl_syms = 0x0, gpl_crcs = 0x0, async_probe_requested = false, gpl_future_syms = 0x0,
gpl_future_crcs = 0x0, num_gpl_future_syms = 0, num_exentries = 0, extable = 0x0, init = 0xbf006000, core_layout = {base = 0xbf000000 <gpio_keys_close>, size = 20480, text_size = 8192,
ro_size = 12288, ro_after_init_size = 12288, mtn = {mod = 0xbf0030c0, node = {node = {{__rb_parent_color = 3204497689, rb_right = 0x0, rb_left = 0x0}, {
__rb_parent_color = 3204497701, rb_right = 0x0, rb_left = 0x0}}}}}, init_layout = {base = 0x0, size = 0, text_size = 0, ro_size = 0, ro_after_init_size = 0, mtn = {
mod = 0xbf0030c0, node = {node = {{__rb_parent_color = 1, rb_right = 0xbf00c118, rb_left = 0xbf0031d8}, {__rb_parent_color = 1, rb_right = 0xbf00c124, rb_left = 0xbf0031e4}}}}},
arch = {unwind = {0xc6a9d480, 0x0, 0xc6a9d4c0, 0xc6a9d500, 0x0, 0x0, 0x0}}, taints = 0, kallsyms = 0xbf003244, core_kallsyms = {symtab = 0xbf004000, num_symtab = 78,
strtab = 0xbf0044e0 ""}, sect_attrs = 0xc6a11400, notes_attrs = 0xc6a9df80, args = 0xc6a9d540 "", percpu = 0x0, percpu_size = 0, num_tracepoints = 0, tracepoints_ptrs = 0x0,
jump_entries = 0x0, num_jump_entries = 0, num_trace_bprintk_fmt = 0, trace_bprintk_fmt_start = 0x0, trace_events = 0x0, num_trace_events = 0, trace_evals = 0x0, num_trace_evals = 0,
num_ftrace_callsites = 22, ftrace_callsites = 0xbf0027d4, source_list = {next = 0xbf003294, prev = 0xbf003294}, target_list = {next = 0xbf00329c, prev = 0xbf00329c}, exit = 0xbf0012f4,
refcnt = {counter = 1}}
目前可供GDB调试的命令还比较少,我们也可以根据规则自己编写我们需要的调试指令。