不同库中的相同符号和链接顺序
Same symbols in different libraries and linking order
我有 2 个库:test.1
和 test.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.1
在 test.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
满足 f
和 g
引用,因此 test.2
是无关紧要的。在失败的情况下,test.2
满足 f
引用,但 g
仍未定义。为了满足 g
,链接器必须从 test.1
中提取一些对象,而这些对象恰好也提供 f
。明明是多重定义。
请注意,为了避免错误,您必须在同一个对象中包含 f
和 g
。如果 test.1
由 2 个对象组成(一个定义 f
,另一个定义 g
),则错误消失。
我有 2 个库:test.1
和 test.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.1
在 test.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
满足 f
和 g
引用,因此 test.2
是无关紧要的。在失败的情况下,test.2
满足 f
引用,但 g
仍未定义。为了满足 g
,链接器必须从 test.1
中提取一些对象,而这些对象恰好也提供 f
。明明是多重定义。
请注意,为了避免错误,您必须在同一个对象中包含 f
和 g
。如果 test.1
由 2 个对象组成(一个定义 f
,另一个定义 g
),则错误消失。