内核oops错误调试学习笔记

驱动开发中遇到的 oops 问题,导致内核崩溃,log 一般如下形式

 Unable to handle kernel paging request at virtual address bfb10be0
 pgd = c0003000
 [bfb10be0] *pgd=80000040006003, *pmd=47ab3003, *pte=00000000
 Internal error: Oops: 80000207 [#1] PREEMPT SMP ARM
Modules linked in: wctdm(+) i2s timer pppoe ppp_async brcmfmac pppox ppp_generic nf_conntrack_ipv6 iptable_nat ipt_REJECT ipt_MASQUERADE cfg80211 xt_time xt_tcpudp xt_state xt_nat xt_multiport xt_mark xt_mac xt_limit xt_conntrack xt_comment xt_TCPMSS xt_REDIRECT xt_LOG xt_FLOWOFFLOAD spidev slhc nf_reject_ipv4 nf_nat_redirect nf_nat_masquerade_ipv4 nf_conntrack_ipv4 nf_nat_ipv4 nf_nat nf_log_ipv4 nf_flow_table_hw nf_flow_table nf_defrag_ipv6 nf_defrag_ipv4 nf_conntrack_rtcache nf_conntrack iptable_mangle iptable_filter ip_tables dahdi crc_ccitt compat brcmutil rtc_sunxi ledtrig_heartbeat echo ip6t_REJECT nf_reject_ipv6 nf_log_ipv6 nf_log_common ip6table_mangle ip6table_filter ip6_tables x_tables snd_pcm_oss snd_mixer_oss snd_rawmidi snd_seq_device snd_hwdep [last unloaded: h3i2s]
 CPU: 0 PID: 0 Comm: swapper/0 Tainted: G        W       4.14.63 #0
 Hardware name: Allwinner sun8i Family
task: c0c07080 task.stack: c0c00000
PC is at 0xbfb10be0
 LR is at vchan_complete+0x188/0x1ac

关注点:
PC is at 0xbfb10be0
说明了出错时的指令位置 0xbfb10be0

1.如何确定 0xbfb10be0 是编进内核里的函数地址还是insmod 模块加载的函数地址?
进入内核源码目录(编译过的)
cat System.map

c0008000 T stext
c00080a0 t __create_page_tables
c000817c t __turn_mmu_on_loc
c0008188 t __fixup_smp
c00081f0 t __fixup_smp_on_up
c0008214 t __fixup_pv_table
c0008268 t __vet_atags
c0200000 T __idmap_text_start
c0200000 T __turn_mmu_on
c0200000 T _stext
.....
.....
c0c9d318 b radix_tree_node_cachep
c0c9d31c B __bss_stop
c0c9d31c B _end

看在不在其中,不在则说明是在 insmod 加载的模块里。

2.如果在 insmod 加载的模块里 ,如何确定是在哪一个ko 里?
答:通过查看/proc/kallsyms 来确定。
cat /proc/kallsyms | grep 0xbfb10b

/proc/kallsyms 的内容节选如下,其中右边 [ ] 里的就是所在驱动名

bf000084 t snd_hwdep_poll       [snd_hwdep]
bf000a04 t snd_hwdep_ioctl      [snd_hwdep]
bf0000ac t snd_hwdep_mmap       [snd_hwdep]
bf000544 t snd_hwdep_open       [snd_hwdep]
bf0004b8 t snd_hwdep_release    [snd_hwdep]
bf0000d4 t snd_hwdep_dev_free   [snd_hwdep]
bf000334 t snd_hwdep_dev_register       [snd_hwdep]
bf000238 t snd_hwdep_dev_disconnect     [snd_hwdep]
bf000cdc t cleanup_module       [snd_hwdep]
bf000110 T snd_hwdep_new        [snd_hwdep]

由此可以根据 PC is at 0xbfb10be0 确定是哪个模块的,同时还可以确定是哪个函数里。

3.反汇编 找到指令位置
如果是确定是在.ko 里,直接反汇编 .ko ,.ko反汇编出来的地址是从0开始。
xxx-objdump -D xxx.ko > xxx.dis

如果是编译进内核里,要反汇编整个内核的话,时间要很久!
这里从网上找到一个快速高效的脚本 objdump_func.sh :

#!/bin/bash

vmlinux=$1 
symbol=$2 
 
if [ -z "$vmlinux" ]; then 
    echo "usage : $0 vmlinux symbol"
    exit 
fi 
#这里指定你 objdump 的路径
objdump=~/work/openwrt/arm-openwrt-linux-objdump
 
startaddress=$(nm -n $vmlinux | grep "\w\s$symbol" | awk '{print "0x"$1;exit}') 
endaddress=$(nm -n $vmlinux | grep -A1 "\w\s$symbol" | awk '{getline; print "0x"$1;exit}') 
  
if [ -z "$symbol" ]; then 
    echo "dump all symbol"
    $objdump -d $vmlinux 
else 
    echo "start-address: $startaddress, end-address: $endaddress" 
    $objdump -d $vmlinux --start-address=$startaddress --stop-address=$endaddress
fi

使用方法 ./objdump_func.sh vmlinux xx_func > xxx.dis

cat xxx.dis 找到指令位置,分析汇编代码,找到出错原因。这十分考验读汇编能力。

鸣谢:要感谢韦东山老师,以上知识学习自韦老师的二期视频。

你可能感兴趣的:(Linux)