不同库中的相同符号和链接顺序

Same symbols in different libraries and linking order

我有 2 个库:test.1test.2。这两个库都包含一个全局 extern "C" void f(); 函数,具有不同的实现(只是一个 cout 用于测试)。

我做了以下测试:

测试 1 动态链接:
如果我在可执行文件的 makefile 中添加 libtest.1.so,然后添加 libtest.2.so,然后在 main 中调用 f();,则会调用 libtest.1.so->f()
如果我更改 makefile 中的顺序,libtest.2.so->f() 会被调用

测试 2 静态链接:
静态库完全一样

测试 3 动态加载
由于库是手动加载的,所以一切都按预期工作。


我预计多个定义会出现错误,但显然没有发生。

另外,这并不违反一定义规则,因为情况不同。

它也不是依赖地狱(与此无关),也不是任何链接失败..

那么,这是什么?未定义的行为?未指定的行为?或者它真的取决于链接顺序?

有没有办法轻松检测到这种情况?


相关问题:
dlopen vs linking overhead
What is the difference between dynamic linking and dynamic loading
Is there a downside to using -Bsymbolic-functions?
Why does the order in which libraries are linked sometimes cause errors in GCC?
linking two shared libraries with some of the same symbols


编辑 我又做了两次测试,证实了这个 UB:

我在 test.1 中添加了第二个函数 void g() 而不是在 test.2 中。

使用动态链接和 .so 库,会发生同样的情况 - f 以相同的方式调用,g 也是可执行的(如预期的那样)。

但是现在使用静态链接改变了事情:如果 test.1test.2 之前是 ,则没有错误,两个函数都来自 test.1 被调用。
但是当顺序改变时,出现"multiple definitions"错误。

很明显,"no diagnostic required"(参见@MarkB 的回答),但 "strange" 错误有时会发生,有时却不会。

无论如何,答案很清楚,并且解释了以上所有内容 - UB。

这绝对违反了情况1&2中的单一定义规则。在情况 3 中,由于您明确指定要执行的函数版本,因此它可能会也可能不会。违反 ODR 是未定义的行为,不需要诊断。

3.2/3:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

库是目标文件的集合。链接器根据需要从库中提取对象以满足未解析的符号。重要的是,链接器按照它们在命令行中出现的顺序检查库,只检查每个库一次(除非命令行多次提到该库),并且只接受满足某些引用的对象。

在您的第一组测试中,一切都很清楚:链接器满足对第一个可用库中 f() 的引用,仅此而已。

现在进行第二组测试。在成功案例中 test.1 满足 fg 引用,因此 test.2 是无关紧要的。在失败的情况下,test.2 满足 f 引用,但 g 仍未定义。为了满足 g,链接器必须从 test.1 中提取一些对象,而这些对象恰好也提供 f。明明是多重定义。

请注意,为了避免错误,您必须在同一个对象中包含 fg。如果 test.1 由 2 个对象组成(一个定义 f,另一个定义 g),则错误消失。