程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 關於C語言靜態鏈接的個人理解,歡迎指正

關於C語言靜態鏈接的個人理解,歡迎指正

編輯:關於C語言

關於C語言靜態鏈接的個人理解,歡迎指正


摘要:本篇主要介紹在靜態鏈接中多個文件合並、地址確定、符號解析和重定位相關問題,以GCC編譯器為例。       首先,鏈接器鏈接多個文件時,采用何種方式合並為一個文件?方式一,按序疊加,即多個文件依次疊加起來;方式二,相似段合並。采用何種方式就要看哪種方式利大於弊。     方式一:這種方式實現簡單,鏈接速度快,基本不需要太多操作。但是,通常簡單的東西往往是粗暴的。我們知道gcc編譯後得到的可重定位目標文件是由各種段(section)組成的,這樣簡單疊加會產生大量零散的段,項目越大這樣的段越多,而且還是大量相同名稱的段。並且由於每個段都有地址和空間的對齊要求,這樣做勢必會浪費大量的內存空間(內部碎片)。所以這種方案並不好,可謂小甜頭換大痛苦。     方式二:這種方式是將相似段合並,比如將多個不同文件的.text段合並為一個大.text段,類似的.data、.bss等等也是如此。最終得到的文件,在段的數量和類型上與原來各個小文件沒有大的區別,只是每個段的大小變大了。當然這樣做實現細節上肯定要復雜,也會犧牲一定的速度。但是這種付出是值得的。     對應方式二這種方式鏈接器一般采用兩步鏈接,即分兩步走。 空間與地址分配:(1)掃描各個輸入文件獲得各個段的長度、屬性、位置等信息;(2)收集各個文件的符號表並建立統一的全局符號表。這一步將根據各個段的信息計算出合並後各個段的長度和位置,並建立映射關系(我的理解是更新段表的信息,段表描述ELF文件包含的所有段的信息,比如段名、長度、偏移量等)。 符號解析與重定位:這一步至關重要,由於上一步對相似段進行合並之後,原先的符號表信息已經過時,並且原先文件中代碼的地址並沒有映射到虛擬地址空間中,所以這一步要完成符號解析與重定位、調整代碼中的地址等。     下面是對上面第二步的進一步解析。     首先調整代碼位置這相對來說比較簡單也易於理解,以Linux為例,在Linux下32bit ELF可執行文件默認地址從0x08048000開始分配,根據合並後各個段的位置做相對移位即可。如有下面示例:     代碼b.c 1 int shared = 1; 2 void swap(int* a, int* b) 3 { 4    *a ^= *b ^= *a ^= *b; 5 }       代碼a.c 復制代碼 1 extern int shared; 2 int main(int argc, char** argv) 3 { 4    int a = 100; 5    swap(&a, &shared); 6    return 0; 7 } 8 ~   復制代碼       編譯後輸出a.o,b.o,cc -c a.c b.c,使用objdump查看a.o、b.o如下:               連接a.o b.o,的到可執行文件ab       可以看到a.o和b.o他們的起始地址都為0,而可執行文件ab的起始地址則是從0x0804000起(.text段之前還有文件頭)。       重點和難點在於符號解析和重定位,即要更新文件合並後的總全局符號表,在構建全局符號表時即完成符號解析,重定位需要在符號解析後完成。在目標文件的結構中有一種section叫重定位表,各個段中如果有需要重定位的符號那麼就會有相應的重定位表,如.text的重定位表是.rel.text。由於在a.c代碼中用到了shared和swap這兩個符號都屬於b.c文件中的定義,鏈接時需要重定位,所以a.o中的的text段就會有相應的重定位表。同樣用objdump可以查看目標文件的重定位表內容:           我們可以看到有兩行是關於需要重定位符號shared和swap的描述,其中OFFSET表示它們在a.o文件中的偏移值,TYPE表示重定位時對指令的修正方式,下面是書中對其解釋的相應的圖表:        

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved