【问题标题】:Restricting symbols in a Linux static library限制 Linux 静态库中的符号
【发布时间】:2010-09-28 11:35:06
【问题描述】:

我正在寻找限制导出到 Linux 静态库(存档)的 C 符号数量的方法。我想将它们限制为仅属于库官方 API 的那些符号。我已经使用“静态”将大多数函数声明为静态,但这将它们限制在文件范围内。我正在寻找一种方法来限制图书馆的范围。

我可以使用 Ulrich Drepper 的 How to Write Shared Libraries 中的技术为共享库执行此操作,但我无法将这些技术应用于静态存档。在他早期的Good Practices in Library Design 论文中,他写道:

唯一的可能是合并所有需要的目标文件 使用“ld -r”将某些内部资源合二为一,然后限制符号 由这个组合的目标文件导出。 GNU 链接器具有以下选项: 就这样做吧。

谁能帮我发现这些选项可能是什么?我用'strip -w -K prefix_*'取得了一些成功,但这感觉很野蛮。理想情况下,我想要一个适用于 GCC 3 和 4 的解决方案。

谢谢!

【问题讨论】:

    标签: c linux gcc static


    【解决方案1】:

    这是对 EmployedRussian 和 JonChesterfield 答案的改进,如果您同时生成动态库和静态库,这可能会有所帮助。

    从在 DSO 中隐藏符号的标准机制开始(您的 lib 的动态版本)。使用-fvisibility=hidden 编译所有文件。在定义 API 的头文件中,更改要公开的类和函数的声明:

       #define DLL_PUBLIC __attribute__ ((visibility ("default")))
       extern DLL_PUBLIC int my_api_func(int);
    

    详情请参阅here。这适用于 C 和 C++。这对于 DSO 来说已经足够了,但是您需要为静态库添加这些构建步骤:

    ld -r obj1.o obj2.o ... objn.o -o static1.o
    objcopy --localize-hidden static1.o static2.o
    ar -rcs mylib.a static2.o
    

    ar 步骤是可选的 - 您可以链接到 static2.o

    【讨论】:

      【解决方案2】:

      此答案的优点将取决于您使用静态库的原因。如果它允许链接器稍后删除未使用的对象,那么我几乎没有什么可添加的。如果是出于组织的目的 - 最大限度地减少必须传递以链接应用程序的对象数量 - 对 Employed Russian 答案的扩展可能会有用。

      在编译时,可以使用以下方式设置编译单元内所有符号的可见性:

      -fvisibility=hidden
      -fvisibility=default
      

      这意味着可以编译具有默认可见性的单个文件“interface.c”和具有隐藏可见性的大量实现文件,而无需注释源。然后,可重定位链接将生成一个对象文件,其中“隐藏”了非 api 函数:

      ld -r interface.o implementation0.o implementation1.o -o relocatable.o
      

      现在可以对组合的目标文件进行 objcopy:

      objcopy --localize-hidden relocatable.o mylibrary.o
      

      因此,我们有一个目标文件“库”或“模块”,它只公开预期的 API。


      上述策略与链接时间优化的交互效果适中。使用 -flto 编译并通过编译器将 -r 传递给链接器来执行可重定位链接:

      gcc -fuse-linker-plugin -flto -nostdlib -Wl,-r {objects} -o relocatable.o
      

      像以前一样使用 objcopy 本地化隐藏符号,然后最后一次调用链接器以去除本地符号以及它可以在 post-lto 对象中找到的任何其他死代码。遗憾的是,relocatable.o 不太可能保留任何 lto 相关信息:

      gcc -nostdlib -Wl,-r,--discard-all relocatable.o mylibrary.o
      

      lto 的当前实现似乎在可重定位链接阶段处于活动状态。开启 lto 后,隐藏的=>local 符号被最终的可重定位链接剥离。如果没有 lto,隐藏的=>local 符号会在最终的可重定位链接中幸存下来。

      lto 的未来实现似乎可能会通过可重定位链接阶段保留所需的元数据,但目前可重定位链接的结果似乎是一个普通的旧目标文件。

      【讨论】:

      • 我明确地向某些函数添加了 visibility=hidden 属性,以便稍后在目标文件中使它们本地化。这可行,但是当我将此目标文件(或包含它的静态库)转移到另一个项目时,它可以通过实现具有相同名称/签名的函数来覆盖内部函数。有没有办法防止这种情况发生,或者更好的是让目标文件调用目标文件中定义的函数并让应用程序调用它自己的定义?
      • 我相信本地化隐藏步骤会为您做到这一点。你可能也对 elf 的弱属性感兴趣。
      • 我只用“localize hidden”对其进行了测试,然后库将从应用程序调用该函数而不是它自己的函数。我现在所做的是使用nm 获取“本地化”符号列表,并使用objcopy 的重新定义选项为所有符号添加前缀。这似乎运作良好。
      【解决方案3】:

      对于使用 GCC 3.x 或 4.x 编译的代码,静态库无法执行您想要的操作。

      如果您可以使用共享对象(库),GNU 链接器可以使用称为版本脚本的功能来满足您的需求。这通常用于提供特定于版本的入口点,但退化的情况只是区分公共符号和私有符号而没有任何版本控制。使用 ld 的 --version-script= 命令行选项指定版本脚本。

      使入口点 foo 和 bar 公开并隐藏所有其他接口的版本脚本的内容:

      { global: foo; bar; local: *; };
      

      查看 ld 文档:http://sourceware.org/binutils/docs/ld/VERSION.html#VERSION

      我是共享库的大力倡导者,这种限制全局变量可见性的能力是他们的一大优点。

      提供更多共享对象优势的文档(由快乐记忆的 Greg Nakhimovsky 编写)为 Solaris 编写,位于 http://developers.sun.com/solaris/articles/linker_mapfiles.html

      我希望这会有所帮助。

      【讨论】:

        【解决方案4】:

        我的做法是用 INTERNAL 标记所有不导出的内容, include 保护所有 .h 文件,使用 -DINTERNAL= 编译开发版本,并使用单个 .c 文件编译发布版本,该文件包含所有其他库 .c 文件,使用 -DINTERNAL=static。

        【讨论】:

        • 如果您将所有内容都放在一个文件中,或者您可以编译成它,它会起作用。通常你不会。有时您需要将小文件与其他语言结合起来(例如 Haskell - 这就是我找到此页面的原因)。
        【解决方案5】:

        我不相信 GNU ld 有任何这样的选择; Ulrich 一定是指objcopy,它有很多这样的选项:--localize-hidden--localize-symbol=symbolname--localize-symbols=filename

        --localize-hidden 特别允许人们非常精细地控制暴露的符号。考虑:

        int foo() { return 42; }
        int __attribute__((visibility("hidden"))) bar() { return 24; }
        
        gcc -c foo.c
        nm foo.o
        000000000000000b T bar
        0000000000000000 T foo
        
        objcopy --localize-hidden foo.o bar.o
        nm bar.o
        000000000000000b t bar
        0000000000000000 T foo
        

        所以bar() 不再从对象中导出(即使它仍然存在并且可用于调试)。您也可以将bar()objcopy --strip-unneeded 一起删除。

        【讨论】:

        • 我看到的一个问题是,您必须对所有目标文件执行此操作。另一个问题是 OP 的情况,当您希望静态库中的内部函数分布在多个目标文件中时,如果您在一个目标文件中“隐藏”一个符号,如何从另一个目标文件中访问它同一个静态库?
        • 谢谢。不知道 objcopy 的存在,它只是解决了我的一个主要问题。
        猜你喜欢
        • 2020-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-26
        相关资源
        最近更新 更多