GCC編譯四步標準
來源:互聯網
上載者:User
標準 ANSI C:這一標準是 ANSI(美國國家標準局)於 1989 年制定的 C 語言標準。後來被 ISO(國際標準組織)接受為標準,因此也稱為 ISO C。 ANSI C 的目標是為各種作業系統上的 C 程式提供可移植性保證,而不僅僅限於 UNIX。 該標準不僅定義了 C 程式設計語言的語發和語義,而且還定義了一個標準庫。這個庫可以根據 標頭檔劃分為 15 個部分,其中包括:字元類型 (<ctype.h>)、錯誤碼 (<errno.h>)、 浮點常數 (<float.h>)、數學常數 (<math.h>)、標準定義 (<stddef.h>)、 標準 I/O (<stdio.h>)、工具函數 (<stdlib.h>)、字串操作 (<string.h>)、 時間和日期 (<time.h>)、可變參數表 (<stdarg.h>)、訊號 (<signal.h>)、 非局部跳轉 (<setjmp.h>)、本地資訊 (<local.h>)、程式斷言 (<assert.h>) 等等。 POSIX:該標準最初由 IEEE 開發的標準族,部分已經被 ISO 接受為國際標準。該標準的具體內容 見 1.1.3。POSIX.1 和 POSIX.2 分別定義了 POSIX 相容作業系統的 C 語言系統介面 以及 shell 和工具標準。這兩個標準是通常提到的標準。 SVID:System V 的介面描述。System V 介面描述(SVID)是描述 AT&&;T Unix System V 操作 系統的文檔,是對 POSIX 標準的擴充超集。 XPG:X/Open 可移植性指南。X/Open 可移植性指南(由 X/Open Company, Ltd.出版), 是比 POSIX 更為一般的標準。X/Open 擁有 Unix 的著作權,而 XPG 則指定成為 Unix 作業系統必須滿足的要求。 --------------------------------------- GCC是GNU CC的簡稱,它是符合上述ANSI C標準的編譯系統,能夠編譯C、C++、Object C等語言編寫的程式。GCC還是一個交叉平台編譯器,能夠在當前CPU平台為多種不同架構的硬體平台開發軟體,因此適合嵌入式領域的開發編譯。 GCC所支援的尾碼名的解釋 .c C原始程式 .C/.cc/.cxx C++原始程式 .m Object C原始程式 .i 已經過預先處理的C原始程式 .ii 已經過預先處理的C++原始程式 .s/.S 組合語言原始程式 .h 預先處理檔案(標頭檔) .o 目標檔案 .a/.so 編譯後的庫檔案 三、詳解GCC編譯流程 使用vi編寫源檔案hello.c。 #include <stdio.h> int main() { int i; for(i=1;i<9;i++) printf("Hello World %d times!\n",i); return 0; } 1、預先處理階段 該階段的作用是把預先處理檔案,也就是標頭檔編譯進來。在此例中,就是要把stdio.h編譯進來。可使用-E選項查看,作用是讓gcc在預先處理結束後停止編譯過程。 [armlinux@lqm program]$ gcc -E hello.c -o hello.i [armlinux@lqm program]$ cat hello.i | less # 1 "hello.c" # 1 "<built-in>" # 1 "<command line>" # 1 "hello.c" # 1 "/usr/include/stdio.h" 1 3 # 28 "/usr/include/stdio.h" 3 # 1 "/usr/include/features.h" 1 3 # 291 "/usr/include/features.h" 3 # 1 "/usr/include/sys/cdefs.h" 1 3 # 292 "/usr/include/features.h" 2 3 # 314 "/usr/include/features.h" 3 # 1 "/usr/include/gnu/stubs.h" 1 3 # 315 "/usr/include/features.h" 2 3 # 29 "/usr/include/stdio.h" 2 3 ... extern void funlockfile (FILE *__stream) ; # 679 "/usr/include/stdio.h" 3 # 2 "hello.c" 2 int main() { int i; for(i=1;i<9;i++) printf("Hello World %d times!\n",i); return 0; } 由此可見,stdio.h的內容已經插入到hello.c中,即GCC完成了預先處理過程。 2、編譯階段 GCC的工作是首先檢查代碼的規範性、是否有語法錯誤,以確定代碼實際要做的工作。檢查無誤後,將之翻譯為組合語言。可用-S來查看,即只編譯而不進入彙編階段。 [armlinux@lqm program]$ gcc -S hello.i -o hello.s [armlinux@lqm program]$ cat hello.s
.file "hello.c" .section .rodata .LC0: .string "Hello World %d times!\n" .text .globl main .type main,@function main: pushl %ebp movl %esp, %ebp subl $8, %esp andl $-16, %esp movl $0, %eax subl %eax, %esp movl $1, -4(%ebp) .L2: cmpl $8, -4(%ebp) jle .L5 jmp .L3 .L5: subl $8, %esp pushl -4(%ebp) pushl $.LC0 call printf addl $16, %esp leal -4(%ebp), %eax incl (%eax) jmp .L2 .L3: movl $0, %eax leave ret .Lfe1: .size main,.Lfe1-main .ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)" 3、彙編階段 GCC把編譯產生的.s檔案轉換為目標檔案.o。這時使用-c選項就可以看到彙編代碼已經轉換成.o的目標代碼了。 [armlinux@lqm program]$ gcc -c hello.s -o hello.o 4、連結階段 成功編譯之後,就進入了連結階段。這裡首先要明白“庫”的概念。這個程式中沒有“printf”的函數實現,且在預先處理階段包含進來的“stdio.h”中只有該函數的聲明,而沒有定義函數的實現。如何?“printf”。答案是:系統把這些函數的實現都做到名位libc.so.6的庫檔案裡了,沒有特別指定時,GCC會到預設的搜尋路徑“/usr/lib”下進行尋找。也就是連結到libc.so.6庫函數中去來實現函數“printf”,這就是連結的作用。 在RedHat 9下的函數庫如下: /lib:系統必備共用庫 /usr/lib:標準共用庫和靜態庫 /usr/X11R6/lib:X11R6的函數庫 /usr/local/lib:本地函數庫 標頭檔: /usr/include:系統標頭檔 /usr/local/include:本地標頭檔 在/etc/ld.so.conf中包含著共用庫的搜尋位置。我的libc.so.6在/lib下面,它是一個link,指向libc-2.3.2.so。這是因為我用的C庫--glibc版本是2.3.2。 函數庫一般可分為靜態庫和動態庫兩種。靜態庫是指連結時把庫檔案的代碼全部加到可執行檔中,因此產生的檔案比較大,但是在運行時就不再需要庫檔案了。其尾碼名一般為“.a”。動態庫在連結時並沒有把庫檔案的代碼加入到可執行檔中,而是在程式執行時由運行時連結檔案載入庫,這樣可以節省系統的開銷。動態庫一般尾碼名為“.so”。GCC在編譯時間預設使用動態庫。 [armlinux@lqm program]$ gcc hello.o -o hello1 [armlinux@lqm program]$ ls -l hello1 -rwxrwxr-x 1 armlinux armlinux 11582 8月 28 17:42 hello1 [armlinux@lqm program]$ file hello1 hello1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not sd [armlinux@lqm program]$ gcc -static hello.o -o hello2 [armlinux@lqm program]$ ls -l hello2 -rwxrwxr-x 1 armlinux armlinux 423442 8月 28 17:43 hello2 [armlinux@lqm program]$ file hello2 hello2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, statically linked, not stripped [armlinux@lqm program]$ ./hello1 Hello World 1 times! Hello World 2 times! Hello World 3 times! Hello World 4 times! Hello World 5 times! Hello World 6 times! Hello World 7 times! Hello World 8 times! [armlinux@lqm program]$ ./hello2 Hello World 1 times! Hello World 2 times! Hello World 3 times! Hello World 4 times! Hello World 5 times! Hello World 6 times! Hello World 7 times! Hello World 8 times! hello2是靜態編譯,大小423442,是動態編譯的36.56倍。利用file可以看出此檔案statically linked,還是dynamically linked (uses shared libs)。 至此,GCC的整個編譯連結過程就完成了。