《程式設計師的自我修養》筆記 靜態鏈結

2021-05-25 12:58:44 字數 2832 閱讀 1614

在通過編譯和彙編後,就生成了目標檔案,鏈結就是把這些目標檔案加工後合併成乙個輸出檔案的過程。

鏈結過程可以分為兩步:

第一步 空間與位址分配。掃瞄所有的輸入目標檔案,獲得它們每個各個段的長度、屬性和位置,並且將輸入目標檔案中的符號表中所有

的符號定義和符號引用收集起來,統一放到乙個全域性符號表。這一步中,鏈結器將能夠獲得所有輸入目標檔案的段長度,並且將它們合併

(相同的

段互相合併,如.text和.text段合併、.data段和.data段合併)

,計算出

輸出檔案中各個段合併後的長度和位置,並建立對映關係。

通過這一

步,輸入檔案中的各個段在鏈結後的虛擬位址就確定了。我們可以通過objdump -h命令可以看到,鏈結前目標檔案中的各段的位址

都為0,這

給可執行檔案的各個段分配位址空間涉及

到作業系統的

一文。分配並計算出可執行檔案中各個段的虛擬位址後,鏈結器開始計算各個符號的虛擬位址。因為各個符號在段內的相對位置是固定的,所以這時候各個符號的位址已經是確定的了。舉個例子,假設「a.o」中的「main」函式相對於「a.o」的「.text」段得偏移是x,並且經過鏈結合併各段後,「a.o」的「.text」段位於虛擬位址0x08048094(在linux中,elf可執行檔案預設從位址0x08048000開始分配),那麼"main"函式的位址應該是 0x08048094+x。通過這種方法,可以計算出所有符號的位址,並儲存至全域性符號表中。在後文中就將看到,這裡計算出全域性符號表中各個符號的位址是為了符號解析和重定位一步中計算指定修正值時使用的。

第二步 符號解析和重定位。使用上面第一步中收集到的所有資訊,讀取輸入檔案中段的資料、重定位資訊,並且進行符號解析和重定

位、通過objdump -d命令對目標檔案進行反彙編後(能看到彙編**),可以看到對應指令中外部引用的位址部分都是暫時用臨時的假地

址來代替,真正的位址計算工作由富豪街西和重定位這一步來進行,也是符號解析和重定位的主要工作。通過前面的空間和位址分配可以得知,

鏈結器在完成地

址修正。通過對

可執行檔案執行使用objdump -d命令進行反彙編,可以看到,相應的執行的位址都已經得到了修正。

那麼鏈結器是怎麼知道哪些指令是要被調整的呢?這些指令的哪些部分需要被調整?應該怎麼調整?這都是通過重定位表來做的。通過

一文,我們知道在elf檔案中,有乙個重定位表用來

儲存與重定

位有關的資訊。對於可重定位的elf檔案(目標檔案)來說,它必須包含有重定

位表。對於每個要重定位的elf段都有乙個對應的重定位表,

比如**段「.text」如有要重定位的地方,那麼會有乙個相對應的「.rel.text」的段

儲存了了**段的重定位表;如果資料段「.data」有要被重

定位的地方,就會有乙個相對應的叫".rel.data"的重定位表。可以用

objdump -r 命令來檢視目標檔案的重定位表,重定位表中列出了目標

檔案中所有引用到外部符號的地方,亦即需要重定位的地方,每個需要重定位的地方叫做乙個重定位入口。對於32位的inter x86系列處理器

來說,重定位表的結構如下:

typedef structelf32_rel;

r_offset是重定位入口的偏移。對於目標檔案來說,這個值是該重定位入口所要修正的位置的第乙個位元組相對於段起始的偏移;對於可

執行檔案來說,這個值是該重定位入口所要修正的位置的第乙個位元組的虛擬位址。我們暫時只關心目標檔案的情況。

r_info是重定位入口的型別和符號。這個成員的低8位表示重定位入口的型別,高24位表示重定位入口的符號在符號表中的下標。

重定位的過程中,每個重定位入口都是對乙個符號的引用,那麼當鏈結器需要對某個符號的引用進行重定位時,就要確定這個符號的目標位址,這時候鏈結器就會去查詢所有輸入目標檔案的符號表組成的全域性符號表,找到相應的符號後進行重定位。

下面進行最後的工作:修正指令。不同的處理器指令對於位址的格式和方式都不一樣。比如對於32位intel x86處理器來說,轉移跳轉指令(jmp指令)、子程式呼叫指令(call指令)和資料傳送指令(mov指令)定址方式千差萬別。但是對於32位x86平台下的elf檔案的重定位入口所修正的指令定址方式只有兩種:絕對近址32位定址、相對近址32位定址。這兩種重定位方式指令修正方式每個被修正的位置的長度都是32位,即4個位元組。而且都是近址定址,不用考慮intel的段間遠址定址。唯一的區別就是絕對定址和相對定址。

前面提過,重定位的r_info成員的低8位表示重定位入口型別,如下所示:

巨集定義             值                        重定位修正方法

r_386_32      1                         絕對定址修正s+a

r_386_pc32   0                         相對定址修正s+a-p

其中a = 被修正位置的值

p = 被修正的位置(相對於段開始的偏移量或者虛擬位址),該值可通過r_offset計算得到

s = 符號的實際位址,即由r_info的高24位指定的符號的實際位址

假設我們有這樣兩個檔案:

現在我們假設在將a.o和b.o連線成最終可執行檔案後,main函式的虛擬位址為0x1000,swap函式的虛擬位址為0x2000,shared變數的虛擬位址為0x3000。那麼鏈結器將如何修正a.o裡面的兩個重定位入口呢?

注:鏈結命令ld用來將幾個目標檔案鏈結起來

ld a.o b.o -e main -o ab(-e main表示將main函式作為程式入口,-o ab表示輸出檔名為ab)

經過了靜態鏈結,便由若干個目標檔案生成了乙個可執行檔案,接下來的就是可執行檔案如何裝載如記憶體的問題了。

程式設計師的自我修養四靜態鏈結

真正了不起的程式設計師對自己的程式的每乙個位元組都了如執掌。a.c extern intshared int main 全域性符號 b.c int shared 1 全域性符號 void swap int a,int b 全域性符號 對於鏈結器來說,整個鏈結過程中,它就是將幾個輸入目標檔案加工後合併...

程式設計師自我修養鏈結庫筆記

第二章節 1.預編譯 gcc e cwj.cpp o cwj.i 展開巨集,刪除空格字元,新增行號,去除所有的注釋 2.彙編 gcc s cwj.i o cwj.s 3.可執行 gcc c cwj.s o cwj.o as cwj.s o cwj.o as是彙編器 5.0掃瞄 1詞法分析 2語法分析...

《程式設計師自我修養》閱讀筆記 動態鏈結

1 動態鏈結的含義。動態鏈結就是將鏈結時的重定位推遲到載入時。相比於靜態鏈結,動態鏈結的乙個優點是可以節省記憶體。因為共享檔案的 可以共享。使用動態鏈結的時候,可執行檔案和共享檔案都會載入到記憶體。但是,如果很多可執行檔案都使用了同乙個共享檔案的時候,共享檔案的 部分只需要裝載一次,這樣就達到了節省...