日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

連接器,是把目標文件連接成可執行文件動態庫的工具。

它是將高級語言代碼轉化成二進制程序的最后一步。

編譯之后的目標文件里,函數和全局變量的地址并不是真實內存地址,而是一個重定位符號。

連接器的作用,就是把這些重定位符號處理成真實的內存地址。

int printf(const char* fmt, ...);

int main()

printf("hello world");

return 0;

這段代碼在編譯時有2個沒法確定的數據:一是printf()函數的地址,二是字符串常量"hello world"的地址。

printf()函數是個庫函數,它的地址可以在動態庫里,也可以在靜態庫里,還可以在其他.o文件里,編譯器是沒法提前知道的。

字符串常量"hello world"是一個全局常量,它要放在.rodata數據段里。

.rodata數據段的位置編譯器也是沒法確定的,因為最終可能是多個目標文件連接成1個可執行程序,.rodata數據段的具體位置需要連接器來確定。

所以,編譯器就在生成.o文件時就添加1個重定位節、1個符號表,他們包含2個重定位信息:printf()和"hello world"。

然后,由連接器重寫真實的內存地址。

上面代碼用gcc -c編譯成.o文件之后,用readelf -a查看它的信息,如下圖:

ELF頭

從ELF頭可以看出,編譯后的文件是可重定位文件,運行的系統架構是x86_64。

從它各個節的列表里可以找到.rela.text重定位節.rodata節,前者存儲重定位信息,后者存儲常量數據。

各個節的列表

重定位節.rela.text的內容有2條:

1,一個指向.rodata節,表示這條重定位的地址在.rodata段里。

2,另一個沒有具體的節,但給了一個函數名puts,表示要找的是這個函數(gcc在編譯時都是把printf轉化成puts函數)。

重定位節和符號表

在上圖的符號表.symtab節里,也可以找到這2條信息:

1,其中的第5條(從0開始)就是"hello world"字符串的信息:它是一個LOCAL的字符串,也就是它的數據在當前文件里的某個節(SECTION),這個節的索引號是5(Ndx列)

去上面的節列表里查找,可以發現.rodata段確實是第5個節。

2,第11條就是puts()函數的信息,它是GLOBAL的全局函數,不在當前文件的某個節里(Ndx是UND,undefined),需要連接器去其他地方找(庫文件、其他.o文件,etc)。

Ndx這一列表示重定位數據所在的節,當前文件里實現的函數或變量都有節的索引號,但外部全局函數的索引號都是不確定的(UND)。

代碼段,main函數的機器碼

代碼段.text里的main()函數的機器碼可以看出,裝載"hello world"字符串的指令和調用printf()的指令里的地址都是00 00 00 00。

也就是說,這里需要的真實內存地址是32位的整數,有待連接器進一步填寫

00 00 00 00也就是高級語言里的NULL,在代碼里都是無效的內存地址,如果不重填的話肯定會發生段錯誤

lea指令裝載全局變量時使用的內存地址,是變量地址當前指令地址的偏移量

rip,指令指針寄存器,它存的是當前指令的地址,x86_64對全局變量的尋址,都是使用的這種方式。

如果是靜態連接,連接器把靜態庫.a和main函數的.o文件合在一起,然后修改這兩個地址就可以了。

如果是動態連接,還需要用到全局偏移量表(GOT,global offset table)和PLT(過程連接表,procedure linkage table)。

動態連接之后的ELF頭

gcc動態連接之后生成的可執行文件。

以前gcc都是生成可執行文件EXEC,現在都是生成動態庫DYN直接運行了(即使main函數所在的文件也這樣)。

上圖ELF頭可以看出類型是DYN,入口地址是0x530。

節的列表

動態鏈接之后文件有特別多的節,其中以.dyn開頭的都是動態庫相關的節。

.plt.plt.got.got,這3個就是動態連接所必須的節。

.rela.plt和.rodata依然存在,內容和靜態連接得差不多。

所需的動態庫信息

因為程序運行時要首先加載所需的動態庫,所以必須含有動態庫的信息,如上圖。

這個程序比較簡單,只需要libc.so.6庫。

以下兩圖是重定位節的內容和動態庫支持的庫函數列表,可以看到他們都包含puts()函數,即main()函數所需的printf()

重定位節

動態庫函數的信息

最后簡單說一下plt和got的內容:

plt分為2個節.plt.plt.got

.plt是只讀的可執行代碼,.plt.got是可寫的數據。

操作系統不允許在運行時修改代碼只允許在運行時修改數據,所以動態連接的程序要想獲得庫函數的地址必須要一個小技巧[呲牙]

加載器必須把庫函數的地址放在一個全局函數指針變量里,然后讓一段過渡代碼去調用這個函數指針,從而實現動態運行。

這個全局的函數指針就是.plt.got里的一項。

當程序需要多個庫函數時,這些函數指針就形成了一個函數指針數組,這就是.plt.got表

調用(多個)庫函數的過渡代碼數組就是.plt表:它是有運行權限的,而且是只讀的。

如下圖:

1,最開始的時候,這個函數指針是加載器的加載函數

2,當第一次調用puts()函數,加載函數會去動態庫里查找它的真實地址,并填寫在這里。

3,之后再調用時,就直接調用puts()函數了。

這是linux系統動態庫函數的需求加載機制。

如果是普通變量,把它的地址放在.got表里就行。

動態庫函數的需求加載

分享到:
標簽:連接器
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定