将静态库链接到共享库并隐藏导出的符号
link a static library to a shared library and hide exported symbols
链接器出现了一个烦人的问题。我想将共享库中的一些符号链接到静态库,但不导出其符号(即,我不能简单地合并库或与--whole-archive
链接)。我想要的是将我的共享库链接到一个静态库,并删除未定义的符号。
我正在寻找的东西可能只是一个链接器选项,但我无法将我的手指放在它上
我会尽我所能描述这个问题(这并不容易),然后提供一个玩具的最小例子。
快速描述:
我想使用LD_PRELOAD
技巧来捕获可执行文件中的一些函数调用。此可执行文件链接到第三方共享库,该库包含我想要捕获的函数的函数定义。
这个第三方库还包含另一个库中的符号,我也在库中使用该库,但使用了不同的(不兼容)版本。
我想做的是编译我的共享库,并在编译时将其与最后一个(静态)库的定义链接起来,而不导出符号,这样我的共享库可使用与我想要捕获的版本不同的版本。
简化的问题描述
我有一个名为libext.so
的第三方库,我没有它的源代码。这定义了一个函数bar
,并使用了另一个库中的函数foo
,但符号都在那里定义:
$> nm libext.so
0000000000000a16 T bar
00000000000009e8 T foo
正如我提到的,foo
是一个外部依赖项,我想使用更新的版本。我有一个更新的库,让我们称之为libfoo.a
:
$> nm libfoo.a
0000000000000000 T foo
现在的问题是,我想创建一个重新定义bar
的动态库,但我希望我的库使用libfoo.a
中foo
的定义,并且我希望libext.so
中的函数调用libext.so
中的函数foo
。换句话说,我想要我的库与libfoo.a
的编译时链接。
我想要的是定义一个使用libfoo.a
但不导出其符号的库。如果我将我的库链接到libfoo.a
,我会得到:
$> nm libmine.so
0000000000000a78 T bar
0000000000000b2c T foo
这意味着我过载了foo
和bar
(我不想覆盖foo
)。如果我不将我的库链接到libfoo.a
,我会得到:
$> nm libmine.so
0000000000000a78 T bar
U foo
所以我的库将使用他们的foo
版本,我也不想要。我想要的是:
$> nm libmine.so
0000000000000a78 T bar
其中foo
在编译时被链接,并且其符号未导出。
最小示例
你不需要读这篇文章,但你可以用它来寻找解决方案。
bar.cpp
:表示我没有代码的第三方应用程序:
#include <iostream>
extern "C" void foo(){ std::cerr << "old::foo" << std::endl; }
extern "C" void bar(){ std::cerr << "old::bar" << std::endl; foo(); }
foo.cpp
:表示我的lib和第三方都使用的函数的更新版本:
#include <iostream>
extern "C" void foo(){ std::cerr << "new::foo" << std::endl; }
trap.cpp
:我的库中的代码,它捕获bar
,调用新的foo
并转发:
#include <iostream>
extern "C" {
#include <dlfcn.h>
}
extern "C" void foo();
extern "C" void bar(){
std::cerr << "new::bar" << std::endl;
foo(); // Should be new::foo
void (*fwd)() = (void(*)())dlsym(RTLD_NEXT, "bar");
fwd(); // Should use old::foo
}
exec.cpp
:调用bar
:的伪可执行文件
extern "C" void bar();
int main(){
bar();
}
Makefile
:仅Unix,抱歉
default:
# The third party library
g++ -c -o bar.o bar.cpp -fpic
gcc -shared -Wl,-soname,libext.so -o libext.so bar.o
# The updated library
g++ -c -o foo.o foo.cpp -fPIC
ar rcs libfoo.a foo.o
# My trapping library
g++ -c -o trap.o trap.cpp -fPIC
gcc -shared -Wl,-soname,libmine.so -o libmine.so trap.o -ldl -L. -lfoo
# The dummy executable
g++ -o test exec.cpp -L. libext.so
在这种情况下,bar
调用foo
;正常执行是:
$> ./test
old::bar
old::foo
预加载我的库拦截bar
,调用我的foo
并转发bar
,当前执行为:
$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
new::foo
最后一行错误,所需输出为:
$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
old::foo
您想要使用链接器版本脚本,该脚本导出您想要的符号(此处为bar
),并且隐藏其他所有内容。
此处为示例。
正如接受的答案中所指出的,我们可以使用链接器版本脚本将不需要的符号的范围从全局更改为本地:
BAR {
global: bar;
local: *;
};
使用链接器版本编译显示foo
是本地的,程序现在的行为与预期一致:
$> gcc -shared -Wl,-soname,libmine.so -Wl,--version-script=libmine.version -o libmine.so trap.o -ldl -L. -lfoo
$> nm libmine.so
0000000000000978 T bar
0000000000000000 A BAR
0000000000000a2c t foo
$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
old::foo
另一种选择是使用属性-fvisibility=hidden
重新编译libfoo.a
,并与之链接。导出的符号的可见性也是本地的,其行为与上述相同。
注意:这个答案最初是OP(Thibaut)在问题中写的
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- 将无符号char*转换为std::istream*C++
- dynamic_cast什么时候会因为隐藏符号而失败?
- 隐藏共享库中的"_init"和"_fini"符号
- 为什么隐藏的符号仍然添加到 DSO 中
- 如何在 c++ 静态库中隐藏符号?
- 静态库将转换为共享的lib符号隐藏
- C++隐藏的符号链接
- g++ - 虚拟方法在生成的二进制中创建符号,我无法隐藏
- 将静态库链接到共享库并隐藏导出的符号
- 如果我将某些符号用作 STL 容器的元素,为什么我无法隐藏它的名称?
- 静态地将私有库链接到公共库以隐藏符号
- /usr/lib/libc_nonshared.a(fstat64.oS) 中的 ld 隐藏符号'fstat64'被 DSO 引用
- C++ Win32 从编辑框中隐藏(禁用)插入符号
- c++隐藏符号增加重定位时间
- 用于在c++中隐藏符号的LD脚本
- 在richit winapi中隐藏插入符号
- 这段代码是如何向链接器隐藏符号的
- Gcc隐藏了来自包含的静态库的符号的可见性