如何处理静态链接库之间的符号冲突?
编写库时最重要的规则和最佳实践之一是将所有符号放在 库到库特定的命名空间中。由于 namespace
关键字,C++ 使这一切变得容易。在 C 通常的方法是在标识符前加上一些库特定的前缀。
C 标准的规则对这些进行了一些限制(为了安全编译):AC 编译器可能只查看第一个 标识符的 8 个字符,因此 foobar2k_eggs
和 foobar2k_spam
可能被解释为相同 标识符有效 - 然而每个现代编译器都允许任意长标识符,所以在我们这个时代 (21世纪)我们不应该为此烦恼。
但是,如果您遇到一些无法更改符号名称/标识符的库怎么办?也许你有 只有静态二进制文件和标头或不想或不允许自己调整和重新编译。
One of the most important rules and best practices when writing a library, is putting all symbols of the
library into a library specific namespace. C++ makes this easy, due to the namespace
keyword. In
C the usual approach is to prefix the identifiers with some library specific prefix.
Rules of the C standard put some constraints on those (for safe compilation): A C compiler may look at only the first
8 characters of an identifier, so foobar2k_eggs
and foobar2k_spam
may be interpreted as the same
identifiers validly – however every modern compiler allows for arbitrary long identifiers, so in our times
(the 21st century) we should not have to bother about this.
But what if you're facing some libraries of which you cannot change the symbol names / idenfiers? Maybe you got
only a static binary and the headers or don't want to, or are not allowed to adjust and recompile yourself.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
至少在静态库的情况下,您可以非常方便地解决它。
考虑一下库 foo 和 bar 的标头。为了本教程的目的,我还将向您提供源文件
example/ex01/foo.h
Examples/ex01/foo.c (这可能是不透明/不可用)
example/ex01/bar.h
Examples/ex01/bar .c(这可能是不透明的/不可用的)
我们想在程序 foobar
example/ex01/foobar.c
中使用它们,一个问题立即变得显而易见:C 不知道重载。所以我们有两个乘以两个函数
名称相同但签名不同。所以我们需要一些方法来区分它们。无论如何,让我们看看是什么
编译器对此不得不说:
好吧,这并不奇怪,它只是告诉我们,我们已经知道或至少怀疑的事情。
那么我们是否可以在不修改原始库的情况下以某种方式解决标识符冲突?
源代码还是标题?事实上我们可以。
首先让我们解决编译时间问题。为此,我们将标头包含在
一堆预处理器
#define
指令,为库导出的所有符号添加前缀。稍后我们用一些漂亮舒适的包装标头来做到这一点,但只是为了演示
正在发生的事情是在 foobar.c 源文件中逐字执行:
example/ex02/foobar.c
现在,如果我们编译这个......
... 首先看起来事情变得更糟。但仔细一看:其实是编译阶段
一切顺利。只是链接器现在抱怨有符号冲突
它告诉我们发生这种情况的位置(源文件和行)。正如我们所看到的
这些符号没有前缀。
让我们用 nm 实用程序看一下符号表:
现在我们面临的挑战是在一些不透明的二进制文件中为这些符号添加前缀。是的,我知道
在这个例子的过程中,我们有源并且可以在那里改变它。但现在,假设
你只有那些.o文件,或者一个.a(实际上只是一堆.o)。
objcopy 来救援
有一个工具对我们来说特别有趣:objcopy
objcopy 适用于临时文件,因此我们可以像就地操作一样使用它。有一个
名为 --prefix-symbols 的选项/操作,您有 3 个猜测它的作用。
因此,让我们把这个家伙扔到我们顽固的图书馆:
nm告诉我们这似乎有效:
让我们尝试链接整个事情:
事实上,它有效:
现在我把它作为练习留给读者实现一个自动提取的工具/脚本
使用nm读取库的符号,编写结构的包装头文件
,并使用objcopy将符号前缀应用于静态库的目标文件。
那么共享库呢?
原则上,共享库也可以做到这一点。然而共享库,顾名思义,
在多个程序之间共享,因此以这种方式弄乱共享库并不是一个好主意。
你无法回避编写一个蹦床包装器。更糟糕的是你无法链接到共享库
在目标文件级别,但被迫进行动态加载。但这值得单独写一篇文章。
请继续关注,祝您编码愉快。
At least in the case of static libraries you can work around it quite conveniently.
Consider those headers of libraries foo and bar. For the sake of this tutorial I'll also give you the source files
examples/ex01/foo.h
examples/ex01/foo.c (this may be opaque/not available)
example/ex01/bar.h
examples/ex01/bar.c (this may be opaque/not available)
We want to use those in a program foobar
example/ex01/foobar.c
One problem becomes apparent immediately: C doesn't know overloading. So we have two times two functions with
identical name but of different signature. So we need some way to distinguish those. Anyway, lets see what a
compiler has to say about this:
Okay, this was no surprise, it just told us, what we already knew, or at least suspected.
So can we somehow resolve that identifer collision without modifying the original libraries'
source code or headers? In fact we can.
First lets resolve the compile time issues. For this we surround the header includes with a
bunch of preprocessor
#define
directives that prefix all the symbols exported by the library.Later we do this with some nice cozy wrapper-header, but just for the sake of demonstrating
what's going on were doing it verbatim in the foobar.c source file:
example/ex02/foobar.c
Now if we compile this...
... it first looks like things got worse. But look closely: Actually the compilation stage
went just fine. It's just the linker which is now complaining that there are symbols colliding
and it tells us the location (source file and line) where this happens. And as we can see
those symbols are unprefixed.
Let's take a look at the symbol tables with the nm utility:
So now we're challenged with the exercise to prefix those symbols in some opaque binary. Yes, I know
in the course of this example we have the sources and could change this there. But for now, just assume
you have only those .o files, or a .a (which actually is just a bunch of .o).
objcopy to the rescue
There is one tool particularily interesting for us: objcopy
objcopy works on temporary files, so we can use it as if it were operating in-place. There is one
option/operation called --prefix-symbols and you have 3 guesses what it does.
So let's throw this fella onto our stubborn libraries:
nm shows us that this seemed to work:
Lets try linking this whole thing:
And indeed, it worked:
Now I leave it as an exercise to the reader to implement a tool/script that automatically extracts the
symbols of a library using nm, writes a wrapper header file of the structure
and applies the symbol prefix to the static library's object files using objcopy.
What about shared libraries?
In principle the same could be done with shared libraries. However shared libraries, the name tells it,
are shared among multiple programs, so messing with a shared library in this way is not such a good idea.
You will not get around writing a trampoline wrapper. Even worse you cannot link against the shared library
on the object file level, but are forced to do dynamic loading. But this deserves its very own article.
Stay tuned, and happy coding.
这不仅仅是现代编译器的扩展;当前的 C 标准还要求编译器支持相当长的外部名称。我忘记了确切的长度,但如果我没记错的话,现在大约是 31 个字符。
然后你就被困住了。向图书馆的作者投诉。我曾经遇到过这样一个错误,由于 Debian 的
libSDL
链接libsoundfile
,我的应用程序的用户无法在 Debian 上构建它,这(至少在当时)污染了全球命名空间中包含像 dsp 这样的变量(我没有骗你!)。我向 Debian 投诉,他们修复了他们的软件包并将修复发送到上游,我认为它已被应用,因为我再也没有听说过这个问题。我确实认为这是最好的方法,因为它为每个人解决了问题。您所做的任何本地黑客行为都会将问题留在库中,供下一个不幸的用户再次遇到并与之战斗。
如果您确实需要快速修复,并且有源代码,则可以在 makefile 中添加一堆
-Dfoo=crappylib_foo -Dbar=crappylib_bar
等来修复它。如果没有,请使用您找到的 objcopy 解决方案。This is not just an extension of modern compilers; the current C standard also requires the compiler to support reasonably long external names. I forget the exact length but it's something like 31 characters now if I remember right.
Then you're stuck. Complain to the author of the library. I once encountered such a bug where users of my application were unable to build it on Debian due to Debian's
libSDL
linkinglibsoundfile
, which (at least at the time) polluted the global namespace horribly with variables likedsp
(I kid you not!). I complained to Debian, and they fixed their packages and sent the fix upstream, where I assume it was applied, since I never heard of the problem again.I really think this is the best approach, because it solves the problem for everyone. Any local hack you do will leave the problem in the library for the next unfortunate user to encounter and fight with again.
If you really do need a quick fix, and you have source, you could add a bunch of
-Dfoo=crappylib_foo -Dbar=crappylib_bar
etc. to the makefile to fix it. If not, use theobjcopy
solution you found.如果您使用 GCC,--allow-multiple-definition 链接器开关是一个方便的调试工具。这迫使链接器使用第一个定义(而不是抱怨它)。有关更多信息,请参见此处。
当我有供应商提供的库的源代码并且由于某种原因需要跟踪库函数时,这在开发过程中对我很有帮助。该开关允许您在源文件的本地副本中进行编译和链接,并且仍然链接到未修改的静态供应商库。一旦发现之旅完成,不要忘记将开关从 make 符号中拉出来。故意发生名称空间冲突的发布代码很容易出现陷阱,包括无意名称空间冲突。
If you're using GCC, the --allow-multiple-definition linker switch is a handy debugging tool. This hogties the linker into using the first definition (and not whining about it). More about it here.
This has helped me during development when I have the source to a vendor-supplied library available and need to trace into a library function for some reason or other. The switch allows you to compile and link in a local copy of a source file and still link to the unmodified static vendor library. Don't forget to yank the switch back out of the make symbols once the voyage of discovery is complete. Shipping release code with intentional name space collisions is prone to pitfalls including unintentional name space collisions.