2009-12-04 124 views
7

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

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

回答

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的共享库。如果DSO在链接命令中的静态库之前,您的DSO将首先链接到您的可执行文件。需要注意的是主要的可执行文件必须通过LD_LIBRARY_PATH-rpath。此外,使用strace表明,由于可执行文件是库的依赖项,因此在DSO加载时会再次加载它。

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

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

总之,如果mmap()的使用一个懒惰的读取,那么最好的办法是你的DSO以正常方式连接,让动态连接器和Linux采取其他的事情。

1

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

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

我在回答你的问题之前,我已经弄清楚了你的情况,以防你对答案感兴趣。

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

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

当您构建共享库时,链接器命令行中对象中找不到的任何符号将保持未解析状态。如果连接器对此抱怨,那么您可能需要将共享库链接到共享的 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的参数与tar类似,tar是磁带的ar ...老式的Unix在这里)。可能不那么容易,因为sscanf可能依赖于.a中其他.o文件中的符号。

+0

对不起libc的困惑。我只是指任何第三方静态库,并以libc为例。我要修改我的问题来澄清这一点。 – KlaxSmashing 2009-12-07 19:30:09

1

回答您的修订更清晰的问题。

请记住,通常共享库的点是多个程序可以链接它。因此,如果主程序始终提供该符号(通过静态库或其他方式),那么只需使用主程序的符号进行优化即可。这通常不是人们想要做的。

如果它只是一些小功能,可能你应该放过它。你最终可能会得到两个函数代码副本,一个在你的shlib中,一个在主程序中。如果它们很小(或者至少不是很大),或者不经常被调用,而且性能不重要,那么由于具有两个副本而产生的代码大小/ I-cache不会被担忧。 (译文:我不知道如何避免它离开我的头顶,所以我可能不会花时间去查看它并制作更复杂的Makefile以避免它。)

查看我的其他答案对于一些关于用ar来从静态库中提取东西的评论。总结:可能并不重要,因为您不知道.a中各个.o文件之间的依赖关系。

通过让共享库导出它从静态库中引入的符号,可以做到您希望做的。然后,当您链接主应用程序时,请将您的共享库放在链接程序命令行上的静态库之前。 ld会在shlib中找到“foo”,并使用该副本(如果这种重新导出技巧是可能的话),但是对于“bar”,它将不得不包含来自静态库的副本。

ld - 导出动态可能是您需要导出动态符号表中的所有符号。试试看。并在docs/man页面中搜索“导出”。 “导出”是在库中使符号可见的行话。 --export-all-symbols在i386 PE(windows DLL)部分,否则它可能会做伎俩。

+0

在ld手册页中注意到了一些事情:--just-symbols = filename:“从文件名读取符号名称及其地址,但不要重定位或将其包含在输出中,这样可以使输出文件以符号方式指向绝对位置的其他程序中定义的内存“。 – 2009-12-10 04:40:58

+0

由于共享库是一个“插件”(即并不总是被加载),因此它不能为其他代码,特别是主应用程序提供符号。 最简单的可能是将第三方静态库变成动态库。 – KlaxSmashing 2009-12-11 14:30:18