Libunwind PC 值不适用于 addr2line
Libunwind PC value not working with addr2line
我正在尝试按照 link 中的示例进行操作:
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)
如上文link所述,可以将函数名左侧的程序计数器值馈送到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
首先为什么libunwind返回的PC值和DW_AT_low_pc不同?
似乎 libunwind 为 PC 返回了错误的值,但如果是这种情况,那么 libunwind 如何获取函数名称?
有什么方法可以使用 addr2line 或其他一些命令行工具从 libunwind 给出 PC 值来获取文件名和文件号?
感谢您的阅读,我知道这个问题有点长。
DW_AT_low_pc
是 first 指令的 relocated 地址(在本例中)功能。查看堆栈跟踪中的地址,您的 executable 似乎已在 0x56154da9c000
处加载,并且 foo()
开始于 0x56154da9c9ba
,即 0x56154da9c000 + 0x9ba
。程序计数器是 0x56154da9c9c3
,正如 libunwind 所暗示的那样,它是 0x56154da9c9ba + 0x9
.
我没有看过 libunwind,但值得指出的是地址-> 函数名称映射不需要 DWARF;一般来说,ELF 符号 table 就足够了(而且导航速度更快)。
我建议尝试未重定位的程序计数器,即 0x9c3。
libunwind 擅长从调用堆栈中获取地址,但除此之外它做的不多。 addr2line 在不涉及重定位时运行良好,但 ASLR 使重定位变得更加普遍。
如今,正确的工具是 libbacktrace by Ian Lance Taylor。它提供了将 libunwind 和 addr2line 组合在一个库中的功能。
我正在尝试按照 link 中的示例进行操作: 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)
如上文link所述,可以将函数名左侧的程序计数器值馈送到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
首先为什么libunwind返回的PC值和DW_AT_low_pc不同?
似乎 libunwind 为 PC 返回了错误的值,但如果是这种情况,那么 libunwind 如何获取函数名称?
有什么方法可以使用 addr2line 或其他一些命令行工具从 libunwind 给出 PC 值来获取文件名和文件号?
感谢您的阅读,我知道这个问题有点长。
DW_AT_low_pc
是 first 指令的 relocated 地址(在本例中)功能。查看堆栈跟踪中的地址,您的 executable 似乎已在0x56154da9c000
处加载,并且foo()
开始于0x56154da9c9ba
,即0x56154da9c000 + 0x9ba
。程序计数器是0x56154da9c9c3
,正如 libunwind 所暗示的那样,它是0x56154da9c9ba + 0x9
.我没有看过 libunwind,但值得指出的是地址-> 函数名称映射不需要 DWARF;一般来说,ELF 符号 table 就足够了(而且导航速度更快)。
我建议尝试未重定位的程序计数器,即 0x9c3。
libunwind 擅长从调用堆栈中获取地址,但除此之外它做的不多。 addr2line 在不涉及重定位时运行良好,但 ASLR 使重定位变得更加普遍。
如今,正确的工具是 libbacktrace by Ian Lance Taylor。它提供了将 libunwind 和 addr2line 组合在一个库中的功能。