dSYM地址查找

时间:2013-09-08 05:34:04

标签: ios objective-c dwarf dsym

我已经解析了iOS应用的dSYM文件中的地址,文件名和行号。我基本上有一个表将地址映射到文件名和行号,这对调试很有帮助。

要获取actual lookup address,我使用崩溃报告中的堆栈跟踪地址,并使用此答案中指定的公式:https://stackoverflow.com/a/13576028/2758234。就像这样。

(actual lookup address) 
= (stack trace address) + (virtual memory slide) - (image load address)

我使用该地址并在我的桌子上查找。我得到的文件名是正确的,但行号总是指向被调用的函数或方法的末尾,而不是在堆栈跟踪上调用以下函数的实际行。

我在某个地方读过,不记得在哪里,那个帧地址必须被去标记,因为它们对齐以使系统指针大小加倍。因此对于32位系统,指针大小为4个字节,因此我们使用如下公式使用8个字节进行解标记:

(de-tagged address) = (tagged address) & ~(sizeof(uintptr_t)*2 - 1)

其中uintptr_t是Objective-C中用于指针的数据类型。

执行此操作后,查找排序有效,但我必须执行类似 的操作,找到小于或等于已取消标记的地址的最近地址

问题#1
为什么我必须取消标记堆栈帧地址?为什么堆栈跟踪不是已经指向正确位置的地址?

问题#2
有时在崩溃报告中似乎缺少框架。例如,如果function1()调用function2()调用function3()调用function4(),则在我的堆栈跟踪中,我会看到如下内容:

0  Exception
1  function4()
2  function3()
4  function1()

function3()(上面的第2帧)的堆栈跟踪地址甚至没有指向正确的行号(但它是正确的文件),即使在去标记之后也是如此。即使我让Xcode象征崩溃报告,我也会看到这一点。

为什么会这样?

1 个答案:

答案 0 :(得分:3)

对于问题#1,iOS崩溃报告中的地址包含三个要考虑的组件:应用的原始加载地址,启动应用时添加到该地址的随机幻灯片值,以及二进制内的偏移量。在崩溃报告结束时,应该有一行显示二进制文件的实际加载地址。

要计算幻灯片,您需要从崩溃报告中获取实际加载地址并减去原始加载地址。这会告诉您应用于此特定应用启动的随机幻灯片值。

我不确定你是如何得出你的桌子的 - 问题可能就在那里。您可能希望使用lldb进行双重检查。您可以将应用程序加载到lldb并告诉lldb它应该在地址0x140000处加载(这将是崩溃报告中的实际加载地址,不用担心幻灯片和原始加载地址)< / p>

% xcrun lldb
(lldb) target create -d -a armv7 /path/to/myapp.app
(lldb) target modules load -f myapp __TEXT 0x140000

现在lldb已将此二进制文件加载到此崩溃报告的实际加载地址中。您可以在lldb中执行所有常见查询,例如

(lldb) image lookup -v -a 0x144100

对地址0x144100进行详细查询(可能会出现在崩溃报告中)。

您还可以使用target modules dump line-table在lldb中执行漂亮的“转储内部线路表”命令。例如,我编译了一个hello-world Mac应用程序:

(lldb) tar mod dump line-table a.c
Line table for /tmp/a.c in `a.out
0x0000000100000f20: /tmp/a.c:3
0x0000000100000f2f: /tmp/a.c:4:5
0x0000000100000f39: /tmp/a.c:5:1
0x0000000100000f44: /tmp/a.c:5:1
(lldb) 

我可以更改二进制文件的加载地址,然后再次尝试转储行表:

(lldb) tar mod load -f a.out __TEXT 0x200000000
section '__TEXT' loaded at 0x200000000
(lldb) tar mod dump line-table a.c
Line table for /tmp/a.c in `a.out
0x0000000200000f20: /tmp/a.c:3
0x0000000200000f2f: /tmp/a.c:4:5
0x0000000200000f39: /tmp/a.c:5:1
0x0000000200000f44: /tmp/a.c:5:1    
(lldb) 

我不确定我是否理解您在解除地址标记方面所做的工作。调用堆栈上的地址是这些函数的 return 地址,而不是调用指令 - 因此这些地址可能指向实际方法调用/调度源代码行之后的行,但是当你查看源代码时,这通常很容易理解。如果所有查找都指向方法的末尾,我认为您的查找方案可能有问题。

对于问题#2,如果帧#0(当前正在执行的帧)是没有设置堆栈帧的叶子函数,或者在帧中,帧#1的展开有时会有点棘手。设置堆栈帧的过程。在这些情况下,帧#1可以被跳过。但是一旦你超过第1帧,特别是在手臂上,放松不应该错过任何帧。

当标记为noreturn的函数调用另一个函数时,有一个非常边缘的皱纹,函数的最后一条指令可能是一个调用 - 没有函数结尾 - 因为它知道它永远不会得到控制再次。很少见。但在这种情况下,一个简单的符号会给你一个指向内存中下一个函数的第一条指令的指针。调试器等使用一种技巧,在执行符号/源代码行查找时,它们会从返回地址中减去1来回避这个问题,但这并不是偶然的符号设计者通常需要担心的问题。并且你必须小心不要在当前正在执行的函数(第0帧)上执行decr-pc技巧,因为函数可能刚刚开始执行而你不想将pc备份到上一个函数并且错误地符号化