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
.