The GNU Compiler Collection,一般簡稱GCC,是一套由GNU開發的編譯器集。爲何會是一個集合,由於它不只支持C語言編譯,還支持C++, Ada, bjective C等語言。另外GCC支持的硬件平臺包括:X86處理器架構,ARM架構,MIPS架構。linux
GCC內部由Binutils, Gcc-core, Glibc等軟件包組成。shell
1)Binutils:它是一組開發工具,包括鏈接器,彙編器和其餘用於目標文件和檔案的工具。這個軟件包會依不一樣的平臺而不一樣,由於不一樣的架構指令集不一樣。sass
2)Gcc-core:GCC的核心部分,可是這部分默認只包含C的編譯器及其公共部分,而對其餘語言(C++, Ada等)的支持包須要另外安裝。架構
3)Glibc:包含經常使用到的一些C的函數庫,這個庫提供基本的函數,用於分配內存,搜素目錄,讀寫文件,字符串處理等。函數
對GNU編譯器來講,程序的翻譯要通過:預處理,彙編,編譯,連接四個步驟。GCC常常把前三個合爲一個步驟來操做,因此就有了編譯模式(-c指定)和編譯連接模式(-o指定輸出)兩種方式。工具
預處理階段主要處理.h之類的頭文件,#include,#ifdef,#define等。生成文件.i。開發工具
彙編階段主要將.i的中間文件轉換爲彙編語言文件.s優化
gnu也有專門的彙編工具,as.net
編譯階段主要將彙編語言文件.s轉換爲2進制機器語言.o命令行
連接階段主要是將不一樣的機器語言文件連接到一塊兒,生成最終的可執行文件。
gnu也有專門的連接工具,ld
ar 庫文件操做命令,能夠查看庫文件中的詳細狀況,將多個對象問價生成一個庫文件。
ar -t libname.a 顯示全部對象文件的列表;
ar -rv libname.a objfile1.o .... objfile10.o 將objfile1.o ... objfile10.o 打包成一個庫文件。
在linux下,庫文件,都以lib開頭,不然gcc沒法識別。
GCC的基本用法:假設源文件程序名是test.c
-Wall,gcc選項,表示打開全部經常使用的警告。
1)無選項編譯連接:gcc test.c 將test.c預處理,彙編,編譯,連接成可執行文件,未指定輸出文件,默認輸出a.out。
2)-o指定輸出文件:gcc test.c 將test.c預處理,彙編,編譯,連接成可執行文件,指定輸出文件,test.out。
3)-E只進行預處理:gcc -E test.c -o test.i 將test.c進行預處理操做,生成test.i文件。生成文件後綴.i,只是簡單的將define和include展開;
4)-S只進行彙編:gcc -S test.i -o test.s 將test.i進行彙編操做,生成test.s文件。
5)-c只進行編譯:gcc -c test.s 將test.s進行編譯操做,生成test.o文件。
6)對編譯後的文件進行無選項操做:gcc test.o -o test 將test.o連接生成可執行文件test。
7)-fPIC ,產生位置無關的目標代碼;
8)-L dir,將dir加到編譯器,進行連接的搜索列表中;
-lname,連接名爲libname.a,libname.so的庫文件。
9) -g,用在debug調試。
10) -Wl, 表示將後面的參數,傳遞給連接器。
11) --whole-achive --no-whole-archive,是ld的專用命令行參數,gcc並不認識,只能經過加Wl,來傳遞給gcc。
將在其以後的全部靜態庫包括的函數和變量輸出給動態庫,--no-whole-achive,表示關掉這個特性。
--whole-achive做用IA的庫中不能有函數重名。
12) -shared,表示生成動態庫文件,進行動態編譯時,儘可能使用動態庫。
13) -static,表示儘可能使用靜態庫。
多個文件處理:
gcc test1.c test2.c -o test 將test1和test2分別預處理,彙編。編譯後連接爲test可執行文件輸出。
gcc test1.o test2.o -o test 將test1編譯後文件和test2編譯後文件,連接爲test可執行文件輸出。
gcc -o輸出以後的文件,能夠直接在shell中運行。
gcc -I +dir:來指明.h文件的路徑,而後在C的.h文件中,能夠直接包含該路徑中的.h文件,不須要寫入路徑。
-D +define:來指明gcc編譯過程當中,添加的define(與ifdef...配套使用)
gcc在編譯的過程當中,會對代碼進行多方面的優化,主要有這幾類:
1) 精簡操做指令,
2) 儘可能知足CPU的流水線操做,
3) 從新調整代碼的執行順序,
4) 函數的展開調度,
gcc提供了o0-o3的幾種優化級別供用戶設置,
-O0:不作優化,默認的編譯選項,
-O1:對程序作部分優化,gcc嘗試減少生成代碼的尺寸,縮短執行時間,
-O2:gcc將執行幾乎全部的空間時間的優化,具體的優化項目:https://blog.csdn.net/qq_31108501/article/details/51842166
-O3:再次打開一些優化選項,
-Os:主要是針對程序的尺寸的優化。
優化代碼帶來的兩個問題:
1) 調試問題,由於不少分支的合併,公用表達式的消除,等優化
2) 內存操做順序的影響,能夠經過選項關閉,asm __ __violatile()。
Linux下的gdb和gcc默認輸出的彙編格式都是AT&T格式,可是他們都有方式來轉換爲intel格式;
-masm=[intel|att] 選擇intel或者att的彙編語法
gcc -S -masm=intel test.c
gdb則是設置環境變量, set disassembly-flavor intel
對於預處理宏, -Dmacro=string,等價於在頭文件中加定義,#define macro string
-Dmacro,等價於在頭文件中加定義,#define macro 1或者#define macro
-D中能夠加空格,也能夠不加空格。
gcc取消預約義,使用-U來作,能夠中間加空格,也能夠不加空格。
交叉編譯:在一種機器結構下編譯的軟件將在另外一種徹底不一樣的機器結構下執行。一個常見的例子是在PC機上編譯運行在ARM,MIPS上的軟件。因爲GCC能夠在命令行顯式調用編譯器,從而適合交叉編譯。 arm-linux-gcc -o test.c。
arm-linux-gcc:基於ARM目標機的交叉編譯軟件,跟GCC所需的安裝包不一樣,X86和ARM的指令集不一樣,因此Binutils不同,gcc-core依賴於Binutils,因此gcc-core也不相同,glibc庫不一樣。
arm-elf-gcc:也是基於ARM目標機的交叉編譯軟件。二者區別主要在glibc的不一樣。arm-linux-gcc使用GNU的glibc,而arm-elf-gc通常使用uClibc等專門爲嵌入式系統開發的C庫。Glibc針對PC開發,uClibc小型C語言庫,實現Glibc的部分功能。
彙編中的幾個字符:
.text 部分表示該部分,須要放在代碼段中;是arm_gcc的編譯關鍵字;(通常會在以後設置爲空間只讀)
.data 部分表示該部分,已初始化的全局變量的一塊內存區域,須要放在數據段中;(屬於靜態內存分配)
.bss 部分表示該部分,未初始化的全局變量的一塊內存區域,(屬於靜態內存分配)
.rodata 部分表示該部分,存放C中的字符串和#define定義的變量;
.heap堆 進程動態運行中被動態分配的內存段,大小不固定,malloc,free來進行分配與回收;
.stack棧 存放程序臨時的局部變量,函數被調用時,參數也會被壓入該棧中。
.global 是一個全局變量,能夠實現彙編和C之間傳遞信息,能夠是main函數的函數地址;
.global _start
_start:
.......
常見的gnu的僞指令:
.global _start @給_start外部連接屬性
.section
咱們經常須要將一些公用函數製做成函數庫,供其餘函數使用,函數庫分爲靜態庫和動態庫,
靜態庫,在程序編譯是,被連接到目標代碼中,程序運行時,不須要該庫,
動態庫,在程序運行時,直接載入,程序運行時,還須要動態庫存在。
生成靜態庫: gcc -c hello.c
ar rv libmyhello.a hello.o
生成可執行文件時,gcc main.c libmyhello.a -o main
生成動態庫,有三種方法,ld -G
gcc -shared
libtool
使用ld命令最複雜,gcc -shared最簡單,可是並非在任何平臺下均可以使用,因此GNU提供了一個更好的工具,libtool。
使用gcc -shared來生成,gcc -shared -o libmyjob.so myjob.o 將myjob.o生成動態庫。
file命令:查看linux下文件格式;
size命令:查看linux下elf文件的各個段大小;
nm命令:查看linux下elf文件的符號表;
strip命令:用來去掉ELF文件中的調試信息;
ldd命令:用來查看一個程序主模塊或者共享庫的依賴;
linux下默認的連接腳本在/usr/lib/ldscripts/,可使用ld --verbose來查看;
objdump -x example.o:詳細顯示目標文件的內容;
-h example.o:顯示目標文件的各個段的信息打印;
-s example.o:將全部段的內容以16進制的方式打印;
-d example.o:將全部包含指令的段反彙編;
-r example.o:顯示目標文件中的重定位表;
readelf -h example.o:顯示elf文件頭信息(格式定義在/usr/include/elf.h);32bit的elf格式頭文件佔52byte,64bit的elf格式頭文件佔64byte。
-S example.o:顯示elf文件的head頭文件(比objdump顯示的符號表要全)(格式定義在/usr/include/elf.h);
-s example.o:顯示elf文件中的符號表信息;
objcopy,將二進制文件copy到目標文件中的某個段,或者將某個目標文件的一部分copy到另外一個目標文件中。
objcopy -I binary -O elf32-i386 -B i386 image.jpg images.o
gcc提供方法,將某些變量和函數能夠存放在指定的段中去:
__attribute__((section("Foo"))) int global = 42;
指定弱符號和強符號:
__attribute__(("Weak")) weak2 = 2;
指定強引用,弱引用:
__attribute__(("weakref")) void foo();
未初始化的全局變量通常當作弱符號來處理,放在common塊中,也能夠顯示聲明不放在common塊中,此時做爲強符號處理;
int global __attribute__((nocommon)) a;