在手册页中,Linux上的backtrace()
函数说:
请注意“静态”功能的名称 没有暴露,并且在回溯中不可用。
但是,启用调试符号(-g
)后,addr2line
和gdb
等程序仍可获取静态函数的名称。有没有办法从过程本身中以编程方式获取静态函数的名称?
答案 0 :(得分:3)
如果您的可执行文件(和链接库)使用调试信息进行编译(即-g
标记为gcc
或g++
),那么您可以使用Ian Taylor的libbacktrace
(从GCC内部宣布here) - 请参阅其代码here
该库(BSD许可的免费软件)正在使用来自该进程链接的可执行文件和共享库的DWARF调试信息。请参阅其README文件。
请注意,如果使用优化进行编译,则可以内联某些函数(即使没有在源代码中明确标记inline
,并且static
内联函数可能没有任何适当的自己的代码)。然后回溯不会说明它们。
答案 1 :(得分:2)
是的,通过使用例如检查自己的可执行文件(/proc/self/exe
)来检查libbfd
或ELF文件解析库,用于解析实际符号本身。从本质上讲,你编写的C代码与
env LANG=C LC_ALL=C readelf -s executable | awk '($5 == "LOCAL" && $8 ~ /^[^_]/ && $8 !~ /\./)'
据我所知,Linux中的动态链接器接口(<dlfcn.h>
)不返回静态(本地)符号的地址。
一种简单而强大的方法是从您的程序中执行readelf
或objdump
。请注意,您不能将/proc/self/exe
伪文件路径赋予这些路径,因为它始终引用进程自己的可执行文件。相反,你必须使用例如。 realpath("/proc/self/exe", NULL)
获取动态分配的绝对路径,指向您可以提供给命令的当前可执行文件。您还确实希望确保环境包含LANG=C
和LC_ALL=C
,以便命令的输出可以轻松解析(并且不会本地化为当前用户喜欢的任何语言)。这可能会让人觉得有点麻烦,但它只需要安装binutils
包就可以了,而且你不需要更新你的程序或库以跟上最新的发展,所以我认为这是整体的一个很好的方法。
你想要一个例子吗?
使其更容易的一种方法是在编译时使用符号信息生成单独的数组。基本上,在生成目标文件之后,通过在相关目标文件上运行objdump
或readelf
来动态生成单独的源文件,生成类似于
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 }
};
可能在头文件中导出一个简单的搜索功能,这样当最终的可执行文件被链接时,它可以轻松有效地访问本地符号数组。
它确实复制了一些数据,因为相同的信息已经存在于可执行文件中,如果我没记错的话,你必须首先将最终的可执行文件与存根数组相链接,以获得符号的实际地址,然后重新链接使用符号数组,在编译时使其有点麻烦。但它避免了对binutils
的运行时依赖。