Debug Linux kernel/module on ubuntu

引用


一. How to get vmlinux?

  • 从官网下载

Where to get debug symbols for kernel X?
http://ddebs.ubuntu.com/pool/main/l/linux/

GPG key import
16.04 and higher
 > sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622 

older distributions
 > sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01 

Add repository config
>
codename=$(lsb_release -c | awk  '{print $2}')
sudo tee /etc/apt/sources.list.d/ddebs.list << EOF
deb http://ddebs.ubuntu.com/ ${codename}      main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-updates  main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse
EOF

> sudo apt-get update
> sudo apt-get install linux-image-$(uname -r)-dbgsym

---------------------------------------------------------------------------------------
vmlinux then can be found here:
/usr/lib/debug/boot/vmlinux-$(uname -r)
  • 自己编译源码获取

1. 首先通过apt-get获取内核代码,当然你可以直接从kernel.org上Git获取代码.
    > sudo apt-get install linux-source

2. 安装编译需要的lib
    > sudo apt-get install libncurses5-dev

3. 将linux-source-4.15.0.tar.bz2解压到/usr/src/目录下
    > tar -jxf /usr/src/linux-source-4.15.0/linux-source-4.15.0.tar.bz2 -C /usr/src/

4. 获取配置文件
    > sudo cp /boot/config-4.15.0-88-generic /usr/src/linux-source-4.15.0/.config

----------------------------------------------------------------------------------
编译可能用到的lib:
sudo apt-get install libncurses5-dev libssl-dev 
sudo apt-get install build-essential openssl 
sudo apt-get install zlibc minizip 
sudo apt-get install libidn11-dev libidn11
sudo apt-get install bison
sudo apt-get install flex
----------------------------------------------------------------------------------

5. 进入/usr/src/linux-source-4.15.0文件夹中,开始编译
    > sudo make -j8    # 8线程编译
--------------------------------------------------------
    > make mrproper    # 清除编译过程产生的中间文件,就是将内核代码还原为刚解压的状态,最好每次编译前执行一下。
    > make clean       # 清除上次编译产生的中间文件,最好每次编译前执行一下。
    > make menuconfig  # 配置编译参数,编译的内核模块选择,内核剪裁需要重点研究的部分。

-----------------------------------------------------------------------------------
vmlinux路径:
    /usr/src/linux-source-4.15.0

6. 如果想继续安装内核,执行:
    > make INSTALL_MOD_STRIP=1 modules_install
    >make install INSTALL_MOD_STRIP=1
----------
    如果用下面的命令,可能会报错误:Couldn't find suitable memory target
    //make modules_install   # 安装内核模块,驱动等
    //make install  # 安装内核

一. objdump,readelf,nm,addr2line,gdb,eu-addr2line

kernel module crash:
[  444.852297] kernel BUG at /home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.c:60!
[  444.852314] invalid opcode: 0000 [#1] SMP PTI
[  444.852325] CPU: 3 PID: 3926 Comm: insmod Kdump: loaded Tainted: G           OE     5.4.55 #1
[  444.852332] Hardware name: ASUSTeK Computer Inc. P43SJ/P43SJ, BIOS P43SJ.308 01/06/2012
[  444.852349] RIP: 0010:simple_init+0xf5/0x100 [kthread_test]
[  444.852360] Code: c0 83 4d c0 48 c7 c7 8f 70 4d c0 e8 58 e0 e3 d7 48 8b 15 ae 22 00 00 48 8b 35 9f 22 00 00 48 c7 c7 d0 70 4d c0 e8 3e e0 e3 d7 <0f> 0b 66 0f 1f 84 00 00 00 00 00 66 66 66 66 90 55 48 8b 3d 73 22

========================================================================================
1. 查询RIP指定的符号地址 (如果是内核API,就换为vmlinux)
objdump:
   > objdump -t kthread_test.ko | grep simple_init
    --------------
    0000000000000040 l     F .text  00000000000000f7 simple_init
    0000000000000000 l     F .text.unlikely 0000000000000049 simple_init.cold
    --------------

   > objdump -dSlr kthread_test.ko > kthread_test.disasm //查询到所有函数及其偏移位置。
   > vim kthread_test.disasm 
    --------------
     /home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.c:58
    207     printk(KERN_ERR "k_data:0x%llx, v_data:0x%llx", (u64)simple_data.k_data, (u64)simple_data.v_data);
    208  11b:   48 8b 15 00 00 00 00    mov    0x0(%rip),%rdx        # 122 <init_module+0xe2>
    209             11e: R_X86_64_PC32  simple_data+0xc
    210  122:   48 8b 35 00 00 00 00    mov    0x0(%rip),%rsi        # 129 <init_module+0xe9>
    211             125: R_X86_64_PC32  simple_data+0x4
    212  129:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi
    213             12c: R_X86_64_32S   .rodata.str1.8
    214  130:   e8 00 00 00 00          callq  135 <init_module+0xf5>
    215             131: R_X86_64_PLT32 printk-0x4
    216 /home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.c:60
    217
    218     assert();
    219  135:   0f 0b                   ud2
    220  137:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
    221  13e:   00 00
    --------------

    > objdump -S kthread_test.ko | grep simple_init
    --------------
    static int simple_init(void)
    0000000000000000 <simple_init.cold>:
   7:   48 89 05 00 00 00 00    mov    %rax,0x0(%rip)        # e <simple_init.cold+0xe>
   e:   e8 00 00 00 00          callq  13 <simple_init.cold+0x13>
  20:   e8 00 00 00 00          callq  25 <simple_init.cold+0x25>
  25:   48 8b 05 00 00 00 00    mov    0x0(%rip),%rax        # 2c <simple_init.cold+0x2c>
  2c:   e9 00 00 00 00          jmpq   31 <simple_init.cold+0x31>
  38:   e8 00 00 00 00          callq  3d <simple_init.cold+0x3d>
  3d:   48 8b 0d 00 00 00 00    mov    0x0(%rip),%rcx        # 44 <simple_init.cold+0x44>

    注意:这里的0000000000000000是相对于 (0000000000000040 <init_module>)而言的,所以,
simple_init的地址应该是  0000000000000040 + 0000000000000000 = 0000000000000040 。
   --------------


readelf:
    > readelf -s kthread_test.ko | grep simple_init
    --------------
    32: 0000000000000040   247 FUNC    LOCAL  DEFAULT    3 simple_init
    33: 0000000000000000    73 FUNC    LOCAL  DEFAULT    5 simple_init.cold
    --------------

nm:
    > nm kthread_test.ko | grep simple_init
    --------------
    0000000000000040 t simple_init
    0000000000000000 t simple_init.cold
    --------------


2. 计算RIP中符号+offset的地址
    eg.
    simple_init+0xf5 ==> 0000000000000040 + 0xf5 = 0x135


3. 查询该地址对应的c语言位置
addr2line:
    > addr2line -e kthread_test.ko -f 0x135
    --------------
    simple_init
    /home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.c:60
    --------------

GDB:
    > gdb "$(modinfo -n kthread_test.ko)"
    (gdb) list *(simple_init+0xf5)
    --------------
    0x175 is in simple_init             (/home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.c:60).
    55
    56
    57              printk(KERN_ERR "simple_dat:0x%llx", (u64)&simple_data);
    58              printk(KERN_ERR "k_data:0x%llx, v_data:0x%llx",    (u64)simple_data.k_data, (u64)simple_data.v_data);
    59
    60              assert();
    61
    62      err:
    63              return ret;
    64      }

    --------------

eu-addr2line:
    > eu-addr2line -f -e <path_to_the_module> -j <section_name> <offset_in_section>
    eg.
        eu-addr2line -e kthread_test.ko -j .text -f 0x135
    --------------
    simple_init
    /home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.c:60:2
    --------------
    

二. crash

crash 常用方法

1. 加载dump文件
> crash vmlinux 202009121232/dump.202009121232

2. 加载指定kernel module
crash> mod -s kthread_test /home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.ko

3. 常用命令 
3.1 crash> dmesg/ log -a
--------------
[    0.000000] microcode: microcode updated early to revision 0x2f, date = 2019-02-17
[    0.000000] Linux version 5.4.55 (root@spc) (gcc version 9.3.0 (Ubuntu 9.3.0-10ubuntu2)) #1 SMP Wed Sep 9 20:48:42 CST 2020 (Ubuntu 5.4.0-47.51-generic 5.4.55)
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-5.4.55 root=UUID=db5337a7-63f9-4023-8b30-613e5a3c3ca6 ro quiet splash crashkernel=512M-:192M vt.handoff=7
[    0.000000] KERNEL supported cpus:
...
--------------

3.2 crash> dis -l simple_init+0xf5
--------------
/home/vec/dev_document/linux_module_driver/kthread_test/kthread_test.c: 60
0xffffffffc04d6135 <simple_init+245>:   ud2
...
--------------

3.4 bt/bt -slf/bt -t/bt -c 1/bt -a (查看堆栈)
crash> bt
--------------
PID: 3926   TASK: ffff93bb6cc90000  CPU: 3   COMMAND: "insmod"
 #0 [ffffa5e3c2d17930] machine_kexec at ffffffff9826fbd3
...
--------------

3.3 struct simple_data_info (要查看的struct结构)
crash> struct simple_data_info
--------------
struct simple_data_info {
    struct task_struct *pth;
    unsigned char *k_data;
    unsigned char *v_data;
}
SIZE: 24
--------------

crash> struct simple_data_info 0xffffffffc04d83c0
--------------
struct simple_data_info {
  pth = 0xffff93bb6c62ae80,
  k_data = 0xffff93bb6f2c6000 "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"...,
  v_data = 0xffffa5e3c081d000 "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"...
}
--------------

crash> struct simple_data_info 0xffffffffc04d83c0 -xo
--------------
struct simple_data_info {
  [ffffffffc04d83c0] struct task_struct *pth;
  [ffffffffc04d83c8] unsigned char *k_data;
  [ffffffffc04d83d0] unsigned char *v_data;
}
SIZE: 0x18
--------------

3.4 读取内存内容 (大小是以8字节为单位)
crash> rd 0xffff93bb6f2c6000 1
--------------
ffff93bb6f2c6000:  5a5a5a5a5a5a5a5a                    ZZZZZZZZ
--------------

3.5 获取线程信息
crash> ps
--------------
   PID    PPID  CPU       TASK        ST  %MEM     VSZ    RSS  COMM
      0      0   0  ffffffff99813780  RU   0.0       0      0  [swapper/0]
      0      0   1  ffff93bb763f8000  RU   0.0       0      0  [swapper/1]
      0      0   2  ffff93bb763fdd00  RU   0.0       0      0  [swapper/2]
...
--------------

内核线程信息
crash> ps -k
--------------
   PID    PPID  CPU       TASK        ST  %MEM     VSZ    RSS  COMM
      0      0   0  ffffffff99813780  RU   0.0       0      0  [swapper/0]
      0      0   1  ffff93bb763f8000  RU   0.0       0      0  [swapper/1]
...
--------------

3.6 获取内存使用信息
crash> kmem -i
--------------
                 PAGES        TOTAL      PERCENTAGE
    TOTAL MEM  1982852       7.6 GB         ----
         FREE  1683852       6.4 GB   84% of TOTAL MEM
         USED   299000       1.1 GB   15% of TOTAL MEM
       SHARED    69714     272.3 MB    3% of TOTAL MEM
...
--------------

Crash命令列表

命令功能
*指针快捷健
alias命令快捷键
asciiASCII码转换和码表
bpfeBPF - extended Berkeley Filter
bt堆栈查看
btop地址页表转换
dev设备数据查询
dis返汇编
eval计算器
exit退出
extend命令扩展
files打开的文件查看
foreach循环查看
fuser文件使用者查看
gdb调用gdb执行命令
help帮助
ipcs查看system V IPC工具
irq查看irq数据
kmem查看Kernel内存
list查看链表
log查看系统消息缓存
mach查看平台信息
mod加载符号表
mountMount文件系统数据
net网络命令
p查看数据结构
ps查看进程状态信息
pte查看页表
ptob页表地址转换
ptov物理地址虚拟地址转换
rd查看内存
repeat重复执行
runq查看run queue上的线程
search搜索内存
set设置线程环境和Crash内部变量
sig查询线程消息
struct查询结构体
swap查看swap信息
sym符号和虚拟地址转换
sys查看系统信息
task查看task_struct和thread_thread信息
timer查看timer队列
tree查看radix树和rb树
union查看union结构体
vm查看虚拟内存
vtop虚拟地址物理地址转换
waitq查看wait queue上的进程
whatis符号表查询
wr改写内存
q退出

三. ubunut apport-unpack crash file

apport-unpack linux-image-5.4.55-202009121232.crash temp

--------------
root@spc:/var/crash/temp# ll
total 100
drwxr-sr-x 2 root whoopsie  4096 9月  13 10:02 ./
drwxrwsrwt 3 root whoopsie  4096 9月  13 10:02 ../
-rw-r--r-- 1 root whoopsie     5 9月  13 10:02 Architecture
-rw-r--r-- 1 root whoopsie    24 9月  13 10:02 Date
-rw-r--r-- 1 root whoopsie    12 9月  13 10:02 DistroRelease
-rw-r--r-- 1 root whoopsie    34 9月  13 10:02 Package
-rw-r--r-- 1 root whoopsie    11 9月  13 10:02 ProblemType
-rw-r--r-- 1 root whoopsie    19 9月  13 10:02 Uname
-rw-r--r-- 1 root whoopsie 68626 9月  13 10:02 VmCoreDmesg
--------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值