【问题标题】:how to get line numbers same as lldb using atos/addr2line/llvm-symbolizer/lldb image lookup --address如何使用 atos/addr2line/llvm-symbolizer/lldb image lookup --address 获取与 lldb 相同的行号
【发布时间】:2020-03-19 16:10:47
【问题描述】:

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

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

  • atos
  • addr2line
  • llvm 符号化器
  • lldb image lookup --address 在 bt 中使用 lldb 的 pc 地址

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

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

注意:在 .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 也是如此

【问题讨论】:

    标签: lldb backtrace addr2line


    【解决方案1】:

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

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

    在这种情况下,下一条指令是下一个源代码行的一部分,因此有点令人困惑。更好的是,如果你有一个函数调用像abort() 这样的“noreturn”函数——函数中的最后一条指令将是一个 CALLQ,如果你查看返回地址指令,它可能指向 下一个函数

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

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

    【讨论】:

    • 谢谢!我试过saved_pc - 1,但第一个地址失败了:image lookup --address (0x0000000100000f54-1)给出了`test_D20191123T162239.c:1`而不是`test_D20191123T162239.c:2:1`;我将如何以编程方式检测这种情况以及您提到的警告(异步中断),以便即使在这种情况下也能获得正确的行号?
    • 最好看一下linetable,看看所有东西的布局。 target modules dump line-table test_D20191123T162239.c,如果有不清楚的地方,请查看程序集。如果您要对经过优化编译的代码进行符号化,则行表可能非常不直观,因为编译器会为了提高效率而四处移动指令。
    猜你喜欢
    • 1970-01-01
    • 2018-10-04
    • 2014-10-11
    • 2015-05-17
    • 1970-01-01
    • 2012-04-02
    • 2019-04-16
    • 2019-06-16
    • 2018-06-23
    相关资源
    最近更新 更多