如何在C中区分静态函数与nm或readelf输出

And*_*rew 8 c static symbol-table nm

我试图在可执行文件上处理nm或readelf -s的输出.但是,我无法在输出中区分静态函数.

这是我正在使用的:

test.c的

static int foo() {
    int x = 6;
}

main() {}
Run Code Online (Sandbox Code Playgroud)

other.c

static int foo() {
    int x = 5;
}
Run Code Online (Sandbox Code Playgroud)

我这样编译它们:

gcc -o test test.c other.c
Run Code Online (Sandbox Code Playgroud)

然后运行nm命令以获取所有符号:

nm test
Run Code Online (Sandbox Code Playgroud)

其中出现以下两个符号(对于我的静态函数):

00000000004004ed t foo
0000000000400500 t foo
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以区分特定foo函数出现的文件?或者我需要在编译之前做一些魔术才能让它发挥作用?

我应该补充一点,对于我的用例,我可以访问最终的二进制文件和它使用的目标文件,但我实际上无法自己构建它以确保它有一个符号表.

谢谢!

Mik*_*han 10

您的问题假设,给定一个可执行文件,您始终可以static使用nm其他工具发现编译到其中的(本地)函数的名称.因此,您将能够看到两个或更多这样的名称何时相同,并提出如何发现它们编译的源文件的问题.

但是,这种假设是错误的.对于gcc,如果使用优化编译文件,则将-O0在目标文件符号表中发出局部符号.-O0是默认值,因此它适用于您的情况:

gcc -o test test.c other.c
Run Code Online (Sandbox Code Playgroud)

但是如果文件是在任何更高的优化级别编译的 - 因为它们肯定是用于发布版本 - 那么从对象文件符号表中省略了本地符号.所以连接器甚至从未看到它们.因此,您无法使用nm其他任何内容从可执行文件中恢复它们.

使用以下代码编译示例文件:

gcc -O1 -o test test.c other.c
Run Code Online (Sandbox Code Playgroud)

nm test一次,你会发现:

00000000004004ed t foo
0000000000400500 t foo
Run Code Online (Sandbox Code Playgroud)

与所有其他静态函数名称一起消失了.

在这种情况下,如果您说无法控制可执行文件的构建方式,那么您无法确保甚至可能出现问题.

如果您可以控制可执行文件的构建方式以确保编译文件-O0,那么有几种方法可以将静态函数名称绑定到源文件.两个同样简单的将是:

readelf -s test
Run Code Online (Sandbox Code Playgroud)

objdump -t test
Run Code Online (Sandbox Code Playgroud)

每个都将在源自它的每个符号块的头部列出源文件名.

(如果它需要说,gdb@ Amol建议的方法不会逃避可执行文件必须通过优化编译的限制-O0)