[笔记分享] [Exception] 内核空间异常之Call Stack解析

1.1 介绍

这一章我们主要对如何分析oops做些描述,以及说下目前我知道的方法。
当然,这里以以下例子来描述。

这里写图片描述


1.2 定位某一行

首先要找到发生exception的函数,虽然bug并不一定是这个函数引起的,而是经过函数一级级传下来造成。但总要知道出错的最终点吧.

这里写图片描述

Call flow为 ret_fast_syscall -> vfs_read -> syfs_read -> sysfs_read_file -> kobj_attr_show ->lcd_display_show, 可见, 是lcd_display_show引起exception处。

一般比较简单的bug在函数中是马上可以找到, 比如我这里p的地址受保护, 内核只能访问0xc0000000以上的address。

那么当函数code较多的时候, 如何定位某一行呢。

这里写图片描述

表示了exception相对函数的offset。
所以,可以想一下,只要我们有了函数的地址,然后就可以找到那一行了。

方法一(gdb):
1, 找到vmlinux ,执行gdb vmlinux

这里写图片描述

  1. 执行 l *lcd_display_show+0x20

这里写图片描述

gdb直接报是62行了。

利用gdb查找比较简单,那么当没有gdb或者vmlinux的时候,我们只有通过反汇编分析了。

方法二(disassemble):
1. 找到lcd_display_show所在目标文件, 这里为lcd_sysfs.o
2. 将lcd_sysfs.o反汇编。

这里写图片描述

3.找到lcd_display_show

这里写图片描述

4.offset 为0x20, 所以是 str ip, [r3, #1656], 将ip存到给r3+1656为地址的内存中。Ip为0, 再去c文件中找赋值为0的地方。这样大概能分析出来了。

利用反汇编分析需要对汇编有一定了解,另外还具有一定的经验才能分析出大型的函数。


1.3 手动分析call stack

当kernel没打印出call stack时,我们只能手动分析了。
仍以上面的code为例, sp信息如下:(不知除了oops外是否还有其他方法得知sp信息)

这里写图片描述

<1>根据pc值找到第一个函数。 pc为0xc02dad2c, 使用vmlinux的反汇编程序vmlinux.dis找到
函数。

这里写图片描述

push {r4, lr} 这几个寄存器保存在stack中, 所以会sp中的信息会这样:

这里写图片描述

其中lr为0xc0292754, 表示lcd_display_show执行完后的返回地址,它是调用函数中的地址。下面使用lr值重复本步骤的回溯过程。

<2> 根据0xc0292754在vmlinux.dis中找到函数:

这里写图片描述

push {r4, lr} 这几个寄存器保存在stack中, 所以会sp中的信息会这样:

这里写图片描述

其中0xc0196774为kobj_attr_show执行后的返回地址。
<3> 使用每个函数的lr值,重复第一步骤,知道分析完sp为止,这样就可以找出function call stack了。


1.4 call stack信息不全

有时候exception问题也会导致call stack不全,这种情况就比较难分析。这里参考了ldd3中的一个例
子来说明:

这里写图片描述

例子中由于字符串的长度长处了数组范围,当函数返回时就会导致缓冲区溢出错误而引起oops。像这样的exception就比较难追踪。

这里写图片描述

Oops中没有将call stack 全部打出来,这样很难找到出错的函数,不过PC的值变成了0xffffffff暗示了内核栈已经溢出了。

碰到这类问题也只能靠积累的经验来分析了。


Questions

  1. 对于没有产生oops的kernel down机,如watchdog,或者是exception nest了,这类问题就更不方便处理,由于没有log,所以不清楚具体是哪个模块引起。目前没有想到具体的方法。
  2. 对于系统挂起或者只有一个系统挂起,在PC机上倒有一些方法。但是对于我们平台,也还没找到比较好的办法。

Reference

[1] Android_TRACE&EXCEPTION原理.doc
[2] http://www.cnblogs.com/pied/archive/2010/06/28/1766818.html [3] kernel exception code