OS X内核恐慌诊断.如何翻译回溯地址

Zoh*_*r81 3 debugging macos kernel backtrace xnu

我正在调试一个在我的mac上导致内核转储的驱动程序.它显示了由一系列地址组成的长回溯:

panic(cpu 6 caller 0xffffff8004dc9986): trying to interlock destroyed mutex  
(0xffffff8049deedb0)
Backtrace (CPU 6), Frame : Return Address
0xffffff93b1c8bb50 : 0xffffff8004ce5307 
0xffffff93b1c8bbd0 : 0xffffff8004dc9986 
0xffffff93b1c8bbe0 : 0xffffff8004d099eb 
0xffffff93b1c8bc20 : 0xffffff7f85604899 
0xffffff93b1c8bc50 : 0xffffff800519776b 
0xffffff93b1c8bc90 : 0xffffff80051f336c 
0xffffff93b1c8be00 : 0xffffff8005205fb3 
0xffffff93b1c8bef0 : 0xffffff80052028a6 
0xffffff93b1c8bf60 : 0xffffff800522afd1 
0xffffff93b1c8bfb0 : 0xffffff8004df4b16 
Run Code Online (Sandbox Code Playgroud)

我假设由于计算机经历了重启,因此地址转换现在是无用的,因为在每次启动迭代之后内存映射可能不同.

是否有任何选项可以将相关方法与回顾中的每个地址相匹配,或者事先设置适当的配置?谢谢

pmd*_*mdj 12

是的,你绝对可以象征内核恐慌痕迹,但为了追溯它,你需要恐慌日志中的更多信息,而不仅仅是原始堆栈跟踪.如你所说,地址只对加载地址有意义.

Apple关于该主题的官方文档,TN2063有点过时了.它提供的示例来自Darwin 9,即OS X 10.5,自那时起事情发生了一些变化,引入了内核ASLR和kext UUID.我会尽力给你一个非常快速的最新指南.

1.简单的方法

如果您的恐慌是可重现的,最简单的方法是让内核为您进行符号化.使用内核引导参数keepsyms=1意味着内核不会丢弃内核和kext图像中存储的任何符号,并且会在发生混乱时查找堆栈跟踪中的返回指针.

只需添加keepsyms=1内核标志设置/Library/Preferences/SystemConfiguration/com.apple.Boot.plist,或添加到boot-argsNVRAM变量.重新启动,任何后续的恐慌都将自动进行符号化.您可以通过c++filt命令行实用程序运行受损的C++符号,以获得正确的C++函数签名.例如,

$ echo __ZN32IOPCIMessagedInterruptController17registerInterruptEP9IOServiceiPvPFvS2_S2_S2_iES2_ | c++filt
IOPCIMessagedInterruptController::registerInterrupt(IOService*, int, void*, void (*)(void*, void*, void*, int), void*)
Run Code Online (Sandbox Code Playgroud)

2.手动方式

如果你有一种无法解释的,神秘的恐慌,你似乎无法重现,那么简单的方法就没有多大帮助.

在堆栈跟踪之后,立即在恐慌日志中查找以"回溯中的内核扩展:"开头的部分.这将列出恐慌中涉及的任何关键字,包括其加载地址,版本和UUID.地址作为范围给出; 起始地址位于->和之后的左侧@.最后一个地址位于箭头右侧.使用此信息,您应该能够识别堆栈跟踪中列出的每个代码地址(右侧的十六进制数)所在的kext.

除了其中一些不匹配任何kext.除了在某些奇怪的情况下,这些将来自内核本身.内核映像(内核或mach_kernel)加载地址进一步下降,其中显示"内核文本库":

一旦知道要查看哪个可执行映像,该atos命令将允许您对每个地址进行符号化.

例如,假设我们恐慌这条线:

…
0xffffff8098c1bba0 : 0xffffff7f80c343f2
…
Run Code Online (Sandbox Code Playgroud)

我们还发现:

  Kernel Extensions in backtrace:
     com.apple.iokit.IOPCIFamily(2.9)[BDA92C3B-AD86-33E5-A7F1-1603465350A7]@0xffffff7f80c1a000->0xffffff7f80c4dfff
Run Code Online (Sandbox Code Playgroud)

请注意,0xffffff7f80c343f2大于(或等于)0xffffff7f80c1a000且小于(或等于)0xffffff7f80c4dfff,因此所讨论的代码位于IOPCIFamily中.

这导致我进入以下命令(及其输出):

$ atos -o /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Extensions/IOPCIFamily.kext/IOPCIFamily -l 0xffffff7f80c1a000 0xffffff7f80c343f2
IOPCIMessagedInterruptController::registerInterrupt(IOService*, int, void*, void (*)(void*, void*, void*, int), void*) (in IOPCIFamily) (IOPCIMessagedInterruptController.cpp:85)
Run Code Online (Sandbox Code Playgroud)

-o指定可执行文件.这通常位于.kext包的Contents/MacOS /子目录中,但Apple的一些kexts直接在.kext目录中.对于内核本身的函数,提供内核映像,例如/Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel.

-l参数指定加载地址.即开始/文本库.

最后,只列出要在该文件中进行符号化的所有地址.在这种情况下,只有一个,但你可以列出多个.您也可以从stdin读取它们(如果命令行中没有列出).

有了这个,您应该能够解码整个跟踪.

关于UUID的说明

您会注意到跟踪中的每个kext和内核本身都列出了UUID.这对于确保您使用正确的版本进行符号化非常方便.这是Mach-O二进制文件中LC_UUID加载程序命令的UUID.您可以使用以下方法检查UUID以获取kext:

$ otool -l /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Extensions/IOPCIFamily.kext/IOPCIFamily | grep uuid
    uuid BDA92C3B-AD86-33E5-A7F1-1603465350A7
Run Code Online (Sandbox Code Playgroud)

确认用于符号化的kext确实与恐慌中的kext匹配.当你遇到奇怪的版本问题,或者你遇到kext缓存问题时,这很好.

  • @ravron 我认为任何地方都没有任何公共规范文档。这主要是我在过去 6 到 7 年的 Mac 内核编程中学到的东西,因为他们慢慢地一次对每个操作系统版本进行了一次或两次更改。我可能应该更详细地写一篇博文;我在我们的内部 wiki 上写了一个比上面更详细的文档,可以将其转换为博客文章……来自 Apple 的最新公共 kext 文档可能是 WWDC 2013 session 707。https://developer。 apple.com/videos/play/wwdc2013/707/ (2认同)