9

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

这是我正在使用的内容:

测试.c

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

main() {}

其他.c

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

我像这样编译这些:

gcc -o test test.c other.c

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

nm test

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

00000000004004ed t foo
0000000000400500 t foo

有没有一种方法可以区分特定的 foo 函数来自哪个文件?或者我需要在编译之前做一些魔法才能让它工作吗?

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

谢谢!

4

4 回答 4

11

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

然而,这种假设是错误的。在 gcc 的情况下,如果文件是用优化编译的,-O0那么本地符号将在目标文件符号表中发出。-O0是默认值,因此它适用于您的情况:

gcc -o test test.c other.c

但是,如果文件是在任何更高的优化级别编译的——因为它们肯定会用于发布版本——那么本地符号将从目标文件符号表中省略。所以链接器甚至从来没有看到它们。因此,您无法使用nm或其他任何方式从可执行文件中恢复它们。

使用以下命令编译示例文件:

gcc -O1 -o test test.c other.c

nm test一次,你会发现:

00000000004004ed t foo
0000000000400500 t foo

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

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

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

readelf -s test

objdump -t test

每个都将在来自它的每个符号块的开头列出一个源文件名。

(如果需要说明的话,gdb@Amol 建议的方法并没有摆脱可执行文件必须经过优化编译的限制-O0

于 2015-06-08T19:39:58.930 回答
3

我尝试了以下顺序。

如果您已经剥离了没有调试符号的输出文件,那么gdb您可以使用创建目标文件。请按照以下命令操作:

$ gdb a.out

将给出以下输出

Reading symbols from /home/amol/amol/a.out...(no debugging symbols found)...done.

然后 (gdb)会进入终端

按顺序给出以下命令(在(gdb)您继续键入命令时默认出现提示)

 (gdb) maint print symbols filename
 (gdb) maint print psymbols filename
 (gdb) maint print msymbols filename

现在在您的文件夹中,您可以看到一个名为filename的文件。在文本编辑器中打开此文件,您可以看到如下信息:

[ 8] t 0x80483b4 foo section .text  test.c
[ 9] T 0x80483c3 main section .text  other.c
[10] t 0x80483c8 foo section .text  other.c

在这里您可以清楚地看到哪个foo()函数来自哪个.c文件。希望这对您有所帮助。

于 2015-06-08T18:51:52.763 回答
2

您可能需要阅读 ELF 符号表并提取 ELF32_ST_BIND 值。

根据 ELF 规范(参见http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf),ELF32_ST_BIND 的值可以是:

       Name         Value
     STB_LOCAL      0
     STB_GLOBAL     1
     STB_WEAK       2
     STB_LOPROC    13
     STB_HIPROC    15

其中 STB_LOCAL 被定义为“本地符号在包含其定义的目标文件之外不可见。同名的本地符号可以存在于多个文件中而不会相互干扰。” 这似乎与静态 C 函数相当匹配。

例如,给你采样并稍微修改一下:

    test.c:

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

    int bar()
    {
         int y = 6;
    }

    main() {}

    other.c:

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

并编译gcc -o test test.c other.c并查看符号表(删除了很​​多条目):

    readelf -s test
    Num:    Value          Size Type    Bind   Vis      Ndx Name
    37: 00000000004004f0    13 FUNC    LOCAL  DEFAULT   13 foo
    39: 0000000000400510    13 FUNC    LOCAL  DEFAULT   13 foo
    52: 00000000004004fd    13 FUNC    GLOBAL DEFAULT   13 bar

我们可以看到两个静态函数显示为 LOCAL,一个“正常”函数显示为 GLOBAL

注意:虽然此方法适用于非调试文件,但如果最终文件被剥离,我们不能使用此方法。

于 2015-06-08T17:49:59.203 回答
0

如果它应该适用于任何剥离的可执行文件,您可以将字符串植入函数并在可执行文件中搜索它们。

#define STR1(x) #x
#define STR(x) STR1(x)
#define FUNCID(funcname) __asm__ __volatile__ (\
    "jmp 1f;"\
    ".string \"" __FILE__ "/" STR(funcname) "()\";"\
    "1:"\
)

static int foo() {
    FUNCID(foo);
    return rand();
}
于 2015-06-08T21:09:30.097 回答