Linux 内核调试篇6

Linux 内核调试篇6(基于Linux6.6)---Oops分析

一、概述

在 Linux 内核中,Oops 是一个术语,用来表示内核中的一个错误或异常情况,通常是由于内核代码中的 bug 或不当操作引起的。具体来说,Oops 是一种内核崩溃的症状,但它并不完全等同于内核崩溃(panic)。当出现 Oops 错误时,内核会记录错误信息并试图恢复执行,以便继续运行,而不是完全停止。

1、Oops 的特征

  1. 错误报告:当内核在执行时遇到一个致命错误时,内核会打印一个详细的错误报告,这个报告称为 "Oops" 信息。它通常包含:

    • 错误类型:如访问无效内存、非法指令等。
    • 堆栈回溯:显示导致错误的调用栈信息。
    • 错误的寄存器内容:包括 CPU 寄存器和内存地址等信息。
    • 发生错误的代码位置:内核代码中的具体地址或模块。
  2. 内核继续运行:尽管出现 Oops 错误,内核通常会尝试恢复并继续执行,尽量避免整个系统崩溃。不同于内核 panic(内核完全崩溃),Oops 不会直接导致系统完全停止。但如果错误较为严重,仍然可能引起系统的不稳定或崩溃。

  3. 与 Panic 区别:内核的 "panic" 是指内核在遇到无法恢复的错误时会完全停止执行,通常会导致系统崩溃。而 Oops 则是内核发现问题后的一种警告机制,允许内核尽量恢复。

2、Oops 的常见原因

  1. 内存访问错误:比如访问了非法的内存地址(例如,空指针解引用)。
  2. 非法操作:例如执行了非法的 CPU 指令或操作。
  3. 内核模块中的 bug:当加载或卸载内核模块时,可能会触发一些潜在的 bug。
  4. 资源竞争:并发访问导致的竞态条件。
  5. 硬件问题:硬件故障,如损坏的内存、硬盘或不兼容的硬件,可能导致内核出现错误。

3、Oops 输出示例

以下是一个典型的 Oops 错误输出示例:

BUG: unable to handle kernel paging request at ffffc90000000000
IP: [<ffffffff8106e62a>] sysfs_add_file+0x9a/0xf0
PGD 0 PUD 0
Oops: 0000 [#1] SMP
Modules linked in: 
CPU: 2 PID: 1234 Comm: test_program Not tainted 5.10.0-8-amd64 #1
Hardware name: Custom Machine
RIP: 0010:[<ffffffff8106e62a>]  [<ffffffff8106e62a>] sysfs_add_file+0x9a/0xf0
Call Trace:
 [<ffffffff8106e721>] sysfs_create_link+0x41/0x50
 [<ffffffff811c68b8>] foo+0x68/0xa0
 [<ffffffff811c6900>] bar+0x20/0x30
 [<ffffffff811c72b4>] do_something+0x114/0x140
 [<ffffffff811c7300>] main+0x20/0x40
 [<ffffffff8100e8e2>] ret_from_fork+0x22/0x30
Code: 0f 0b 5b 5d c3 90 90 90 90 90 90 90 90 90 90 90 90

在这个例子中,内核报告了一个 分页请求错误,即访问了一个非法的内存地址,导致了一个 Oops 错误。报告中还给出了相关的堆栈回溯信息。

4、如何处理 Oops 错误?

  1. 查看日志:通过 dmesg 命令或查看 /var/log/messages 来查看内核日志中输出的 Oops 错误信息。日志中通常包含有助于调试错误的详细信息。

  2. 分析调用栈:通过堆栈回溯信息,开发者可以找到错误发生的具体位置。这有助于定位导致错误的代码行,通常需要内核源代码或者符号调试信息。

  3. 重现错误:如果可能,尝试重现 Oops 错误,这样可以更好地理解错误发生的条件。

  4. 修复 bug:根据错误信息,定位内核代码中的 bug,进行修复或补丁更新。如果是第三方模块引起的错误,可能需要联系模块的开发者。

  5. 更新内核:有时 Oops 错误是由于内核的 bug 或已知问题引起的,更新到最新的内核版本可能会解决这个问题。

什么是Oops?

从语言学的角度说,Oops应该是一个拟声词。当出了点小事故,或者做了比较尴尬的事之后,你可以说"Oops",翻译成中国话就叫做“哎呦”。“哎呦,对不起,对不起,我真不是故意打碎您的杯子的”。看,Oops就是这个意思。

在Linux内核开发中的Oops是什么呢?其实,它和上面的解释也没什么本质的差别,只不过说话的主角变成了Linux。当某些比较致命的问题出现时,我们的Linux内核也会抱歉的对我们说:“哎呦(Oops),对不起,我把事情搞砸了”。Linux内核在发生kernel panic时会打印出Oops信息,把目前的寄存器状态、堆栈内容、以及完整的Call trace都show给我们看,这样就可以帮助我们定位错误。

二、实例应用

来看一个实例。为了突出本文的主角--Oops,这个例子唯一的作用就是造一个空指针引用错误。

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
 
 
 
static int usb_simple_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
 
    int *pvalue = NULL;        
 
 
    printk(KERN_INFO"usb_simple_probe\n");
 
    printk(KERN_INFO"bcdUSB = 0x%x\n",dev->descriptor.bcdUSB);
    printk(KERN_INFO"VID    = 0x%x\n",dev->descriptor.idVendor);
    printk(KERN_INFO"PID    = 0x%x\n",dev->descriptor.idProduct);
        
    
    /*
     * 给0地址写值
     */
    *pvalue = 0;        
 
    return 0;
}
 
 
 
static void usb_simple_disconnect(struct usb_interface *intf)
{
    printk(KERN_INFO"usb_mouse_disconnect\n");
}
 
static const struct usb_device_id usb_simple_id_table[] = {
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
        USB_INTERFACE_PROTOCOL_MOUSE) },
    {} /* Terminating entry */
};
 
static struct usb_driver usb_simple_driver = {
    .name       = "usb_simple",
    .probe      = usb_simple_probe,
    .disconnect = usb_simple_disconnect,
    .id_table   = usb_simple_id_table,
};
 
 
module_usb_driver(usb_simple_driver);
MODULE_LICENSE("GPL");
 

看一下这个驱动装载后,打印出来的oops信息

[root@linux]/# insmod drivers/simple_usb.ko 
simple_usb: loading out-of-tree module taints kernel.
usbcore: registered new interface driver usb_simple
[root@linux]/# usb 1-1.4: new low-speed USB device number 3 using exynos-ehci
usb 1-1.4: New USB device found, idVendor=2188, idProduct=0ae1, bcdDevice= 1.00
usb 1-1.4: New USB device strings: Mfr=0, Product=1, SerialNumber=0
usb 1-1.4: Product:  USB OPTICAL MOUSE
usb_simple_probe
bcdUSB  = 0x110
VID     = 0x2188
PID     = 0xae1
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = ed300654
[00000000] *pgd=00000000
Internal error: Oops: 817 [#1] PREEMPT ARM
Modules linked in: simple_usb(O)
CPU: 0 PID: 1147 Comm: kworker/0:2 Tainted: G           O      4.19.0-ga2a89a6-dirty #16
Hardware name: Samsung S5PC110/S5PV210-based board
Workqueue: usb_hub_wq hub_event
PC is at usb_simple_probe+0x54/0x5c [simple_usb]
LR is at usb_simple_probe+0x50/0x5c [simple_usb]
pc : [<7f000060>]    lr : [<7f00005c>]    psr: 60000013
sp : 9ecc5c00  ip : 60000013  fp : ffffffed
r10: 7f001020  r9 : 00000000  r8 : 9f46c200
r7 : 7f002040  r6 : 9ef81c00  r5 : 9ef81c78  r4 : 9ef81c78
r3 : 00000000  r2 : 00000000  r1 : 9ecc5ba0  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5387d  Table: 4efa0019  DAC: 00000051
Process kworker/0:2 (pid: 1147, stack limit = 0x0b557c53)
Stack: (0x9ecc5c00 to 0x9ecc6000)
5c00: 9f46c220 804321f4 804320fc 809722d4 9f46c220 00000000 00000000 7f002040
5c20: 00000004 9f46c220 809250dc 803e6830 00000000 00000000 9ecc5c98 7f002040
5c40: 00000001 00000000 809722b0 803e6a28 7f002040 9f46c200 9f46c220 00000000
5c60: 9ecc5c98 803e6c3c 00000001 00000000 809722b0 00000000 809250dc 803e4cb8
5c80: 9f611c90 9ef0e444 9f46c220 9f46c220 9f46c254 803e6594 9f46c220 00000001
5ca0: 9ef81c78 9f46c228 9f46c220 809250f4 9ef81c78 803e5aec 9f46c228 80905048
5cc0: 9f46c220 803e3cd4 00000001 8023f5c8 00000000 00000000 00000000 f3a5755b
5ce0: 9ed968c0 9f46c200 00000000 9ef81c78 00000000 9ef81c00 9f46c050 00000001
5d00: 00000001 804303f4 00000001 00000000 00000000 00000000 00001388 805dd6f0
5d20: 9f46d120 8023c684 9ed35480 00000001 9edffc00 8092525c 8042f34c 809250f4
5d40: 9f46c000 00000001 9ef81c04 9ed35480 00000001 00000001 00000000 9ef81c00
5d60: 00000001 00000000 00000000 809258c8 00000004 9ef81c78 80924f00 8043ade4
5d80: 8043adb8 809258c8 9ef81c00 804320e4 804320c0 809722d4 9ef81c78 803e6830
5da0: 00000000 00000000 9ecc5e08 809258c8 00000001 00000000 9f434500 803e6a28
5dc0: 60000013 805df7cc 9ecc5df0 00000000 9ecc5e08 803e6c3c 00000001 00000000
5de0: 9f434500 00000000 80924f00 803e4cb8 9f611c90 9f576ec4 9ef81c78 9ef81c78
5e00: 9ef81cac 803e6594 9ef81c78 00000001 00000000 9ef81c80 9ef81c78 809250f4
5e20: 9f589078 803e5aec 9ef81c80 80905048 9ef81c78 803e3cd4 20000013 393831e0
5e40: 9e00323a 9ecc5e60 00000001 f3a5755b 9ef81c00 9ef81c00 9ef81c78 00000000
5e60: 9f589000 00000000 9ef34de4 00000003 00000004 80427600 00000000 00000000
5e80: a0000013 9ef81c00 00000000 9ef08718 9f589000 00000000 9ef34de4 00000003
5ea0: 00000004 80428a18 9ecc5f0a 00000000 9edffc3c 9ef34c00 9f589000 9ef08600
5ec0: 9f5890ac 9ef08400 9ef34c08 9ef34de4 9ef08603 9ef08640 9ef08644 9ef08420
5ee0: 9ef0864c 9ef34c00 80924f28 80972798 80747d1c 00000064 9ef34de4 9edffc00
5f00: 0000000c 9ef084eb 00000301 80132c34 9f5dad00 9edf1f00 9ef08718 8090b828
5f20: 9fbde500 9ef0871c 00000000 00000000 8090b828 8012c650 8090b828 8012c8b8
5f40: 9edf1f00 8090b828 8090b84c 8012c8b8 80912d30 9edf1f14 00000008 8012c9f4
5f60: 80932d52 80912d30 60000013 9edf1f80 9ecc1300 9edf1fa8 9edf1f00 8012c878
5f80: 9f46deb8 00000000 00000000 8013164c 9ecc1300 80131520 00000000 00000000
5fa0: 00000000 00000000 00000000 801010e8 00000000 00000000 00000000 00000000
5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
[<7f000060>] (usb_simple_probe [simple_usb]) from [<804321f4>] (usb_probe_interface+0xf8/0x25c)
[<804321f4>] (usb_probe_interface) from [<803e6830>] (really_probe+0x234/0x2cc)
[<803e6830>] (really_probe) from [<803e6a28>] (driver_probe_device+0x60/0x16c)
[<803e6a28>] (driver_probe_device) from [<803e4cb8>] (bus_for_each_drv+0x58/0x8c)
[<803e4cb8>] (bus_for_each_drv) from [<803e6594>] (__device_attach+0xb0/0x110)
[<803e6594>] (__device_attach) from [<803e5aec>] (bus_probe_device+0x84/0x8c)
[<803e5aec>] (bus_probe_device) from [<803e3cd4>] (device_add+0x484/0x608)
[<803e3cd4>] (device_add) from [<804303f4>] (usb_set_configuration+0x584/0x76c)
[<804303f4>] (usb_set_configuration) from [<8043ade4>] (generic_probe+0x2c/0x78)
[<8043ade4>] (generic_probe) from [<804320e4>] (usb_probe_device+0x24/0x3c)
[<804320e4>] (usb_probe_device) from [<803e6830>] (really_probe+0x234/0x2cc)
[<803e6830>] (really_probe) from [<803e6a28>] (driver_probe_device+0x60/0x16c)
[<803e6a28>] (driver_probe_device) from [<803e4cb8>] (bus_for_each_drv+0x58/0x8c)
[<803e4cb8>] (bus_for_each_drv) from [<803e6594>] (__device_attach+0xb0/0x110)
[<803e6594>] (__device_attach) from [<803e5aec>] (bus_probe_device+0x84/0x8c)
[<803e5aec>] (bus_probe_device) from [<803e3cd4>] (device_add+0x484/0x608)
[<803e3cd4>] (device_add) from [<80427600>] (usb_new_device+0x268/0x428)
[<80427600>] (usb_new_device) from [<80428a18>] (hub_event+0x7bc/0x10ac)
[<80428a18>] (hub_event) from [<8012c650>] (process_one_work+0x118/0x340)
[<8012c650>] (process_one_work) from [<8012c9f4>] (worker_thread+0x17c/0x540)
[<8012c9f4>] (worker_thread) from [<8013164c>] (kthread+0x12c/0x160)
[<8013164c>] (kthread) from [<801010e8>] (ret_from_fork+0x14/0x2c)
Exception stack(0x9ecc5fb0 to 0x9ecc5ff8)
5fa0:                                     00000000 00000000 00000000 00000000
5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5fe0: 00000000 00000000 00000000 00000000 00000013 00000000
Code: e30100a0 e3470f00 eb452b0a e3a00000 (e5800000) 
---[ end trace 4a254ce2086195ee ]---

首先看一下最先打印出来的信息

 Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = ed300654
[00000000] *pgd=00000000
Internal error: Oops: 817 [#1] PREEMPT ARM
Modules linked in: simple_usb(O)
CPU: 0 PID: 1147 Comm: kworker/0:2 Tainted: G           O      4.19.0-ga2a89a6-dirty #16
Hardware name: Samsung S5PC110/S5PV210-based board
Workqueue: usb_hub_wq hub_event

无法在虚拟地址00000000处理内核NULL指针解除引用。

pgd = ed300654为页面全局目录的地址(Page Global Directory)。

同时给出了的错误信息[00000000] *pgd=00000000。

内部错误。Oops号为817。

说明了错误发生在名字为simple_usb的一个模块上。

CPU编号为0,进程ID为1147,单板型号为S5PV110/S5PV210。

在usb的工作队列调用时中出现的错误。。

PC is at usb_simple_probe+0x54/0x5c [simple_usb]
LR is at usb_simple_probe+0x50/0x5c [simple_usb]
pc : [<7f000060>]    lr : [<7f00005c>]    psr: 60000013
sp : 9ecc5c00  ip : 60000013  fp : ffffffed
r10: 7f001020  r9 : 00000000  r8 : 9f46c200
r7 : 7f002040  r6 : 9ef81c00  r5 : 9ef81c78  r4 : 9ef81c78
r3 : 00000000  r2 : 00000000  r1 : 9ecc5ba0  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5387d  Table: 4efa0019  DAC: 00000051
Process kworker/0:2 (pid: 1147, stack limit = 0x0b557c53)

第二点,打印出了更多详细的信息,比如PC在执行到usb_simple_probe标号+0x54位置出现的错误。

同时打印出了所有寄存器出错时里面的内容。

表明是在那个进程/线程上出现的错误(kworker进程,进程编号为1147)。

Stack: (0x9ecc5c00 to 0x9ecc6000)
5c00: 9f46c220 804321f4 804320fc 809722d4 9f46c220 00000000 00000000 7f002040
5c20: 00000004 9f46c220 809250dc 803e6830 00000000 00000000 9ecc5c98 7f002040
5c40: 00000001 00000000 809722b0 803e6a28 7f002040 9f46c200 9f46c220 00000000
5c60: 9ecc5c98 803e6c3c 00000001 00000000 809722b0 00000000 809250dc 803e4cb8
5c80: 9f611c90 9ef0e444 9f46c220 9f46c220 9f46c254 803e6594 9f46c220 00000001
5ca0: 9ef81c78 9f46c228 9f46c220 809250f4 9ef81c78 803e5aec 9f46c228 80905048
5cc0: 9f46c220 803e3cd4 00000001 8023f5c8 00000000 00000000 00000000 f3a5755b
5ce0: 9ed968c0 9f46c200 00000000 9ef81c78 00000000 9ef81c00 9f46c050 00000001
5d00: 00000001 804303f4 00000001 00000000 00000000 00000000 00001388 805dd6f0
5d20: 9f46d120 8023c684 9ed35480 00000001 9edffc00 8092525c 8042f34c 809250f4
5d40: 9f46c000 00000001 9ef81c04 9ed35480 00000001 00000001 00000000 9ef81c00
5d60: 00000001 00000000 00000000 809258c8 00000004 9ef81c78 80924f00 8043ade4
5d80: 8043adb8 809258c8 9ef81c00 804320e4 804320c0 809722d4 9ef81c78 803e6830
5da0: 00000000 00000000 9ecc5e08 809258c8 00000001 00000000 9f434500 803e6a28
5dc0: 60000013 805df7cc 9ecc5df0 00000000 9ecc5e08 803e6c3c 00000001 00000000
5de0: 9f434500 00000000 80924f00 803e4cb8 9f611c90 9f576ec4 9ef81c78 9ef81c78
5e00: 9ef81cac 803e6594 9ef81c78 00000001 00000000 9ef81c80 9ef81c78 809250f4
5e20: 9f589078 803e5aec 9ef81c80 80905048 9ef81c78 803e3cd4 20000013 393831e0
5e40: 9e00323a 9ecc5e60 00000001 f3a5755b 9ef81c00 9ef81c00 9ef81c78 00000000
5e60: 9f589000 00000000 9ef34de4 00000003 00000004 80427600 00000000 00000000
5e80: a0000013 9ef81c00 00000000 9ef08718 9f589000 00000000 9ef34de4 00000003
5ea0: 00000004 80428a18 9ecc5f0a 00000000 9edffc3c 9ef34c00 9f589000 9ef08600
5ec0: 9f5890ac 9ef08400 9ef34c08 9ef34de4 9ef08603 9ef08640 9ef08644 9ef08420
5ee0: 9ef0864c 9ef34c00 80924f28 80972798 80747d1c 00000064 9ef34de4 9edffc00
5f00: 0000000c 9ef084eb 00000301 80132c34 9f5dad00 9edf1f00 9ef08718 8090b828
5f20: 9fbde500 9ef0871c 00000000 00000000 8090b828 8012c650 8090b828 8012c8b8
5f40: 9edf1f00 8090b828 8090b84c 8012c8b8 80912d30 9edf1f14 00000008 8012c9f4
5f60: 80932d52 80912d30 60000013 9edf1f80 9ecc1300 9edf1fa8 9edf1f00 8012c878
5f80: 9f46deb8 00000000 00000000 8013164c 9ecc1300 80131520 00000000 00000000
5fa0: 00000000 00000000 00000000 801010e8 00000000 00000000 00000000 00000000
5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
[<7f000060>] (usb_simple_probe [simple_usb]) from [<804321f4>] (usb_probe_interface+0xf8/0x25c)
[<804321f4>] (usb_probe_interface) from [<803e6830>] (really_probe+0x234/0x2cc)
[<803e6830>] (really_probe) from [<803e6a28>] (driver_probe_device+0x60/0x16c)
[<803e6a28>] (driver_probe_device) from [<803e4cb8>] (bus_for_each_drv+0x58/0x8c)
[<803e4cb8>] (bus_for_each_drv) from [<803e6594>] (__device_attach+0xb0/0x110)
[<803e6594>] (__device_attach) from [<803e5aec>] (bus_probe_device+0x84/0x8c)
[<803e5aec>] (bus_probe_device) from [<803e3cd4>] (device_add+0x484/0x608)
[<803e3cd4>] (device_add) from [<804303f4>] (usb_set_configuration+0x584/0x76c)
[<804303f4>] (usb_set_configuration) from [<8043ade4>] (generic_probe+0x2c/0x78)
[<8043ade4>] (generic_probe) from [<804320e4>] (usb_probe_device+0x24/0x3c)
[<804320e4>] (usb_probe_device) from [<803e6830>] (really_probe+0x234/0x2cc)
[<803e6830>] (really_probe) from [<803e6a28>] (driver_probe_device+0x60/0x16c)
[<803e6a28>] (driver_probe_device) from [<803e4cb8>] (bus_for_each_drv+0x58/0x8c)
[<803e4cb8>] (bus_for_each_drv) from [<803e6594>] (__device_attach+0xb0/0x110)
[<803e6594>] (__device_attach) from [<803e5aec>] (bus_probe_device+0x84/0x8c)
[<803e5aec>] (bus_probe_device) from [<803e3cd4>] (device_add+0x484/0x608)
[<803e3cd4>] (device_add) from [<80427600>] (usb_new_device+0x268/0x428)
[<80427600>] (usb_new_device) from [<80428a18>] (hub_event+0x7bc/0x10ac)
[<80428a18>] (hub_event) from [<8012c650>] (process_one_work+0x118/0x340)
[<8012c650>] (process_one_work) from [<8012c9f4>] (worker_thread+0x17c/0x540)
[<8012c9f4>] (worker_thread) from [<8013164c>] (kthread+0x12c/0x160)
[<8013164c>] (kthread) from [<801010e8>] (ret_from_fork+0x14/0x2c)
Exception stack(0x9ecc5fb0 to 0x9ecc5ff8)
5fa0:                                     00000000 00000000 00000000 00000000
5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5fe0: 00000000 00000000 00000000 00000000 00000013 00000000
Code: e30100a0 e3470f00 eb452b0a e3a00000 (e5800000) 

第三点就是打印出了,错误进程,在出错时的栈信息。

当然这里还有一个点就是,函数调用是压栈的,所以也打印出来栈中函数的名称(也就是调用步骤)

第一点、可以看到,可以通过进程号,来找。当然这个范围太大,一般不容易找。

第二点、通过寄存器的方式来找,比如PC给出了一个函数名(+函数内的偏移)

PC is at usb_simple_probe+0x54/0x5c [simple_usb]
LR is at usb_simple_probe+0x50/0x5c [simple_usb]
pc : [<7f000060>]    lr : [<7f00005c>]    psr: 60000013
sp : 9ecc5c00  ip : 60000013  fp : ffffffed
r10: 7f001020  r9 : 00000000  r8 : 9f46c200
r7 : 7f002040  r6 : 9ef81c00  r5 : 9ef81c78  r4 : 9ef81c78
r3 : 00000000  r2 : 00000000  r1 : 9ecc5ba0  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5387d  Table: 4efa0019  DAC: 00000051
Process kworker/0:2 (pid: 1147, stack limit = 0x0b557c53)

如果PC只给出了地址,没给出函数名的话,需要根据pc的值来找到函数。

可以看到,这里的pc是0x7f000060

这里需要知道,这个地址是我们的模块安装的代码某条指令的地址,还是内核编译时某条指令的地址。

这时我们需要查看map表

 vi System.map  

对于32位系统,内核可以配置,用户空间和内核空间的大小。我们这边配置的是各2G空间,所以内核空间是从0x80000000地址开始的。

看一下map表(也叫做符号表,存放的是函数地址,全局变量地址等)。

首先是可以看到,这个map表的地址是从小到大排序的。

看一下最后面的,也是递增的。

这里通过两点可以知道0x7f000060是不在内核编译的代码段中的:

  • 内核空间地址大于0x8000000,而我们的地址小于0x80000000。
  • map表找不到这个标号(也找不到和这个标号地址接近的)。

对于模块安装的map,内核在安装后,会重新给这些标号分配地址。

可以在proc中查看,这个是所有内核的符号表都在这个里面。

/proc/kallsyms

直接查看,因为里面内容太多,所以我们通过管道查看。

 cat /proc/kallsyms | less

可以看到和System.map中的标号和地址都是能对应上的。

因为模块安装后,分配的地址都是在proc里面导出的符号表后面,所以查看:

这些符号表示在0x7f000000开始的和我们的比较接近了。

同时我们可以看到,pC里的值肯定是代码段,所以0x7f000060是在某个函数里面。

因为上面的符号表中代码段都是按地址递增的,所以只需要找到我们pc地址 0x7f000060在两个函数标号地址段之间就可以。

上面很明显是在下面这个标号(函数)中:

usb_simple_probe

至此已经定位到了函数级别。

是否可以继续定位到哪一行代码呢?

上面的符号表可以看到,内核给usb_simple_probe函数的地址是7f00000c,也就是函数中的第一行汇编代码的地址是7f00000c。

错误的代码段地址是0x7f000060,偏移为0x60 - 0xc

接下来反汇编一下这个驱动的反汇编文件。

 arm-none-linux-gnueabi-objdump  -S simple_usb.ko |less 

拷贝出来usb_simple_probe函数的反汇编。

  
static int usb_simple_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
   c:   e92d4010        push    {r4, lr}
};
#define to_usb_device(d) container_of(d, struct usb_device, dev)
 
static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf)
{
        return to_usb_device(intf->dev.parent);
  10:   e5904020        ldr     r4, [r0, #32]
        struct usb_device *dev = interface_to_usbdev(intf);
 
        int *pvalue = NULL;
 
 
        printk(KERN_INFO"usb_simple_probe\n");
  14:   e3000000        movw    r0, #0
  18:   e3400000        movt    r0, #0
  1c:   ebfffffe        bl      0 <printk>
 
        printk(KERN_INFO"bcdUSB = 0x%x\n",dev->descriptor.bcdUSB);
  20:   e30031ca        movw    r3, #458        ; 0x1ca
  24:   e19410b3        ldrh    r1, [r4, r3]
  28:   e3000000        movw    r0, #0
  2c:   e3400000        movt    r0, #0
  30:   ebfffffe        bl      0 <printk>
        printk(KERN_INFO"VID    = 0x%x\n",dev->descriptor.idVendor);
  34:   e3a03e1d        mov     r3, #464        ; 0x1d0
  38:   e19410b3        ldrh    r1, [r4, r3]
  3c:   e3000000        movw    r0, #0
  40:   e3400000        movt    r0, #0
  44:   ebfffffe        bl      0 <printk>
        printk(KERN_INFO"PID    = 0x%x\n",dev->descriptor.idProduct);
  48:   e30031d2        movw    r3, #466        ; 0x1d2
  4c:   e19410b3        ldrh    r1, [r4, r3]
  50:   e3000000        movw    r0, #0
  54:   e3400000        movt    r0, #0
  58:   ebfffffe        bl      0 <printk>
        
 
        *pvalue = 0;
  5c:   e3a00000        mov     r0, #0
  60:   e5800000        str     r0, [r0]
 
        return 0;
}
  64:   e8bd8010        pop     {r4, pc}
 
Disassembly of section .init.text:

可以看到第一行代码的地址是0xc,入口地址是0xc。那么错误地址就是0x60了。

  *pvalue = 0;
  5c:   e3a00000        mov     r0, #0
  60:   e5800000        str     r0, [r0]

分析代码和反汇编,也可以看到,这句就是在NULL地址写数据。即也就是当初制造错误的位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值