这个答案的优点将取决于您使用静态库的原因。如果它允许链接器稍后删除未使用的对象,那么我几乎没有什么可添加的。如果它是为了组织的目的 - 最大限度地减少必须传递以链接应用程序的对象的数量 - 这个扩展的 Employed Russian's answer 可能是有用的。
在编译时,可以使用以下方法设置编译单元中所有符号的可见性:
-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 的未来实现似乎可能会通过可重定位链接阶段保留所需的元数据,但目前可重定位链接的结果似乎是一个普通的旧目标文件。