Linux庫函數制作(靜態庫、動態庫)


Linux庫函數制作(靜態庫、動態庫)

 

靜態庫與動態庫

 

鏈接方式

鏈接分為兩種:靜態鏈接、動態鏈接

 

靜態鏈接:

由鏈接器在鏈接時將庫的內容加入到可執行程序中

靜態鏈接的特點是:

優點:

    對運行環境的依賴性較小,具有較好的兼容性

缺點:

    生成的程序比較大,需要更多的系統資源,在裝入內存時會消耗更多的時間

    庫函數有了更新,必須重新編譯應用程序

 

 

動態鏈接:

連接器在鏈接時僅僅建立與所需庫函數的之間的鏈接關系,在程序運行時才將所需資源調入可執行程序

動態鏈接的特點:

優點:

    在需要的時候才會調入對應的資源函數

    簡化程序的升級;有着較小的程序體積

    實現進程之間的資源共享(避免重復拷貝)

缺點:

    依賴動態庫,不能獨立運行

    動態庫依賴版本問題嚴重

 

/*************************************************************************
> File Name: myprintf.c
> Author: lsgxeva
> Mail: lsgxeva@163.com
> Created Time: 2017年09月28日 星期四 11時52分57秒
************************************************************************/

#include <stdio.h>

void myprintf(void)
{
    printf("hello, world!\n");
}

 

/*************************************************************************
> File Name: myprintf.h
> Author: lsgxeva
> Mail: lsgxeva@163.com
> Created Time: 2017年09月28日 星期四 11時53分15秒
************************************************************************/

#ifndef _MYPRINTF_H_
#define _MYPRINTF_H_

extern void myprintf(void);

#endif // _MYPRINTF_H_

 

/*************************************************************************
> File Name: mytest.c
> Author: lsgxeva
> Mail: lsgxeva@163.com
> Created Time: 2017年09月28日 星期四 11時54分26秒
************************************************************************/

#include "myprintf.h"

int main()
{
    myprintf();
    return 0;
}

 

目錄結構

drwxr-xr-x 5 root root 94 9月 28 12:22 .
drwxr-xr-x 5 root root 54 9月 28 11:08 ..
-rw-r--r-- 1 root root 360 9月 28 11:53 myprintf.c
-rw-r--r-- 1 root root 380 9月 28 11:54 myprintf.h
-rw-r--r-- 1 root root 351 9月 28 12:22 mytest.c
drwxr-xr-x 2 root root 6 9月 28 12:24 output
drwxr-xr-x 2 root root 6 9月 28 11:56 shared
drwxr-xr-x 2 root root 6 9月 28 12:23 static

 

制作靜態鏈接庫

 

 

 

 

 

 

 制作動態鏈接庫

 

 

 

解決無法打開動態庫的常用簡便方法:
聲明臨時變量環境
export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH

或者修改 /etc/ld.so.conf 文件 在其中添加庫的搜索路徑,一行一個路徑。
sudo ldconfig 更新 /etc/ld.so.cache 文件
那 ./etc/ld.so.conf 中所有路徑的庫文件都被緩存達到 /etc/ld.so.cache 中。

 

注意: 將生成共享庫的編譯參數-shared錯誤地用於生成可執行文件,將導致程序運行時發生段錯誤!

 

 

編譯產生動態鏈接庫,並支持 major 和 minor 版本號。

 

 

 

 

 

動態鏈接和靜態鏈接時,可執行文件的區別:

 

 

 

Linux共享對象之編譯參數fPIC

 
  最近在看Linux編程的基礎知識,打算對一些比較有趣的知識做一些匯總備忘,本文圍繞fPIC展開,學習參考見文末。
  在Linux系統中,動態鏈接文件稱為動態共享對象(DSO,Dynamic Shared Objects),簡稱共享對象,一般是以.so為擴展名的文件。在Windows系統中,則稱為動態鏈接庫(Dynamic Linking Library),很多以.dll為擴展名。這里只備忘Linux的共享對象。
    在實現一共享對象時,最一般的編譯鏈接命令行為: 
 g++ -fPIC -shared test.cc -o lib.so
    或者是:
    g++ -fPIC test.cpp -c -o test.o
    ld -shared test.o -o lib.so
    上面的命令行中-shared表明產生共享庫,而-fPIC則表明使用地址無關代碼。PIC:Position Independent Code.    
    Linux下編譯共享庫時,必須加上-fPIC參數,否則在鏈接時會有錯誤提示(有資料說AMD64的機器才會出現這種錯誤,但我在Inter的機器上也出現了):
/usr/bin/ld: test.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
test.o: could not read symbols: Bad value
collect2: ld returned 1 exit status
   如何確認一個共享對象是PIC呢?
readelf -d foo.so |grep TEXTREL 
如果上邊的shell有任何輸出,則說明這foo.so不是PIC。TEXTREL表示代碼段重定位表地址,PIC的共享對象不會包含任何代碼段重定位表。
     fPIC的目的是什么?共享對象可能會被不同的進程加載到不同的位置上,如果共享對象中的指令使用了絕對地址、外部模塊地址,那么在共享對象被加載時就必須根據相關模塊的加載位置對這個地址做調整,也就是修改這些地址,讓它在對應進程中能正確訪問,而被修改到的段就不能實現多進程共享一份物理內存,它們在每個進程中都必須有一份物理內存的拷貝。fPIC指令就是為了讓使用到同一個共享對象的多個進程能盡可能多的共享物理內存,它背后把那些涉及到絕對地址、外部模塊地址訪問的地方都抽離出來,保證代碼段的內容可以多進程相同,實現共享。
    抽離出這部分特殊的指令、地址之后,放到了一個叫做GOT(Global Offset Table)的地方,它放在數據段中,每一個進程都有獨立的一份,里面的內容可能是變量的地址、函數的地址,不同進程它的內容很可能是不同的,這部分就是被隔離開的“地址相關”內容。模塊被加載的時候,會把GOT表的內容填充好(在沒有延遲綁定的情況下)。代碼段要訪問到GOT時,通過類似於window的call/pop/sub指令得到GOT對應項的地址。
    對於模塊中全局變量的訪問,為了解決可執行文件跟模塊可能擁有同一個全局變量的問題(此時,模塊內的全局變量會被覆蓋為可執行文件中的全局變量),對模塊中的全局變量訪問也通過GOT間接訪問。
    這樣子,每一次訪問全局變量、外部函數都需要去計算在GOT中的位置,然后再通過對應項的值訪問變量、調用函數。從運行性能上來說,比裝載時重定位要差點。裝載時重定位就是不使用fPIC參數,代碼段需要一個重定位表,在裝載時修正所有特殊地址,以后運行時不需要再有GOT位置計算和間接訪問。(但是,我在自己機子上測試,編譯鏈接共享庫時,沒法不使用fPIC參數,可能多數系統都要求必須有fPIC)
    如果在裝載時就去計算GOT的內容,那么會影響加載速度,於是就有了 延遲綁定(Lazy Binding),直到用時才去填充GOT。它使用到了 PLT(Procedure Linkage Table):每一項都是一小段代碼,對應於本運行模塊要引用的函數。函數調用時,先到這里,然后再到GOT。在函數第一次被調用時,進入PLT跳到加載器,加載器計算函數的真正地址,然后將地址寫入GOT對應項,以后的調用就直接從PLT跳到GOT記錄的函數位置。這樣也減少了運行時多次調用多次計算GOT位置。
    PIC的共享對象也會有重定位表,數據段中的GOT、數據的絕對地址引用,這些都是需要重定位的。
  readelf -r Lib.so
  可以看到共享對象的重定位表,.rel.dyn是對數據引用的修正,.rel.plt是對函數引用的修正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM