内核模块死机调试

介绍一种内核模块死机调试的方法。在程序中,通常必现的死机很好解决,但是如果是拷机死机的话,则如果有好的方法和工具借助往往会极大的提高解决问题的速度。在linux中,应用空间程序死机通常可以借助GDB、addr2line等工具进行快速定位。但是,在内核空间中,大部分嵌入式芯片都不支持内核空间的KGDB调试。通常在内核中死掉的话,可以根据死机地址再加反汇编,或者是addr2line找到死机的函数或者是那一行,但是对于动态插入的模块却不行。这是因为,内核在编译的时候就把地址链接好了,运行的时候代码段的地址空间在链接的时候就决定了。但是,对于模块来说的话,在insmod的时候,内核是动态的分配一段内存,在这段内存中完成模块和内核的链接,并决定该模块运行时的模块代码段、数据段,等等。而且模块文件本质就是一个目标文件,当然他比普通的目标文件还含有更多一些帮助模块装载的东西。这样,我们如果在运行的时候死机在模块中的话,即使内核最后打印出了死机地址以及堆栈,我们也没有办法将这些地址与代码对应。当然,如果该模块是带-g编译的话,运气好的话,内核会打印出函数名字的堆栈。现在,我讨论的时候如果只有地址,该如何和模块代码对应。
        首先,我们用objdum -d test.ko > test.asm的话。可以将模块文件反汇编。但是因为模块是目标文件,所以会看到,text段的地址是从0开始的。而该模块在内核在运行的时候,该模块的代码段显然不是在0地址。另外,该代码段中你会看到函数间调用,以及函数内部跳转都是用的b开头的分支指令(仅限于mips和arm的体系结构的讨论),分支指令跳转是以pc为基准值前后跳转的。除非,跳转符号不属于这个模块,则需要32位的跳转。实际上,模块链接到内核的时候,模块的代码段是作为一个整体链接到内核,所以我们只需要知道模块链接到的基地址,再用死机的地址减去这个基地址这就得到了,该地址在模块中代码段的偏移,再通过刚才的反汇编文件就找到了是死在那个函数中。而要得到各个模块的链接地址就很简单了,直接cat /proc/modules就得到了。需要注意的是用__init修饰了的模块初始化函数,是放在单独的段的,通常叫.init.text 。该段会在模块初始化完成后内存就释放掉了。
        说了这么多,其实就一句话,通过cat /proc/modules获得模块内核链接基地址,用死机地址减去链接基地址得到模块内偏移,再反汇编,找该偏移对应的函数就找到了死机函数。呵呵,这对于拷机死机这种问题还是非常有帮助的。这篇文章很多涉及到编译链接原理,以及elf文件格式的细节,就不累述了,否则可能要整成一本书了。

你可能感兴趣的:(内核模块死机调试)