Значение ПК в 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
  1. Во-первых, почему значение ПК, возвращаемое libunwind, отличается от значения DW_AT_low_pc?

  2. Кажется, что libunwind возвращает неверное значение для ПК, но если это так, то как libunwind может получить имена функций?

  3. Есть ли какой-нибудь способ, учитывая значение ПК из libunwind, что я могу получить имя файла и номер файла, используя addr2line или какой-либо другой инструмент командной строки?

Спасибо за чтение, я знаю, это довольно длинный вопрос.

1

Решение

  1. DW_AT_low_pc это переселены адрес первый инструкция, связанная с (в данном случае) функцией. Глядя на адреса в вашей трассировке стека, кажется, что ваш исполняемый файл был загружен в 0x56154da9c000 и это foo() начинается в 0x56154da9c9baт.е. 0x56154da9c000 + 0x9ba, Счетчик программ 0x56154da9c9c3 который, как предполагает libunwind, является 0x56154da9c9ba + 0x9,

  2. Я не смотрел на libunwind, но стоит отметить, что для отображения адреса-> имени функции не требуется DWARF; в общем, таблицы символов ELF будет достаточно (и будет намного быстрее перемещаться).

  3. Я предлагаю попробовать не перемещенный программный счетчик, то есть 0x9c3.

1

Другие решения

Других решений пока нет …