Search code examples
c++cdwarfaddr2linelibunwind

Libunwind PC value not working with addr2line


I am trying to follow along with an example from the link: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/

However I have run into a few issues. I have a piece of code like so that uses libunwind to print backtrace information:

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;
}

Running this code produces output like so i.e. the program counter value: (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)

As mentioned in the link above the program counter value to the left of the function name can be fed to addr2line to obtain file name and line number information. However whenever I try to do so (eg. for the function foo):

$ addr2line 56154da9c9c3 -e libunwind_backtrace
??:0

After looking at the objdump file I have found that function foo has a debug entry:

$ 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
...

and when I enter the value for DW_at_low_pc into addrline it manages to produce the correct output.

addr2line 0x9c6 -e libunwind_backtrace
/root/Desktop/test.cpp:36
  1. Firstly why does the PC value returned by libunwind differ from that of the DW_AT_low_pc?

  2. It seems like libunwind is returning an incorrect value for the PC but if this is the case then how is libunwind able to obtain the function names?

  3. Is there any way given the PC value from libunwind that I can obtain the file name and file number using addr2line or some other command line tool?

Thank you for reading, I know its a kinda long question.


Solution

  • libunwind is good at fetching addresses from the call stack, but it does not do much more than this. addr2line works well when no relocation is involved, but ASLR as made relocations much more common.

    Nowadays, the right tool is libbacktrace by Ian Lance Taylor. It provides the functionality of both libunwind and addr2line combined in a single library.