【问题标题】:Libunwind PC value not working with addr2lineLibunwind PC 值不适用于 addr2line
【发布时间】:2018-08-11 11:54:43
【问题描述】:

我正在尝试跟随链接中的示例: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/

但是我遇到了一些问题。我有一段像这样使用 libunwind 打印回溯信息的代码:

test.cpp

#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <stdio.h>

// Call this function to get a backtrace.
void backtrace() {
  unw_cursor_t cursor;
  unw_context_t context;

  // Initialize cursor to current frame for local unwinding.
  unw_getcontext(&context);
  unw_init_local(&cursor, &context);

  // Unwind frames one by one, going up the frame stack.
  while (unw_step(&cursor) > 0) {
    unw_word_t offset, pc;
    unw_get_reg(&cursor, UNW_REG_IP, &pc);
    if (pc == 0) {
      break;
    }
    printf("0x%lx:", pc);

    char sym[256];
    if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
      printf(" (%s+0x%lx)\n", sym, offset);
    } else {
      printf(" -- error: unable to obtain symbol name for this frame\n");
    }
  }
}

void foo() {
  backtrace(); // <-------- backtrace here!
}

void bar() {
  foo();
}

int main(int argc, char **argv) {
  bar();

  return 0;
}

运行此代码会产生如下输出,即程序计数器值:(function_name+0xoffset)

$ gcc -o libunwind_backtrace -Wall -g test.cpp -lunwind
$ LD_LIBRARY_PATH=/usr/local/lib ./libunwind_backtrace
0x56154da9c9c3: (_Z3foov+0x9)
0x56154da9c9cf: (_Z3barv+0x9)
0x56154da9c9e6: (main+0x14)
0x7facd1cc82e1: (__libc_start_main+0xf1)
0x56154da9c7da: (_start+0x2a)

正如上面链接中提到的,函数名称左侧的程序计数器值可以馈送到 addr2line 以获取文件名和行号信息。但是,每当我尝试这样做时(例如,对于函数 foo):

$ addr2line 56154da9c9c3 -e libunwind_backtrace
??:0

查看 objdump 文件后发现函数 foo 有一个调试入口:

$ objdump --dwarf=info libunwind_backtrace
...
<1><723>: Abbrev Number: 27 (DW_TAG_subprogram)
   <724>   DW_AT_external    : 1
   <724>   DW_AT_name        : foo
   <728>   DW_AT_decl_file   : 1
   <729>   DW_AT_decl_line   : 32
   <72a>   DW_AT_linkage_name: (indirect string, offset: 0x1cb): _Z3foov
   <72e>   DW_AT_low_pc      : 0x9ba
   <736>   DW_AT_high_pc     : 0xc
   <73e>   DW_AT_frame_base  : 1 byte block: 9c     (DW_OP_call_frame_cfa)
   <740>   DW_AT_GNU_all_tail_call_sites: 1
...

当我将 DW_at_low_pc 的值输入到 addrline 时,它​​设法产生正确的输出。

addr2line 0x9c6 -e libunwind_backtrace
/root/Desktop/test.cpp:36
  1. 首先为什么 libunwind 返回的 PC 值与 DW_AT_low_pc 的不同?

  2. 似乎 libunwind 为 PC 返回了不正确的值,但如果是这种情况,那么 libunwind 如何获取函数名称?

  3. 有没有什么方法可以从 libunwind 给定 PC 值,我可以使用 addr2line 或其他命令行工具获取文件名和文件号?

感谢您的阅读,我知道这是一个有点长的问题。

【问题讨论】:

    标签: c++ c dwarf addr2line libunwind


    【解决方案1】:
    1. DW_AT_low_pc 是与(在这种情况下)函数关联的第一个指令的重定位地址。查看堆栈跟踪中的地址,您的可执行文件似乎已在0x56154da9c000 加载,而foo()0x56154da9c9ba 开始,即0x56154da9c000 + 0x9ba。程序计数器是 0x56154da9c9c3,正如 libunwind 建议的那样,是 0x56154da9c9ba + 0x9

    2. 我没有看过 libunwind,但值得指出的是,地址->函数名映射不需要 DWARF;一般来说,ELF 符号表就足够了(而且导航速度更快)。

    3. 我建议尝试未重定位的程序计数器,即 0x9c3。

    【讨论】:

      【解决方案2】:

      libunwind 擅长从调用堆栈中获取地址,但它并没有做更多的事情。 addr2line 在不涉及重定位时运行良好,但 ASLR 使重定位更加常见。

      如今,正确的工具是libbacktrace by Ian Lance Taylor。它提供了 libunwind 和 addr2line 组合在一个库中的功能。

      【讨论】:

        猜你喜欢
        • 2011-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-03
        • 2014-01-21
        • 1970-01-01
        • 2017-03-28
        相关资源
        最近更新 更多