I am augmenting my applications which run on Linux to emit a backtrace when terminal signals (such as SIGSEGV, SIGABRT) are received by a process or thread.

The code works very well using backtrace(3), backtrace_symbols(3), dladdr(3), abi::__cxa_demangle to produce useful information for the backtrace.

In trying to produce source filenames and line numbers in addition to this, I've found that addr2line works well when I send in the executable with the address provided by backtrace(3), but, lately, it is liable to produce ??:0 whenever the symbol is in a shared object. It looks like the address reported from backtrace and friends is affected by ASLR. This is a problem that affects the majority if not all of my stack traces, currently diminishing the value of forking addr2line.

This is where internet results started to thin out for me in terms of how to work around it and get the files and line numbers. But I've learned enough recently about how memory mapping works to work out the following:

  • backtrace_symbols(3) provides me with a string that contains the path of the shared object (dladdr(3) also provides this in dli_fname), I can use nm with this shared object to look up the symbol that backtrace_symbols also provides, and when I add this address in the shared object with the offset, and send that to addr2line -e <sharedobjectpath> it correctly produces the file and line number.
  • I can look up the shared object path in /proc/<pid>/maps for the r-xp mapping address, and subtract this value from the address reported by backtrace(3) and that provides the exact same address as above into the shared object.
  • I may be able to (still haven't tested this but have a hunch) directly obtain the shared object mapping address via dladdr1(3), which would allow me to skip accessing /proc/<pid>/maps.

No matter what, here I seem to require forking addr2line in order to obtain files and line numbers, which is slightly painful (and takes hundreds of milliseconds), but it appears to be necessary without bringing libbfd into my application as a dependency.

Update: I wasn't looking closely enough and now I realize that the address found from /proc/<pid>/maps is identical to the dli_fbase value from dladdr(3). So that solves that. No need to look up /proc/<pid>/maps at all.

Is there a cleaner or more direct way to do it? I'm hoping my third bullet point can work to allow me to only just fork addr2line. (edit: I can get the address. Ignore my references to /proc/<pid>/maps.) The second bullet already shows I can read /proc/<pid>/maps to get what I need for the fork, which is only a little worse; although scanning the rather large memory mapping output is a lot of work, it is bound to execute far faster than the subsequent call to addr2line. The question is (if the third bullet turns out not workable) are there any security/access/security concerns related with accessing /proc/<pid>/maps from my program? What about running addr2line?

1 Answer 1


I know, this is a very late answer, but maybe it'll help anyway. I've been looking into the same issue recently, and I think if you want to avoid using addr2line you will necessarily have to extract debug symbols and lines with libbfd. Alternatively there are a few projects that do this for you (I haven't had the chance to test them myself)

  • Thank you. What you wrote is highly consistent with what I remember from my dive into this earlier!
    – Steven Lu
    Commented Sep 28, 2020 at 17:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .