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

2

8

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?

Irremovable answered 8/9, 2013 at 1:34 Comment(0)
G
3

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.

Garibald answered 8/9, 2013 at 3:0 Comment(0)
T
3

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.

Tera answered 8/9, 2013 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.