h5自適應(yīng)網(wǎng)站模板下載優(yōu)化推廣排名網(wǎng)站教程
目錄
靜態(tài)庫
動態(tài)庫
目標(biāo)文件
ELF文件?
ELF形成可執(zhí)行
ELF可執(zhí)行加載
?ELF加載
全局偏移量表GOT(global offset table)?
庫是寫好的,成熟的,可以復(fù)用的代碼
現(xiàn)實中每個程序都要依賴很多的基礎(chǔ)的底層庫,不可能都是從零開始的
庫有兩種:
- 靜態(tài)庫 .a[Linux]、.lib[windows]
- 動態(tài)庫 .so[Linux]、.dll[windows]
靜態(tài)庫
程序在編譯鏈接的時候把庫的代碼鏈接到可執(zhí)行文件中,程序運行的時候?qū)⒉辉傩枰o態(tài)庫
一個可執(zhí)行程序可能用到多個庫,這些庫可能有的是靜態(tài)庫,有的是動態(tài)庫,而我們編譯默認(rèn)鏈接為動態(tài)庫,只有在該庫下找不到動態(tài).so的時候才會采用同名靜態(tài)庫。
若是想使用靜態(tài)庫我們可以使用gcc的 -static 強轉(zhuǎn)設(shè)置鏈接靜態(tài)庫
?靜態(tài)庫的生成:
ar -rc libmylib.a mylib.o
?若我們有一個自己寫的庫叫mylib,那么我們該如何使用它呢?
若頭文件和庫文件都被我們安裝到系統(tǒng)的路徑下
/usr/lib、/usr/lib64、/usr/local/lib等等
gcc main.c -lmystdio
?頭文件和庫文件和我們自己的源文件在同一個路徑下
gcc main.c -L. -lmylib
頭文件和庫文件都有自己獨立的路徑
gcc main.c -I頭?件路徑 -L庫?件路徑 -lmylib
- ?-L:指定庫路徑
- -I:指定頭文件搜索路徑
- -l:指定庫名
動態(tài)庫
程序在運行的時候才去鏈接動態(tài)庫,多個程序可以共享使用庫的代碼
一個與動態(tài)庫鏈接的可執(zhí)行程序僅僅包含它用到的函數(shù)入口地址的一個表,而不是外部函數(shù)所在目標(biāo)文件的整個機器碼
在可執(zhí)行文件開始運行以前,外部函數(shù)的機器碼由操作系統(tǒng)從磁盤上的該動態(tài)庫中復(fù)制到內(nèi)存中,這個過程稱為動態(tài)鏈接
動態(tài)庫可以在多個程序間共享,所以動態(tài)鏈接使得可執(zhí)行文件更小,節(jié)省了磁盤的空間。操作系統(tǒng)采用虛擬內(nèi)存機制允許物理內(nèi)存中的一份動態(tài)庫被要用到該庫的所有進程共用,節(jié)省了內(nèi)存和磁盤空間
動態(tài)庫的生成:
gcc -fPIC -c main.c -o mylib.o
gcc -o libmylib.so mylib.o -shared
動態(tài)庫需要使用位置無關(guān)碼PIC,因此需要加上 -fPIC選項
動態(tài)庫的命名約定通常以lib開頭,后綴為.so
- shared:表示生成共享庫格式
- fPIC:產(chǎn)生位置無關(guān)碼
- 庫名規(guī)則:libxxx.so
動態(tài)庫使用:
若頭文件和庫文件都被我們安裝到系統(tǒng)的路徑下
gcc main.c -lmylib
頭文件和庫文件和我們自己的源文件在同一個路徑下
gcc main.c -L. -lmylib
頭文件和庫文件都有自己獨立的路徑
gcc main.c -I頭?件路徑 -L庫?件路徑 -lmylib
我們可以使用ldd命令來查看庫或者可執(zhí)行程序的依賴
ldd libmylib.so
目標(biāo)文件
在編譯之后會產(chǎn)生擴展名為.o的文件,它們被稱作目標(biāo)文件?
若是我們有多個源文件,需要修改其中一個,那么我們只需要編譯這一個為目標(biāo)文件再一起鏈接,這樣就不需要浪費時間重新編譯整個工程
目標(biāo)文件是一個二進制文件,文件的格式是ELF,是對二進制代碼的一種封裝?
ELF文件?
以下四種都是ELF文件
- 可重定位文件:即xxx.o文件。包含適合于與其他?標(biāo)?件鏈接來創(chuàng)建可執(zhí)??件或者共享?標(biāo)?件的代碼和數(shù)據(jù)
- 可執(zhí)行文件:即可執(zhí)行程序
- 共享目標(biāo)文件:即 xxx.so?件
- 內(nèi)核轉(zhuǎn)儲:存放當(dāng)前進程的執(zhí)?上下?,?于dump信號觸發(fā)
一個ELF文件由四個部分組成
- ELF頭(ELF header):描述?件的主要特性。其位于?件的開始位置,它的主要?的是定位?件的其他部分
- 程序頭表(Program header table):列舉了所有有效的段(segments)和他們的屬性。表? 記著每個段的開始的位置和位移(offset)、?度,畢竟這些段,都是緊密的放在?進制?件中,需要段表的描述信息,才能把他們每個段分割開
- 節(jié)頭表(Section header table):包含對節(jié)(sections)的描述
- 節(jié)(Section):ELF?件中的基本組成單位,包含了特定類型的數(shù)據(jù)。ELF?件的各種信息和數(shù)據(jù)都存儲在不同的節(jié)中,如代碼節(jié)存儲了可執(zhí)?代碼,數(shù)據(jù)節(jié)存儲了全局變量和靜態(tài)數(shù)據(jù)等
常見的節(jié)有:
- 代碼節(jié)(.text):?于保存機器指令,是程序的主要執(zhí)?部分
- 數(shù)據(jù)節(jié)(.data):保存已初始化的全局變量和局部靜態(tài)變量
ELF形成可執(zhí)行
1. 首先將多份C/C++源代碼翻譯稱為目標(biāo).o文件
2. 將多份.o文件Section進行合并
ELF可執(zhí)行加載
?個ELF會有多種不同的Section,在加載到內(nèi)存的時候,也會進?Section合并,形成segment
合并原則:相同屬性,?如:可讀,可寫,可執(zhí)?,需要加載時申請空間等。這樣,即便是不同的Section,在加載到內(nèi)存中,可能會以segment的形式,加載到?起
?
Section合并的主要原因是為了減少??碎?,提?內(nèi)存使?效率?
?ELF加載
?
用來初始化的數(shù)據(jù)也是從ELF的各個segment來,每個segment有自己的起始地址和長度,用來初始化內(nèi)核結(jié)構(gòu)中[start, end]等范圍的數(shù)據(jù)
?
程序調(diào)用庫方法:只需要知道庫的起始虛擬地址+方法偏移量即可定位庫中的?法
全局偏移量表GOT(global offset table)?
因為.data區(qū)域是可讀寫的,所以可以?持動態(tài)進?修改?
?
?
完?