我正在寻找限制导出到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的解决方案。
谢谢!
答案 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文件。