如何定位下边oops信息oops错误提示位置

debug(19)
linux(225)
定位Oops错误代码行【整理】&&
10:09:26|&&分类:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:218876次
积分:2992
积分:2992
排名:第8960名
原创:46篇
转载:233篇
评论:35条
(1)(2)(2)(6)(1)(4)(8)(1)(2)(3)(3)(12)(8)(3)(4)(2)(5)(7)(3)(3)(3)(6)(1)(1)(3)(8)(15)(19)(27)(8)(10)(20)(4)(31)(3)(11)(3)(9)(7)(7)(1)(2)关于oop错误定位的学习
内核异常分析(3)
接下来的这些信息,和这个模块的调试没多大关系,它们是虚拟内存页目录、页表信息、oops错误号以及最后访问的sysfs文件等。
再接下来是寄存器信息,这部分信息比较重要,其中最可能帮助定位错误的寄存器当然是PC。在这部分信息中,下面这句最为关键。
它直接地告诉了我们,oops出错时,PC是位于func_D函数标号之后的0x1c处(怎么去寻找它?后面会进行分析。另外请思考后面的0x28代表什么?)。
寄存器信息之后是栈信息,但这里还用不上,先略过。
最后的部分,也就是Backtrace标号开始的地方,它是oops的精华。它表示回溯信息,也告诉调试者在oops出错之前,模块调用了那些函数。当然,在本实例中,可以看到模块调用了func_D后就出错了,显然错误就在func_D中了。
结尾部分还有一点信息请注意。
Code标号开始的字段记录了模块出错前最后几条机器码,其中被括号括起来的就是oops出错对应的机器码。
(4)根据上面的分析,可以使用反汇编来确定出错的位置。在RHEL5中,使用命令:arm-linux-objdump -D -S oops.ko &log,将模块文件反汇编到log中,使用vim打开该文件log,直接找到func_D标号处,如图6-17所示。
(点击查看大图)图6-17& 反汇编结果
根据前面的信息,出错位置应该在func_D+0x1c处,func_D在0x1c,所以出错地址应该是0x38。看看这句汇编代码,前面的语句将寄存器r3赋值为0,然后这句又试图将寄存器r2的值存入到r3指向的地址处,也就是向0地址写。因此出错。再来看看这句出错代码对应的机器码e5832000,显然就是之前在opps的Code字段中看到的被括起来的那个。
(5)通过反汇编程序,定位了汇编代码中的错误位置,但对于用C语言编写的内核模块而言,这样还不够。如何才能准确地定位到C语言中的语句呢?回忆一下在&&Linux应用程序开发班&&中学习gdb调试时,能够在调试中看到对应的源码。当时为了进行gdb调试,在编译时加入了-g选项,这样可以将调试信息加入到目标文件中。由此得到灵感,在这里也加入-g试试。进入实验代码目录2-3-1中的内核源码目录,修改其顶层Makefile,如图6-18所示。
(点击查看大图)图6-18& 内核调试信息开关
提示&可见编译模块时加入调试信息,需要定义CONFIG_DEBUG_INFO这个宏,由于它默认是关闭的,所以内核并没有启用-g选项,可以暂时把这个宏开关注释掉,使KBUILD_CFLAGS标识拥有-g选项。
(6)修改内核Makefile后,再次编译模块。然后将新得到的oops.ko反汇编,使用命令:arm-linux-objdump -D -S oops.ko &log。用vim打开log文件。这时,通过汇编混合C语言调试信息的结果,结合前面的分析,可以很轻松的定位到C语言的错误语句就出现在&*p = a + 5&处,如图6-19所示。
(点击查看大图)图6-19& 定位出错的C语言语句
通过本节实验,可以学会oops信息的分析方法,掌握利用oops信息调试内核模块的基本方法。下面列出利用oops定位出错点的基本步骤。
(1)oops出错时,首先搜集到所有oops打印信息,如果模块中本身有很多printk打印语句,首先根据oops开头的打印信息分析出错点的大概位置。
(2)通过Backtrace字段,分析发生oops错误前模块程序的执行路径,将范围缩小到某个函数中。
(3)如果通过前面两步仍无法定位出错点,那么就直接通过PC来定位。查看出错时
oops信息中打印出的PC的值并记录下来。在内核Makefile中加入-g选项,重新编译模块。
(4)通过objdump反汇编该模块。在反汇编得出的汇编混合C语言调试信息的代码中,结合前3步的分析结论,精确定位出错点的位置。
本分类共有文章12篇,更多信息详见
& 2012 - 2016 &
&All Rights Reserved. &
/*爱悠闲图+*/
var cpro_id = "u1888441";3428人阅读
实验目的与意义
、掌握printk
的使用、设置及实现原理,理解分级别进行打印log
信息的实现方法
、掌握如何分析oops
、掌握strace
工具的移植和使用方法
基本原理和方法
、请回顾栈的工作原理,尤其是栈帧的作用
、请对照printk
的源代码来进行printk
相关实验,并在实验中进一步理解源代码
实验内容及步骤&
、在内核中编写自己的printk
代码,可利用上次系统调用实验中已有的代码,也可利用之前驱动实验中的模块。
、在根文件系统中增加/proc
目录,用来挂载proc
、重新烧录uImage
(如果有所改动的话)和根文件系统,进入控制台之后,输入命令挂载proc
文件系统:mount
t proc none /proc
如果挂载成功,
目录应该可以看到文件,比如下面的结果:
1&&&&&&&&&&&&&
74&&&&&&&&&&&&
devices&&&&&&&
kpagecount&&&&
100&&&&&&&&&&&
diskstats&&&&&
kpageflags&&&&
101&&&&&&&&&&&
773&&&&&&&&&&&
driver&&&&&&&&
loadavg&&&&&&&
102&&&&&&&&&&&
783&&&&&&&&&&&
execdomains&&&
locks&&&&&&&&&
103&&&&&&&&&&&
784&&&&&&&&&&&
fb&&&&&&&&&&&&
meminfo&&&&&&&
sysrq-trigger
786&&&&&&&&&&&
filesystems&&&
misc&&&&&&&&&&
3&&&&&&&&&&&&&
790&&&&&&&&&&&
fs&&&&&&&&&&&&
modules&&&&&&&
timer_list
4&&&&&&&&&&&&&
804&&&&&&&&&&&
ide&&&&&&&&&&&
mounts&&&&&&&&
5&&&&&&&&&&&&&
806&&&&&&&&&&&
interrupts&&&&
mtd&&&&&&&&&&&
6&&&&&&&&&&&&&
buddyinfo&&&&&
iomem&&&&&&&&&
net&&&&&&&&&&&
60&&&&&&&&&&&&
bus&&&&&&&&&&&
ioports&&&&&&&
pagetypeinfo&&
65&&&&&&&&&&&&
cmdline&&&&&&&
irq&&&&&&&&&&&
partitions&&&&
71&&&&&&&&&&&&
cpu&&&&&&&&&&&
kallsyms&&&&&&
sched_debug&&&
737&&&&&&&&&&&
cpuinfo&&&&&&&
kmsg&&&&&&&&&&
、检查并修改printk
级别,比如下面的命令:
# cat /proc/sys/kernel/printk
7&&&&&& 4&&&&&& 1&&&&&& 7
# echo 1 &/proc/sys/kernel/printk
# cat /proc/sys/kernel/printk
1&&&&&& 4&&&&&& 1&&&&&& 7
修改之后,默认的printk
打印(级别为4
)不会显示到串口终端,但仍可以通过&
cat /proc/kmsg
&看到打印结果。
、通过代码和
/proc/sys/kernel/printk
分别修改log
级别,并对应printk
的源代码来分析结果。
语言可变参数实验
、在内核代码kernel/printk.c
中的printk
函数用到了C
语言中的可变参数的用法。请参考下面的代码来学习如何使用可变参数。以下代码可直接在x86
环境测试:
#include &stdio.h&
typedef char *va_
* Storage alignment properties
_AUPBND&&&&&&&&
(sizeof (signed int) - 1)
_ADNBND&&&&&&&&&&&&&&&
(sizeof (signed int) - 1)
* Variable argument list macro definitions
#define _bnd(X, bnd)&&&&&&&&&&&
(((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)&&&&&&&&&&
(*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)&&&&&&&&&&&&&
#define va_start(ap, A)&&&&&&&&
(void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
int max ( int num, ... )
int m = -0x7FFFFFFF;
va_start ( ap, num );
for ( i= 0; i& i++ )
int t = va_arg (ap, int);
if ( t & m )
&&&&&&&&&&&
va_end (ap);
int main ( int argc, char* argv[] )
int n = max ( 5, 2, 6 ,3 ,8 ,1);
printf(&n=%d./n&, n);
、自己动手
分析上面代码可变参数的用法及实现方式。提示:va_start ( ap, num )
是为了取得可变参数在栈中的位置,该宏展开执行后,ap
将指向第一个可变参数。可利用GDB
和汇编代码协助分析。
架构中通常使用寄存器而不是栈来传递参数,那么,上述可变参数的方式能够用于ARM
架构中吗?请想办法找到证据
来支持你的猜测。
、在上次系统调用实验的代码中,人为的制造产生oops
的条件,比如下面的改动:
asmlinkage long sys_mytest()
printk(&pid: %d:/tThis is my call./n&,current-&pid);
*(int *)0 = 0;
、从串口终端得到所产生的oops
消息,并进行初步分析
、在内核代码中,利用arm-linux-objdump
得到kernel/sys.o
的汇编代码,对照汇编代码进行分析
、arm-linux-addr2line
工具可用来寻找代码地址所对应的c
代码(),可尝试:
arm-linux-addr2line
e sys.o 0xnnnn(
你出错的代码地址)
在kernel/sys.c
#arm-linux-gcc
c sys.o sys.c(
生成汇编文件)
#vim sys.o
寻找:sys_mytest
找到地址是:
而通过下面Oops
信息知道:PC is at sys_mytest+0x28/0x34
代码出错的地址)=000000b0+0x28=0xd8
arm-linux-addr2line
e sys.o 0xd8
结果输出是137
则查出出错的地方在sys.c
、下面是我实验中遇到的oops
,请尝试做一些初步分析:
Unable to handle kernel NULL pointer dereference at virtual address
pgd = c3eb0000
[] *pgd=33ddd031, *pte=, *ppte=
Internal error: Oops: 817 [#1]
Modules linked in:
Not tainted&
(2.6.25.8 #7)
PC is at sys_mytest+0x28/0x34
(表示在sys_mytest+0x28
至sys_mytest+0x34
个字节为一个单位)
LR is at 0xc031781c
pc : [&c00561ec&]&&&
lr : [&c031781c&]&&&
sp : c3ec3f98&
ip : c031781c&
fp : c3ec3fa4
r9 : c3ec2000&
r8 : c002cb84
r6 : beeced1c&
r4 : 0000000a
Flags: nZCv&
Mode SVC_32&
Segment user
Control: 0000717f&
Table: 33eb0000&
Process syscall_test (pid: 777, stack limit = 0xc3ec2260)
Stack: (0xc3ec3f98 to 0xc3ec4000)
3f80:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
3fa0: c002c9e0 c00000 beeced1c
beecef14 beecef1c 0000000a
3fc0: 000000 beeced1c
3fe0: beeced08 beececfc
fafd eff7fbbd
Backtrace:
[&c00561c4&] (sys_mytest+0x0/0x34) from [&c002c9e0&] (ret_fast_syscall+0x0/0x2c)
Code: e59fd8 ebffcf47 e3a00000 (e5800000)
---[ end trace e1600 ]---
、从www.sourceforge.net
上下载strace
、配置并编译strace
./configure --host=arm-linux
.将strace
工具加入到你的根文件系统中,并测试使用它
:定位Oops的具体代码行 作者:
(百無一用書生)
来自Linus Torvalds的讨论:
[url]/group/linux.kernel/browse_thread/thread/b70bffe/ed9c0a0cfcd31111[/url]
又,[url]http://kerneltrap.org/Linux/Further_Oops_Insights[/url]
& && &&&例如这样的一个Oops:
& && && && && & Oops: 0000 [#1] PREEMPT SMP&&
&& && && && & Modules linked in: capidrv kernelcapi isdn slhc ipv6 loop
dm_multipath snd_ens1371 gameport snd_rawmidi snd_ac97_codec ac97_bus
snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device
snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd parport_pc floppy
parport pcnet32 soundcore mii pcspkr snd_page_alloc ac i2c_piix4
i2c_core button power_supply sr_mod sg cdrom ata_piix libata dm_snapshot
dm_zero dm_mirror dm_mod BusLogic sd_mod scsi_mod ext3 jbd mbcache
uhci_hcd ohci_hcd ehci_hcd
& && && && && & Pid: 1726, comm: kstopmachine Not tainted (2.6.24-rc3-module #2)
& && && && && & EIP: 0060:[&c04e53d6&] EFLAGS:
& && && && && & EIP is at list_del+0xa/0x61
& && && && && & EAX: e0c3cc04 EBX:
ECX: 0000000e EDX: dec62000
& && && && && & ESI: df6e8f08 EDI: 000006bf EBP: dec62fb4 ESP: dec62fa4
& && && && && &&&DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
& && && && && & Process kstopmachine (pid: 1726, ti=dec62000 task=df8d2d40 task.ti=dec62000)
& && && && && & Stack: 000006bf dec62fb4 c00020 dec62fbc c044ab4c dec62fd0 c045336c
& && && && && && && &&&df6e8f08 c00000 dec62fe0 c043deb0 c043de75 00000
& && && && && && && &&&c0405cdf df6e8eb4 00 00000
& && && && && & Call Trace:
& && && && && &&&[&c0406081&] show_trace_log_lvl+0x1a/0x2f
& && && && && &&&[&c0406131&] show_stack_log_lvl+0x9b/0xa3
& && && && && &&&[&c04061dc&] show_registers+0xa3/0x1df
& && && && && &&&[&c0406437&] die+0x11f/0x200
& && && && && &&&[&c0613cba&] do_page_fault+0x533/0x61a
& && && && && &&&[&c06123ea&] error_code+0x72/0x78
& && && && && &&&[&c044ab4c&] __unlink_module+0xb/0xf
& && && && && &&&[&c045336c&] do_stop+0xb8/0x108
& && && && && &&&[&c043deb0&] kthread+0x3b/0x63
& && && && && &&&[&c0405cdf&] kernel_thread_helper+0x7/0x10
& && && && && &&&=======================
&& && && && & Code: 6b c0 e8 2e 7e f6 ff e8 d1 16 f2 ff b8 01 00 00 00
e8 aa 1c f4 ff 89 d8 83 c4 10 5b 5d c3 90 90 90 55 89 e5 53 83 ec 0c 8b
48 04 &8b& 11 39 c2 74 18 89 54 24 08 89 44 24 04 c7 04 24 be 32
& && && && && & EIP: [&c04e53d6&] list_del+0xa/0x61 SS:ESP 0068:dec62fa4
& && && && && & note: kstopmachine[1726] exited with preempt_count 1
& && &&&1, 有自己编译的vmlinux: 使用gdb
& && && &&&编译时打开complie with debug info选项。
& && && &&&注意这行:
& && && && && & EIP is at list_del+0xa/0x61
& && && &&&这告诉我们,list_del函数有0x61这么大,而Oops发生在0xa处。 那么我们先看一下list_del从哪里开始:
& && && && && & # grep list_del /boot/System.map-2.6.24-rc3-module
& && && && && & c10e5234 T plist_del
& && && && && & c10e53cc T list_del
& && && && && & c120feb6 T klist_del
& && && && && & c12d6d34 r __ksymtab_list_del
& && && && && & c12dadfc r __ksymtab_klist_del
& && && && && & c12e1abd r __kstrtab_list_del
& && && && && & c12e9d03 r __kstrtab_klist_del
& && && &&&于是我们知道,发生Oops时的EIP值是:
& && && && && & c10e53cc + 0xa&&== c10e53d6
& && && &&&然后用gdb查看:
& && && && && & # gdb /home/arc/build/linux-2.6/vmlinux
& && && && && & (gdb) b *0xc10e53d6
& && && && && & Breakpoint 1 at 0xc10e53d6: file /usr/src/linux-2.6.24-rc3/lib/list_debug.c, line 64.
& && && &&&看,gdb直接就告诉你在哪个文件、哪一行了。
& && && &&&gdb中还可以这样:
& && && && && & # gdb Sources/linux-2.6.24/vmlinux
& && && && && & (gdb) l *do_fork+0x1f
& && && && && & 0xc102b7ac is in do_fork (kernel/fork.c:1385).
& && && && && & 1380
& && && && && & 1381& & static int fork_traceflag(unsigned clone_flags)
& && && && && & 1382& & {
& && && && && & 1383& && && && &if (clone_flags & CLONE_UNTRACED)
& && && && && & 1384& && && && && && &&&return 0;
& && && && && & 1385& && && && &else if (clone_flags & CLONE_VFORK) {
& && && && && & 1386& && && && && && &&&if (current-&ptrace & PT_TRACE_VFORK)
& && && && && & 1387& && && && && && && && && & return PTRACE_EVENT_VFORK;
& && && && && & 1388& && && && &} else if ((clone_flags & CSIGNAL) != SIGCHLD) {
& && && && && & 1389& && && && && && &&&if (current-&ptrace & PT_TRACE_CLONE)
& && && && && & (gdb)
& && && && &也可以直接知道line number。
& && && && &或者:
& && && && && & (gdb) l *(0xffffffff8023eaf0 + 0xff)&&/* 出错函数的地址加上偏移 */
& && &&&2, 没有自己编译的vmlinux: TIPS
& && && &&&如果在lkml或bugzilla上看到一个Oops,而自己不能重现,那就只能反汇编以&Code:&开始的行。 这样可以尝试定位到
& && && &&&源代码中。
& && && &&&注意,Oops中的Code:行,会把导致Oops的第一条指令,也就是EIP的值的第一个字节, 用尖括号&&括起来。 但是,有些
& && && &&&体系结构(例如常见的x86)指令是不等长的(不一样的指令可能有不一样的长度),所以要不断的尝试(trial-and-error)。
& && && &&&Linus通常使用一个小程序,类似这样:
& && && && && & const char array[] = &/xnn/xnn/xnn...&;
& && && && && & int main(int argc, char *argv[])
& && && && && & {
& && && && && && && && &printf(&%p/n&, array);
& && && && && && && && &*(int *)0 = 0;
& && && && && & }
e.g. /*{{{*/ /* 注意, array一共有从array[0]到array[64]这65个元素, 其中出错的那个操作码&8b& == arry[43] */
#include &stdio.h&
#include &stdlib.h&
char array[]
=&/x6b/xc0/xe8/x2e/x7e/xf6/xff/xe8/xd1/x16/xf2/xff/xb8/x01/x00/x00/x00/xe8/xaa/x1c/xf4/xff/x89/xd8/x83/xc4/x10/x5b/x5d/xc3/x90/x90/x90/x55/x89/xe5/x53/x83/xec/x0c/x8b/x48/x04/x8b/x11/x39/xc2/x74/x18/x89/x54/x24/x08/x89/x44/x24/x04/xc7/x04/x24/xbe/x32/x6b/xc0&;
int main(int argc, char *argv[])
& && &&&printf(&%p/n&, array);
& && &&&*(int *)0 = 0;
& && && &&&用gcc -g编译,在gdb里运行它:
& && && && && & [arc@dhcp-cbjs05-218-251 ~]$ gdb hello
& && && && && & GNU gdb Fedora (6.8-1.fc9)
& && && && && & Copyright (C) 2008 Free Software Foundation, Inc.
& && && && && & License GPLv3+: GNU GPL version 3 or later &[url]http://gnu.org/licenses/gpl.html[/url]&
& && && && && & This is free software: you are free to change and redistribute it.
& && && && && & There is NO WARRANTY, to the extent permitted by law.&&Type &show copying&
& && && && && & and &show warranty& for details.
& && && && && & This GDB was configured as &x86_64-redhat-linux-gnu&...
& && && && && & (no debugging symbols found)
& && && && && & (gdb) r
& && && && && & Starting program: /home/arc/hello
& && && && && & 0x80484e0
& && && && && & Program received signal SIGSEGV, Segmentation fault.
& && && &&&注意,这时候就可以反汇编0x80484e0这个地址:
& && && && && & (gdb) disassemble 0x80484e0
& && && && && & Dump of assembler code for function array:
& && && && && & 0x &array+0&:& &imul& &$0xffffffe8,%eax,%eax
& && && && && & 0x &array+3&:& &jle,pn 0x80484dc &__dso_handle+20&
& && && && && & 0x &array+6&:& &ljmp& &*&internal disassembler error&
& && && && && & 0x &array+8&:& &rcll& &(%esi)
& && && && && & 0x080484ea &array+10&:&&repnz (bad)
& && && && && & 0x080484ec &array+12&:&&mov& & $0x1,%eax
& && && && && & 0x &array+17&:&&call& &0x7f8a1a0
& && && && && & 0x &array+22&:&&mov& & %ebx,%eax
& && && && && & 0x &array+24&:&&add& & $0x10,%esp
& && && && && & 0x080484fb &array+27&:&&pop& & %ebx
& && && && && & 0x080484fc &array+28&:&&pop& & %ebp
& && && && && & 0x080484fd &array+29&:&&ret
& && && && && & 0x080484fe &array+30&:&&nop
& && && && && & 0x080484ff &array+31&:&&nop
& && && && && & 0x &array+32&:&&nop
& && && && && & 0x &array+33&:&&push& &%ebp
& && && && && & 0x &array+34&:&&mov& & %esp,%ebp
& && && && && & 0x &array+36&:&&push& &%ebx
& && && && && & 0x &array+37&:&&sub& & $0xc,%esp
& && && && && & 0x &array+40&:&&mov& & 0x4(%eax),%ecx
& && && && && & 0x0804850b &array+43&:&&mov& & (%ecx),%edx
& && && && && & 0x0804850d &array+45&:&&cmp& & %eax,%edx
& && && && && & 0x0804850f &array+47&:&&je& &&&0x8048529
& && && && && & 0x &array+49&:&&mov& & %edx,0x8(%esp)
& && && && && & 0x &array+53&:&&mov& & %eax,0x4(%esp)
& && && && && & 0x &array+57&:&&movl& &$0xc06b32be,(%esp)
& && && && && & 0x &array+64&:&&add& & %ah,0xa70
& && && && && & End of assembler dump.
& && && && && & (gdb)
& && && & OK, 现在你知道出错的那条指令是array[43],也就是mov& & (%ecx),%edx,也就是说,(%ecx)指向了一个错误内存地址。
为了使汇编代码和C代码更好的对应起来, Linux内核的Kbuild子系统提供了这样一个功能: 任何一个C文件都可以单独编译成汇编文件,例如:
make path/to/the/sourcefile.s
例如我想把kernel/sched.c编译成汇编,那么:
make kernel/sched.s V=1
make kernel/sched.lst V=1
& & & &&&编译出*.s文件
& & & && &
& & & && & 有时侯需要对*.s文件进行分析,以确定BUG所在的位置。 对任何一个内核build目录下的*.c文件,都可以
& & & && & 直接编译出*.s文件。
& & & && & & & & & # make drivers/net/e100.s V=1
& & & && &
& & & && & 而对于自己写的module,就需要在Makefile中有一个灵活的target写法:
& & & && & & & & &
& & & & & & & & # cat Makefile
& & & & & & & & obj-m := usb-skel.o
& & & & & & & & KDIR := /lib/modules/`uname -r`/build
& & & & & & & & traget := modules
& & & & & & & & default:
& & & & & & & & & & & & make -C $(KDIR) M=$(shell pwd) $(target)
& & & & & & & & clean:
& & & & & & & & & & & & rm -f *.o *.ko .*.cmd *.symvers *.mod.c
& & & & & & & & & & & & rm -rf .tmp_versions
& & & & & & & & # make target=usb-skel.s V=1
& & & && &
& & & && & 这样,kbuild系统才知道你要make的目标不是modules,而是usb-skel.s。
另外, 内核源代码目录的./scripts/decodecode文件是用来解码Oops的:
./scripts/decodecode & Oops.txt
定位可动态加载的内核模块的OOPS代码行
(To be 千里马!)
最近又仔细学习了albcamus版主提供的《定位Oops的具体代码行》(链接:
),并且进行了实践。因此这里简单总结一下,并且以实例的方式给出定位可动态加载模块Oops信息的方法。
本文欢迎自由转载,但请标明出处,并保证本文的完整性。
Apr 19, 2009
1. 从vmlinux获取具体的代码行
章中albcamus版主也提到了,需要有自己编译的vmlinux,而且编译时打开compile with debug info.
这个选项打开之后会使vmlinux文件比不加调试信息大一些。我这里代调试信息的是49M。建议如果学习的时候,想使用gdb的方式获取出错代码行的
话,就加上这个编译条件。
然后就可以按照具体的方法去操作,可以定位到具体的C 代码行。
2. 从自己编译的内核模块出错信息中获取代码行
以ldd3中提供的misc-modules/faulty.c为例。主要以faulty_write函数作分析。
(1)由于作者提供的函数代码就一样,过于简单,我这里简单加上一些代码(也就是判断和赋值),如下:
ssize_t faulty_write (
&&&&&&&&&&&&&&&&loff_t *
&&&&&&&&/* make a simple fault by dereferencing a NULL pointer */
&&&&&&&&if
&&&&&&&&&&&&&&&&count
&&&&&&&&return
(2)编译该模块,并且mknod /dev/faulty
(3)向该模块写入数据:echo 1 & /dev/faulty, 内核OOPS,信息如下:
unable to handle kernel NULL
pointer dereference at virtual
printing eip:
Modules linked in:
faulty autofs4 hidp rfcomm l2cap .
//此处省略若干字符
tainted VLI
EIP is at faulty_write+
f4f6ca40 ecx:
f8e80000 edi:
b7c2d000 ebp:
Process bash (
f4dc5000 task=
f7c8d4d0 task.
cdc5fa4 f4f6ca40 fffffff7 b7c2d000 f4dcf06 f4dc5fa4
&&&&&&&00 0d0b c2d000
&&&&&&&0d000 bfd40aa8 ffffffda 0000007b c000004
Call Trace:
vfs_write+
sys_write+
syscall_call+
Bad EIP value.
faulty_write+
中,我们应该关注的信息是第一行红色标出部分:告诉我们操作了NULL指针。其次,就是第二行红色部分:EIP is at
faulty_write+0xe/0x19。这个出错信息告诉我们EIP指针出现问题的地方时faulty_write函数,而且指出了是faulty
同时,faulty_write+0xe/0x19的后半部分0xe/0x19,说明该函数的大小时0x019,出错位置是在0x0e。这两个值应该值得都是汇编代码的值。
(4)将faulty模块反汇编出汇编代码:
& && && & objdump -d faulty.ko & faulty.s
& && &&&或
& && && & objdump -d faulty.o & faulty.s
然后,我们打开faulty.s文件。由于我们需要关注的部分正好在文件的前面,因此我这里只贴出文件的前面一部分内容:
format elf32-
Disassembly of section .
faulty_write&
81 f9 00 01 00 00 cmp $0x100,
b8 00 01 00 00 mov $0x100,
0f 46 c1 cmovbe %
c7 05 00 00 00 00 00 movl $0x0,
cleanup_module&
a1 00 00 00 00 mov 0x0,
ba 00 00 00 00 mov $0x0,
e9 fc ff ff ff jmp 24 &
cleanup_module+
faulty_init&
a1 00 00 00 00 mov 0x0,
b9 00 00 00 00 mov $0x0,
ba 00 00 00 00 mov $0x0,
e8 fc ff ff ff call 38 &
faulty_init+
85 c0 test
78 13 js 53 &
faulty_init+
83 3d 00 00 00 00 00 cmpl $0x0,
74 03 je 4c &
faulty_init+
a3 00 00 00 00 mov %
由以上汇编代码可以看出,faulty_write函数的大小确实是0x18 -0x00 +1 = 0x19. 那么EIP指针出问题的地方是0x0e处,代码为:
c7 05 00 00 00 00 00 movl $0x0,
这行汇编代码就是将0值保存到0地址的位置。那么很显然是非法的。这一行对应的C 代码应该就是:
*(int *)0 = 0;
(5)以上是对模块出错信息的分析。不过也有一定的局限。
& &首先就是EIP出错的位置正好在本模块内部,这样可以在本模块定位问题;
& &其次,要求一定的汇编基础,特别是当一个函数的代码比较多时,对应的汇编代码也比较大,如何准确定位到C代码行需要一定的经验和时间。
& & 实际运用中,可以将内核代码行的定位和可动态加载的内核模块代码行的定位结合起来使用,应该可以较快的定位问题。
& & 分析中有纰漏或者不妥的地方希望大家指出,也希望有网友分享更有效的方法。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:11010次
排名:千里之外
(1)(1)(1)(1)(3)(2)(1)

我要回帖

更多关于 七日杀oops错误 的文章

 

随机推荐