關於連接的一些總結

庫是一種可執行代碼的二進制形式,能夠被操做系統載入內存執行。庫有兩種:靜態庫(.a、.lib)和動態庫(.so、.dll)。linux

a. 靜態庫 
  之因此稱爲【靜態庫】,是由於在連接階段,會將彙編生成的目標文件.o與引用到的庫一塊兒連接打包到可執行文件中。所以對應的連接方式稱爲靜態連接。首先,靜態庫對函數庫的連接是放在編譯時期完成的。其次,程序在運行時與函數庫再無瓜葛,移植方便。靜態庫也有缺點以下圖所示,形成空間內存浪費; bash

b. 動態庫
  動態庫在程序編譯時並不會被鏈接到目標代碼中,而是在程序運行是才被載入。不一樣的應用程序若是調用相同的庫,那麼在內存裏只須要有一份該共享庫的實例,規避了空間浪費問題。動態庫在程序運行時才被載入,也解決了靜態庫對程序的更新、部署和發佈頁會帶來麻煩。用戶只須要更新動態庫便可,增量更新。app

程序的編譯過程

   假設如今有以下兩個c程序文件,sum.c內容以下:函數

int sum(int *a, int n){
	int i, s = 0;
	for( i = 0; i < n ; i++){
		s += a[i];
	}
	return s;
}

  主程序main.c的內容以下:工具

int sum(int *a,int n);

int array[2]  = {1, 2};

int main(){
	int val = sum(array,2 );
	return val;
}

  在linux上執行以下語句:操作系統

gcc -Og -o prog main.c sum.c

  以main.c爲例,程序首先調用C預處理器,將main.c翻譯成一個Ascii碼的中間件,mian.i,接下來將main.i翻譯成一個彙編語言文件main.s,而後調用匯編器將main.s翻譯可重定位的目標文件main.o,同理會生成相應的sum.o文件。最後,運行連接器程序ld,將main.o和sum.o以及一些必要的文件組合起來,建立一個可執行文件prog。.net

  在構造可執行文件的時候,連接器要完成兩個任務:翻譯

  一、符號解析,符號解析的目的是將每一個全局符號引用與符號定義關聯起來,便可重定位的目標文件定義了符號,而可執行的目標文件引用符號,將二者的符號相互綁定,這裏的符號對應一個函數、一個全局變量、或者一個靜態變量。中間件

  二、重定位,編譯器和彙編器生成從0開始的的代碼和數據節,連接器將符號定義和內存的位置關聯起來來重定位代碼和數據節,而後修改全部這些對符號的引用,使得它們指向內存的位置。對象

一些基礎知識

  目標文件有三種類型:

    可重定位的目標文件:包含二進制數據和代碼,其形式能夠在編譯時與其餘可重定位合併起來,建立一個可執行的目標文件

    可執行的目標文件:包含二進制和代碼,能夠直接複製到內存並執行

    共享目標文件:一類特殊的可重定位目標文件,能夠在加載或者運行時被動態的加載到內存執行。

  一個典型的ELF(可執行、可連接格式)的可重定位目標文件包含幾個重要的部分,以下圖:

 

  .text 已編譯程序的機器代碼

  .data 已初始化的全局和靜態C變量,局部C變量的值保存在棧中。

  .bss 未初始化的全局和靜態C變量,以及全部被初始化爲0的靜態或全局變量。

  .sysmtab 一個符號表,存放程序中定義和引用的函數以及全局或靜態變量,不包含局部變量的信息。

 

連接器實際上會處理三種不一樣的符號,對應於代碼中不一樣寫法的部分:
全局符號 Global symbols
在當前模塊中定義,且能夠被其餘代碼引用的符號,例如非靜態 C 函數和非靜態全局變量。
外部符號 External symbols 
一樣是全局符號,可是是在其餘模塊(也就是其餘的源代碼)中定義的,可是能夠在當前模塊中引用。
本地符號 Local symbols
在當前模塊中定義,只能被當前模塊引用的符號,例如靜態函數和靜態全局變量。
注意,Local linker symbol 並非 local program variables

連接過程

第一步 符號解析 Symbol resolution 
  符號解析會將每一個符號引用恰好和一個符號定義聯繫起來。彙編器生成可重定位目標文件後,內部符號都已被正確地符號解析, 外部符號可能會引用了非本模塊的符號定義,彙編器沒法找到符號定義,所以沒法解析。 彙編器把外部符號放入」符號表「.symtab,同時把如何解析該符號的方法放入」重定位表「。連接器只知道非靜態的全局變量/函數,而對於局部變量一無所知,局部非靜態變量和局部靜態變量的區別:

局部非靜態變量會保存在棧中
局部靜態變量會保存在 .bss 或 .data 中

連接器符號解析時會用到符號表:

強符號與弱符號: 函數和初始化的全局變量叫強符號, 未初始化的全局變量叫弱符號。(extern int a是一個弱符號定義, int a 也是弱符號)

符號解析規則:

 1.(定義多個強符號) 當引用符號時,該符號的符號定義有不止一個強符號定義時,會出現符號重定義錯誤。
 2.(定義一個強符號和一個或者多個弱符號)當引用符號時, 該符號的符號定義有個強符號定義和一個和多個弱符號定義, 使用強符號定義。
 3.(定義多個弱符號)當引用符號時, 該符號的符號定義都是弱符號時, 選擇任意一個定義。

第二步 重定位 Relocation 

就是把不一樣可重定位對象文件拼成可執行對象文件,有三步,以下:

1.合併可重定位目標文件中相同的節。
2.重定位節和符號定義,修改符號表。爲節和符號定義分配虛擬地址。修改符號表中符號定義的值爲剛分配的虛擬地址。
3.重定位節中的符號引用,修改代碼段和數據段符號引用。使用重定位表.rel.text .rel.data, 修改text,data中符號引用的地址。

例以下圖,多個可重定位對象文件合併成可執行文件的過程所示:

 

對重定位的理解,在重定位,有兩個步驟。

  首先重定位節和符號定義,連接器將不一樣目標文件全部相同類型的節合併到同一個新的聚合節中,例如,來自全部輸入模塊的.data節會被合併到同一個節中。而後連接器將運行時內存的地址賦給新的聚合節,賦給輸入模塊中定義的那個節和其中定義的每一個符號。此時,程序中的每條指令和全局變量都有一個惟一的運行時內存了。

  其次是重定位符號的引用,修改數據節和代碼節中符號的引用,使得他們指向正確的運行時內存地址。

 

處理目標文件的一些工具,以下:

AR :建立靜態庫,插入、刪除、列出和提取成員。

NM :列出一個目標文件的符號表中定義的符號。

LDD :列出一個可執行程序文件在運行時所需的共享庫。

 

 

收藏:不周山之讀薄 CSAPP

符號及符號解析

參考:《深刻理解計算機系統》

相關文章
相關標籤/搜索