日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Linux動(dòng)態(tài)庫(kù)相關(guān)知識(shí)總結(jié)

動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)在C/C++開(kāi)發(fā)中很常見(jiàn),相比靜態(tài)庫(kù)直接被編譯到可執(zhí)行程序,動(dòng)態(tài)庫(kù)運(yùn)行時(shí)加載使得可執(zhí)行程序的體積更小,更新動(dòng)態(tài)庫(kù)可以不用重新 編譯可執(zhí)行程序等諸多好處。作者是一個(gè)Linux后臺(tái)開(kāi)發(fā),這些知識(shí)經(jīng)常用到,所以整理了一下這方面的知識(shí)。靜態(tài)庫(kù)相對(duì)簡(jiǎn)單,本文只關(guān)心Linux平臺(tái)下 的動(dòng)態(tài)庫(kù)。

公司主營(yíng)業(yè)務(wù):成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶(hù)真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶(hù)帶來(lái)驚喜。創(chuàng)新互聯(lián)推出鐘山免費(fèi)做網(wǎng)站回饋大家。

創(chuàng)建動(dòng)態(tài)庫(kù)

這里我把一個(gè)短小卻很有用的哈希函數(shù)編譯成動(dòng)態(tài)庫(kù)做為示例,ELFhash用于對(duì)字符串做哈希,返回一個(gè)無(wú)符號(hào)整數(shù)。

 
 
  1. //elfhash.h 
  2. #include 
  3. unsigned long  ELFhash(const char* key); 
  4.  
  5. //elfhash.c 
  6. #include "elfhash.h" 
  7. unsigned long ELFhash(const char* key)  
  8.   unsigned long h = 0, g; 
  9.   while( *key ) { 
  10.     h = ( h > 24; 
  11.     h &= ~g; 
  12.   return h; 

接下來(lái)使用gcc編譯以上代碼,并用ld將編譯的目標(biāo)文件鏈接成動(dòng)態(tài)庫(kù)

 
 
  1. gcc -fPIC -c -Wall elfhash.c  
  2. ld  -shared elfhash.o -o libelfhash.so 

其中-fPIC意思是生成位置無(wú)關(guān)的代碼(Position Independent Code),適用于動(dòng)態(tài)庫(kù),在多個(gè)進(jìn)程中共享動(dòng)態(tài)庫(kù)的同一份代碼。ld的-shared選項(xiàng)告訴鏈接器創(chuàng)建的是動(dòng)態(tài)庫(kù)。gcc也可以間接調(diào)用ld生成動(dòng)態(tài)庫(kù)

 
 
  1. gcc -fPIC -shared -Wall -o libelfhash.so elfhash.c 

使用動(dòng)態(tài)庫(kù)

動(dòng)態(tài)庫(kù)的使用方法有兩種一種是隱式使用,第二種是顯式使用。隱式使用的方法很簡(jiǎn)單。

 
 
  1. #include "elfhash.h" 
  2. int main()  
  3.     printf("%ldn", ElfHash("key-for-test")); 
  4.     return 0; 

顯式使用動(dòng)態(tài)庫(kù)需要借助以下幾個(gè)函數(shù)

#include
void *dlopen(const char *filename, int flag); //flag可以是RTLD_LAZY,執(zhí)行共享庫(kù)中的代碼時(shí)解決未定義符號(hào),RTLD_NOW則是dlopen返回前解決未定義符號(hào)。 
char *dlerror(void); //當(dāng)發(fā)生錯(cuò)誤時(shí),返回錯(cuò)誤信息 
void *dlsym(void *handle, const char *symbol); //獲取符號(hào) 
int dlclose(void *handle); //關(guān)閉

應(yīng)用上面幾個(gè)函數(shù),調(diào)用ELFhash實(shí)現(xiàn)跟隱式調(diào)用一樣的功能

 
 
  1. #include "elfhash.h" 
  2. #include 
  3. #include 
  4.  
  5. int main() {  
  6.     void *handle; 
  7.     unsigned long (*hash)(const char*); 
  8.     char *error; 
  9.     handle = dlopen ("./libelfhash.so", RTLD_LAZY); 
  10.     if (!handle) { 
  11.         fputs (dlerror(), stderr); 
  12.         exit(1); 
  13.     } 
  14.     hash = dlsym(handle, "ElfHash"); 
  15.     if ((error = dlerror()) != NULL)  { 
  16.          fputs(error, stderr); 
  17.          exit(1); 
  18.     } 
  19.     printf ("%ldn", (*hash)("key-for-test")); 
  20.     dlclose(handle); 

至此了解以上的知識(shí)就可以創(chuàng)建和使用動(dòng)態(tài)庫(kù)了。 實(shí)際應(yīng)用中我們可能還是會(huì)遇到一些問(wèn)題。

動(dòng)態(tài)庫(kù)的加載

動(dòng)態(tài)庫(kù)創(chuàng)建那一節(jié),我演示如何隱式使用動(dòng)態(tài)庫(kù),那么編譯運(yùn)行這段代碼試一下。

gcc main.c -L./ -lelfhash 
./a.out //執(zhí)行可執(zhí)行程序
//以下是輸出結(jié)果
./a.out: error while loading shared libraries: libelfhash.so: cannot open shared object file: No such file or directory

結(jié)果運(yùn)行時(shí)報(bào)錯(cuò),可執(zhí)行程序找不到動(dòng)態(tài)庫(kù)。 網(wǎng)上有一些說(shuō)法是編譯時(shí)設(shè)置-L選項(xiàng),但在Linux上面證明是不行的(SunOS上可行),這個(gè)選項(xiàng)只能在編譯鏈接時(shí)有效, 可以讓你使用-l如上面的-lelfhash。使用readelf -d a.out可以看到可執(zhí)行文件依賴(lài)的動(dòng)態(tài)庫(kù)信息。

 0x0000000000000001 (NEEDED)  Shared library: [libelfhash.so]

可以看到這里面并沒(méi)有包含動(dòng)態(tài)庫(kù)的路徑信息。查閱一下動(dòng)態(tài)鏈接器的文檔man ld-linux.so可以發(fā)現(xiàn)這樣一句話(huà)(有的沒(méi)有,版本問(wèn)題)

If a slash is found, then the dependency string is interpreted as a (relative or absolute) pathname, and the library is loaded using that pathname

這段話(huà)太長(zhǎng),我只截取一部分,大致就是說(shuō),當(dāng)依賴(lài)中有/符號(hào),那么會(huì)被解析成動(dòng)態(tài)庫(kù)加載的路徑,隱式使用的例子換一種編譯方法。

gcc main.c ./libelfhash.so 
./a.out
23621492 //輸出正常

再用readelf -d a.out查看會(huì)發(fā)現(xiàn),依賴(lài)信息中有了一個(gè)路徑。

0x0000000000000001 (NEEDED)  Shared library: [./libelfhash.so]

這種方法雖然解決了問(wèn)題,但是依賴(lài)中的路徑是硬編碼,不是很靈活。 動(dòng)態(tài)鏈接器是如何查找的動(dòng)態(tài)庫(kù)的需要進(jìn)一步查閱文檔。關(guān)于查找的順序有點(diǎn)長(zhǎng),這里就不直接引用了,大致是這樣:

  • (僅ELF文件) 使用可執(zhí)行文件中DT_RPATH區(qū)域設(shè)置的屬性,如果DT_RUNPATH被設(shè)置,那么忽略DT_RPATH(在我的Linux對(duì)應(yīng)的是RPATH和RUNPATH)。

  • 使用環(huán)境變量LD_LIBRARY_PATH,如果可執(zhí)行文件中有set-user-id/set-group-id, 會(huì)被忽略。

  • (僅ELF文件) 使用可執(zhí)行文件中DT_RUNPATH區(qū)域設(shè)置的屬性

  • 從/etc/ld.so.cache緩存文件中查找

  • 從默認(rèn)路徑/lib, /usr/lib文件目錄中查找

我們需要設(shè)置RPATH或者RUNPATH,可以這樣做

gcc main.c -Wl,-rpath,/home/xxx,--enable-new-dtags -L./  -lelfhash

這里的-Wl選項(xiàng)告訴鏈接器ld如果如何處理,接下來(lái)傳遞的-rpath(或者使用-R)告訴ld動(dòng)態(tài)庫(kù)的路徑信息(注意-Wl,和后面選項(xiàng)之間不能有空格)。如果沒(méi)有--enable-new-dtags那么只會(huì)設(shè)置RPATH,反之,RPATH和RUNPATH會(huì)同時(shí)被設(shè)置。使用readelf -d a.out查看結(jié)果:

0x000000000000000f (RPATH)  Library rpath: [/home/xxx] 
0x000000000000001d (RUNPATH)  Library runpath: [/home/xxx]

如果使用環(huán)境變量LD_LIBRARY_PATH,那么一般這樣用 export

export LD_LIBRARY_PATH=/home/xxx;$LD_LIBRARY_PATH

RPATH和RUNPATH指定動(dòng)態(tài)庫(kù)的路徑,用起來(lái)簡(jiǎn)單,但是也缺乏靈活性,LDLIBRARYPATH在臨時(shí)測(cè)試的也是很有用的,但是在正式環(huán)境中,直接使用它也不是好的實(shí)踐,因?yàn)榄h(huán)境變量跟用戶(hù)的環(huán)境關(guān)系比較大。動(dòng)態(tài)庫(kù)不僅要考慮自己使用, 還有分發(fā)給別的用戶(hù)使用的情況。

更通用的方法是使用ldconfig,有幾種方法,先在/etc/ld.so.conf.d/目錄下創(chuàng)建一個(gè)文件,然后把你的動(dòng)態(tài)庫(kù)路徑寫(xiě)進(jìn)去?;蛘邔⒛愕膭?dòng)態(tài)庫(kù)放到/lib,/lib64(64位),/usr/lib,/usr/lib64(64位)然后運(yùn)行sudo ldconfig重建/etc/ld.so.cache文件。

動(dòng)態(tài)庫(kù)版本

通常在使用第三方給的動(dòng)態(tài)庫(kù)的時(shí)候,都是帶有版本(文件命名),可以在/usr/lib64下看到很多這樣的動(dòng)態(tài)庫(kù)?,F(xiàn)在我重新編譯動(dòng)態(tài)庫(kù),這次加上版本信息。

gcc -fPIC -shared -Wall -Wl,-soname,libelfhash.so.0  -o libelfhash.so.0.0.0 elfhash.c

每個(gè)動(dòng)態(tài)庫(kù)都有一個(gè)名字,如這里的libelfhash.so.0.0.0,叫real name,命名規(guī)則跟簡(jiǎn)單,通常是libxxx.so.MAJOR.MINOR.VERSION(有 的時(shí)候VERSION會(huì)被省略),如果動(dòng)態(tài)庫(kù)在接口上的兼容性,比如刪除了接口或者修改了接口參數(shù),MAJOR增加,如果接口兼容,只是做了更新或者 bug修復(fù)那么MINOR和VERSION增加。也就是說(shuō)MAJOR相同的庫(kù)接口都是兼容的,反之不兼容,如果使用不兼容的動(dòng)態(tài)庫(kù)需要重新編譯可執(zhí)行程 序。

編譯動(dòng)態(tài)庫(kù)時(shí),通過(guò)給ld傳遞連接選項(xiàng)-soname可以指定一個(gè)soname, 如這里的libelfhash.so.0 只保留MAJOR,可執(zhí)行程序運(yùn)行加載動(dòng)態(tài)庫(kù)時(shí),會(huì)加載這個(gè)指定名字的庫(kù)。

動(dòng)態(tài)庫(kù)還有一個(gè)名字是link name,編譯可執(zhí)行程序時(shí),傳個(gè)鏈接器ld的動(dòng)態(tài)庫(kù)名字,通常是沒(méi)有版本號(hào)以.so結(jié)尾的文件名。 一般作法是對(duì)soname創(chuàng)建軟鏈。

按照這個(gè)規(guī)則來(lái)命名的動(dòng)態(tài)庫(kù)可以ldconfig識(shí)別,我們把libelfhash.so.0.0.0放到/usr/lib64文件夾中,執(zhí)行以下指令

 
 
  1. $sudo ldconfig -v | grep libelfhash.so 
  2. libelfhash.so.0 -> libelfhash.so.0.0.0 

可以發(fā)現(xiàn)ldconfig根據(jù)libelfhash.so.0.0.0的信息,創(chuàng)建了一個(gè)soname指向real name的軟鏈,當(dāng)動(dòng)態(tài)庫(kù)更新(MINOR,VERSION增加),拷貝新庫(kù)到相應(yīng)的位置,再執(zhí)行sudo ldconfig會(huì)自動(dòng)更新軟鏈指向***的動(dòng)態(tài)庫(kù),動(dòng)態(tài)庫(kù)更新就完成了。

總結(jié)

OK,關(guān)于Linux動(dòng)態(tài)庫(kù)知識(shí)整理就到這里了,這些知識(shí)雖說(shuō)都是些基礎(chǔ),少有涉及動(dòng)態(tài)庫(kù)內(nèi)部的一些原理,但是卻很常用。整理過(guò)程中我?guī)е蓡?wèn)去閱讀了ldld-linux.so的文檔,收獲頗豐。同樣,希望本文能幫你解釋遇到的部分問(wèn)題或疑惑。


當(dāng)前題目:Linux動(dòng)態(tài)庫(kù)相關(guān)知識(shí)總結(jié)
本文網(wǎng)址:http://www.5511xx.com/article/cdiccji.html