首页 > 技术知识 > 正文

1. Linux Vmlinux反汇编操作和代码分析 1.1 对vmlinx反汇编

arm-linux-gnueabi-objdump -d vmlinux > debug.s

1.2 debug.s代码段分析 vmlinux: file format elf32-littlearm Disassembly of section .head.text: 内核地址 汇编对应的机器码 汇编指令 c0008000 <stext>: //ARM在链接脚本里面,指定了内核的入口是stext //在vmlinux.lds指定了ENTRY(stext) ,然后. = 0xC0000000 +0x00008000; // .head.text :…,所以执行地址是内核地址0xc0008000 c0008000: e10f9000 mrs r9, CPSR c0008004: e229901a eor r9, r9, #26 c0008008: e319001f tst r9, #31 c000800c: e3c9901f bic r9, r9, #31 c0008010: e38990d3 orr r9, r9, #211 ; 0xd3 c0008014: 1a000004 bne c000802c <stext+0x2c> c0008018: e3899c01 orr r9, r9, #256 ; 0x100 c000801c: e28fe00c add lr, pc, #12

下面这段是函数代码段,当我们把一个函数编译进内核(有系统)的时候,系统加载器会自动帮我们分配执行地址,如下面的c0008084,当内核调用这个函数的时候,根据函数名称就能找到对应的地址并执行

c0008084 <__create_page_tables>: c0008084: e2884901 add r4, r8, #16384 ; 0x4000 c0008088: e1a00004 mov r0, r4 c000808c: e3a03000 mov r3, #0 c0008090: e2806901 add r6, r0, #16384 ; 0x4000 c0008094: e4803004 str r3, [r0], #4 c0008098: e4803004 str r3, [r0], #4 c000809c: e4803004 str r3, [r0], #4

说明:那么我们得到这个反汇编的文件后有什么用处呢?下面结合oops栈回溯来介绍

2. oops 栈回朔分析 2.1 oops是什么?

这里,我们要理解一下oops是什么?oops语义上类似于惊讶,也类似于拟声词,像”哎呦”,所以对于程序来说,就如“哎呦,出错了!”,然后把出错的地址,各个寄存器的值等等打印出来。 首先,我们根据内核打印信息的段错误信息来分析,例子如下:

Unable to handle kernel paging request at virtual address 56000050 //内核使用56000050来访问时发生了错误 pgd = c3eb0000 [56000050] *pgd=00000000 Internal error: Oops: 5 [#1] //5表示错误代码,#1表示这个错误发生一次 Modules linked in: first_drv // 在哪个模块出错 CPU: 0 Not tainted (2.6.22.6 #1) // 发生错误的CPU序号 PC is at first_drv_open+0x18(该指令的偏移)/0x3c(该函数的总大小) [first_drv] // 出错函函数,PC就是发生错误的指令的地址 LR is at chrdev_open+0x14c/0x164 // LR寄存器的值,错误函数的栈返回地址 pc = 0xbf000018 // 出错时各个寄存器的值 pc : [<bf000018>] lr : [<c008d888>] psr: a0000013 sp : c3c7be88 ip : c3c7be98 fp : c3c7be94 r10: 00000000 r9 : c3c7a000 r8 : c049abc0 r7 : 00000000 r6 : 00000000 r5 : c3e740c0 r4 : c06d41e0 r3 : bf000000 r2 : 56000050 r1 : bf000964 r0 : 00000000 // 执行这条导致错误的指令时各个寄存器的值 Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user Control: c000717f Table: 33eb0000 DAC: 00000015 Process firstdrvtest (pid: 777, stack limit = 0xc3c7a258) //发生错误时当前进程的名称是firstdrvtest 栈 Stack: (0xc3c7be88 to 0xc3c7c000) be80: c3c7bebc c3c7be98 c008d888 bf000010 00000000 c049abc0 bea0:c3e740c0 c008d73c c0474e20 c3e766a8 c3c7bee4 c3c7bec0 c0089e48 c008d74c bec0: c049abc0 c3c7bf04 00000003 ffffff9c c002c044 c3d10000 c3c7befc c3c7bee8 bee0: c0089f64 c0089d58 00000000 00000002 c3c7bf68 c3c7bf00 c0089fb8 c0089f40 bf00: c3c7bf04 c3e766a8 c0474e20 00000000 00000000 c3eb1000 00000101 00000001 bf20: 00000000 c3c7a000 c04a7468 c04a7460 ffffffe8 c3d10000 c3c7bf68 c3c7bf48 bf40: c008a16c c009fc70 00000003 00000000 c049abc0 00000002 bec1fee0 c3c7bf94 bf60: c3c7bf6c c008a2f4 c0089f88 00008520 bec1fed4 0000860c 00008670 00000005 bf80: c002c044 4013365c c3c7bfa4 c3c7bf98 c008a3a8 c008a2b0 00000000 c3c7bfa8 bfa0: c002bea0 c008a394 bec1fed4 0000860c 00008720 00000002 bec1fee0 00000001 bfc0: bec1fed4 0000860c 00008670 00000002 00008520 00000000 4013365c bec1fea8 bfe0: 00000000 bec1fe84 0000266c 400c98e0 60000010 00008720 00000000 00000000
<

Backtrace: (回溯) // 在内核里面,选中FRAME_POINTER ,出错时才会打印出这些消息

Linux Vmlinux反汇编操作和 oops stack 回朔分析

[<bf000000>](first_drv_open+0x0/0x3c[first_drv])from[<c008d888>] (chrdev_open+0x14c/0x164) [<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8) r8:c3e766a8 r7:c0474e20 r6:c008d73c r5:c3e740c0 r4:c049abc0 [<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48) [<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48) r4:00000002 [<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4) r5:bec1fee0 r4:00000002 [<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28) [<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c) Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000) Segmentation fault 2.2 oops调试步骤

根据上面的回朔信息,我们可以如下调试: (1) 根据pc值,查看出错的地址是在内核还是在insmod 加载的驱动在内核里面,执行 cat System.map 查看内核的地址范围

(2) 查看内核函数,加载函数的地址 cat /proc/kallsyms > xx.text 这个xx.text里面,现在就有各个函数里面的地址根据pc值,找出一个相近的地址,这个地址<= pc值,找到 bf000000 t first_drv_open [first_drv] 出错的就是first_drv_open函数,t代是static函数

(3)反汇编

如果是在insmod加载的驱动的错误 arm-linux-objdump -D xxx.ko > xxx.dis 如果是在内核中的错误 arm-linux-objdump -D vmlinux > vmlinux.s

(4)在反汇编文件(.s)里面搜索first_drv_open

如果是内核,在内核的.dis里面查找pc值,例如

c014e6a8 <first_drv_open>: c014e6a8: e1a0c00d mov ip, sp c014e6ac: e92dd800 stmdb sp!, {fp, ip, lr, pc} c014e6b0: e24cb004 sub fp, ip, #4 ; 0x4 c014e6b4: e59f1024 ldr r1, [pc, #36] ; c014e6e0 <.text+0x1276e0> c014e6b8: e3a00000 mov r0, #0 ; 0x0 c014e6bc: e5912000 ldr r2, [r1] c014e6c0: e5923000 ldr r3, [r2] // 在此出错 r2=56000050

如果是insmod加载的函数,找个first_drv_open,然后确定是哪个寄存器的值出错了。first_drv.s文件insmod后

00000000 <first_drv_open>: bf000000 t first_drv_open [first_drv] 00000018 pc = bf000018

这里的00000018 就是.s出错的地址,后面有汇编代码,可以确定是哪个寄存器,根据上面寄存器的值,就可以得出哪里出错。

猜你喜欢