0

我想以编程方式将回溯堆栈地址(例如从 backtrace_symbols/libunwind 获得)转换为 file:line:column。我在 OSX 上,但怀疑这会有所作为。

所有这些都为调用 fun1() 提供了错误的行号(第 11 行):

  • 阿托斯
  • addr2line
  • llvm-符号化器
  • lldbimage lookup --address在 bt 中使用 lldb 的 pc 地址

lldbbt本身给出了正确的文件:行:列,(第 7 行),如下所示。

如何以编程方式获取正确的堆栈地址,以便在使用 atos/addr2line/llvm-symbolizer/image lookup --address 时,它会解析为正确的行号?lldbbt做得正确,所以必须有办法做到这一点。请注意,如果我使用backtrace_symbolsor (从调用后libunwind减去),我最终会得到与lldb bt 中所示相同的地址,该地址指向错误的行号 11info.dli_saddrdladdr0x0000000100000f74

注意:在 .lldbinit 中,如果我添加settings set frame-format frame start-addr:${line.start-addr}\n它将显示正确的地址(即解析为 0x0000000100000f6f 而不是 0x0000000100000f74,这将解析为正确的第 7 行)。但是,我如何以编程方式从 ac 程序生成 start-addr 而不调用产生调用lldb -p $pid(调用 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

4

1 回答 1

0

如果您查看反汇编,则更容易理解fun1_aux- 您会看到 CALLQ 指令 to fun1,然后是类似 a或类似的东西,您的行mov %rax, $rbp-16的第一条指令。a++当您调用fun1时,返回地址fun1是退出时将执行的指令,mov %rax, $rbp-16或其他。

这不是大多数人对计算机工作的直观看法——他们希望查看第 1 帧fun1_aux,并看到“当前 pc 值”是 CALLQ,因为调用正在执行。但是当然这样是不对的,调用指令已经完成,保存的pc要指向下一条指令。

在这种情况下,下一条指令是下一条源代码行的一部分,所以有点额外的混乱。更好的是,如果你有一个调用“noreturn”函数的函数,比如abort()——函数中的最后一条指令将是一个 CALLQ,如果你查看返回地址指令,它可能指向下一个函数

因此,当 lldb 对帧 #0 上方的堆栈帧进行符号化时,它知道要进行符号查找saved_pc - 1以将地址移回 CALLQ 指令。那不是一个有效的地址,所以它不应该显示给你saved_pc - 1,但它应该基于它进行符号/文件和行查找。

通过执行相同的操作,您可以为手动符号化获得相同的效果。需要注意的是,如果您有异步中断(_sigtramp在 macOS 上),则上面的帧_sigtramp不应减少其保存的 pc 值。当接收到信号时,您可能正在执行函数的第一条指令,并且递减它会使您进入前一个函数,这将非常令人困惑。

于 2019-11-24T03:38:07.560 回答