本文共 5470 字,大约阅读时间需要 18 分钟。
我们知道在内核里面不能够处理指向0地址的指针,我们故意引入这样一个指针,并根据打印的出错信息来进行分析,下面是我们的程序:
#include <linux/module.h> #include <linux/kernel.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static int first_drv_init(void) static void first_drv_exit(void) module_init(first_drv_init); module_exit(first_drv_exit); //第一行就指出了出错的原因是由于无法处理指向0地址的指针 Unable to handle kernel NULL pointer dereference at virtual address 00000000 [00000000] *pgd=33c90031, *pte=00000000, *ppte=00000000 Internal error: Oops: 817 [#1] CPU: 0 Not tainted (2.6.22.6 #12) //0x1c:该指令的偏移,0x28 :该函数的大小 //大多时候PC值只会是一个地址,不会指示说在哪个函数里面 PC is at first_drv_init+0x1c/0x28 [test] LR is at sys_init_module+0x1424/0x1514 pc : [<bf00001c>] lr : [<c006285c>] psr: a0000013 sp : c3e25ec8 ip : c3e25ed8 fp : c3e25ed4 r10: c486e000 r9 : c3e29d14 r8 : 00000018 r7 : c48795b8 r6 : bf000360 r5 : bf000360 r4 : 00000000 r3 : 00000002 r2 : c0357028 r1 : 00000001 r0 : 00000000 Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user Control: c000717f Table: 33e4c000 DAC: 00000015 Process insmod (pid: 751, stack limit = 0xc3e24258) Stack: (0xc3e25ec8 to 0xc3e26000) 5ec0: c3e25fa4 c3e25ed8 c006285c bf000010 00000000 00000398 5ee0: c02a9f60 c02a9f60 000000fc 0000001c 00000018 c3c13370 c3e25f18 00000000 5f00: 00000028 00000028 0000005c 00000058 00000014 c3e24000 00000000 00000000 5f20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 5f40: 00000003 00000000 00000005 00000000 00000000 00000000 00000017 00000016 5f60: c487cd48 c3e0db80 c48794e4 000cb060 beb46918 000c7f7c c3e25f9c 0000eddb 5f80: 000ca1b0 000cb050 00000080 c002b044 c3e24000 000c7f7c 00000000 c3e25fa8 5fa0: c002aea0 c0061448 000ca1b0 000cb050 00900080 000cb248 0000eddb 000cb070 5fc0: 0000eddb 000ca1b0 000cb050 00000000 000cb060 beb46918 000c7f7c 00000002 5fe0: beb44fb8 beb44fac 00052354 401b7a00 60000010 00900080 00000000 00000000 [<bf000000>] (first_drv_init+0x0/0x28 [test]) from [<c006285c>] (sys_init_module+0x1424/0x1514) [<c0061438>] (sys_init_module+0x0/0x1514) from [<c002aea0>] (ret_fast_syscall+0x0/0x2c) Code: e59f3010 e3a00000 e5830000 e3a03002 (e5803000) 先判断是否属于内核的地址: 看System.map确定内核的函数的地址范围:c0004000~c03be254 而本PC值为bf00001c,并不属于内核模块,而是属于加载模块 cat /proc/kallsyms (内核函数、加载的函数的地址) bf000000 t first_drv_init [test] 这行表示first_drv_init地址是bf000000 ,显然出错位置就在0xbf000000 + 0x1c处,且对应的加载模块式test.ko 我们根据打印信息:PC is at first_drv_init+0x1c/0x28 [test] 在PC上反汇编它: arm-linux-objdump -D test.ko > test.dis 1c: e5803000 str r3, [r0] 2、然后查看:System.map,判断出错位置是属于内核模块还是加载模块 3、如果是在加载模块的话,我们可以用命令:cat /proc/kallsyms > /kallsyms.txt来查看距离出错pc最近的一个函数是什么,并查看它属于哪一个模块。 最后我们反汇编该模块,并找到我们上面找到的那个函数,根据偏移地址找到出错pc的位置,根据该位置的汇编代码来判断出错位置对应c语言的位置。 上面的错误位置是出现在加载模块,那么如果出现在内核模块又怎么做呢?其实方法是完全一样的: 比如我们将上面的文件命名为test.c,然后将其放入内核的drivers/char/目录下面,然后将drivers/char/目录下的Makefile里面加上这么一句: Unable to handle kernel NULL pointer dereference at virtual address 00000000 Internal error: Oops: 805 [#1] CPU: 0 Not tainted (2.6.22.6 #15) PC is at first_drv_init+0x1c/0x28 LR is at kernel_init+0xd4/0x28c pc : [<c019ad88>] lr : [<c0008a94>] psr: 60000013 sp : c0471f74 ip : c0471f84 fp : c0471f80 r10: 00000000 r9 : c0023864 r8 : c0022838 r7 : c0470000 r6 : 00000000 r5 : 00000000 r4 : 00000000 r3 : 00000002 r2 : c002292c r1 : 00000000 r0 : 00000000 Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment kernel Control: c000717f Table: 30004000 DAC: 00000017 Process swapper (pid: 1, stack limit = 0xc0470258) Stack: (0xc0471f74 to 0xc0472000) 1f60: c0471ff4 c0471f84 c0008a94 1f80: c019ad7c 0a00002e e5943000 00000000 00000001 e59f10fc 00000000 00000000 1fa0: 00000000 c0471fb0 c002af24 c0040330 00000000 00000000 c00089c0 c00466f0 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 1fe0: 00000000 00000000 00000000 c0471ff8 c00466f0 c00089d0 e5840058 15803024 [<c019ad6c>] (first_drv_init+0x0/0x28) from [<c0008a94>] (kernel_init+0xd4/0x28c) [<c00089c0>] (kernel_init+0x0/0x28c) from [<c00466f0>] (do_exit+0x0/0x760) Code: e59f3010 e3a00000 e5830000 e3a03002 (e5803000) Kernel panic - not syncing: Attempted to kill init! 2、然后:cat System.map,查看错误位置是否在内核模块 我们发现内核模块函数的地址范围是:c0004000~c03bd168 3、接下来我们反汇编内核:arm-linux-objdump -D vmlinux > vmlinux.dis c019ad6c <first_drv_init>: c019ad6c: e1a0c00d mov ip, sp c019ad70: e92dd800 stmdb sp!, {fp, ip, lr, pc} c019ad74: e24cb004 sub fp, ip, #4 ; 0x4 c019ad78: e59f3010 ldr r3, [pc, #16] ; c019ad90 <.text+0x170d90> c019ad7c: e3a00000 mov r0, #0 ; 0x0 c019ad80: e5830000 str r0, [r3] c019ad84: e3a03002 mov r3, #2 ; 0x2 c019ad88: e5803000 str r3, [r0] c019ad8c: e89da800 ldmia sp, {fp, sp, pc} c019ad90: c03b33d8 ldrgtsb r3, [fp], -r8 OK!根据 函数名 以及 c019ad88: e5803000 str r3, [r0]就可以找到对应的c程序的位置了!
转载地址:http://rjzub.baihongyu.com/