Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative to backtrace() on Linux that can find symbols for static functions

In the man page, the backtrace() function on Linux says:

Note that names of "static" functions are not exposed, and won't be available in the backtrace.

However, with debugging symbols enabled (-g), programs like addr2line and gdb can still get the names of static functions. Is there a way to get the names of static functions programmatically from within the process itself?

like image 608
user1775117 Avatar asked Sep 08 '13 01:09

user1775117


People also ask

How do I get GDB backtrace?

To print a backtrace of the entire stack, use the backtrace command, or its alias bt . This command will print one line per frame for frames in the stack. By default, all stack frames are printed. You can stop the backtrace at any time by typing the system interrupt character, normally Ctrl-c .

How do you read a backtrace?

A backtrace is a summary of how your program got where it is. It shows one line per frame, for many frames, starting with the currently executing frame (frame zero), followed by its caller (frame one), and on up the stack. Print a backtrace of the entire stack: one line per frame for all frames in the stack.

What is backtrace programming?

A backtrace is a list of the function calls that are currently active in a thread. The usual way to inspect a backtrace of a program is to use an external debugger such as gdb. However, sometimes it is useful to obtain a backtrace programmatically from within a program, e.g., for the purposes of logging or diagnostics.


2 Answers

Yes, by examining its own executable (/proc/self/exe) using e.g. libbfd or an ELF file parsing library, to parse the actual symbols themselves. Essentially, you'd write C code that does the equivalent of something like

env LANG=C LC_ALL=C readelf -s executable  | awk '($5 == "LOCAL" && $8 ~ /^[^_]/ && $8 !~ /\./)'

As far as I know, the dynamic linker interface in Linux (<dlfcn.h>) does not return addresses for static (local) symbols.

A simple and pretty robust approach is to execute readelf or objdump from your program. Note that you cannot give the /proc/self/exe pseudo-file path to those, since it always refers to the process' own executable. Instead, you have to use eg. realpath("/proc/self/exe", NULL) to obtain a dynamically allocated absolute path to the current executable you can supply to the command. You also definitely want to ensure the environment contains LANG=C and LC_ALL=C, so that the output of the command is easily parseable (and not localized to whatever language the current user prefers). This may feel a bit kludgy, but it only requires the binutils package to be installed to work, and you don't need to update your program or library to keep up with the latest developments, so I think it is overall a pretty good approach.

Would you like an example?

One way to make it easier, is to generate separate arrays with the symbol information at compile time. Basically, after the object files are generated, a separate source file is dynamically generated by running objdump or readelf over the related object files, generating an array of names and pointers similar to

const struct {
    const char *const name;
    const void *const addr;
} local_symbol_names[] = {
    /* Filled in using objdump or readelf and awk, for example */
    { NULL, NULL }
};

perhaps with a simple search function exported in a header file, so that when the final executable is linked, it can easily and efficiently access the array of local symbols.

It does duplicate some data, since the same information is already in the executable file, and if I remember correctly, you have to first link the final executable with a stub array to obtain the actual addresses for the symbols, and then relink with the symbol array, making it a bit of a hassle at a compile time.. But it avoids having a run-time dependence on binutils.

like image 178
Nominal Animal Avatar answered Sep 22 '22 09:09

Nominal Animal


If your executable (and linked libraries) are compiled with debugging information (i.e. with -g flag to gcc or g++) then you could use Ian Taylor's libbacktrace (announced here) from inside GCC - see its code here

That library (BSD licensed free software) is using DWARF debug information from executables and shared libraries linked by the process. See its README file.

Beware that if you compile with optimizations, some functions could be inlined (even without being explicitly tagged inline in the source code, and static inlined functions might not have any proper own code). Then backtracing won't tell much about them.

like image 25
Basile Starynkevitch Avatar answered Sep 26 '22 09:09

Basile Starynkevitch