gcc編譯過程
以helloworld為例分析gcc編譯過程:
#include
int main()
{
printf(“Hello World\n”);
return 0;
}
通常我們使用gcc來生成可執行程式,命令為:gcc hello.c,生成可執行檔案a.out
實際上gcc hello.c可以分解為4個步驟,分別是預處理(Preprocess),編譯(Compilation),彙編(Assembly)和連結(Linking),如下圖所示:
1預編譯
gcc –E hello.c –o hello.i,以下為預處理後的輸出檔案hello.i的內容
# 1 "hello.c"
# 1 ""
# 1 ""
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
/*****省略了部分內容,包括stdio.h中的一些宣告及定義*****/
# 2 "hello.c" 2
int main()
{
printf("Hello World\n");
return 0;
}
預編譯過程主要處理那些原始碼中以#開始的預編譯指令,主要處理規則如下:
l將所有的#define刪除,並且展開所有的巨集定義;
l處理所有條件編譯指令,如#if,#ifdef等;
l處理#include預編譯指令,將被包含的檔案插入到該預編譯指令的位置。該過程遞迴進行,及被包含的檔案可能還包含其他檔案。
l刪除所有的註釋//和/**/;
l新增行號和檔案標識,如#2 “hello.c” 2,以便於編譯時編譯器產生除錯用的行號資訊及用於編譯時產生編譯錯誤或警告時能夠顯示行號資訊;
l保留所有的#pragma編譯器指令,因為編譯器須要使用它們;
2編譯
編譯過程就是把預處理完的檔案進行一系列詞法分析,語法分析,語義分析及優化後生成相應的彙編程式碼檔案。
gcc –S hello.i –o hello.s,以下為編譯後的輸出檔案hello.s的內容
.file"hello.c"
.section.rodata
.LC0:
.string"Hello World"
.text
.globl main
.typemain, @function
main:
pushl%ebp
movl%esp, %ebp
andl$-16, %esp
subl$16, %esp
movl$.LC0, (%esp)
callputs
movl$0, %eax
leave
ret
.sizemain, .-main
.ident"GCC: (GNU) 4.4.0 20090506 (Red Hat 4.4.0-4)"
.section.note.GNU-stack,"",@progbits
3彙編
彙編器是將彙編程式碼轉變成機器可以執行的命令,每一個彙編語句幾乎都對應一條機器指令。彙編相對於編譯過程比較簡單,根據彙編指令和機器指令的對照表一一翻譯即可。
gcc –c hello.c –o hello.o,由於hello.o的內容為機器碼,不能以文字形式方便的呈現。
4連結
連結器ld將各個目標檔案組裝在一起,解決符號依賴,庫依賴關係,並生成可執行檔案。
ld –static crt1.o crti.o crtbeginT.o hello.o –start-group –lgcc –lgcc_eh –lc-end-group crtend.o crtn.o (省略了檔案的路徑名)。