从零开始之驱动发开、linux驱动(六十八、内核调试篇--Oops解析)

什么是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里面导出的符号表后面,所以我们查看

在继续分析之前我们知道一下地址和标号之间的那个字符表示什么意思

T   The symbol is in the text(code) section
D   The symbol is in the initialized data section
R   The sysbol is in a read only data section
t   static 
d   static
R   const
r   static const

详细的可以看一下我之前的符号表的导出那篇文章。

https://blog.csdn.net/qq_16777851/article/details/83931530

这里我们可以看到,这些符号表示在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地址写数据。即也就是我们当初制造错误的位置。

当然很多时候,我们不需要这样分析,只需要找到大概位置,通过分析代码就可以找到原因了。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/89737457
今日推荐