Значение ПК в Libunwind не работает с addr2line
Я пытаюсь следовать примеру по ссылке: 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;
}
Выполнение этого кода приводит к выводу примерно так, т.е. значение счетчика программы: (имя_функции +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
Во-первых, почему значение ПК, возвращаемое libunwind, отличается от значения DW_AT_low_pc?
Кажется, что libunwind возвращает неверное значение для ПК, но если это так, то как libunwind может получить имена функций?
Есть ли какой-либо способ, учитывая значение ПК из libunwind, чтобы я мог получить имя файла и номер файла, используя addr2line или какой-либо другой инструмент командной строки?
Спасибо за чтение, я знаю, это довольно длинный вопрос.
2 ответа
DW_AT_low_pc
это перемещенный адрес первой инструкции, связанной с (в данном случае) функцией. Глядя на адреса в вашей трассировке стека, кажется, что ваш исполняемый файл был загружен в0x56154da9c000
и этоfoo()
начинается в0x56154da9c9ba
т.е.0x56154da9c000 + 0x9ba
, Счетчик программ0x56154da9c9c3
который, как предполагает libunwind, является0x56154da9c9ba + 0x9
,Я не смотрел на libunwind, но стоит отметить, что для отображения адреса-> имени функции не требуется DWARF; в общем, таблицы символов ELF будет достаточно (и будет намного быстрее перемещаться).
Я предлагаю попробовать неродный программный счетчик, то есть 0x9c3.
libunwind хорошо извлекает адреса из стека вызовов, но не более того. addr2line хорошо работает, когда не требуется перемещение, но ASLR сделал перемещения гораздо более распространенными.
В настоящее время правильным инструментом является libbacktrace от Ian Lance Taylor . Он обеспечивает функциональность как libunwind, так и addr2line, объединенных в одной библиотеке.