【问题标题】:Why do internally linked names appear in my object file's symbol table?为什么内部链接名称出现在我的目标文件符号表中?
【发布时间】:2019-03-14 02:15:27
【问题描述】:

为什么内部链接的名称会出现在我的目标文件的符号表中?

这个问题并不重要。我只是好奇。

这里是示例代码:

namespace {
    static int foo() {return 10;}
}
static int bar() {return 20;}

使用 GNU 的 readelf -s foobar.o | c++filt -t,可以在目标文件的符号表中找到这两个条目:

   Value  Size Type    Bind   Vis      Ndx Name
00000000    11 FUNC    LOCAL  DEFAULT    1 (anonymous namespace)::foo()
0000000b    11 FUNC    LOCAL  DEFAULT    1 bar()

尚未要求编译器发出调试符号。

我偶然注意到,如果我将static 更改为static inline,符号就会消失。

作为参考,另请参阅this answer,,它回答了一个不同的问题,解释了如何读取readelf 的输出。然而,我的问题并不认为 Readelf 工具是这样的,而是为什么编译器会导出符号,而不是据我所知需要的其他文件。

【问题讨论】:

  • @S.M.谢谢你的链接。不过,我不相信我的问题会重复那个问题。
  • 为什么您认为内部链接的名称应该在目标文件中消失?他们根据绑定列将那里标记为本地符号。目标文件在链接步骤之前生成,因此如果目标文件中出现本地名称,则不会损坏任何内容。从internally linked names这句话可以清楚地看出,链接器是对此类名称的某种障碍,在链接之前可以,在链接之后它们应该消失。尝试使用 readelf 解析您的可执行文件。这就是为什么我将您的问题标记为重复的原因,在原始问题中,Bind 列清楚地描述了。

标签: c++ elf linkage object-files


【解决方案1】:

然而,我的问题并不认为 Readelf 工具是这样的,而是为什么编译器会导出符号,而据我所知,没有其他文件需要。

编译器导出这些符号(它们具有LOCAL 绑定)。

编译器只是为它们创建符号表条目,以方便调试。

尚未要求编译器发出调试符号。

即使没有调试符号,符号表中的(非调试)符号也可用于调试。考虑:

#include <stdlib.h>

namespace {
    static int foo() {abort();}
}
static int bar() {return 20 + foo();}

int main() { return bar(); }


g++ t.cc && gdb -q ./a.out


(gdb) run
Starting program: /tmp/a.out 

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51  ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff7a6e3fa in __GI_abort () at abort.c:89
#2  0x0000555555554653 in (anonymous namespace)::foo() ()
#3  0x000055555555465c in bar() ()
#4  0x000055555555466a in main ()

请注意第 2 帧和第 3 帧的用处。如果您不希望这种情况发生,您可以随时去除符号:

gcc t.cc -Wl,--strip-all && gdb -q ./a.out

(gdb) run
Starting program: /tmp/a.out 

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51  ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff7a6e3fa in __GI_abort () at abort.c:89
#2  0x0000555555554653 in ?? ()
#3  0x000055555555465c in ?? ()
#4  0x000055555555466a in ?? ()
#5  0x00007ffff7a5a2b1 in __libc_start_main (main=0x555555554661, argc=1, argv=0x7fffffffde08, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffddf8)
    at ../csu/libc-start.c:291
#6  0x000055555555456a in ?? ()

【讨论】:

    【解决方案2】:

    打开优化器,它们就会被优化掉。

    带有-O0的编译器资源管理器:https://godbolt.org/z/xyOBgN
    带有-O2 的编译器资源管理器:https://godbolt.org/z/OlPQu3

    注意,开启优化后怎么没有生成程序集。

    如果这些链接对您不起作用,或者您不想打开它们,它们都将问题中的代码编译为使用 g++ 进行汇编。第一个链接禁用了优化器,生成以下程序集:

    (anonymous namespace)::foo():
            push    rbp
            mov     rbp, rsp
            mov     eax, 10
            pop     rbp
            ret
    bar():
            push    rbp
            mov     rbp, rsp
            mov     eax, 20
            pop     rbp
            ret
    

    启用了优化器的那个,输出如下:

    <No assembly generated>
    

    【讨论】:

    • 嗯,你是对的,但当然,一个现实的源文件会使用这些函数来做一些事情。尽管如此,根据您的建议,我发现即使我的源文件确实使用了这些函数,-O2 也会杀死这些符号。有趣,+1。
    猜你喜欢
    • 2018-09-06
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    • 2020-09-09
    • 2011-12-15
    • 2013-12-14
    相关资源
    最近更新 更多