《程序員的自我修養(yǎng)》筆記

第二章 靜態(tài)鏈接

疑問:

  1. 問什么靜態(tài)鏈接不會(huì)把所有代碼鏈接進(jìn)程序
  2. 為什么要靜態(tài)鏈接

被隱藏的過程

gcc helloc.c 包含了 預(yù)處理、編譯弯予、匯編戚宦、鏈接4個(gè)過程

編譯

編譯是經(jīng)過詞法分析、語(yǔ)法分析锈嫩、語(yǔ)義分析等操作后生成匯編代碼文件受楼。執(zhí)行g(shù)cc命令時(shí)垦搬,會(huì)根據(jù)參數(shù)調(diào)用預(yù)編譯程序,Object-C的就是ccl obj c是ccl

匯編

匯編器將匯編文件轉(zhuǎn)成機(jī)器碼艳汽。匯編器as猴贰。.o結(jié)尾的一般就是匯編器最后生成的目標(biāo)文件。

中間語(yǔ)言(源碼級(jí)優(yōu)化器)

編譯器分為前端和后端河狐,前端生成與機(jī)器無關(guān)的中間代碼米绕,后端將中間代碼轉(zhuǎn)為機(jī)器碼

目標(biāo)代碼生成與優(yōu)化

編譯器后端包括代碼生成器和目標(biāo)代碼優(yōu)化器

  • 代碼生成器將中間語(yǔ)言轉(zhuǎn)成目標(biāo)機(jī)器代碼
  • 優(yōu)化器將上述代碼優(yōu)化,如刪除多余指令馋艺,選擇合適尋址方式
靜態(tài)鏈接

不同模塊需要知道其他模塊函數(shù)地址栅干,變量地址,需要通過鏈接的過程確認(rèn)捐祠。
鏈接包括

  • 地址和空間分配
  • 符號(hào)決議
  • 重定位(將未鏈接時(shí)的占位地址(書中是0)在鏈接后替換成真正的目標(biāo)地址)

注:后面 .o的文件都稱為目標(biāo)文件

第三章 目標(biāo)文件

目標(biāo)文件如上面說的碱鳞,是匯編過程生成的,從結(jié)構(gòu)上踱蛀,他跟最后的可執(zhí)行程序是差不多的窿给,只是沒進(jìn)行鏈接的過程,有些符號(hào)和地址還沒被調(diào)整率拒”琅荩可以說,目標(biāo)文件的格式和可執(zhí)行的文件格式是一樣的猬膨。Window下為PE(.exe) Linux下為ELF角撞,動(dòng)態(tài)鏈接庫(kù)和靜態(tài)鏈接庫(kù)的格式也是。

目標(biāo)文件組成

目標(biāo)文件的段

  • 機(jī)器指令碼(代碼段勃痴,.text)
  • 數(shù)據(jù)(數(shù)據(jù)段靴寂,以初始化在.data召耘,未初始化在.bss)
  • 符號(hào)表
  • 調(diào)試信息
  • 字符串等
BSS段

存放未初始化的全局變量和局部靜態(tài)變量

EFL文件描述

ELF文件最開頭為統(tǒng)一魔數(shù) 0x7f 0x45 0x4c 0x4, 0x7f為ASCII里的DEL控制符,后三個(gè)為ELF字母的ASCII(a.out的魔數(shù)為0x01 )

重定位表

ELF文件里.text .data段一般都對(duì)應(yīng)一個(gè)重定位表段庶弃,用來在鏈接器處理目標(biāo)文件時(shí)歇攻,對(duì)代碼段和數(shù)據(jù)段中那些對(duì)絕對(duì)地址引用的位置進(jìn)行重定位

字符串表

將字符串集中存放到一個(gè)表葬毫,再用偏移量來引用不同的字符串忽肛。字符串表用來保普通的字符串,如符號(hào)的名字

鏈接的接口----符號(hào)

稱函數(shù)名和變量名為符號(hào)名。每一個(gè)目標(biāo)文件都有對(duì)應(yīng)的一個(gè)符號(hào)表淑掌,這個(gè)表定義了目標(biāo)文件中用到的所有符號(hào),每個(gè)定義的符號(hào)都有個(gè)對(duì)應(yīng)的值,叫符號(hào)值柄错,對(duì)于變量和函數(shù)來說,符號(hào)值就是他們的地址,符號(hào)包含:

  • 定義在本目標(biāo)文件的全局符號(hào)恒削,可以被其他引用
  • 本目標(biāo)文件的全局符號(hào),卻沒有定義在本目標(biāo)文件,稱外部符號(hào)
  • 段名
  • 局部符號(hào)(編譯單元內(nèi)部李茫,局部變量名),局部符號(hào)對(duì)鏈接過程無用娜庇,
  • 行號(hào)信息
    鏈接過程只關(guān)系全局符號(hào)的相互引用

ELF符號(hào)表結(jié)構(gòu)

符號(hào)表段名一般為".symtab",符號(hào)表的結(jié)構(gòu)為一個(gè)Elf32_Sym結(jié)構(gòu)體數(shù)組藕溅。

typedef struct {
        Elf32_Word      st_name;  //符號(hào)名,包含了該符號(hào)名在字符串表中的下標(biāo)
        Elf32_Addr      st_value; //符號(hào)值
        Elf32_Word      st_size; //符號(hào)大小,對(duì)于包含數(shù)據(jù)的符號(hào),應(yīng)該是值類型大小
        unsigned char   st_info; //符號(hào)類型和綁定信息
        unsigned char   st_other;
        Elf32_Half      st_shndx; //符號(hào)所在的段
} Elf32_Sym;

符號(hào)修飾與函數(shù)簽名

如C語(yǔ)言源代碼中的 全局變量和函數(shù)經(jīng)過編譯后,相對(duì)應(yīng)的符號(hào)名前會(huì)加下劃線(如果是GCC,不會(huì)加入下劃線)申鱼,主要用來減少符號(hào)沖突,C++則有名稱空間

C++符號(hào)修飾

C++里語(yǔ)言特性比較復(fù)雜猴鲫,如重載func(int)和func(double),對(duì)于這些復(fù)雜的語(yǔ)法谣殊,使用符號(hào)修飾或符號(hào)改編的機(jī)制拂共。C++函數(shù)名只是函數(shù)簽名一部分宜狐,C++將源代碼編譯時(shí),會(huì)將函數(shù)名和變量的名字進(jìn)行修飾春贸,形成符號(hào)名厘熟。

  • int func(int) _Z4funci
  • int C::func(int) _Z4funcf

上面的例子包含了命名空間绳姨,函數(shù)名,變量類型等信息购撼,形成符號(hào)名跪削,對(duì)于全局變量,規(guī)則也是一樣迂求,只是全局變量沒有變量類型信息碾盐。

extern "C"

C++為了與C兼容,在符號(hào)管理上揩局,C++用extern "C"來聲明或定義一個(gè)C的符號(hào)毫玖,括號(hào)內(nèi)的代碼會(huì)當(dāng)作c語(yǔ)言來處理,c++的機(jī)制會(huì)不起作用。
如果一個(gè)c函數(shù)庫(kù)被C++文件引用付枫,C++文件里調(diào)用c函數(shù)庫(kù)里的函數(shù)就會(huì)被認(rèn)為是調(diào)用C++的函數(shù)烹玉,會(huì)使用C++的符號(hào)修飾,如memset阐滩,會(huì)被修飾成_Z6memsetPvii,這樣鏈接器就無法與C語(yǔ)言庫(kù)中的memset符號(hào)進(jìn)行鏈接二打。一個(gè)好的方法是使用_cplusplus宏

#ifdef __cplusplus
extern "c" {
#endif

void *memset (void *, int , size_t)

#ifdef __cplusplus
}

#endif

調(diào)試信息

ELF文件段中有debug信息的段。ELF文件采用DWARF(Debug With Arbitrary Record Format)的標(biāo)準(zhǔn)調(diào)試信息格式掂榔。調(diào)試信息往往必代碼和數(shù)據(jù)本身大幾倍继效,realse時(shí)記得去掉調(diào)試信息

第四章 靜態(tài)鏈接

空間與地址分配

將兩個(gè)不同的目標(biāo)文件合并成一個(gè)可執(zhí)行文件,一般會(huì)將他們ELF文件的同名段合并衅疙,一般采用兩步鏈接法

  • 空間與地址分配(掃描所有輸入文件莲趣,合并所有符號(hào),計(jì)算輸出文件各個(gè)段合并后的長(zhǎng)度與位置饱溢,并建立映射關(guān)系)
  • 符號(hào)解釋與重定位 (鏈接后的地址是進(jìn)程中的虛擬地址喧伞,Linux下ELF可執(zhí)行文件的默認(rèn)地址從0x08048000開始分配)

符號(hào)地址的決定

合并后的符號(hào)可以根據(jù)偏移量進(jìn)行計(jì)算

符號(hào)解析與重定位

重定位

目標(biāo)文件里代碼段引用的外部函數(shù)和變量,一般以0填充绩郎,鏈接過后潘鲫,會(huì)替換成鏈接后確定的虛擬地址

重定位表

ELF文件里可以包含一個(gè)或者多個(gè)重定位表(段),如.text的段的重定位表一般為.rel.text,.data的一般對(duì)于.rel.data.重定位表是Elf32_Rel的結(jié)構(gòu)數(shù)組

typedef struct {
  Elf32_Addr r_offset; //重定位入口的偏移肋杖,表示該入口在要被重定位的段中的位置溉仑,簡(jiǎn)單來說就是代碼要被調(diào)整的位置
  Elf32_Word r_info //低8位表示重定位入口類型,高24位表示重定位入口的符號(hào)在符號(hào)表中的下標(biāo)
} Elf32_Rel

符號(hào)解析

鏈接時(shí)状植,鏈接器會(huì)對(duì)需要重定位的符號(hào)(在段里的類型為GBLBAL)浊竟,在合并后的符號(hào)表里尋找對(duì)應(yīng)的符號(hào),如果沒找到津畸,會(huì)報(bào)符號(hào)未定義的錯(cuò)誤

指令修正方式

指令修正方式很多種振定,對(duì)于32位x86平臺(tái)下的ELF只有兩種

  • 絕對(duì)近址32位尋址 R_386_32 (S+A)
  • 相對(duì)近址32位尋址 R_386_PC32 (S+A-P)

S是符號(hào)實(shí)際的位置,A是被修正未知的值肉拓,P是被修正位置的虛擬地址

common塊

common塊機(jī)制用于處理弱符號(hào)沖突問題后频,當(dāng)鏈接器檢測(cè)到多個(gè)文件里有不同類型的相同定義的全局變量的符號(hào),會(huì)以最大類型的大小分配空間暖途。

為什么不給未定義的全局變量在BSS段分配空間卑惜,而給未初始化的局部靜態(tài)變量分配?
因?yàn)榫幾g器將編譯單元編譯成目標(biāo)文件時(shí)驻售,弱符號(hào)所占空間的大小是未知的露久,只有經(jīng)過鏈接階段后才知道,最終鏈接后的BSS段欺栗,還是有占空間的

C++相關(guān)問題

重復(fù)代碼消除

C++編譯過程抱环,模板壳快、外部?jī)?nèi)聯(lián)函數(shù)和虛函數(shù)表可能在不同的編譯單元產(chǎn)生相同的代碼

函數(shù)級(jí)別鏈接

讓所有函數(shù)單獨(dú)保存到一個(gè)段,當(dāng)鏈接器需要用到某個(gè)函數(shù)時(shí)镇草,他就合并到輸出文件中,對(duì)于沒用的函數(shù)則拋棄

全局構(gòu)造與析構(gòu)

C++全局構(gòu)造在main函數(shù)執(zhí)行前瘤旨,全局析構(gòu)在main函數(shù)執(zhí)行完再執(zhí)行梯啤。對(duì)此ELF文件還有兩個(gè)對(duì)應(yīng)的段

  • .init main函數(shù)調(diào)用前需要執(zhí)行的指令
  • .fini main函數(shù)結(jié)束后需要執(zhí)行的指令
C++與ABI(Application Binary Interface)

把符號(hào)修飾標(biāo)準(zhǔn)、變量?jī)?nèi)存布局存哲、函數(shù)調(diào)用方式等這些跟可執(zhí)行代碼二進(jìn)制兼容性相關(guān)的內(nèi)容稱為ABI因宇。ABI相互不兼容,目標(biāo)文件無法相互鏈接祟偷,受硬件平臺(tái)察滑、編程語(yǔ)言、編譯器修肠、鏈接器和操作系統(tǒng)影響

靜態(tài)庫(kù)鏈接

.a靜態(tài)庫(kù)其實(shí)是由“ar”壓縮程序?qū)⑺?o文件打包起來贺辰,并且對(duì)其進(jìn)行編號(hào)和索引。
如何在眾多目標(biāo)文件里找到指定的目標(biāo)文件嵌施?可以使用"objdump"或者"readelf"加上文本查找grep

QA

為什么靜態(tài)運(yùn)行庫(kù)里的一個(gè)目標(biāo)文件只包含一個(gè)函數(shù)饲化?如libc.a里面的printf.o只有print函數(shù)?
鏈接器在鏈接階段以目標(biāo)文件為單位吗伤,如果一個(gè)目標(biāo)文件里函數(shù)過多吃靠,會(huì)容易鏈接很多無用的目標(biāo)文件

鏈接過程控制

鏈接控制腳本

最小的程序

TinyHelloWorld.c
gcc -c -fno-builtin TinyHelloWorld.c
ld -static -e nomain -o TinyHelloWorld TinyHelloWorld.o
上面兩段,是使用gcc將c文件編譯成目標(biāo)文件足淆,鏈接器使用靜態(tài)鏈接方式巢块,指定nonmain函數(shù)為入口并輸出為可執(zhí)行文件

使用ld鏈接腳本

gcc -c -fno-builtin TinyHelloWorld.c
ld -static -T TinyHelloWorld.lds(腳本) -o TinyHelloWorld TinyHelloWorld.o
鏈接控件腳本可以合并或去除輸入文件里ELF文件里各段,就是可以指定鏈接過程段的轉(zhuǎn)換

BFD庫(kù)

統(tǒng)一接口處理不同的目標(biāo)文件格式巧号,以適應(yīng)各種不同軟硬件平臺(tái)

第六章 可執(zhí)行文件的裝載與進(jìn)程

進(jìn)程虛擬地址控件

虛擬地址空間一般由硬件平臺(tái)決定族奢,現(xiàn)在一般為32位或者64位,決定了地址空間的上限裂逐,一般c語(yǔ)言指針大小的位數(shù)與虛擬空間的位數(shù)一樣

pae

通過pae(Physical Adress Extensio) 歹鱼,如32位cpu有36地址線,可以通過映射程序擴(kuò)展內(nèi)存訪問

裝載的方式

覆蓋裝入

比較古老的技術(shù)卜高,互相不依賴的模塊可共用內(nèi)存弥姻,用到哪個(gè)模塊加載哪個(gè)模塊,如果原有內(nèi)存有一個(gè)模塊掺涛,新的模塊將覆蓋他

頁(yè)映射

將內(nèi)存和磁盤中的數(shù)據(jù)和指令按頁(yè)為單分成若干個(gè)頁(yè)庭敦,頁(yè)的大小一般為4096字節(jié)、8192字節(jié)薪缆、2MB秧廉、4MB等伞广,也是用到哪個(gè)加載哪個(gè),如果內(nèi)存滿了疼电,將按如FIFO嚼锄,LUR等法則替換內(nèi)存

從操作系統(tǒng)角度看可執(zhí)行文件的裝載

進(jìn)程的建立

從操作系統(tǒng)的角度,一個(gè)進(jìn)程擁有獨(dú)立的地址空間蔽豺。創(chuàng)建一個(gè)進(jìn)程区丑,然后裝載相應(yīng)的可執(zhí)行文件并且執(zhí)行這個(gè)過程的步驟有

  • 創(chuàng)建一個(gè)獨(dú)立的虛擬地址空間(由一組頁(yè)映射函數(shù)將虛擬空間的各個(gè)頁(yè)映射至相應(yīng)的物理空間,是虛擬空間到物理內(nèi)存的映射關(guān)系)
  • 讀取可執(zhí)行文件頭修陡,并且建立虛擬空間與控制可執(zhí)行文件的映射關(guān)系沧侥。(這種映射關(guān)系只是保存在操作系統(tǒng)內(nèi)部的一個(gè)數(shù)據(jù)結(jié)構(gòu))
  • 將CPU的指令寄存器設(shè)置成可執(zhí)行文件的入口地址,啟動(dòng)運(yùn)行(ELF文件頭中保存的入口地址)

由于可執(zhí)行文件在裝載時(shí)實(shí)際上是被映射的虛擬空間魄鸦,所以可執(zhí)行文件很多時(shí)候又被叫做映像文件(Image)
Linux中將進(jìn)程虛擬地址空間中的一個(gè)段叫做虛擬內(nèi)存區(qū)域(VMA宴杀,Virtual Memory Area)

頁(yè)錯(cuò)誤

上面三個(gè)步驟執(zhí)行完,可執(zhí)行文件真正的指令和數(shù)據(jù)都沒有被裝入內(nèi)存中拾因。操作系統(tǒng)只是通過可執(zhí)行文件的頭部信息建立起可執(zhí)行文件和進(jìn)程虛擬內(nèi)存之間的映射關(guān)系旺罢。cpu開始執(zhí)行指令時(shí),發(fā)現(xiàn)入口是個(gè)空頁(yè)面盾致,就會(huì)認(rèn)為是一個(gè)頁(yè)錯(cuò)誤(Page Fault)主经。CPU將控制權(quán)交給操作系統(tǒng),操作系統(tǒng)將查詢虛擬內(nèi)存與可執(zhí)行文件映射的數(shù)據(jù)結(jié)構(gòu)庭惜,找到空頁(yè)面在可執(zhí)行文件的偏移罩驻,然后在物理內(nèi)存分配一個(gè)物理頁(yè)面,將進(jìn)程中的虛擬頁(yè)與物理內(nèi)存頁(yè)建立映射關(guān)系(MMU)护赊。

進(jìn)程虛存空間分布

ELF文件鏈接視圖與執(zhí)行視圖

ELF文件映射到虛擬內(nèi)存時(shí)惠遏,是以頁(yè)面為單位的,但是ELF文件有多個(gè)section骏啰,如果一個(gè)section單獨(dú)一個(gè)VMA占多個(gè)頁(yè)時(shí)节吮,容易出現(xiàn)內(nèi)存碎片,因此判耕,系統(tǒng)在裝載可執(zhí)行文件時(shí)透绩,會(huì)根據(jù)section的讀寫權(quán)限來劃分虛擬內(nèi)存區(qū)域(VMA)。ELF文件section的讀寫權(quán)限一般有:

  • 以代碼段為代表的權(quán)限可讀可執(zhí)行的段
  • 以數(shù)據(jù)段和BSS段為代表的權(quán)限為可讀可寫的段
  • 以制度數(shù)據(jù)為代表的權(quán)限為只讀的段

像上面那種將多個(gè)ELF section合并到一起的概念叫做 segment壁熄,在將目標(biāo)文件鏈接成可執(zhí)行文件的時(shí)候帚豪,鏈接器會(huì)盡量把權(quán)限相同的段分配在同一個(gè)空間,因此草丧,系統(tǒng)是按segment來映射可執(zhí)行文件的狸臣,而不是ELF文件中的section〔矗總的來說烛亦,Segment和Section是從不同角度來劃分ELF文件诈泼,Section的角度來看就是鏈接視圖,Segment的角度是執(zhí)行視圖

堆和棧

VMA除了被用來映射可執(zhí)行文件各個(gè)segment外煤禽,進(jìn)程中的堆和棧也被映射成VMA铐达,這種VMA稱為匿名虛擬內(nèi)存區(qū)域(Anonymous Virtual Memory Area)

堆的最大申請(qǐng)數(shù)量

Linux下虛擬地址空間給 進(jìn)程本身的是3GB,windows 是2GB檬果,但實(shí)際的大小受動(dòng)態(tài)庫(kù)數(shù)量娶桦、程序本身大小和操作系統(tǒng)版本等各種因素影響

第七章 動(dòng)態(tài)鏈接

把鏈接這個(gè)過程推遲到運(yùn)行時(shí)再進(jìn)行,就是動(dòng)態(tài)鏈接(dynamic Linking)的基本思想

動(dòng)態(tài)鏈接的基本實(shí)現(xiàn)

Linux中汁汗,ELF動(dòng)態(tài)鏈接文件被稱為動(dòng)態(tài)共享對(duì)象(DSO,Dynamic Shared Objects)栗涂,一般以.so結(jié)尾
Windows中知牌,動(dòng)態(tài)鏈接文件被稱為動(dòng)態(tài)鏈接庫(kù)(Dynamical Linking Library),一般以.dll結(jié)尾

程序與動(dòng)態(tài)鏈接庫(kù)直接鏈接的工作由動(dòng)態(tài)鏈接器完成斤程,而不是靜態(tài)鏈接庫(kù)的由ld完成角寸。動(dòng)態(tài)鏈接把鏈接過程由程序裝載前推到程序裝載后。但每次裝載時(shí)鏈接動(dòng)態(tài)庫(kù)忿墅,會(huì)有一點(diǎn)時(shí)間的消耗扁藕,,但是這個(gè)過程可以優(yōu)化疚脐,比如延遲綁定(lazy binding)等方法

動(dòng)態(tài)鏈接程序運(yùn)行時(shí)地址空間分布

在整個(gè)虛擬地址空間中亿柑,會(huì)多處引用的動(dòng)態(tài)庫(kù)和動(dòng)態(tài)庫(kù)鏈接器部分。在系統(tǒng)開始運(yùn)行程序時(shí)棍弄,首先會(huì)把控制權(quán)交給動(dòng)態(tài)鏈接器望薄,由它完成所有的動(dòng)態(tài)鏈接工作后再把控制器交給程序。動(dòng)態(tài)鏈接模塊在ELF文件中的裝載地址是無效地址0x000000000呼畸,但最終裝載地址并不是這個(gè)

地址無關(guān)代碼(PIC痕支,Position-independent Code)

裝載時(shí)重定位

程序模塊在編譯時(shí)目標(biāo)地址不確定時(shí)需要在裝載時(shí)重定位。稱裝載時(shí)重定位(Load Time Relocation)

地址無關(guān)代碼

裝載時(shí)重定位無法解決共享動(dòng)態(tài)鏈接庫(kù)絕對(duì)地址引用的問題蛮原。解決這問題方案為地址無關(guān)代碼技術(shù)(Position-independent Code)卧须,讓共享指令部分在裝載時(shí)不需要因?yàn)檠b載地址的改變而改變,基本思想就是將這部分指令分離出來儒陨,跟數(shù)據(jù)部分放在一起(數(shù)據(jù)部分可以拷貝副本花嘶,不同程序因此可以任意修改)

地址無關(guān)代碼的地址引用方式

指令跳轉(zhuǎn)、調(diào)用 數(shù)據(jù)訪問
模塊內(nèi)部 相對(duì)跳轉(zhuǎn)和調(diào)用 相對(duì)地址訪問
模塊外部 間接跳轉(zhuǎn)和調(diào)用(GOT) 間接訪問(GOT)

上面的GOT稱為全局偏移表(Global Offset table)框全,專門用于其他模塊數(shù)據(jù)和指令調(diào)用的映射表察绷,就是上面說的,存在數(shù)據(jù)段的映射表津辩,當(dāng)代碼需要引用全局變量時(shí)拆撼,可以通過GOT中對(duì)應(yīng)的項(xiàng)間接引用

共享模塊的全局變量問題

當(dāng)一個(gè)可執(zhí)行文件引用一個(gè)動(dòng)態(tài)鏈接庫(kù)的全局變量時(shí)容劳,可執(zhí)行文件是無法使用GOT的,編譯時(shí)就要確認(rèn)全局變量的地址闸度。解決方法是當(dāng)共享模塊裝載時(shí)竭贩,如果某個(gè)全局變量在可執(zhí)行文件中擁有副本,動(dòng)態(tài)鏈接器會(huì)把GOT中的相應(yīng)地址指向該副本莺禁。如果全局變量在程序主模塊中沒有副本留量,那么將執(zhí)行GOT

數(shù)據(jù)段地址無關(guān)性

static int a;
static int *b = &a

如果共享對(duì)象存在上面的代碼,會(huì)產(chǎn)生絕對(duì)地址的引用哟冬。對(duì)于這種絕對(duì)地址引用楼熄,使用裝載時(shí)重定位的方法,編譯器和鏈接器會(huì)產(chǎn)生一個(gè)重定位表浩峡,包含“R_386_RELATIVE”類型的重定位入口可岂,動(dòng)態(tài)鏈接器就會(huì)對(duì)該共享對(duì)象進(jìn)行重定位。

裝載時(shí)重定位比GOT要快翰灾,省去了GOt中每次訪問全局?jǐn)?shù)據(jù)和函數(shù)時(shí)做一次計(jì)算當(dāng)前地址以及間接地址的尋址過程

延遲綁定

ELF使用PLT(Procedure Linkage Table)的方法實(shí)現(xiàn)缕粹,當(dāng)調(diào)用某個(gè)外部模塊函數(shù)時(shí),常規(guī)做法是通過GOT中相應(yīng)的項(xiàng)進(jìn)行間接跳轉(zhuǎn)纸淮。PLT為了實(shí)現(xiàn)延遲綁定平斩,加了一個(gè)中間跳轉(zhuǎn),不直接通過GOT咽块,而是通過一個(gè)PLT結(jié)構(gòu)進(jìn)行跳轉(zhuǎn)绘面。原理是調(diào)用一個(gè)外部函數(shù),PLT會(huì)調(diào)用_dl_runtime_relsove,傳入模塊id和函數(shù)來進(jìn)行解析糜芳。

動(dòng)態(tài)鏈接相關(guān)數(shù)據(jù)結(jié)構(gòu)

.interp 段

interpreter縮寫飒货,表示解析器,ELF里面的一個(gè)段峭竣,存放動(dòng)態(tài)鏈接器的路徑字符串

.dynamic段

.dynamic段跟靜態(tài)鏈接的ELF頭文件信息類似塘辅,包含了動(dòng)態(tài)鏈接符號(hào)表的地址、動(dòng)態(tài)鏈接字符串表地址皆撩、依賴的共享對(duì)象文件名等

動(dòng)態(tài)符號(hào)表

為了表示動(dòng)態(tài)鏈接這些模塊之間的符號(hào)導(dǎo)入導(dǎo)出關(guān)系扣墩,ELF專門有一個(gè)焦動(dòng)態(tài)符號(hào)表(Dynamic Symbol Table)來保存這些信息,這個(gè)段為.dynsym扛吞。與.symtab保存所有符號(hào)呻惕,包括內(nèi)部變量,而.dynsym只保存動(dòng)態(tài)鏈接相關(guān)的符號(hào)滥比。

.dynsym的輔助表有動(dòng)態(tài)符號(hào)字符串表和符號(hào)哈希表(用于提高程序運(yùn)行時(shí)符號(hào)查找速率)

動(dòng)態(tài)鏈接重定位表

動(dòng)搖鏈接的可執(zhí)行文件使用PIC方法亚脆,代碼段都是地址無關(guān),但是數(shù)據(jù)段里的GOT需要重定位盲泛,數(shù)據(jù)段里一些絕對(duì)地址引用也需要

動(dòng)態(tài)鏈接重定位相關(guān)結(jié)構(gòu)

重定位表

代碼段 數(shù)據(jù)段
靜態(tài)鏈接 .rel.text .rel.data
動(dòng)態(tài)鏈接 .rel.dyn .rel.plt

.rel.dyn修正的位置位于.got以及數(shù)據(jù)段 濒持,.rel.plt用于修正.got.plt

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末键耕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柑营,更是在濱河造成了極大的恐慌屈雄,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件官套,死亡現(xiàn)場(chǎng)離奇詭異酒奶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)奶赔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門惋嚎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人站刑,你說我怎么就攤上這事瘸彤。” “怎么了笛钝?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)愕宋。 經(jīng)常有香客問我玻靡,道長(zhǎng),這世上最難降的妖魔是什么中贝? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任囤捻,我火速辦了婚禮,結(jié)果婚禮上邻寿,老公的妹妹穿的比我還像新娘蝎土。我一直安慰自己,他們只是感情好绣否,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布誊涯。 她就那樣靜靜地躺著,像睡著了一般蒜撮。 火紅的嫁衣襯著肌膚如雪暴构。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天段磨,我揣著相機(jī)與錄音取逾,去河邊找鬼。 笑死欺劳,一個(gè)胖子當(dāng)著我的面吹牛鹅心,可吹牛的內(nèi)容都是我干的吨铸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼晴埂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼究反!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起邑时,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奴紧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后晶丘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體黍氮,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年浅浮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沫浆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡滚秩,死狀恐怖专执,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郁油,我是刑警寧澤本股,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站桐腌,受9級(jí)特大地震影響拄显,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜案站,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一躬审、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蟆盐,春花似錦承边、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至痹愚,卻和暖如春翔始,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背里伯。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工城瞎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疾瓮。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓脖镀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜒灰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容