限制Linux静态库中的符号

时间:2008-12-26 16:48:19

标签: c linux gcc static

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

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

  

唯一的可能性是组合所有需要的目标文件   使用'ld -r'将某些内部资源合并为一个,然后限制符号   由此组合对象文件导出。 GNU链接器有选项   做到这一点。

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

谢谢!

5 个答案:

答案 0 :(得分:10)

静态库无法为使用GCC 3.x或4.x编译的代码执行所需操作。

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

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

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

请参阅ld doc:http://sourceware.org/binutils/docs/ld/VERSION.html#VERSION

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

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

我希望这会有所帮助。

答案 1 :(得分:9)

我不相信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一起删除。

答案 2 :(得分:8)

这个答案的优点将取决于你使用静态库的原因。如果允许链接器稍后删除未使用的对象,那么我几乎无法添加。如果它是为了组织的目的 - 最小化必须传递给链接应用程序的对象的数量 - 这可以使用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

因此,我们有一个目标文件“library”或“module”,它只公开预期的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,隐藏的=>本地符号被最终的可重定位链接剥离。没有lto,hidden =>局部符号在最终的可重定位链接中幸存。

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

答案 3 :(得分: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

答案 4 :(得分:0)

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