代码之家  ›  专栏  ›  技术社区  ›  Sergio

为什么OSX的strip不能删除弱符号?

  •  1
  • Sergio  · 技术社区  · 6 年前

    当我试图从链接的可执行文件中去掉不需要的东西时,我发现了一些奇怪的东西。 假设我们有一个简单、直接的C++程序:

    class Foo {
    public:
        template <typename T>
        char* getPtr() {
            static char c;
            return &c;
        }
    };
    
    char* bar() {
        Foo i;
        return i.getPtr<int>();
    }
    
    int main() {
        bar();
    } 
    

    正在使用 clang++ t.cc

    $ gobjdump -T ./a.out
    
    ./a.out:     file format mach-o-x86-64
    
    DYNAMIC SYMBOL TABLE:
    0000000100000f40 g       0f SECT   01 0000 [.text] __Z3barv
    0000000100000f60 g       0f SECT   01 0080 [.text] __ZN3Foo6getPtrIiEEPcv
    0000000100001020 g       0f SECT   08 0080 [.data] __ZZN3Foo6getPtrIiEEPcvE1c
    0000000100000000 g       0f SECT   01 0010 [.text] __mh_execute_header
    0000000100000f80 g       0f SECT   01 0000 [.text] _main
    0000000000000000 g       01 UND    00 0200 dyld_stub_binder
    

    考虑到它是一个可执行文件,而不是dylib,我想去掉所有的条目,除了那些未定义的符号。从理论上讲,二进制仍然可以工作,因为关于所需dyld绑定的信息仍然存在,并且入口点在mach-o报头之后的某个load命令中定义(所以与符号表无关)。

    尝试 strip 给出了一些奇怪的结果:

    $ strip ./a.out
    $ gobjdump -T ./a.out
    
    ./a.out:     file format mach-o-x86-64
    
    DYNAMIC SYMBOL TABLE:
    0000000005614542      d  3c OPT    00 0000 radr://5614542
    0000000100000f60 g       0f SECT   01 0080 [.text] __ZN3Foo6getPtrIiEEPcv
    0000000100001020 g       0f SECT   08 0080 [.data] __ZZN3Foo6getPtrIiEEPcvE1c
    0000000100000000 g       0f SECT   01 0010 [.text] __mh_execute_header
    0000000000000000 g       01 UND    00 0200 dyld_stub_binder
    

    _main __Z3barv class Foo 他们还在那里。他们和脱光衣服的唯一区别是前者有 N_WEAK_DEF 标志集(0080)。以下是一些来自 <mach-o/nlist.h>

     /*
     * The N_WEAK_DEF bit of the n_desc field indicates to the static and dynamic
     * linkers that the symbol definition is weak, allowing a non-weak symbol to
     * also be used which causes the weak definition to be discared.  Currently this
     * is only supported for symbols in coalesed sections.
     */
    #define N_WEAK_DEF  0x0080 /* coalesed symbol is a weak definition */
    

    不幸的是,这并不能解释为什么 忽略带有该标志的符号。

    所以问题是-怎么教 除去偶数 N\u弱定义 以防用户不想导出它们。

    -N 也删除未定义的符号,因此它不是一个选项)。用声明类 visibility("hidden") 制造了难题,但不幸的是,这是不容易做到真正的项目。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Kristina    6 年前

    我的第一个结论是跳转到它是雷达错误5614542的结果,因此这个奇怪的符号,但它与它无关。

    我将从以下事实中得出一些假设和猜测:似乎您使用的是nlist重定位,而不是新的基于字节码的重定位(您可以通过查找dyld info load命令进行检查),这要么是使用古老的工具链构建的,要么是 MH_OBJECT 尚未完成最终链接步骤的主可执行文件。我不能百分之百确定这是不是真的-但不管怎样,


    一个较长的解释-这是与C++语义,即 一个定义规则 (ODR)是一个相近但不相同的概念,因为不能在同一个命名空间中复制重复的强符号(我是指一个链接命名空间,而不是C++命名空间,这很快就混淆了)。

    动态链接 ,因为重用该模板会再次实例化它(导致ODR冲突,并根据上下文出现链接时间错误),因为它是隐式实例化,可能需要也可能不需要合并(在静态甚至动态链接时间之前都不知道,当然,除非你把它定义为隐藏的,否则你必须非常小心,因为语义会根据很多因素而变化,比如它是模块化构建还是不(我是指LLVM)模块,而不是C++的模块TS。

    通过将其定义为弱,dyld能够为每个最终链接对象选择正确的定义,即共享库或可执行文件(不要忘记共享缓存) 运行时

    以上是很多可以推断的编译器没有任何形式的提示,隐藏链接是一个非常糟糕的主意,除非你明白的含义,你想 internal 可见性,如果您确实希望每次都重新实例化和复制模板。OSX通常有一个相当复杂的连接模型,有很多潜在的地雷要踩。

    如果我对目标文件的看法是对的, 在对象文件被送入静态链接器之前,不应该对它们运行strip .

        2
  •  0
  •   Sergio    6 年前

    多亏了苹果开源项目,我终于找到了答案。看起来像 strip

        ...
        *
        * In 64-bit applications, we only need to save coalesced
        * symbols that are used as weak definitions.
        */
        ...
    

    (不幸的是,网站没有每行导航,或者我可能没有仔细查看) 既然我的二进制是 mach-o-x86-64

    然而,这种行为背后的动机仍不清楚。

    编辑 :查看克里斯蒂娜的答案,了解原因