我有鏈接一個惱人的問題導出的符號。我想將共享庫中的一些符號鏈接到一個靜態庫,但不會導出它的符號(也就是說,我不能簡單地合併庫或鏈接到--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