這一部分一直很好奇,不過一直也沒仔細記。這次記一下。
源代碼 --(預處理、編譯、優化)–> 彙編代碼 --(彙編)–> 目標文件 --(鏈接)–> 可執行文件
預處理
讀取源程序,對其中的僞指令(以#開頭的指令)和特殊符號進行處理。
主要包括:宏定義,條件編譯指令,頭文件包含指令,特殊符號(如LINE,FILE)。
通常使用以下命令來進行預處理:
$ gcc -E hello.c -o hello.i
參數-E表示只進行預處理 或者也可以使用以下指令完成預處理過程
$ cpp hello.c > hello.i
編譯
編譯階段就是把預處理後的文件進行一系列的詞法分析,語法分析,語義分析及優化後生成相應的彙編代碼。
$ gcc –S hello.i –o hello.s
或者
$ /usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c
優化
優化一部分是對中間代碼的優化。這種優化不依賴於具體的計算機。主要的工作是刪除公共表達式、循環優化(代碼外提、強度削弱、變換循環控制條件、已知量的合併等)、複寫傳播,以及無用賦值的刪除,等等。
另一部分優化則主要針對目標代碼的生成而進行的,同機器的硬件結構密切相關,最主要的是考慮是如何充分利用機器的各個硬件寄存器存放的有關變量的值,以減少對於內存的訪問次數。另外,如何根據機器硬件執行指令的特點(如流水線、RISC、CISC、VLIW等)而對指令進行一些調整使目標代碼比較短,執行的效率比較高。
彙編
彙編過程就是把彙編語言翻譯成機器碼。
$ gcc –c hello.c –o hello.o
或者
$ as hello.s –o hello.co
鏈接
由彙編程序生成的目標文件並不能立即就被執行,其中可能還有許多沒有解決的問題。例如,某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變量或者函數調用等);在程序中可能調用了某個庫文件中的函數,等等。所有的這些問題,都需要經鏈接程序的處理方能得以解決。
通過調用鏈接器ld來鏈接程序運行需要的一大堆目標文件,以及所依賴的其它庫文件,最後生成可執行文件。
ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o (省略了文件的路徑名)。
根據開發人員指定的同庫函數的鏈接方式的不同,鏈接處理可分爲兩種:
- 靜態鏈接庫
庫中函數將從其所在的靜態鏈接庫中被拷貝到最終的可執行程序中。靜態鏈接庫實際上是一個目標文件的合集,其中的每個文件含有庫中的一個或者一組相關函數的代碼。 - 動態鏈接庫
函數代碼放在動態鏈接庫或者共享對象的某個目標文件中。而在生成的程序中只加入一些描述信息。程序執行時,再從系統中把相應的動態庫加載到內存中。