Linux内核使用gdb调试

这里记录平时使用gdb调试内核ke的步骤和方法. 有不足的地方也请大家指出和完善.
1 必备工具和文件
gdb,addr2line,vmlinux以及内核coredump文件
在64位平台,gdb和addr2line 分别使用aarch64-linux-android-gdb
aarch64-linux-android-addr2line.
2. 调试过程
mtk平台coredump文件名为: sys_mini_rdump,用gat工具解析db文件得到.
2.1 启动gdb
aarch64-linux-android-gdb vmlinux coredump
aarch64-linux-android-gdb ./vmlinux./aee_exp_backup/db.fatal.00.ke/20151107_170222_178/db.fatal.00.ke.dbg.dec/sys_mini_rdump
控制台输出内容:
#0 0xffffffc00088d2c8 in eth_start_xmit(skb=0xffffffc023ba8300, net=0xffffffc06d3d2000)
at kernel-3.10/drivers/usb/gadget/u_ether.c:893
(gdb)
可以看出异常点在u__ether.c文件893行.
2.2 gdb常用指令
bt : 打印堆栈调用信息.
down : 跳转到下一级fp指针
up : 回到上一级fp指针
p : 打印变量值
x : 打印内存内容
x / (n,f,u为可选参数)
n: 需要显示的内存单元个数,也就是从当前地址向后显示几个内存单元的内容,一个内存单元的大小由后面的u定义
f:显示格式
x(hex) 按十六进制格式显示变量。
d(decimal) 按十进制格式显示变量。
u(unsigned decimal) 按十进制格式显示无符号整型。
o(octal) 按八进制格式显示变量。
t(binary) 按二进制格式显示变量。
a(address) 按十六进制格式显示变量。
c(char) 按字符格式显示变量。
f(float) 按浮点数格式显示变量
u:每个单元的大小,按字节数来计算。默认是4 bytes。gdb会从指定内存地址开始读取指定字节,并把其当作一个值取出来,并使用格式f来显示
b:1 byte h:2 bytes w:4 bytes g:8 bytes
比如x/3uh 0x54320表示从内存地址0x54320读取内容,h表示以双字节为单位,3表示输出3个单位,u表示按照十六进制显示。
list : 以c语言列出当前函数内容(c语言)
disassemble :以汇编方式列出当前函数内容
2.3 异常点分析
可以从last_kmsg或者db文件解析出的sys_kernel_log中得知异常类型.重要信息为pc和寄存器值.
unable to handle kernel null pointerdereference at virtual address 000000e4
[6464.203080]-(0)[3:ksoftirqd/0]pc is at eth_start_xmit+0x1fc/0x748
[6464.203112]-(0)[3:ksoftirqd/0]lr is at eth_start_xmit+0x1d8/0x748
[6464.203143]-(0)[3:ksoftirqd/0]pc : [] lr :[] pstate: 800001c5
[6464.203168]-(0)[3:ksoftirqd/0]sp : ffffffc071877b40
[ 6464.203192]-(0)[3:ksoftirqd/0]x29:ffffffc071877b40 x28: 00000000000005bc
[6464.203231]-(0)[3:ksoftirqd/0]x27: 00000000000005bc x26:ffffffc01ee14c40
[6464.203270]-(0)[3:ksoftirqd/0]x25: ffffffc06ee12510 x24:ffffffc06d3d2730
[ 6464.203308]-(0)[3:ksoftirqd/0]x23:ffffffc023ba8300 x22: ffffffc06d3d2720
[6464.203347]-(0)[3:ksoftirqd/0]x21: ffffffc06d3d2000 x20:ffffffc06d3d2700
[6464.203385]-(0)[3:ksoftirqd/0]x19: ffffffc00141e000 x18:0000000000000000
[ 6464.203422]-(0)[3:ksoftirqd/0]x17:0000007f7ed6fcf8 x16: ffffffc000278828
[6464.203459]-(0)[3:ksoftirqd/0]x15: 0000007f7eda9a24 x14:228f252b6f65a378
[6464.203498]-(0)[3:ksoftirqd/0]x13: 9939719eb9fc9521 x12:0260832913e230f2
[6464.203535]-(0)[3:ksoftirqd/0]x11: 63530fe2e6e696f3 x10:399aa79385bb3861
[6464.203573]-(0)[3:ksoftirqd/0]x9 : 01a6b3c12e057068 x8 :2421eada8933ba1d
[6464.203610]-(0)[3:ksoftirqd/0]x7 : e4324d79f1892abb x6 :ffffffc0393165bc
[6464.203646]-(0)[3:ksoftirqd/0]x5 : 0000000000000000 x4 :0000000000000003
[6464.203682]-(0)[3:ksoftirqd/0]x3 : 0000000000000002 x2 :0000000000000000
[6464.203718]-(0)[3:ksoftirqd/0]x1 : 0000000000000140 x0 :ffffffc06d3d2000
启动gdb时会显示最后出现点, 以上面的异常来分析,u_ether.c:893
c语言代码为:
if ((dev->tx_skb_hold_count dl_max_pkts_per_xfer) && (length port_usb->dl_max_transfer_len - dev->net->mtu)))
从上面的log看,是由null指针引起. 这里涉及到三个指针,dev, dev->port_usb, dev->net.
那么怎么查找到底是哪个指针有问题了?
2.3.1直接打印变量值
p dev
$1 =
可以看出已经被编译器优化了,无法用p直接打印
2.3.2 pc+偏移量法
首先确定偏移量:
p&(((struct eth_dev *)0)->net)
$1 = (struct net_device **) 0x10
(gdb) p &(((struct eth_dev*)0)->port_usb)
$2 = (struct gether **) 0x8
p (((struct gether*)0)->dl_max_transfer_len)
cannot access memory at address 0xe4
p (((struct net_device *)0)->mtu)
cannot access memory at address 0x1b8
可以看出dev->net和port_usb的偏移量为16和8,
dl_max_transfer_len和mtu的偏移量为:0xe4 ,0x1b8
在log中提示无法处理虚拟地址为0x000000e4
unable to handle kernel null pointerdereference at virtual address 000000e4
而dl_max_transfer_len的偏移量刚好为0xe4,则可以证明port_usb为空指针.
2.3.3 汇编+偏移量+寄存器
用disassemble 打印出当前函数的汇编语言(这里只列举部分)
再查找16,8, 228(0xe4),440(0x1b8)
0xffffffc00088d27c : bl 0xffffffc0004803c0
0xffffffc00088d280 : ldr w28, [x23,#104]
0xffffffc00088d284 : ldr w1, [x26,#-56]
0xffffffc00088d288 : mov x0, x23
0xffffffc00088d28c : add w28, w28, w1
0xffffffc00088d290 : str w28, [x26,#-56]
0xffffffc00088d294 : mov w27, w28
0xffffffc00088d298 : bl 0xffffffc0009ce020
0xffffffc00088d29c : mov x0, x22
0xffffffc00088d2a0 : bl 0xffffffc000b52434
0xffffffc00088d2a4 : mov x1, x0
0xffffffc00088d2a8 : ldr w2, [x20,#88] /*dev->tx_skb_hold_count */
0xffffffc00088d2ac : ldr w4, [x20,#136]
0xffffffc00088d2b0 : add w2, w2, #0x1
0xffffffc00088d2b4 : str w2, [x20,#88]
0xffffffc00088d2b8 : cmp w2, w4
0xffffffc00088d2bc : b.cs 0xffffffc00088d2dc
---type to continue, or q to quit---
0xffffffc00088d2c0 : ldr x2, [x20,#8] /*dev->port_usb*/
0xffffffc00088d2c4 : ldr x0, [x20,#16]/*dev->net*/
=> 0xffffffc00088d2c8: ldr w2, [x2,#228]/*dev->port_usb->dl_max_transfer_len*/
0xffffffc00088d2cc : ldr w0, [x0,#440]/*dev->net->mtu*/
pc在0xffffffc00088d2c8出现异常,说明x2寄存器为null,可以证明dev->port_usb为null 。
另外这里寄存器x20保存有dev的指针,x20的值为ffffffc06d3d2700 ,也可尝试用p打印这个地址,port_usb的确为null.
p *(struct eth_dev*)0xffffffc06d3d2700
$10 = {lock = {{rlock = {raw_lock = {lock =0}, break_lock = 0}}}, port_usb = 0x0, net =0xffffffc06d3d2000, gadget = 0xffffffc06ee132a0, req_lock = {{rlock = {
raw_lock = {lock = 1}, break_lock= 0}}}, reqrx_lock = {{rlock = {raw_lock = {lock = 0}, break_lock = 0}}},tx_reqs = {next = 0xffffffc06d3d2730,
prev = 0xffffffc06d3d2730}, rx_reqs = {next = 0xffffffc06d3d2740, prev =0xffffffc06d3d2740}, tx_qlen = 1, no_tx_req_used = 0, tx_skb_hold_count = 1,
tx_req_bufsize = 4740, rx_frames = {next = 0xffffffc06d3d2760, prev =0xffffffc06d3d2760, qlen = 0, lock = {{rlock = {raw_lock = {lock = 0},break_lock = 0}}}},
header_len = 0, ul_max_pkts_per_xfer = 1, dl_max_pkts_per_xfer = 3, wrap= 0x0, unwrap = 0x0, work = {data = {counter = 68719476704}, entry = {
next = 0xffffffc06d3d27a8, prev = 0xffffffc06d3d27a8}, func =0xffffffc00089dda8 }, rx_work = {data = {counter = 512}, entry= {
next = 0xffffffc06d3d27c8, prev = 0xffffffc06d3d27c8}, func =0xffffffc00088db5c }, rx_work1 = {data = {counter = 512},entry = {
next = 0xffffffc06d3d27e8, prev = 0xffffffc06d3d27e8}, func = 0xffffffc00089dd74}, todo = 0, zlp = false,
host_mac = \246\030\003, }
3. 调试总结
1 调试时需要确定vmlinux与db文件对应.不然无法精准定位, 打开vmlinux ,搜索smp关键字,可以确认vmlinu的编译时间.
2. 内存标示
有时用p打印出变量的值全部为0x6b6b6b6b,这说明内存已经被其他地方释放
内核有定义.
#define poison_inuse 0x5a
/* for use-uninitialised poisoning */
#define poison_free 0x6b
/* for use-after-free poisoning */
#define poison_end 0xa5
/* end-byte of poisoning */

信号完整性基础-反射
RT-Thread操作系统已经成功部署至芯驰E3
东芝8TBMG05评测 到底怎么样
国家工业互联网政策力度空前
恩智浦在CES 2017上展示面向未来的安全,物联网和汽车创新解决方案
Linux内核使用gdb调试
芯片市场增长趋势放缓——低功耗遥控器反迎转机
电感器的结构
P6070手持终端搭载Android 11.0操作系统
国内手机业者表态反对博通收购高通 否则将形成垄断且将扼杀创新
阿联酋民航局局长表示波音737MAX在2020年第一季度复飞的可能性很大
魅族zero真无孔手机:USB 3.0超级无线快充+发声技术及Flyme 7加持
联芸展示了基于Intel 3D QLC闪存的SSD样品,容量高达4TB
等离子电视为什么淘汰_等离子电视为什么停产
工业互联网是工业智能化发展的关键综合信息基础设施
荣耀9最新消息:荣耀9荣耀中的颜值担当,其中还有惊喜
Pixelworks 逐点半导体正式成为Unity 验证解决方案合作伙伴共同推进手游的视觉显示体验
华为联合保时捷设计打造出了Ultra One笔记本电脑
昀通科技浅谈UV胶水所使用的光固化技术七大热点趋势
板材激光切割机对比传统加工工艺的优劣势