共享库中库函数的选择性静态链接

时间:2009-12-04 15:58:01

标签: linux shared-libraries ld static-linking

我想创建一个使用第三方静态库中的函数的共享库。例如,来自foo的{​​{1}}和bar。我知道我的主应用程序也使用libfoobar.a并将导出该符号。因此,我只想在foo中链接以保存代码大小并保持'foo'未解析(因为它将由主应用程序提供)。如果我包含bar,链接器 ld 将在我的共享库中包含这两个函数。如果我不包含libfoobar.a,我的图书馆将无法访问功能libfoobar.a,因为应用程序本身未在bar中进行链接。问题:

  • 有没有办法让 ld 仅在构建共享库时解析某些符号?
  • bar变为共享库?
  • libfoobar.a中提取包含函数bar的文件并在链接器行上指定它?
  • 不用担心,运行时加载器将使用您应用程序中的libfoobar.a,因此不会加载共享库中的bar副本?

3 个答案:

答案 0 :(得分:4)

以下几点试图回答我提出的问题:

  • ld 似乎不允许您忽略静态库中某些符号的链接。使用--just-symbols--undefined(或EXTERN链接描述文件命令)不会阻止 ld 链接符号。
  • 将静态库 libfoobar.a 转换为共享库 libfoobar.so.1.0 ,并导出所有可见符号。您还可以使用--version-script和其他方法仅导出符号的子集。

    ld -shared -soname libfoobar.so.1 -o libfoobar.so.1.0 --whole-archive libfoobar.a --no-whole-archive

  • 最好从静态库的副本 删除存档成员,而不是提取它们,因为可能存在内部依赖关系。例如,假设您要导出所有符号,则可以从主可执行文件生成映射文件。然后,您可以grep执行可执行文件从静态库副本中提取的所有存档成员,并从副本中删除它们。因此,当您的DSO在静态库中进行链接时,它将保留相同的符号未解析。

  • 如果使用--pie选项编译可执行文件,则可以将主可执行文件指定为DSO的共享库。如果DSO位于link命令中的静态库之前,则DSO将首先链接到您的可执行文件。需要注意的是,主要可执行文件必须通过LD_LIBRARY_PATH-rpath提供。此外,使用 strace 会发现,由于可执行文件是库的依赖项,因此在加载DSO时会再次加载它。

    ld -shared -rpath '$ORIGIN' -L. -lc -ldl -o DSO.so DSO.o app libfoobar.a

  • 动态链接器将首先使用可执行文件的 foo 版本,除非您使用RTLD_DEEPBIND标志调用 dlopen()。使用 strace 显示整个DSO已将文件映射到 mmap2()到内存中。然而,维基百科声称对于mmap“在访问特定位置后,从磁盘的实际读取以”懒惰“的方式执行。”如果是这样,则不会加载重复的 foo 。请注意,只有当您的DSO 导出函数 foo 时才会出现覆盖。否则,只要您的DSO调用 foo ,就会使用静态链接到DSO的函数 foo

总之,如果 mmap()使用延迟读取,那么最好的解决方案是以正常方式链接您的DSO,让动态链接器和Linux负责其余的工作。

答案 1 :(得分:1)

我不是共享库的最大专家,所以我可能在这里错了!

如果我正在猜测你要做什么,只需将你的共享库链接到libc.so.您不希望在库中嵌入额外的sscanf副本。

在我弄清楚你得到了什么之前,我已经回答了你的问题,以防你对这些答案感兴趣。

  

有没有办法告诉ld在构建共享库时只解析某些符号?

只有extern,而不是静态的函数和变量才会进入共享库的符号表。

构建共享库时,链接器命令行中对象中找不到的任何符号仍将无法解析。如果链接器抱怨这一点,您可能需要将共享库链接到共享 libc。您可以拥有依赖于其他共享库的共享库,而ld.so可以处理依赖关系链。

如果我有更多的代表,我会问这个评论:  您是否有自定义版本的sprintf / sscanf,或者您的共享库可以在-lc中使用该实现吗?如果-lc很好,那么我的回答可能解决了你的问题。如果没有,那么您需要使用仅具有所需功能的对象构建共享库。即不要将它与/usr/lib/libc.a链接。

也许我对你的

感到困惑
  

libc.a(实际上不是“真正的”libc)    线。 /usr/lib/libc.a真的是glibc(在linux上)。它是libc.so中相同代码的静态链接副本。除非你在谈论自己的libc.a(这是我最初的想法)......

     

将libc.a变成共享库?    你可能,但不是,因为它可能没有被编译为与位置无关的代码,所以在运行时需要ld.so进行大量的重定位。

     

从libc.a中提取sscanf并在链接器行上指定它?

可能是可能的。 ar t /usr/lib/libc.a列出内容。 (ar的args类似于tar。tar是针对磁带的....旧学校unix在这里。)可能不那么容易,因为sscanf可能依赖于.a中其他.o文件中的符号。

答案 2 :(得分:1)

回答你修改过的更明确的问题。

请记住,通常共享库的重点是多个程序可以链接它。因此,只有主程序始终提供该符号(通过静态库或其他方式)时,才能优化使用主程序符号来实现所需的功能。这通常不是人们想要做的事情。

如果它只是几个小功能,可能你应该放手。你最终可能会得到两个函数代码副本,一个在shlib中,一个在主程序中。如果它们很小(或者至少不是很大),或者不经常调用而且不是性能关键的,那么代码大小/ I-cache来自拥有两个副本就不用担心了。 (翻译:我不知道如何避开它,所以我可能不会花时间查找它并制作一个更复杂的Makefile来避免它。)

请参阅我的其他答案,了解有关使用ar从静态库中提取内容的一些注意事项。总结:可能非常重要,因为你不知道.a中各种.o文件之间的依赖关系。

通过让共享库导出从静态库中提取的符号,可以执行您希望的操作。然后,当您链接主应用程序时,将您的共享库放在链接器命令行上的静态库之前。 ld会在你的shlib中找到“foo”,并使用该副本(如果可以重新导出这个技巧),但是对于“bar”,它必须包含来自静态lib的副本。

ld --export-dynamic可能是导出动态符号表中所有符号所需的内容。试试吧。并在docs / man页面中搜索“export”。 “export”是使符号在库中可见的术语。 --export-all-symbols在i386 PE(windows DLL)部分,否则它可能会有所作为。