我有链接一个恼人的问题导出的符号。我想将共享库中的一些符号链接到一个静态库,但不会导出它的符号(也就是说,我不能简单地合并库或链接到--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
,但我想我的图书馆使用的foo
的定义从libfoo.a
,我想从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
解决方案
1)作为公认的答案中指出,我们可以使用一个连接器的版本脚本不需要符号的范围从全球气候变化到地方:
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
2)另一种方法是重新编译libfoo.a
与attribu te -fvisibility=hidden
和反对。导出的符号的可见性也是本地的,行为与上面相同。
工作。奇怪的是,没有链接器选项可以直接在链接时降低链接依赖关系的可见性。 – Thibaut