Hello,
I am writing an application which involves obtaining a backtrace, so I tried to find an easy way to get the backtrace for the running program. I used the glibc backtrace() function to get the return addresses from the stack. However, I need to convert those addresses into meaningful information (such as function name, file name and line number). I tried to use the addr2line code, but ran into problems when using dynamically loaded libraries, because the addresses (the one returned by backtrace(), and the actual address in the library -- the one returned by gdb, for instance) didn't match.
I also noticed that the libbfd documentation suggested libdwarf as a better solution for new programs, so I would like to use this library to get information from those addresses. I looked over libdwfl.h, and saw that it contains functions which could make my work easier. However, information about this library is pretty hard to find (at least for me), so I was hoping you could point me in the right direction. If you don't know of a tutorial either, could you please give some tips and starting points? I'd be happy to write a tutorial about what I've learned once I figure it out, which could then help other users.
Or maybe someone knows a better way to do this?
Thanks,
Mihai
Hi -
I am writing an application which involves obtaining a backtrace, so I tried to find an easy way to get the backtrace for the running program. I used the glibc backtrace() function to get the return addresses from the stack. However, I need to convert those addresses into meaningful information (such as function name, file name and line number). I tried to use the addr2line code, but ran into problems when using dynamically loaded libraries, because the addresses (the one returned by backtrace(), and the actual address in the library -- the one returned by gdb, for instance) didn't match.
No offline debuginfo parser library can perform this address mapping by itself since the shared library address data is only available at run time. When you store your backtrace() output, you should also store your shared library address map (e.g. /proc/self/maps). With all that information available, elfutils can let you work with such addresses to get correct debuginfo address matching. One has to be aware that there are several additive/subtractive mappings in play (and some of them have buggy implementations in some versions of elfutils) so getting it just right is tricky.
- FChE
No offline debuginfo parser library can perform this address mapping
by itself since the shared library address data is only available at run time.
I'm using the dladdr() GNU extension to parse the output from backtrace() at runtime, and that gives me more information about each return address on the stack. Specifically, it gives me the shared object file name and base address, nearest symbol name and address. For the binary itself, that address is the one I need. For dynamically loaded objects, subtracting the base gives me the address I need.
gdb's backtrace command however gives me more information, such as line number and line content (when compiled with -g); that is the information I want to extract using elfutils (I know it's available, since I can read it with readelf -wL). Any help in this direction would be appreciated.
Thanks,
Mihai
I'm not entirely clear on the context where you are trying to do this. Is it for the process in question to examine itself?
With e.g. eu-addr2line you can use -p or -M (see --help). What those are doing is driving libdwfl in a fairly straightforward way that you can also do yourself.
Thanks, Roland
I am trying to implement a library which substitutes libc (using LD_PRELOAD), in order to be able to inject errors into programs under test making standard library calls (e.g. fopen, malloc, etc.). I would like to log the backtrace when such a call is made, to see the execution path which lead to the error.
backtrace_symbols() only works if the main program was compiled with -rdynamic, which is not always the case, so I need to read the file names and line numbers from the debug information in the files.
Thank you for all the prompt replies, I'll look at the eu-addr2line implementation.
Mihai
Hi -
I am trying to implement a library which substitutes libc (using LD_PRELOAD), in order to be able to inject errors into programs under test making standard library calls (e.g. fopen, malloc, etc.). I would like to log the backtrace when such a call is made, to see the execution path which lead to the error. [...]
(We're not quite there yet, but this sort of thing will be pretty easy to express with systemtap, as in ...)
# inject a fault probe process("/lib/libc.so.6").function("fread_unlocked").return { $fp->_flags |= 4 } # print the backtrace probe process("/lib/libc.so.6").function("fclose") { print_backtrace() }
- FChE
(We're not quite there yet, but this sort of thing will be pretty easy to express with systemtap, as in ...)
I hadn't heard of systemtap, but now that I looked it up, I have to say that I think it will be a great tool. Great work!
This is fine an encouraged if the resulting code you're producing is not closed source and you're trying to sell it.
As I have said in a previous post, I'm writing this application as my diploma thesis at university. And yes, I intend to release the code under the GPL.
I looked at the eu-addr2line tool, and it does pretty much everything I need. In order to provide it with the correct addresses, I do the following: - get the base pointer of the main program file (dlopen(NULL), dlsym("main")) - use dladdr() on all the stack return addresses, to determine to which object file they belong to - if the object file differs from the main program file (its base pointer differs), I subtract its base pointer from it, to get its address in the object file
This works as long as I use the -rdynamic flag, but most tested programs won't have this flag. I should be able to solve the problem by reading the information directly from debug section (since I have the address and file name) -- I should be able to do that with help from eu-addr2line.
There is still one problem how can I determine which object file contains the main() function? This file will have the stack return addresses which I can process directly. When I remove the -rdynamic flag, the dlopen/dlsym doesn't work anymore, as expected. Any ideas?
Sorry for making this post so long, I just wanted to give you a better image of what I am trying to do. Thanks for your help,
Mihai
You might consider collecting /proc/PID/maps data and feeding that to -M (dwfl_linux_proc_maps_report).
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Mihai Tarce wrote:
I'll look at the eu-addr2line implementation.
This is fine an encouraged if the resulting code you're producing is not closed source and you're trying to sell it.
- -- ➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
elfutils-devel@lists.fedorahosted.org