Linux内核死机调试方法总结

使用空指针和缓冲区溢出是产生oops的两个最常见原因。

1、直接查看oops信息,首先查找源代码发生oops的位置,通过查看指令寄存器EIP的值,可以找到位置。再查找函数调用栈可以得到更多的信息。从函数调用栈可辨别出局部变量,全局变量和函数参数。较为重要的信息就是指令指针(EIP),即出错指令的地址。

例如:在函数faulty_read的oops信息的函数调用栈中,栈顶为ffffffff,栈顶值应为一个小于ffffffff的值,为此值,说明再找不回调用函数地址,说明有可能因缓冲区溢出等原因造成指针错误。

如果oops信息显示触发oops的地址为0xa5a5a5a5,则说明很可能是因为没有初始化动态内存引起的。

2、使用\prebuilts\gcc\linux-x86\arm\arm-eabi-4.7\bin\arm-eabi-addr2line命令找到地址对应的程序位置,显示对应的程序文件名和行号。

1.arm-eabi-addr2line 将类似libxxx.so 0x00012345的调用栈16进制值翻译成文件名和函数名

arm-eabi-addr2line -e libxxx.so 0x00012345

2.arm-eabi-nm 列出文件的符号信息

arm-eabi-nm -l -C -n -S libdvm.so > dvm.data

3.arm-eabi-objdump 列出文件的详细信息

arm-eabi-objdump -C -d libc.so > libc.s

通过以上工具的分析 ,我们可以得到较完整的调用栈以及调用逻辑的汇编码。

addr2line -e -f libc.so 0001173c

objdump -S -D libc.so > deassmble_libc.txt

打开这个反汇编过后的重定向文件,在查询的时候输入1173c这个偏移地址,你会看到在茫茫人海中

00011684 <pthread_create>:
   11684:       e92d4ff0        push    {r4, r5, r6, r7, r8, r9, sl, fp, lr}
   11688:       e24dd01c        sub     sp, sp, #28     ; 0x1c
   1168c:       e1a06001        mov     r6, r1
   11690:       e1a08002        mov     r8, r2
   11694:       e1a09003        mov     r9, r3
   11698:       e3a04001        mov     r4, #1  ; 0x1
   1169c:       e59f521c        ldr     r5, [pc, #540]  ; 118c0 <pthread_create+0x23c>
   116a0:       e58d000c        str     r0, [sp, #12]
   116a4:       eb009a35        bl      37f80 <strncmp+0x20>
   116a8:       e59f2214        ldr     r2, [pc, #532]  ; 118c4 <pthread_create+0x240>
   116ac:       e1a03000        mov     r3, r0
   116b0:       e1a01004        mov     r1, r4
   116b4:       e593c000        ldr     ip, [r3]
   116b8:       e3a0003c        mov     r0, #60 ; 0x3c
   116bc:       e08f3005        add     r3, pc, r5
   116c0:       e7933002        ldr     r3, [r3, r2]
   116c4:       e5834000        str     r4, [r3]
   116c8:       e58dc010        str     ip, [sp, #16]
   116cc:       eb009a3b        bl      37fc0 <strncmp+0x60>
   ...

1173c:       ebffec2b        bl      c7f0 <__pthread_clone>-->就是他了,对你成功了。

...

3、在分析转储映像之前,用户应重启动进入一个稳定的内核。用户可以用GDB对拷贝出的转储进行有限分析。编译vmlinux时应加上-g选项,才能生成调试用的符号,然后,用下面的命令调试vmlinux:

gdb vmlinux <dump-file>首先 objdump -D vmlinx 反汇编你的内核然后 你可以通过以下几个寄存器来判断: 1. epc    挂在哪个函数里2. ra     函数的返回地址,3. Cause  通过这个寄存器可以分析是什么类型的异常.

4、通过cat /proc/modules获得模块内核链接基地址,用死机地址减去链接基地址得到模块内偏移,再反汇编,找该偏移对应的函数就找到了死机函数。

用objdump -d test.ko > test.asm的话。可以将模块文件反汇编。但是因为模块是目标文件,所以会看到,text段的地址是从0开始的。而该模块在内核在运行的时候,该模块的代码段显然不是在0地址。另外,该代码段中你会看到函数间调用,以及函数内部跳转都是用的b开头的分支指令(仅限于mips和arm的体系结构的讨论),分支指令跳转是以pc为基准值前后跳转的。除非,跳转符号不属于这个模块,则需要32位的跳转。实际上,模块链接到内核的时候,模块的代码段是作为一个整体链接到内核,所以我们只需要知道模块链接到的基地址,再用死机的地址减去这个基地址这就得到了,该地址在模块中代码段的偏移,再通过刚才的反汇编文件就找到了是死在那个函数中。而要得到各个模块的链接地址就很简单了,直接cat /proc/modules就得到了。需要注意的是用__init修饰了的模块初始化函数,是放在单独的段的,通常叫.init.text 。该段会在模块初始化完成后内存就释放掉了

5、google提供了一个python脚本,可以从 http://code.google.com/p/android-ndk-stacktrace-analyzer/ 下载这个python脚本,然后使用
adb logcat -d > logfile 导出 crash 的log,
使用 arm-eabi-objdump (位于build/prebuilt/linux-x86/arm-eabi-4.2.1/bin下面)把so或exe转换成汇编代码,如:
arm-eabi-objdump -S mylib.so > mylib.asm,
然后使用脚本
python parse_stack.py <asm-file> <logcat-file>

http://blog.csdn.net/lickylin/article/details/19172725

如上崩溃信息,可知发生崩溃的函数为rb_init_debugfs,崩溃的地址为0x804386f8

1>在linux下,到工程的如下目录下:kernel/linux,找到文件vmlinux,执行命令gdb vmlinux:

在gdb命令下执行如下命令即可查找到出错函数所在的文件与行数

(gdb) b *0x804386f8

2>如果不确定崩溃的地址是否是0x804386f8,可以在文件System.map中

查找函数rb_init_debugfs获取该函数的地址,然后加上偏移地址(本例中偏移地址为0x14 rb_init_debugfs+0x14/0x70)即可。

3>直接函数名加偏移量也可以

(gdb) b *rb_init_debugfs+0x14

上面是出错模块是编译进内核的,对于编译进内核的模块可以通过gdb vmlinux来确定出错函数所在的文件与行数。

那如果出错模块是动态加载进内核的该怎么办呢?

这就需要使用objdump进行反汇编操作了,使用如下命令,就会将C语言与汇编语言同时显示(需要加-g命令)

#objdump -S  **.o -g

如果使用上面命令,还是只显示汇编,而没有c语言的话,不用担心,在你编译驱动模块的Makefile中,编译成.o文件时,增加-g选项,

对于新生成的.o文件再使用上面的命令就 可以看到汇编与c语言共存了。然后根据kernel panic提示中显示的函数名加上偏移量就能找到出错行了。

http://blog.csdn.net/wuruixn/article/details/38320643

1. 为了测试GDB操作,故意在kernel/linux/fs/ioctl.c文件的do_vfs_ioctl方法中加入空指针操作代码,然后编译image烧入单板,启动单板,内核crash,部分log如下:

CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 800a73b8, ra == 800a793c

Oops[#1]:
Cpu 0
$ 0   : 00000000 10008d00 00000000 ffffffea
$ 4   : fffffdfd 10008d01 00000001 00000000
$ 8   : 00000000 7fed2e40 00001cb2 00000b3b
$12   : 00031c7f 2ab5ead7 2aaac7c9 15010000
$16   : 7fed2e18 878ca5a0 00000000 00000001
$20   : 2ab84980 00000000 00000007 00000000
$24   : 00000000 2ab62090                 
$28   : 8782c000 8782fe98 7fed2fc8 800a793c
Hi    : 0000002a
Lo    : 000311fc
epc   : 800a73b8 do_vfs_ioctl+0x88/0x5c8
    Not tainted
ra    : 800a793c sys_ioctl+0x44/0x98
Status: 10008d03    KERNEL EXL IE
Cause : 00000008
BadVA : 00000000
PrId  : 0002a080 (Broadcom4350)
Modules linked in:

Process init (pid: 1, threadinfo=8782c000, task=8782bb68, tls=00000000)
Stack : 878ca1a0 00000004 00000000 10008d00 00000603 2aabcfff 87b0bea8 00000001
        87beeaf0 2aabc000 2aabd000 87aa9cb0 2aabd000 2aabd000 8782ff08 fffffff8
        00000001 7fed3258 00000007 00000000 878ca5a0 0000540d 00000000 00000001
        2ab84980 800a793c 08100871 00000001 87bea41c 0000fff2 00000000 2abbdff0
        7fed2e18 00000001 7fed2e60 2abbdff0 00000120 8001ba7c 00000000 00000000
        ...
Call Trace:(--Raw--
[<800a793c>] sys_ioctl+0x44/0x98
[<8001ba7c>] stack_done+0x20/0x3c

Call Trace:
[<800a73b8>] do_vfs_ioctl+0x88/0x5c8
[<800a793c>] sys_ioctl+0x44/0x98
[<8001ba7c>] stack_done+0x20/0x3c

Code: 0c029c9f  02003021  8fbf0064 <8c020000> 8fb40060  8fb3005c  8fb20058  8fb10054  8fb00050
Disabling lock debugging due to kernel taint
Kernel panic - not syncing: Attempted to kill init!
Rebooting in 1 seconds..<6>
stopping CPU 1
kerSysMipsSoftReset: called on cpu 0

2. 启动GDB, 直接在主机(开发机)控制台运行gdb工具(或./mips-linux-uclibc-gdb),然后敲入 file  ...../vmlinux启动带调试信息的内核,注意此时要配置内核debug开关重新编译内核生成文件vmlinux(比之前的文件大10倍左右,50M以上),配置内核debug开关如下,

Kernel hacking  --->

[*] Kernel debugging

[*] Compile the kernel with debug info

编译内核命令:make kernelbuild,要在project的根目录下运行该命令进行编译。

启动GDB命令如下:

3. GDB调试定位

Checked call trace log, the most important part in log is “epc” (exception program counter), it is where the crash happened. In this example, the “epc” is 0xc0d1d488. For X1000 and X3500, the address like 0x8XXXXXXXX is in kernel, and the address like 0xcXXXXXXX is in some module.

地址0x800a73b8就是对应于地址(do_vfs_ioctl+0x88),后者表示位于函数do_vfs_ioctl的偏移地址0x88处。

调试过程如下:

(gdb) list *(0x800a73b8)

0x800a73b8 is in do_vfs_ioctl (fs/ioctl.c:569).
564                             error = vfs_ioctl(filp, cmd, arg);
565                     break;
566             }
567             error = *test;
568             return error;
569     }
570
571     SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
572     {
573             struct file *filp;
(gdb) list*(do_vfs_ioctl+0x88)
0x800a73b8 is in do_vfs_ioctl (fs/ioctl.c:569).
564                             error = vfs_ioctl(filp, cmd, arg);
565                     break;
566             }
567             error = *test;
568             return error;
569     }
570
571     SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
572     {
573             struct file *filp;
(gdb) list*(sys_ioctl+0x44)

可以分析该函数附近代码发现错误位置在line567,test为空指针(故意在前面赋值NULL)。

注:如果list*(0x80xxxxxx)命令提示如下信息:No source file for address 0x80xxxxxx. 原因是没有在make menuconfig中打开对应的调试开关进行编译。

时间: 2024-08-11 07:49:00

Linux内核死机调试方法总结的相关文章

android 动态库死机调试方法 .

原地址:http://blog.csdn.net/andyhuabing/article/details/7074979 这两种方法都不是我发明了,都是网上一些高手公共出来的调试方法,无奈找不到出处的地方了,所以就在此总结一下,以方便android下的调试: 简要说明: android系统中调试Java非常容易,一般遇到错误都在logcat中打印出错时函数的调用关系,而C库中出错时只看到一些二进制信息,使用gdbserver调试环境搭建又比较复杂. 方法一:下在介绍一个简单的调试库的方法,当然需

嵌入式/X86下linux系统死机及内存优化

一.  CPU 过高或死锁导致系统卡死 1. CPU占用过高 (1)开线程太多导致CPU占用过高,系统卡死 解决:优化应用层业务逻辑,有些业务不必开线程就不开 (2)频繁清缓存导致读spi-flash频繁,引起CPU过高 解决:在内核中文件系统部分进行处理,不去清缓存,然后在应用层用后台服务进程清缓存.这样可能导致系统内存不够用,这样就需要再对系统内存进行优化 2. CPU死锁 (1)Spinlock+死等导致死锁 (2)应用层pthread_mutex_lock死锁 3.  CPU过热导致系统

linux系统死机分析及解决方法

一.常见死机原因 二.日志分析 日志系统,通过rsyslog.service服务进行控制,分别用于记录系统内核和各应用程序的日志信息.配置文件/etc/rsyslog.conf /var/log/messages    记录系统内核消息及各种应用程序的公共日志信息,包括启动.IO错误.网络错误.程序报错等,对于未使用独立日志文件的应用程序或服务,一般都可以从该文件获得相关事件的日志记录信息. /var/log/cron    记录crond计划任务产生的事件消息 /var/log/dmesg  

Linux 内核与模块调试

一.简介 内核开发比用户空间开发更难的一个因素就是内核调试艰难.内核错误往往会导致系统宕机,很难保留出错时的现场.调试内核的关键在于你的对内核的深刻理解.   二.方法总结 1)内核调试指南 http://blog.csdn.net/blizmax6/article/details/6747601/ 2)调试方法总结 http://my.oschina.net/fgq611/blog/113249

轻松学习linux内核源码的方法

轻松学习Linux操作系统内核源码的方法 针对好多Linux 爱好者对内核很有兴趣却无从下口,本文旨在介绍一种解读linux内核源码的入门方法,而不是解说linux复杂的内核机制:一.核心源程序的文件组织:1.Linux核心源程序通常都安装在/usr/src/linux下,而且它有一个非常简单的编号约定:任何偶数的核心(例如2.0.30)都是一个稳定地发行的核心,而任何奇数的核心(例如2.1.42)都是一个开发中的核心. 本文基于稳定的2.2.5源代码,第二部分的实现平台为 RedHat Lin

linux下core文件调试方法(转载)

转自于:http://blog.csdn.net/fcryuuhou/article/details/8507775 在程序遇到段错误不寻常退出时,一般是访问内存出错.但是不会给出程序哪里出现的问题,这个时候就需要core文件来帮助调试. 内核会在当前工作目录下生成一个core文件(是一个内存映像,同时加上调试信息).使用gdb来查看core文件,可以指示出导致程序出错的代码所在文件和行数. 1.core文件的生成开关和大小限制 1)使用ulimit -c命令可查看core文件的生成开关.若结果

Linux内核源码分析方法

  一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都知道,想成为优秀的程序员,需要大量的实践和代码的编写.编程固然重要,但是往往只编程的人很容易把自己局限在自己的知识领域内.如果要扩展自己知识的广度,我们需要多接触其他人编写的代码,尤其是水平比我们更高的人编写的代码.通过这种途径,我们可以跳出自己知识圈的束缚,进入他人的知识圈,了解更多甚至我们一

电脑死机是什么原因造成?解决电脑死机的方法

死机原因:电脑待机后会自动降低cpu频率及风扇转速来节省能耗,现在的cpu频率普遍较高,风扇转速过小或者是不动,cpu就会热的发烫,所以从待机进入正常模式就容易死机. 解决方法: 1.开机进入bios界面,选择"Power Management Setup"选项,如图所示: 2.找到"Fan Off When Suspend"选项并回车修改设置成"Disable",即可.

Linux内核驱动--mmap设备方法【原创】

mmap系统调用(功能) void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) 内存映射函数mmap , 负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read, write等操作. addr:        指定映射的起始地址,通常设为NULL, 由系统指定. len:          映射到内存的文件长度 prot: