How well do those work with function pointers?
I've described in another thread I like to use ELF tools for this, specifically
-fdata-sections -ffunction-sections when compiling source to object files, followed by
objdump -tr on the object files to obtain full call graphs, including initialized/immutable data structures with function pointers. This works even when the data structures have only local visibility, and requires no specific coding conventions.
The reason for using per-section data and functions, is that on some architectures, multiple symbols in the same section need additional processing to map correctly. With per-section data objects and functions, no offset calculations are needed, as the section itself yields the source/target name at sufficient precision. Also, run-time modifications to the structures, for example changing a function pointer, cannot be automatically resolved.
I prefer this approach over source-level examination, because I can compile my sources normally (with just the two additional options), with all optimizations I prefer enabled, and observe the actual dependencies from the object code; specifically, the ELF relocation records.
You might wish to try the above with a codebase you already have, to see what the end result looks like. (I recommend explicitly setting
LANG=C LC_ALL=C in the environment to ensure no translation, and remembering that the symbol table output is mixed delimited/fixed-width format. The developers recommend against automatically parsing the output –– one
can parse the ELF object files directly to do that ––, but for something like this, with an output parser that complains if the input format does not conform to its expectations, it is absolutely fine.)