Dya*_*yan 3 objective-c dwarf ios dsym
我已经从 iOS 应用程序的 dSYM 文件中解析出地址、文件名和行号。我基本上有一个将地址映射到文件名和行号的表,这对于调试非常有帮助。
为了获取actual lookup address,我使用崩溃报告中的堆栈跟踪地址并使用此答案中指定的公式:/sf/answers/950321991/。所以像这样的事情。
(actual lookup address)
= (stack trace address) + (virtual memory slide) - (image load address)
Run Code Online (Sandbox Code Playgroud)
我使用该地址并在我的桌子上查找它。我得到的文件名是正确的,但行号始终指向被调用的函数或方法的末尾,而不是调用堆栈跟踪上的以下函数的实际行。
我在某处读到,不记得在哪里,必须取消标记帧地址,因为它们对齐到系统指针大小的两倍。因此,对于 32 位系统,指针大小为 4 字节,因此我们使用 8 字节进行脱标记,使用如下公式:
(de-tagged address) = (tagged address) & ~(sizeof(uintptr_t)*2 - 1)
Run Code Online (Sandbox Code Playgroud)
其中uintptr_t是 Objective-C 中指针使用的数据类型。
完成此操作后,查找就可以工作了,但我必须做一些事情,例如找到小于或等于去标记地址的最近地址。
问题#1:
为什么我必须取消堆栈帧地址的标记?为什么堆栈跟踪中的地址没有指向正确的位置?
问题#2:
有时崩溃报告中似乎缺少帧。例如,如果function1()调用function2()哪个调用function3()哪个调用function4(),在我的堆栈跟踪中我会看到类似的内容:
0 Exception
1 function4()
2 function3()
4 function1()
Run Code Online (Sandbox Code Playgroud)
即使在取消标记之后,(上面第 2 帧)的堆栈跟踪地址function3()甚至没有指向正确的行号(但它是正确的文件)。即使当我让 Xcode 表示崩溃报告时我也会看到这一点。
为什么会出现这种情况?
对于问题#1,iOS 崩溃报告中的地址需要考虑三个组成部分:应用程序的原始加载地址、应用程序启动时添加到该地址的随机幻灯片值以及应用程序内的偏移量。二进制。在崩溃报告的末尾,应该有一行显示二进制文件的实际加载地址。
要计算幻灯片,您需要从崩溃报告中获取实际加载地址并减去原始加载地址。这会告诉您应用于该特定应用程序启动的随机幻灯片值。
我不确定你是如何得出你的表的 - 问题可能就在那里。您可能需要使用 lldb 进行双重检查。您可以将您的应用程序加载到 lldb 中,并告诉 lldb 它应该在地址 0x140000 处加载(这将是崩溃报告中的实际加载地址,不用担心幻灯片和原始加载地址)
% xcrun lldb
(lldb) target create -d -a armv7 /path/to/myapp.app
(lldb) target modules load -f myapp __TEXT 0x140000
Run Code Online (Sandbox Code Playgroud)
现在 lldb 已将您的二进制文件加载到此崩溃报告的实际加载地址。您可以在 lldb 中执行所有常用查询,例如
(lldb) image lookup -v -a 0x144100
Run Code Online (Sandbox Code Playgroud)
对地址 0x144100 进行详细查找(可能会出现在崩溃报告中)。
您还可以使用 lldb 在 lldb 中执行一个漂亮的“转储内部行表”命令target modules dump line-table。例如,我编译了一个 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)
Run Code Online (Sandbox Code Playgroud)
我可以更改二进制文件的加载地址并尝试再次转储行表:
(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)
Run Code Online (Sandbox Code Playgroud)
我不确定我是否理解您在取消地址标记方面所做的事情。调用堆栈上的地址是这些函数的返回地址,而不是调用指令 - 因此这些可能指向实际方法调用/调度源代码行之后的行,但是当您查看源代码时,这通常很容易理解代码。如果您所有的查找都指向方法的末尾,我认为您的查找方案可能有问题。
至于问题#2,如果帧#0(当前正在执行的帧)是一个不设置堆栈帧的叶函数,或者正在设置堆栈帧,则帧#1 的展开有时会有点棘手。向上一个堆栈框架。在这些情况下,可以跳过第 1 帧。但是一旦你超过了第 1 帧,尤其是在手臂上,放松就不应该错过任何帧。
当一个被标记的函数调用另一个函数时,有一个非常边缘情况的问题noreturn,该函数的最后一条指令可能是一个调用——没有函数尾声——因为它知道它永远不会再获得控制权。相当罕见。但在这种情况下,简单的符号将为您提供指向内存中下一个函数的第一条指令的指针。调试器等人使用一种技巧,在进行符号/源代码行查找时从返回地址中减去 1 来回避这个问题,但这不是休闲符号通常需要担心的事情。并且您必须小心,不要对当前正在执行的函数(第 0 帧)执行 decr-pc 技巧,因为函数可能刚刚开始执行,并且您不希望将 pc 备份到前一个函数中并错误地进行符号化。
| 归档时间: |
|
| 查看次数: |
3431 次 |
| 最近记录: |