1. GCC編譯/連接/優化等選項
- 1. GCC編譯/連接/優化等選項
- 1.1. 簡介
- 1.2. 常用選項
- 1.2.1.
-c -E -S -o
- 1.2.2.
-L<path> -l<library>
- 1.2.3.
-D<macro>
- 1.2.4.
-I<path>
- 1.2.1.
- 1.3. 代碼生成和優化
- 1.3.1.
-std=<standard>
- 1.3.2.
-shared
- 1.3.3.
-fPIC
- 1.3.4.
-static
- 1.3.5.
-O0 -O1 -O2 -O3 -Os
- 1.3.6.
-ffunction-sections
和-fdata-sections
- 1.3.1.
- 1.4. 調試與警告選項
- 1.4.1.
-g -ggdb
- 1.4.2.
-Wall -Werror -Wextra
- 1.4.3.
-Wno-pointer-sign
- 1.4.4.
-fno-strict-aliasing -fstrict-aliasing
- 1.4.1.
- 1.5. 安全與連接選項
- 1.5.1.
-z noexecstack -z relro -z now
- 1.5.2.
-Wl,option
- 1.5.3.
-Wl,--strip-all,--gc-sections,--as-needed
- 1.5.4.
-Wl,--verbose,-soname,<name>
- 1.5.5.
-Wl,-T,<script>,-Ttext,<address>,-Tdata,<address>
- 1.5.1.
- 1.6. 高級選項
- 1.6.1.
-pedantic
- 1.6.2.
-fsanitize=<type>
- 1.6.3.
-rpath <path>
- 1.6.1.
1.1. 簡介
GCC 編譯流程分為四個階段:預處理、編譯、匯編、鏈接,這里講解gcc用到的一些參數
-
GCC 原生選項:直接由 GCC 處理,如:
- -o(輸出文件名)
- -lm(鏈接數學庫 libm)
- -Wall(啟用警告)
- -O2(優化級別)
-
鏈接器專屬選項:需通過 -Wl, 傳遞給鏈接器(如 ld),如:
- –gc-sections(移除未使用的節)
- -soname(設置共享庫運行時名稱)
- -T(使用自定義鏈接腳本)
1.2. 常用選項
1.2.1. -c -E -S -o
-E 僅進行預處理,輸出預處理后的代碼。
gcc -E source.c -o source.i
-c 只進行編譯,不進行鏈接,生成目標文件(.o)
gcc -c source.c
# 生成 source.o
-S 生成匯編代碼(.s)。
gcc -S source.c
# 生成 source.s
-o 指定輸出文件的名稱
gcc source.c -o program
1.2.2. -L<path> -l<library>
-l 鏈接指定的庫(如-lm鏈接數學庫)。
-L 指定庫文件的搜索路徑。
gcc source.c -L/path/to/lib -lmylib
1.2.3. -D<macro>
定義宏(等價于在代碼中使用#define)。
1.2.4. -I<path>
指定頭文件的搜索路徑。
1.3. 代碼生成和優化
1.3.1. -std=<standard>
指定 C/C++ 標準(如-std=c99、-std=c++17)。
1.3.2. -shared
生成共享庫(.so)
1.3.3. -fPIC
生成位置無關代碼(用于共享庫)。
1.3.4. -static
靜態鏈接所有庫(生成獨立可執行文件)。
gcc -static source.c
1.3.5. -O0 -O1 -O2 -O3 -Os
選項 | 優化目標 | 編譯時間 | 代碼體積 | 運行性能 | 典型場景 |
---|---|---|---|---|---|
-O0 | 無優化(調試) | 最快 | 最大 | 最慢 | 開發調試 |
-O1 | 平衡性能與時間 | 較快 | 較小 | 一般 | 日常開發的 release 版本 |
-O2 | 優化性能 | 較長 | 中等 | 快 | 生產環境默認 |
-O3 | 最高性能 | 最長 | 最大 | 最快 | 高性能計算 |
-Os | 最小代碼體積 | 較長 | 最小 | 接近 | - O2 嵌入式系統、固件 |
1.3.6. -ffunction-sections
和 -fdata-sections
-ffunction-sections
:將每個函數放在獨立的 ELF 節(section)中。
-fdata-sections
:將每個全局變量放在獨立的節中。
目的:配合鏈接器選項 -Wl,--gc-sections
(垃圾回收),在最終二進制文件中移除未使用的函數和變量,減小文件體積。
1.4. 調試與警告選項
1.4.1. -g -ggdb
-g 生成調試信息,用于 GDB 調試。
-ggdb 生成更詳細的 GDB 調試信息。
一般配合 -O0 使用, 優化選項(如 -O2)可能會改變代碼結構,導致調試信息與實際執行不匹配。
選項 | 調試信息格式 | 適用調試器 | 文件大小 | 調試體驗 |
---|---|---|---|---|
-g | 標準 DWARF | 通用 | 較小 | 基本調試 |
-ggdb | GDB 擴展 | GDB 專用 | 較大 | 高級調試 |
1.4.2. -Wall -Werror -Wextra
-Wall 啟用常見的編譯警告(如未使用的變量、隱式轉換等)。
-Werror 將所有警告視為錯誤,強制修復警告。
-Wextra 啟用額外的警告(如未初始化的變量、冗余代碼等)。一般用的比較少
1.4.3. -Wno-pointer-sign
-Wno-pointer-sign 是 GCC 編譯器的一個警告選項,用于禁用關于指針符號不匹配的警告
禁用警告:通過 -Wno-pointer-sign 可以關閉這類警告,避免編譯時產生干擾信息。
相反選項:對應的啟用警告選項是 -Wpointer-sign,但通常通過 -Wall 或 -Wextra 自動啟用。
1.4.4. -fno-strict-aliasing -fstrict-aliasing
-fstrict-aliasing
(默認啟用):嚴格遵循別名規則,進行激進優化。
-fno-strict-aliasing
:禁用規則,允許不同類型指針別名,可能導致顯著的性能損失。
- 禁用 GCC 的嚴格別名規則優化,允許不同類型指針指向同一塊內存。
- 禁用優化會降低性能,建議
優先修改代碼以符合嚴格別名規則
(如使用 union)。 - 適用于遺留代碼或依賴類型雙關(type punning)的場景(如底層硬件訪問)。
int a = 1;
float* f = (float*)&a; // 違反規則
*f = 3.14f;
printf("%d\n", a); // 可能輸出 1(而非預期的修改后的值)
解決方案:
- 使用 union 實現類型雙關,確保類型兼容。
- 使用字符指針轉換
- 使用 memcpy
1.5. 安全與連接選項
1.5.1. -z noexecstack -z relro -z now
-z noexecstack
- 現代操作系統(如 Linux)支持內存區域的 NX 位(No-eXecute),通過該選項將棧標記為不可執行。
- 若攻擊者嘗試在棧上執行惡意代碼,會觸發內存訪問異常。
- 某些動態鏈接庫(如使用 setjmp/longjmp 的庫)可能需要可執行棧,此時需謹慎使用。
- 現代 Linux 發行版默認啟用 NX 保護,但通過 -z execstack 可禁用(不推薦)。
-z relro
(Relocation Read-Only)- 功能:啟用只讀重定位(Relocation Read-Only)。
- 防御機制:將全局偏移表(GOT,Global Offset Table)和動態符號表標記為只讀,防止攻擊者修改這些表中的函數地址。
-z now
- 功能:強制鏈接器立即綁定所有符號(而非延遲綁定)。
- 防御機制:減少動態加載時的符號表修改機會,增強 RELRO 的安全性。
1.5.2. -Wl,option
-Wl,option 是 GCC 編譯器用于將參數傳遞給鏈接器(如 ld)的語法。通過 , 分隔多個選項,例如 -Wl,–gc-sections,-Map=output.map
gcc source.c -Wl,-o,program.out # -o 選項,指定輸出文件名,等價于 gcc source.c -o program.out
gcc source.c -Wl,-e,my_entry_point # -e 選項,指定入口點,代替 main 函數,等價于 gcc source.c -emy_entry_point
gcc source.c -Wl,-L,/path/to/lib # -L 選項,指定庫搜索路徑,等價與 gcc source.c -L/path/to/lib
gcc source.c -Wl,-l,m # -l 選項,等價與 gcc source.c -lm,建議直接使用 -lm,語法更簡潔。
- 選項類別 必須使用 -Wl, 的典型選項
- 代碼優化 --gc-sections, --as-needed
- 內存布局 -T, -Ttext, -Tdata
- 共享庫 -soname, --version-script
- 安全增強 -z noexecstack, -z relro, -z now
- 調試 --verbose, --cref
1.5.3. -Wl,--strip-all,--gc-sections,--as-needed
gcc source.c -Wl,--strip-all,--gc-sections,--as-needed
gcc source.c -Wl,--wrap,malloc # 使用 __wrap_malloc 替代 malloc
--gc-sections
移除未使用的代碼和數據段(需配合 -ffunction-sections 和 -fdata-sections)。- 用于移除可執行文件中未使用的代碼和數據段(sections),從而減小二進制文件體積。
- 若某個節被標記為 USED(如通過 attribute((used))),則不會被回收。
- 啟用 GC 會增加鏈接時間(需分析所有符號),但通常對運行時性能無影響。
- 若二進制體積不是關鍵因素,可關閉此選項以加快編譯。
- -O2 會自動啟用一些內聯優化,但無法替代 --gc-sections 的全局代碼刪除能力。
- 驗證:使用 size 命令對比文件大小
--strip-all 或 --strip-debug
移除符號表和調試信息。--as-needed
僅鏈接真正使用的共享庫(減少依賴)。--wrap <symbol>
允許替換標準庫函數(用于測試或鉤子)。
1.5.4. -Wl,--verbose,-soname,<name>
gcc source.c -Wl,--verbose,-soname,libmylib.so.1
--verbose
輸出鏈接過程的詳細信息。
-soname <name>
設置共享庫的運行時名稱(用于版本控制)。
1.5.5. -Wl,-T,<script>,-Ttext,<address>,-Tdata,<address>
gcc source.c -Wl,-T,memory_layout.ld
gcc source.c -Wl,-Ttext,0x10000
gcc source.c -Wl,-Tdata,0x20000
-T <script>
使用自定義鏈接腳本(用于嵌入式系統或特殊內存布局)。
-Ttext <address>
指定代碼段(.text)的加載地址。
-Tdata <address>
指定數據段(.data)的加載地址。
1.6. 高級選項
1.6.1. -pedantic
嚴格遵循語言標準,拒絕非標準特性。
1.6.2. -fsanitize=<type>
啟用運行時檢查(如內存泄漏、未定義行為)。
1.6.3. -rpath <path>
設置運行時庫搜索路徑(替代 LD_LIBRARY_PATH)。
gcc source.c -Wl,-rpath,/path/to/lib