天天看點

連結的一些基礎了解

7.1 編譯器驅動程式

1. 一般調用預處理,編譯器,彙編器,連結器來完成一個程式的驅動

1.首先運作C預處理器cpp,将C源程式main.c翻譯成一個ASCII碼的中間檔案main.i。

2.接下來,C編譯器cc1将main.i翻譯成一個ASCII彙編語言檔案main.s。

3.最後,彙編器as将main.s翻譯成一個可重定位目标檔案main.o。

4.類似地,生成其他.o檔案。最後,運作連結器程式ld,将main.o和其他.o以及一些必要的系統目标檔案組合起來,建立一個可執行目标檔案。

7.2 靜态連結

在以上的這個過程中ld連結器的主要工作:

① 符号解析。目标檔案定義和引用符号,符号解析的目的是将每個符号引用和一個符号定義聯系起來。

(符号:一個函數,一個全局變量,一個靜态變量)

②重定位:把每個符号定義與一個存儲器位置聯系起來,然後修改對這些符号的引用,是的他們指向這個存儲器位置,進而實作重定位。

目标檔案是位元組塊的集合,有的包含代碼,有的包含程式資料,其他則是包含引導連結器和加載器的資料結構。

7.3 目标檔案

目标檔案共有三種形式:

1.可重定位目标檔案。包含二進制代碼和資料,其可以在編譯時與其他可重定位目标檔案合并起來,建立一個可執行目标檔案。(.o檔案)

2.可重定位目标檔案:包含二進制代碼和資料,在運作的時候可以将和其他可重定位目标檔案合并變成可執行檔案。(類似a.out)

3.共享目标檔案。一種特殊類型的可重定位目标檔案,可以在加載或者運作時被動态地加載到存儲器并連結。(.so檔案也稱庫檔案)

編譯器和彙編器生成可重定位目标檔案(包括共享目标檔案),連結器生成可執行目标檔案。一般讨論可執行可連結格式(ELF)

7.4 可重定位目标檔案

1.ELF的格式:

連結的一些基礎了解

.text:已編譯程式的機器代碼。

.rodata:制隻讀資料,比如printf語句中格式串和開關語句的跳轉表。

.data:已初始化的全局和靜态C變量。局部C變量在運作時被儲存在棧中,既不出現在.data節中,也不在.bss節中。

.bss:未初始化的全局和靜态C變量,以及所有被初始化為0的全局或靜态變量。

.symtab:符号表,存放在程式中定義和引用的函數和全局變量的資訊。

.rel.text:一個.text節中位置的清單,當連結器把這個目标檔案和其他檔案組合時,需要修改這些位置。

.rel.data:被子產品引用或定義的全局變量的重定位資訊。

.debug:一個調試符号表,包含程式中定義的局部變量和類型定義,程式中定義和引用的全局變量,以及原始的C源檔案。

.line:原始C源程式中行号和.text節中機器指令之間的映射。

.strtab:一個字元串表,包含.symtab和.debug節中的符号表,以及節頭部中的節名字。

這部分知識點看其他的文章摘下來的。

7.5 符号和符号表

每個可重定位目标子產品都有一個符号表,它包含m所定義和引用的符号的資訊。在連結器的上下文中,有三種不同的符号:

有m定義且能被其他子產品引用的全局符号:全局連結器符号對應于非靜态的C函數以及被定義為不帶C static屬性的全局變量

由其他子產品定義并被子產品m引用的全局符号:稱之為外部符号(external),對應于定義在其他子產品中的C函數和變量。

隻被子產品m定義和引用的本地符号:這些符号在m中随處可見但不能被其他子產品引用。

符号表是由彙編器構造的,使用編譯器輸出到彙編語言.s檔案中的符号。

其中.symtab節包含ELF符号表,這張符号表包含一個條目的數組,也可以說是一個結構體數組。

7.6 符号解析

對多重定義的全局符号的解析:

當我們遇到多重定義的全局符号怎麼辦呢?首先全局符号被分為強或弱,這個資訊包含在符号表中,函數和已初始化的全局變量是強符号,未初始化的全局變量是弱符号。根據強弱符号的定義,Unix連結器使用下面的規則來處理多重定義的符号:

規則1:不允許有多個強符号。

規則2:如果有一個強符号和多個弱符号,那麼選擇強符号。

規則3:如果有多個弱符号,那麼從這些弱符号中任意選擇一個。

如果連結的程式定義了多個強符号,則會報錯。但是規則2和3會導緻一些不易察覺的錯誤:

連結的一些基礎了解

函數f将x的值由15213改為15212,這會給main函數的作者帶來意外。

靜态庫:

編譯系統提供一種機制,将所有相關的目标子產品打包成為一個單獨的檔案,稱為靜态庫,其檔案字尾為.a,裡面包含多個.o檔案。它可以用做連結器的輸人,當連結器構造一個輸出的可執行檔案時,它隻拷貝靜态庫裡被應用程式引用的目标子產品。

使用靜态庫可以減少可執行檔案在磁盤和記憶體中的大小。

連結的一些基礎了解

使用ar工具ar rcs .a .o .o 建立靜态庫

使用gcc -static -o prog(自己起名) .o ./.a 連結靜态庫,(注意,.a檔案必須在要引用它的檔案後面,否則會出錯)