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

发布于 2024-08-13 07:09:14 字数 605 浏览 3 评论 0原文

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

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

I want to create a shared library that uses functions from a 3rd-party static library. For example, foo and bar from libfoobar.a. I know that my main application is also using foo and will be exporting that symbol. So I simply want to link in bar to save code size and leave 'foo' unresolved (as it will be provided by main application). If I include libfoobar.a, the linker ld will include both functions in my shared library. If I don't include libfoobar.a, my library will not have access to function bar because the application itself is not linking in bar. Questions:

  • Is there a way to tell ld to only resolve certain symbols when building the shared library?
  • Turn libfoobar.a into a shared library?
  • Extract file containing function bar from libfoobar.a and specify that on the linker line?
  • Don't worry about it, the run-time loader will use bar from your application so the copy of bar in the shared library will not be loaded?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

很快妥协 2024-08-20 07:09:14

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

  • 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 将首先链接到可执行文件。需要注意的是,主可执行文件必须可通过 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,“在访问特定位置后,从磁盘的实际读取是以“惰性”方式执行的。”如果这是 true,则不会加载重复的 foo。请注意,仅当您的 DSO 导出函数 foo 时才会发生覆盖。否则,只要 DSO 调用 foo,就会使用静态链接到 DSO 的函数 foo

总之,如果mmap()使用惰性读取,那么最好的解决方案是以正常方式链接你的DSO,并让动态链接器和linux处理剩下的事情。

The following points attempt to answer the questions I had posed:

  • ld does not seem to allow you to omit linking in certain symbols from a static library. The usage of --just-symbols or --undefined (or the EXTERN linker script command) will not prevent ld from linking the symbols.
  • To convert a static library, libfoobar.a, into a shared one, libfoobar.so.1.0, and exporting all visible symbols. You can also use --version-script and other methods to export only a subset of symbols.

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

  • It is better to delete archive members from a copy of your static library than it is to extract them because there may be internal dependencies you have to manage. For example, assuming you are exporting all symbols, you can generate a map file from your main executable. You can then grep for all the archive members that the executable pulled in from the copy of the static library and delete them from the copy. So when your DSO is linking in the static library, it will leave the same symbols unresolved.

  • It is possible to specify your main executable as a shared library for your DSO if you compile the executable with the --pie option. Your DSO will link first to your executable if it preceded the static library in the link command. The caveat is that the main executable must be available via LD_LIBRARY_PATH or -rpath. Furthermore, using strace reveals that, since the executable is a dependency of your library, it is loaded again when your DSO loads.

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

  • The dynamic linker will use the executable's version of foo first unless you call dlopen() with the RTLD_DEEPBIND flag. Using strace reveals that the entire DSO is file mapped mmap2() into memory. However, Wikipedia claims that for mmap "The actual reads from disk are performed in "lazy" manner, after a specific location is accessed." If this is true, then the duplicate foo will not be loaded. Note that the override only happens if your DSO exported the function foo. Otherwise, the function foo that was statically linked into your DSO will be used whenever your DSO calls foo.

In conclusion, if mmap() uses a lazy read, then the best solution is to link your DSO in the normal manner and let the dynamic linker and linux take care of the rest.

星光不落少年眉 2024-08-20 07:09:14

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

如果我猜对了你想要做什么,只需将你的共享库链接到 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 并在链接器行上指定它?

也许有可能。 art /usr/lib/libc.a 列出内容。 (ar 的参数与 tar 类似。tar 是磁带的 ar...这里是老派的 Unix。)可能没那么容易,因为 sscanf 可能依赖于 .a 中其他 .o 文件中的符号。

I'm not the biggest expert on shared libraries, so I may be wrong here!

If I'm guessing right about what you're trying to do, just link your shared lib against libc.so. You don't want an extra copy of sscanf embedded in your library.

I answered your questions before I had quite figured out what you were getting at, in case you're interested in the answers.

Is there a way to tell ld to only resolve certain symbols when building the shared library?

only extern, not static, functions and variables go in the shared library's symbol table.

When you build your shared library, any symbols not found in objects on the linker command line will remain unresolved. If the linker complains about that, you probably need to link your shared lib against shared libc. You can have shared libs that depend on other shared libs, and ld.so can deal with the dependency chains.

If I had more rep, I'd ask this as a comment:
Do you have a customized version of sprintf/sscanf, or would it be ok for your shared lib to use the implementation in -lc? If -lc is fine, then my answer probably solves your problem. If not, then you need to build your shared lib out of objects that only have the functions you need. i.e. don't link it against /usr/lib/libc.a.

Maybe I'm getting confused by your

libc.a (not actually the "real" libc)
line. /usr/lib/libc.a is really glibc (on linux). It's a statically linked copy of the same code in libc.so. Unless you're talking about your own libc.a (which is what I was thinking at first)...

Turn libc.a into a shared library?
You probably can, but don't, because it's probably not compiled as position-independent code, so it would be require a lot of relocations by ld.so at run time.

Extract sscanf from libc.a and specify that on the linker line?

May be possible. ar t /usr/lib/libc.a to list contents. (ar's args are similar to tar. tar was ar for tapes.... Old school Unix here.) Probably not that easy, because sscanf probably depends on symbols in other .o files in the .a.

肤浅与狂妄 2024-08-20 07:09:14

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

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

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

请参阅我的其他答案以获取一些评论乱搞 ar 从静态库中提取内容。摘要:可能并不简单,因为您不知道 .a 中各个 .o 文件之间的依赖关系。

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

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

Answering your revised more-clear question.

Keep in mind that normally the point of a shared lib is that multiple programs can link against it. So your optimization of using the main program's symbol for a function you need will only work if the main program always provides that symbol (via a static lib or otherwise). This is not usually what people want to do.

If it's just a couple small functions, probably you should let it be. You'll probably end up with two copies of the code for the functions, one in your shlib, and one in the main program. If they're small (or at least not huge), or not called often and not performance-critical, then the code-size / I-cache hit from having two copies isn't something to worry about. (translation: I don't know how to avoid it it off the top of my head, so I might not take the time to look it up and make a more complex Makefile to avoid it.)

See my other answer for some comments on messing around with ar to extract stuff from a static library. summary: probably non-trivial, since you don't know the dependencies between the various .o files in the .a.

It may be possible to do what you're hoping for by having your shared library export the symbols that it pulls in from the static library. Then, when you link the main app, put your shared library before the static lib on the linker command line. ld will find "foo" in your shlib, and use that copy (if this re-exporting trick is possible), but for "bar" it will have to include a copy from the static lib.

ld --export-dynamic may be what you need to export all symbols in the dynamic symbol table. Try that. And search for "export" in the docs/man page. "export" is the jargon for making a symbol visible in a library. --export-all-symbols is in the i386 PE (windows DLL) section, otherwise it would probably do the trick.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文