中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么編譯鏈接

發布時間:2021-10-20 13:41:03 來源:億速云 閱讀:172 作者:iii 欄目:web開發

本篇內容介紹了“怎么編譯鏈接”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

不知道大家平時編程過程中使用動態鏈接庫的情況多不多,如果一個程序引用了無數個動態鏈接庫,那就有可能引入符號沖突的問題,問題如下:

想象中

怎么編譯鏈接

實際上

怎么編譯鏈接

下面我們嘗試解決它:

最開始介紹下g++基本命令參數:

g++ -c <file> 編譯源文件,但是不進行鏈接 -o <file> 指定輸出文件的名字 -s        strip,移除符號信息 -L <dir>  指令搜索鏈接庫的路徑 -l <lib>  指定要鏈接的鏈接庫 -shared   產生動態目標文件

先來看一段代碼:

#include <stdio.h>  void DoThing() { printf("work \n"); }

再定義一個簡單的main.cc程序:

#include <stdio.h>  void DoThing();  int main() {     printf("start \n");     DoThing();     printf("finished \n");     return 0; }

編譯這兩個文件,并分別打包成靜態庫:

g++ -c work.cc -o work.o ar rc libwork.a work.o g++ -c main.cc -o main.o ar rc libmain.a main.o

現在將這兩個靜態庫鏈接成一個可執行文件,注意鏈接器如果發現當前庫中使用了沒有被定義的符號,它只會向后查找,因此,最低級別沒有其它依賴的庫應該放在最右邊,如果出現了符號沖突問題,鏈接器會使用最左邊的符號。

如果這樣進行鏈接:

$ g++ -s -L. -o main.exe -lwork -lmain ./libmain.a(main.o): In function `main': main.cc:(.text+0x11): undefined reference to `DoThing()' collect2: error: ld returned 1 exit status

鏈接失敗,因為main庫里的DoThing符號沒有被定義,鏈接器向后查找,沒有找到對應的符號定義,這里更改下鏈接庫的順序:

g++ -s -L. -o main.exe -lmain -lwork $ ./main.exe start work finished

鏈接成功。

現在寫一個簡單的容易產生符號沖突的文件conflict.cc:

#include <stdio.h>  void DoThing() { printf("conflict \n"); }

編譯并打包成靜態庫:

g++ -c conflict.cc -o conflict.o ar rc libconflict.a conflict.o

如果按這樣的順序鏈接成一個可執行程序:

$ g++ -s -L. -o main.exe -lmain -lwork -lconflict $ ./main.exe start work finished

如果稍微更改一下鏈接的順序:

$ g++ -s -L. -o main.exe -lmain -lconflict -lwork $ ./main.exe start conflict finished

這里發現順序的不同導致了程序輸出內容不同,究其原因就是那潛在的符號沖突。

現在再試試動態庫,先介紹如何使用動態庫:

$ rm libconflict.a $ g++ -shared conflict.o -o libconflict.so $ g++ -s -L. -o main.exe -lmain -lconflict $ LD_LIBRARY_PATH=. ./main.exe start conflict finished

現在再引用一個中間層在動態鏈接庫中調用conflict的文件layer.cc

#include <stdio.h> void DoThing(); void DoLayer() {     printf("layer \n");     DoThing(); }

并把layer和conflict打包成一個動態鏈接庫:

$ g++ -c layer.cc -o layer.o $ g++ -shared layer.o conflict.o -o libconflict.so

然后更新main.c程序,main里面調用layer,layer里調用conflict:

#include <stdio.h> void DoLayer(); int main() {     printf("start \n");     DoLayer();     printf("finished \n");     return 0; }

編譯鏈接執行:

$ g++ -c main.cc -o main.o $ ar rc libmain.a main.o $ g++ -s -L. -o main.exe -lmain -lconflict $ LD_LIBRARY_PATH=. ./main.exe start layer conflict finished

正常輸出,沒啥問題,現在再把之前的work.cc也塞到main.cc中,觀察下沖突:

#include <stdio.h> void DoThing(); void DoLayer(); int main() {     printf("start \n");     DoThing();     DoLayer();     printf("finished \n");     return 0; }

把work.o和main.o打包成一個庫,之后和conflict鏈接成一個可執行程序,運行:

$ g++ -c main.cc -o main.o $ ar rc libmain.a main.o work.o $ g++ -s -L. -o main.exe -lmain -lconflict $ LD_LIBRARY_PATH=. ./main.exe start work layer work finished

這里輸出了兩個work,正常情況下第二個work應該輸出conflict,怎么解決呢?可以考慮使用-fvisibility=hidden來隱藏內部的符號,鏈接庫內部使用的符號把它隱藏掉,不讓它被導出,外部也不會改變它的調用路徑。

先使用nm看一下libconflict.so里面的符號:

$ nm -CD libconflict.so                  w _ITM_deregisterTMCloneTable                  w _ITM_registerTMCloneTable 000000000000065a T DoLayer() 0000000000000672 T DoThing() 0000000000201030 B __bss_start                  w __cxa_finalize                  w __gmon_start__ 0000000000201030 D _edata 0000000000201038 B _end 0000000000000688 T _fini 0000000000000528 T _init                  U puts

如果把符號隱藏掉,

$ g++ -fvisibility=hidden -c layer.cc -o layer.o $ g++ -fvisibility=hidden -c conflict.cc -o conflict.o $ g++ -shared layer.o conflict.o -o libconflict.so 再使用nm看一下libconflict.so里面的符號: $ nm -CD libconflict.so                  w _ITM_deregisterTMCloneTable                  w _ITM_registerTMCloneTable 0000000000201028 B __bss_start                  w __cxa_finalize                  w __gmon_start__ 0000000000201028 D _edata 0000000000201030 B _end 0000000000000618 T _fini 00000000000004c0 T _init                  U puts

這樣的話main函數肯定不能調用DoLayer啦,因為DoLayer符號沒有暴露出來:

$ g++ -s -L. -o main.exe -lmain -lconflict ./libmain.a(main.o): In function `main': main.cc:(.text+0x16): undefined reference to `DoLayer()' collect2: error: ld returned 1 exit statu

那怎么暴露出來特定符號呢,直接看代碼,改動了layer.cc:

#include <stdio.h> void DoThing(); __attribute__ ((visibility ("default"))) void DoLayer() {     printf("layer \n");     DoThing(); }

再編譯鏈接運行看看結果:

$ g++ -fvisibility=hidden -c layer.cxx -o layer.o $ g++ -shared layer.o conflict.o -o libconflict.so $ g++ -s -L. -o main.exe -lmain -lconflict $ LD_LIBRARY_PATH=. ./main.exe start work layer conflict finished

發現已經是我們期待的結果啦,符號沖突的問題因此被解決。

是不是感覺很麻煩,難道每個要暴露的符號都要加上__attribute__這種修飾嗎,這里其實可以寫一個export文件,告訴編譯器要導出的所有符號有哪些。

export.txt  {  global: *DoLayer*;  local: *; }; g++ -Wl,--version-script=export.txt -s -shared layer.o conflict.o -o libconflict.so

但是這種方式只有在gcc中才可以被使用,我在clang中嘗試使用但是失敗啦,所以為了兼容性不建議使用這種方式,還是消停的使用__attribute__來解決符號沖突問題吧。

“怎么編譯鏈接”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

雷州市| 乌鲁木齐市| 高青县| 普安县| 白城市| 碌曲县| 兴文县| 义乌市| 巨鹿县| 安多县| 榆社县| 家居| 武义县| 安顺市| 东台市| 吴川市| 灵武市| 商南县| 黔西| 惠水县| 松桃| 金堂县| 永胜县| 甘洛县| 古丈县| 大庆市| 绩溪县| 阿城市| 铁岭市| 商洛市| 桓台县| 正定县| 方山县| 桑植县| 廊坊市| 西乡县| 青浦区| 临西县| 从化市| 农安县| 武冈市|