我想以编程方式将回溯堆栈地址(例如从backtrace_symbols / libunwind获得)转换为file:line:column。我正在使用OSX,但怀疑是否会有所不同。
所有这些都给fun1()的调用提供了错误的行号(第11行):
image lookup --address
使用bt中lldb的pc地址lldb bt
本身给出了正确的file:line:column,(第7行),如下所示。
我如何以编程方式获取正确的堆栈地址,以便在使用atos / addr2line / llvm-symbolizer / image查找--address时,它将解析为正确的行号? lldb bt
正确执行此操作,因此必须有一种方法可以执行此操作。请注意,如果我使用backtrace_symbols
或libunwind
(在调用info.dli_saddr
之后从dladdr
减去),我将得到与lldb bt所示相同的地址0x0000000100000f74
,该地址指向错误的行号11
注意:在.lldbinit中,如果我添加settings set frame-format frame start-addr:${line.start-addr}\n
,它将显示正确的地址(即解析为0x0000000100000f6f而不是0x0000000100000f74,它将解析为正确的第7行)。但是,如何在不调用生成对lldb -p $pid
的调用的情况下以编程方式从ac程序生成start-addr(调用lldb还有其他问题,例如,与llvm-symbolizer相比开销大,实际上甚至可以永久挂起,即使-batch
也是如此)。
clang -g -o /tmp/z04 test_D20191123T162239.c
test_D20191123T162239.c:
void fun1(){
}
void fun1_aux(){
int a = 0;
fun1(); // line 7
mylabel:
if(1){
a++; // line 11
}
}
int main(int argc, char *argv[]) {
fun1_aux();
return 0;
}
lldb /tmp/z04
(lldb) target create "/tmp/z04"
Current executable set to '/tmp/z04' (x86_64).
(lldb) b fun1
Breakpoint 1: where = z04`fun1 + 4 at test_D20191123T162239.c:2:1, address = 0x0000000100000f54
(lldb) r
Process 7258 launched: '/tmp/z04' (x86_64)
Process 7258 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000f54 z04 fun1 + 4 at test_D20191123T162239.c:2:1
1 void fun1(){
-> 2 }
3
4 void fun1_aux(){
5 int a = 0;
6
7 fun1();
Target 0: (z04) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000100000f54 z04 fun1 + 4 at test_D20191123T162239.c:2:1
frame #1: 0x0000000100000f74 z04 fun1_aux + 20 at test_D20191123T162239.c:7:3
frame #2: 0x0000000100000fab z04 main(argc=1, argv=0x00007ffeefbfb748) + 27 at test_D20191123T162239.c:16:3
frame #3: 0x00007fff71c182e5 libdyld.dylib start + 1
frame #4: 0x00007fff71c182e5 libdyld.dylib start + 1
(lldb)
(lldb) image lookup --address 0x0000000100000f74
Address: z04[0x0000000100000f74] (z04.__TEXT.__text + 36)
Summary: z04`fun1_aux + 20 at test_D20191123T162239.c:11:8
echo 0x0000000100000f74 | llvm-symbolizer -obj=/tmp/z04
fun1_aux
test_D20191123T162239.c:11:8
atos -o /tmp/z04 0x0000000100000f74
fun1_aux (in z04) (test_D20191123T162239.c:11)
与addr2line
相同
[如果您查看fun1_aux
的反汇编会更容易理解-您会看到fun1
的CALLQ指令,后跟mov %rax, $rbp-16
之类的内容,或者是a++
的第一条指令]行。当您调用fun1
时,返回地址是将在fun1
退出,mov %rax, $rbp-16
或其他任何内容时执行的指令。
这不是直观上大多数人如何看待计算机的工作方式-他们希望看一下第1帧fun1_aux
,并将“当前pc值”看成CALLQ,因为调用是执行中 。但是,当然,这是不正确的,调用指令已完成,并且保存的pc将指向下一条指令。
在这种情况下,下一条指令是下一条源代码行的一部分,因此这会造成一些混乱。更好的是,如果您有一个调用诸如abort()
之类的“ noreturn”函数的函数,则该函数中的最后一条指令将是CALLQ,并且如果您查看返回地址指令,它可能指向next功能。
因此,当lldb在第0帧上方表示堆栈帧时,它知道使用saved_pc - 1
进行符号查找,以将地址移回CALLQ指令。这不是一个有效的地址,因此它永远不要向您显示saved_pc - 1
,但应基于该地址进行符号/文件和行查找。
通过执行相同的操作,您可以为手动符号获得相同的效果。一个警告是,如果您有一个异步中断(在macOS上为_sigtramp
),则_sigtramp
上方的帧不应减小其已保存的pc值。当接收到信号时,您可能正在执行一个函数的第一条指令,并将其递减将使您进入previous function,这将非常令人困惑。