共享库 libbar.so 静态链接到 libfoo.a,程序链接到 libbar.so 和 libfoo.so,会发生什么?
Shared library libbar.so static linking to libfoo.a, program linking to libbar.so and libfoo.so, what happens?
(希望这不会变得太复杂...)
我构建了一个第三方 C++ 库 libfoo
,同时具有静态 (libfoo.a
) 和共享 (libfoo.so
) 版本。目标文件是使用 -fPIC 创建的。 libfoo
有几个用于配置其行为的全局变量,以及依赖于这些全局变量的函数,为简单起见,函数 func1()
与变量 global_a
.
交互
我已经构建了静态链接到 libfoo.a
的 C++ 共享库 libbar
。这是在没有 --whole-archive 选项的情况下完成的。所以我相信 libbar.so
包括它需要的所有符号和定义 libfoo.a
。该库调用 libfoo
中的函数,这些函数使用全局变量来控制行为。在这种情况下,假设它调用函数 func1()
.
最后,我有一个 C++ 客户端程序 baz
,它动态链接到 libfoo.so
和 libbar.so
。此客户端还使用来自 libfoo
的相同 functions/global 变量。它修改 global_a
并调用 func1()
.
一般来说,在这种情况下,客户端 baz
的行为是什么?这种配置是我应该避免的,还是可以的?如果 libfoo.a
和 libfoo.so
是不同的版本会怎样?
对于全局变量,我有点期待 libbar.so
中的代码会使用它自己的副本,而 baz
中的任何更改都会修改 libfoo.so
中的副本,但是那不是我所看到的。当baz
修改global_a
时,效果见libbar.so
。据我所知,客户端 baz
没有任何其他异常行为。
我还尝试将 baz
与 libfoo.a
链接起来,看到了相同的(显然是正确的)行为。
无论哪种情况,Valgrind 均未显示任何错误。
In general, what will be the behavior of the client baz in this scenario?
通常,共享库在 UNIX/ELF 系统上的行为旨在模仿存档库的行为。
特别是,如果您的二进制 baz
link 同时针对 libfoo.so
和 libbar.so
,两者 export global_a
,则第一个定义获胜。也就是说,baz
、libfoo.so
和 libbar.so
对 global_a
的所有引用都将绑定到 link 顺序中找到的第一个实例(大概是 libfoo.so
这里)。
可以通过使用例如隐藏符号来修改此行为。 -fvisibility-hidden
,使用 __attribute__((visibility("hidden")))
或 linker 脚本。
您可以通过
查看每个库导出的内容
nm -AD libfoo.so libbar.so | grep global_a
(此命令只会显示导出的符号)。
For the global variables I was sort of expecting that the code in libbar.so would use its own copies
这种期望是不正确的。
Is this sort of configuration something I should avoid, or is it OK?
这是您通常应该避免的事情,因为例如更新 libbar.so
而不重新 linking libfoo.so
可能会产生不一致。假设 global_a
的类型在后来的修订版中从 int
更改为 double
。突然一个简单的语句,如:
global_a = 0.0;
libbar.so
内的 现在可能会损坏 libfoo.so
内 global_a
之后的不相关变量(libfoo.so
内的 global_a
仍然是一个 4 字节 int
,但现在写入了 8 个字节 double
。
Valgrind in either case has no shown errors.
Valgrind 在检查全局变量方面异常 薄弱,而且几乎从不抱怨它们。您应该养成使用 gcc -fsanitize=address
检查程序的习惯。
(希望这不会变得太复杂...)
我构建了一个第三方 C++ 库 libfoo
,同时具有静态 (libfoo.a
) 和共享 (libfoo.so
) 版本。目标文件是使用 -fPIC 创建的。 libfoo
有几个用于配置其行为的全局变量,以及依赖于这些全局变量的函数,为简单起见,函数 func1()
与变量 global_a
.
我已经构建了静态链接到 libfoo.a
的 C++ 共享库 libbar
。这是在没有 --whole-archive 选项的情况下完成的。所以我相信 libbar.so
包括它需要的所有符号和定义 libfoo.a
。该库调用 libfoo
中的函数,这些函数使用全局变量来控制行为。在这种情况下,假设它调用函数 func1()
.
最后,我有一个 C++ 客户端程序 baz
,它动态链接到 libfoo.so
和 libbar.so
。此客户端还使用来自 libfoo
的相同 functions/global 变量。它修改 global_a
并调用 func1()
.
一般来说,在这种情况下,客户端 baz
的行为是什么?这种配置是我应该避免的,还是可以的?如果 libfoo.a
和 libfoo.so
是不同的版本会怎样?
对于全局变量,我有点期待 libbar.so
中的代码会使用它自己的副本,而 baz
中的任何更改都会修改 libfoo.so
中的副本,但是那不是我所看到的。当baz
修改global_a
时,效果见libbar.so
。据我所知,客户端 baz
没有任何其他异常行为。
我还尝试将 baz
与 libfoo.a
链接起来,看到了相同的(显然是正确的)行为。
无论哪种情况,Valgrind 均未显示任何错误。
In general, what will be the behavior of the client baz in this scenario?
通常,共享库在 UNIX/ELF 系统上的行为旨在模仿存档库的行为。
特别是,如果您的二进制 baz
link 同时针对 libfoo.so
和 libbar.so
,两者 export global_a
,则第一个定义获胜。也就是说,baz
、libfoo.so
和 libbar.so
对 global_a
的所有引用都将绑定到 link 顺序中找到的第一个实例(大概是 libfoo.so
这里)。
可以通过使用例如隐藏符号来修改此行为。 -fvisibility-hidden
,使用 __attribute__((visibility("hidden")))
或 linker 脚本。
您可以通过
查看每个库导出的内容nm -AD libfoo.so libbar.so | grep global_a
(此命令只会显示导出的符号)。
For the global variables I was sort of expecting that the code in libbar.so would use its own copies
这种期望是不正确的。
Is this sort of configuration something I should avoid, or is it OK?
这是您通常应该避免的事情,因为例如更新 libbar.so
而不重新 linking libfoo.so
可能会产生不一致。假设 global_a
的类型在后来的修订版中从 int
更改为 double
。突然一个简单的语句,如:
global_a = 0.0;
libbar.so
内的 现在可能会损坏 libfoo.so
内 global_a
之后的不相关变量(libfoo.so
内的 global_a
仍然是一个 4 字节 int
,但现在写入了 8 个字节 double
。
Valgrind in either case has no shown errors.
Valgrind 在检查全局变量方面异常 薄弱,而且几乎从不抱怨它们。您应该养成使用 gcc -fsanitize=address
检查程序的习惯。