? ? 這里記錄平時使用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語言)
2.3 異常點分析
可以從last_kmsg或者db文件解析出的SYS_KERNEL_LOG中得知異常類型.重要信息為PC和寄存器值.
Unable to handle kernel NULL pointerdereference at virtual address 000000e4
[6464.203080]<0>-(0)[3:ksoftirqd/0]PC is at eth_start_xmit+0x1fc/0x748
[6464.203112]<0>-(0)[3:ksoftirqd/0]LR is at eth_start_xmit+0x1d8/0x748
[6464.203143]<0>-(0)[3:ksoftirqd/0]pc : [] lr :[] pstate: 800001c5
[6464.203168]<0>-(0)[3:ksoftirqd/0]sp : ffffffc071877b40
[ 6464.203192]<0>-(0)[3:ksoftirqd/0]x29:ffffffc071877b40 x28: 00000000000005bc
[6464.203231]<0>-(0)[3:ksoftirqd/0]x27: 00000000000005bc x26:ffffffc01ee14c40
[6464.203270]<0>-(0)[3:ksoftirqd/0]x25: ffffffc06ee12510 x24:ffffffc06d3d2730
[ 6464.203308]<0>-(0)[3:ksoftirqd/0]x23:ffffffc023ba8300 x22: ffffffc06d3d2720
[6464.203347]<0>-(0)[3:ksoftirqd/0]x21: ffffffc06d3d2000 x20:ffffffc06d3d2700
[6464.203385]<0>-(0)[3:ksoftirqd/0]x19: ffffffc00141e000 x18:0000000000000000
[ 6464.203422]<0>-(0)[3:ksoftirqd/0]x17:0000007f7ed6fcf8 x16: ffffffc000278828
[6464.203459]<0>-(0)[3:ksoftirqd/0]x15: 0000007f7eda9a24 x14:228f252b6f65a378
[6464.203498]<0>-(0)[3:ksoftirqd/0]x13: 9939719eb9fc9521 x12:0260832913e230f2
[6464.203535]<0>-(0)[3:ksoftirqd/0]x11: 63530fe2e6e696f3 x10:399aa79385bb3861
[6464.203573]<0>-(0)[3:ksoftirqd/0]x9 : 01a6b3c12e057068 x8 :2421eada8933ba1d
[6464.203610]<0>-(0)[3:ksoftirqd/0]x7 : e4324d79f1892abb x6 :ffffffc0393165bc
[6464.203646]<0>-(0)[3:ksoftirqd/0]x5 : 0000000000000000 x4 :0000000000000003
[6464.203682]<0>-(0)[3:ksoftirqd/0]x3 : 0000000000000002 x2 :0000000000000000
[6464.203718]<0>-(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 <(dev->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 <+432>:? ? ? ? ?bl? ? ? 0xffffffc0004803c0
0xffffffc00088d280 <+436>:? ? ? ?ldr? ? w28, [x23,#104]
0xffffffc00088d284 <+440>:? ? ? ?ldr? ? w1, [x26,#-56]
0xffffffc00088d288 <+444>:? ? ? ?mov x0, x23
0xffffffc00088d28c <+448>:? ? ? ?add? w28, w28, w1
0xffffffc00088d290 <+452>:? ? ? ?str? ? w28, [x26,#-56]
0xffffffc00088d294 <+456>:? ? ? ?mov w27, w28
0xffffffc00088d298 <+460>:? ? ? ?bl? ? ? 0xffffffc0009ce020
0xffffffc00088d29c <+464>:? ? ? ?mov x0, x22
0xffffffc00088d2a0 <+468>:? ? ? ?bl? ? ? 0xffffffc000b52434<_raw_spin_lock_irqsave>
0xffffffc00088d2a4 <+472>:? ? ? ?mov x1, x0
0xffffffc00088d2a8 <+476>:? ? ? ?ldr? ? w2, [x20,#88]? /*dev->tx_skb_hold_count */
0xffffffc00088d2ac <+480>:? ? ? ?ldr? ? w4, [x20,#136]
0xffffffc00088d2b0 <+484>:? ? ? ?add? w2, w2, #0x1
0xffffffc00088d2b4 <+488>:? ? ? ?str? ? w2, [x20,#88]
0xffffffc00088d2b8 <+492>:? ? ? ?cmp w2, w4
0xffffffc00088d2bc <+496>:? ? ? ?b.cs? 0xffffffc00088d2dc
---Type to continue, or q to quit---
0xffffffc00088d2c0 <+500>:? ? ? ?ldr? ? x2, [x20,#8] /*dev->port_usb*/
0xffffffc00088d2c4 <+504>:? ? ? ?ldr? ? x0, [x20,#16]/*dev->net*/
=> 0xffffffc00088d2c8<+508>:? ? ? ? ldr? ? w2, [x2,#228]/*dev->port_usb->dl_max_transfer_len*/
0xffffffc00088d2cc <+512>:? ? ? ?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 */
?
評論
查看更多