C/C++多个链接库含有同名函数,编译会报错吗
C/C++多個鏈接庫含有同名函數(shù),編譯會報錯嗎
- 起因
- 基本概念
- 同名函數(shù)測試
- 測試1:`.o`目標文件
- 測試2:靜態(tài)庫
- 測試3:動態(tài)庫
- 同名函數(shù)的應(yīng)用
起因
由于業(yè)務(wù)需要,我司使用了Mellanox某閉源C++程序,Mellanox推薦的定制化開發(fā)方法是:對其鏈接的動態(tài)庫進行定制化開發(fā),以添加額外的功能。
在方案討論階段,發(fā)現(xiàn)很多同事對動態(tài)庫/靜態(tài)庫所代表的的含義并不十分清楚,特別是當同名函數(shù)存在時,編譯、鏈接、運行的結(jié)果是什么也沒有明確的認識,故寫下這篇文章。
基本概念
程序函數(shù)庫可分為下面幾種類型:
- 動態(tài)加載函數(shù)庫(dynamically loaded libraries),在進程運行期間,使用dlfcn.h中的函數(shù)加載、調(diào)用、關(guān)閉動態(tài)庫
關(guān)于動態(tài)庫和靜態(tài)庫的優(yōu)缺點,相關(guān)文章很多,這里不再贅述
同名函數(shù)測試
使用兩個.c文件test2.c和test2.c包含同名函數(shù)void test()
// test1.c #include <stdio.h>void test() {printf("call from test1.c"); } // test2.c #include <stdio.h>void test() {printf("call from test2.c"); }含有main函數(shù)的文件main.c
// main.c extern void test(); int main() {test(); }測試1:.o目標文件
使用如下命令行,將test2.c和test2.c生成目標文件,并編譯可執(zhí)行文件
gcc -c ./test1.c gcc -c ./test2.c gcc -o main ./test1.o ./test2.o ./main.c結(jié)果報錯:
./test2.o: In function `test': test2.c:(.text+0x0): multiple definition of `test' ./test1.o:test1.c:(.text+0x0): first defined here collect2: error: ld returned 1 exit status可見,將包含同名函數(shù)的目標文件進行鏈接,如果其在同一個命名空間中,會報multiple definition錯誤。
測試2:靜態(tài)庫
使用如下命令行編譯靜態(tài)庫libtest1.a和libtest2.a
g++ -c ./test1.c g++ -c ./test2.c ar crv libtest1.a test1.o ar crv libtest2.a test2.o接著我們鏈接編譯:
gcc -L. ./main.c -ltest1 -ltest2 -o main可以編譯成功,無報錯。執(zhí)行結(jié)果如下
$ LD_LIBRARY_PATH=. ./main call from test1.c有朋友會問:“為什么沒有報錯呢?我明明把包含同名函數(shù)的兩個靜態(tài)庫鏈接進同一個可執(zhí)行文件了。”
為了探究為什么沒有報錯,我們增加ld選項-Wl,--verbose來看看鏈接時到底發(fā)生了什么。再執(zhí)行編譯,我們得到輸出:
...attempt to open ./libtest1.so failed attempt to open ./libtest1.a succeeded (./libtest1.a)test1.o attempt to open ./libtest2.so failed attempt to open ./libtest2.a succeeded...可以發(fā)現(xiàn),最終的鏈接結(jié)果,輸出的二進制文件只鏈接了libtest1.a背后的test1.o文件,而沒有鏈接libtest2.a。編譯器這么做的含義是:
Stack Overflow中有個問題也談到了這點。
如果使用ld參數(shù)--whole-archive強行鏈接libtest1.a和libtest2.a,我們會看到和測試1同樣的報錯:
$ gcc -L. ./main.c -Wl,--whole-archive -ltest1 -ltest2 -Wl,--no-whole-archive -o main ./libtest2.a(test2.o): In function `test': test2.c:(.text+0x0): multiple definition of `test' ./libtest1.a(test1.o):test1.c:(.text+0x0): first defined here collect2: error: ld returned 1 exit status測試3:動態(tài)庫
使用如下命令行編譯動態(tài)庫libtest1.so和libtest2.so并編譯可執(zhí)行文件。
gcc -shared -fPIC -o libtest1.so test1.c gcc -shared -fPIC -o libtest2.so test2.c gcc -L. ./main.c -ltest1 -ltest2 -o main編譯無報錯,ldd檢查,libtest1.so和libtest2.so確實都鏈接進main可執(zhí)行文件中。執(zhí)行結(jié)果如下:
$ LD_LIBRARY_PATH=. ./main call from test1.c可見,在動態(tài)鏈接時,不同的鏈接庫可以有同名函數(shù),不影響編譯。這是由動態(tài)鏈接庫的性質(zhì)決定的,其只有在運行時才會動態(tài)加載,并且加載的順序是由編譯時鏈接的順序決定的。這也就說符號會以第一個查找到的為準(Symbols are resolved on a first match basis)。
我們也可以通過設(shè)置LD_PRELOAD,提前將某動態(tài)庫load進內(nèi)存。
同名函數(shù)的應(yīng)用
有朋友會提出這樣的疑問,上面雖然做了這么多實驗,但多少有點語言律師的感覺,這些知識能改善我們?nèi)粘I顔?#xff1f;日常工作中能用的到嗎?答案當然是能用得到。
最簡單的應(yīng)用場景,比如某開源庫中有個函數(shù)我不喜歡,我想寫個自己的版本替換掉,那么完全可以利用上述的知識,將自己實現(xiàn)的某函數(shù)以動態(tài)或者靜態(tài)的方式鏈接進可執(zhí)行文件中,替換自己不喜歡的版本。
工業(yè)上常見的應(yīng)用有以下幾種:
總結(jié)
以上是生活随笔為你收集整理的C/C++多个链接库含有同名函数,编译会报错吗的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从C++20 shared_ptr移除u
- 下一篇: 云存储快照技术