7

我想创建一个使用第三方静态库中的函数的共享库。例如foobarlibfoobar.a. 我知道我的主要应用程序也在使用foo并将导出该符号。所以我只是想链接bar以节省代码大小并使'foo'未解决(因为它将由主应用程序提供)。如果我包含libfoobar.a,链接器ld将在我的共享库中包含这两个函数。如果我不包含libfoobar.a,我的库将无法访问函数bar,因为应用程序本身没有链接到bar. 问题:

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

3 回答 3

4

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

  • ld似乎不允许您省略静态库中某些符号的链接。--just-symbolsor --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 在链接命令中位于静态库之前,您的 DSO 将首先链接到您的可执行文件。需要注意的是,主可执行文件必须通过LD_LIBRARY_PATH或可用-rpath。此外,使用strace表明,由于可执行文件是库的依赖项,因此在 DSO 加载时会再次加载它。

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

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

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

于 2009-12-16T15:40:57.883 回答
1

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

如果我猜对了您要执行的操作,只需将您的共享库与 libc.so 链接即可。您不希望在库中嵌入额外的 sscanf 副本。

在我完全弄清楚你的意思之前,我回答了你的问题,以防你对答案感兴趣。

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

共享库的符号表中只有外部函数和变量,而不是静态函数和变量。

构建共享库时,链接器命令行上的对象中未找到的任何符号都将保持未解析。如果链接器对此抱怨,您可能需要将您的共享库链接到共享。您可以拥有依赖于其他共享库的共享库,并且 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 是用于磁带的 ar....这里是老式 Unix。)可能没那么容易,因为 sscanf 可能依赖于 .a 中其他 .o 文件中的符号。

于 2009-12-05T08:16:18.807 回答
1

回答您修改后的更清晰的问题。

请记住,通常共享库的意义在于多个程序可以链接到它。因此,只有在主程序始终提供该符号(通过静态库或其他方式)时,您对使用主程序符号的函数的优化才会起作用。这通常不是人们想要做的。

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

有关使用 ar 从静态库中提取内容的一些评论,请参阅我的其他答案。摘要:可能很重要,因为您不知道 .a 中各种 .o 文件之间的依赖关系。

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

ld --export-dynamic 可能是您导出动态符号表中所有符号所需的。试试看。并在文档/手册页中搜索“导出”。“export”是使符号在库中可见的术语。--export-all-symbols 在 i386 PE(windows DLL)部分,否则它可能会成功。

于 2009-12-07T21:16:37.537 回答