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檔案必須在要引用它的檔案後面,否則會出錯)