简体   繁体   English

限制Linux静态库中的符号

[英]Restricting symbols in a Linux static library

I'm looking for ways to restrict the number of C symbols exported to a Linux static library (archive). 我正在寻找限制导出到Linux静态库(存档)的C符号数量的方法。 I'd like to limit these to only those symbols that are part of the official API for the library. 我想将这些仅限于那些属于该库官方API的符号。 I already use 'static' to declare most functions as static, but this restricts them to file scope. 我已经使用'static'将大多数函数声明为static,但是这会将它们限制为文件范围。 I'm looking for a way to restrict to scope to the library. 我正在寻找一种限制图书馆范围的方法。

I can do this for shared libraries using the techniques in Ulrich Drepper's How to Write Shared Libraries , but I can't apply these techniques to static archives. 我可以使用Ulrich Drepper的“ 如何编写共享库”中的技术为共享库执行此操作,但我无法将这些技术应用于静态存档。 In his earlier Good Practices in Library Design paper, he writes: 在他早期的图书馆设计良好实践论文中,他写道:

The only possibility is to combine all object files which need certain internal resources into one using 'ld -r' and then restrict the symbols which are exported by this combined object file. 唯一的可能性是使用'ld -r'将需要某些内部资源的所有目标文件合并为一个,然后限制由此组合目标文件导出的符号。 The GNU linker has options to do just this. GNU链接器可以选择执行此操作。

Could anyone help me discover what these options might be? 谁能帮助我发现这些选项可能是什么? I've had some success with 'strip -w -K prefix_*', but this feels brutish. 我在'strip -w -K prefix_ *'上取得了一些成功,但这感觉很野蛮。 Ideally, I'd like a solution that will work with both GCC 3 and 4. 理想情况下,我想要一个适用于GCC 3和4的解决方案。

Thanks! 谢谢!

Static libraries can not do what you want for code compiled with either GCC 3.x or 4.x. 对于使用GCC 3.x或4.x编译的代码,静态库无法执行您想要的操作。

If you can use shared objects (libraries), the GNU linker does what you need with a feature called a version script. 如果您可以使用共享对象(库),GNU链接器将通过称为版本脚本的功能来执行您所需的操作。 This is usually used to provide version-specific entry points, but the degenerate case just distinguishes between public and private symbols without any versioning. 这通常用于提供特定于版本的入口点,但是退化情况只是区分公共和私有符号而没有任何版本控制。 A version script is specified with the --version-script= command line option to ld. 使用ld的--version-script =命令行选项指定版本脚本。

The contents of a version script that makes the entry points foo and bar public and hides all other interfaces: 版本脚本的内容使得入口指向foo和bar为public并隐藏所有其他接口:

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

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

I'm a big advocate of shared libraries, and this ability to limit the visibility of globals is one their great virtues. 我是共享库的主要倡导者,这种限制全局变量可见性的能力是他们的伟大优点之一。

A document that provides more of the advantages of shared objects, but written for Solaris (by Greg Nakhimovsky of happy memory), is at http://developers.sun.com/solaris/articles/linker_mapfiles.html 提供共享对象的更多优点但是为Solaris编写的文档(由Greg Nakhimovsky撰写的快乐记忆)位于http://developers.sun.com/solaris/articles/linker_mapfiles.html

I hope this helps. 我希望这有帮助。

I don't believe GNU ld has any such options; 我不相信GNU ld有任何这样的选择; Ulrich must have meant objcopy , which has many such options: --localize-hidden , --localize-symbol=symbolname , --localize-symbols=filename . Ulrich必须具有objcopy ,它有许多这样的选项: - --localize-hidden , - --localize-symbol=symbolname , - --localize-symbols=filename

The --localize-hidden in particular allows one to have a very fine control over which symbols are exposed. --localize-hidden特别允许人们对暴露的符号进行非常精细的控制。 Consider: 考虑:

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

So bar() is no longer exported from the object (even though it is still present and usable for debugging). 因此bar()不再从对象中导出(即使它仍然存在并可用于调试)。 You could also remove bar() all together with objcopy --strip-unneeded . 您也可以使用objcopy --strip-unneeded删除bar()

The merits of this answer will depend on why you're using static libraries. 这个答案的优点将取决于您使用静态库的原因。 If it's to allow the linker to drop unused objects later then I have little to add. 如果允许链接器稍后删除未使用的对象,那么我几乎无法添加。 If it's for the purpose of organisation - minimising the number of objects that have to be passed around to link applications - this extension of Employed Russian's answer may be of use. 如果它是为了组织的目的 - 最小化必须传递给链接应用程序的对象的数量 - 这种对Employed Russian的答案的扩展可能是有用的。

At compile time, the visibility of all symbols within a compilation unit can be set using: 在编译时,可以使用以下命令设置编译单元中所有符号的可见性:

-fvisibility=hidden
-fvisibility=default

This implies one can compile a single file "interface.c" with default visibility and a larger number of implementation files with hidden visibility, without annotating the source. 这意味着可以编译具有默认可见性的单个文件“interface.c”和具有隐藏可见性的大量实现文件,而无需注释源。 A relocatable link will then produce a single object file where the non-api functions are "hidden": 然后,可重定位链接将生成单个目标文件,其中非api函数被“隐藏”:

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

The combined object file can now be subjected to objcopy: 现在可以对组合的目标文件进行objcopy:

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

Thus we have a single object file "library" or "module" which exposes only the intended API. 因此,我们有一个目标文件“library”或“module”,它只公开预期的API。


The above strategy interacts moderately well with link time optimisation. 上述策略与链接时间优化适度地相互作用。 Compile with -flto and perform the relocatable link by passing -r to the linker via the compiler: 使用-flto编译并通过编译器将-r传递给链接器来执行可重定位链接:

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

Use objcopy to localise the hidden symbols as before, then call the linker a final time to strip the local symbols and whatever other dead code it can find in the post-lto object. 使用objcopy像以前一样本地化隐藏的符号,然后最后一次调用链接器来剥离本地符号以及它可以在post-lto对象中找到的任何其他死代码。 Sadly, relocatable.o is unlikely to have retained any lto related information: 遗憾的是,relocatable.o不太可能保留任何与lto相关的信息:

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

Current implementations of lto appear to be active during the relocatable link stage. lto的当前实现在可重定位链接阶段期间似乎是活动的。 With lto on, the hidden=>local symbols were stripped by the final relocatable link. 使用lto,隐藏的=>本地符号被最终的可重定位链接剥离。 Without lto, the hidden=>local symbols survived the final relocatable link. 没有lto,hidden => local符号在最终的可重定位链接中幸存下来。

Future implementations of lto seem likely to preserve the required metadata through the relocatable link stage, but at present the outcome of the relocatable link appears to be a plain old object file. lto的未来实现似乎可能通过可重定位链接阶段保留所需的元数据,但是目前可重定位链接的结果似乎是一个普通的旧目标文件。

This is a refinement of the answers from EmployedRussian and JonChesterfield, which may be helpful if you're generating both dynamic and static libraries. 这是对EmployedRussian和JonChesterfield的答案的改进,如果您同时生成动态和静态库,这可能会有所帮助。

Start with the standard mechanism for hiding symbols in DSOs (the dynamic version of your lib). 从标准机制开始,用于在DSO中隐藏符号(lib的动态版本)。 Compile all files with -fvisibility=hidden . 使用-fvisibility=hidden编译所有文件。 In the header file which defines your API, change the declarations of the classes and functions you want to make public: 在定义API的头文件中,更改要公开的类和函数的声明:

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

See here for details. 详情请见此处 This works for both C and C++. 这适用于C和C ++。 This is sufficient for DSOs, but you'll need to add these build steps for static libraries: 这对于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

The ar step is optional - you can just link against static2.o . ar步骤是可选的 - 您只需链接static2.o

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM