❶ Gcc 和 Clang
GCC 編譯器作為 linux 系統下的主要 C/C++ 編譯工具,廣泛安裝於多數 Linux 發行版中。其命令形式通常為「gcc」,並提供了豐富的選項來輔助編譯過程。其中,常用選項包括:-E 僅執行預處理,-c 編譯或匯編源文件但不執行鏈接,-S 完成編譯但不匯編,僅生成匯編代碼,-o 用於指定輸出文件名。在 Linux 系統中,未指定輸出文件名時,默認輸出名為「a.out」,源文件後綴生成為「.o」,匯編文件後綴為「.s」。GCC支持多種環境的代碼生成,如使用-m32、-m64、-m16選項生成不同位數環境的代碼,例如,-m32下int、long和指針類型均為32位,-m64下int為32位,long和指針類型為64位,-m16與-m32類似,但在匯編文件開頭添加了gcc匯編制導,用於運行16位模式的二進制文件。
編譯過程主要分為預處理、編譯、匯編和鏈接四個階段。下面以一段源碼為例,詳細分析每個階段的內容。
首先,預處理過程會展開宏定義和條件編譯,生成預處理文件。使用cpp命令執行預處理,得到的sample.i文件中,宏定義和條件編譯已根據實際情況展開,宏引用被替換為實際值。通過-D指令可以自定義宏的值,進行預處理。在Linux系統下,通過「man gcc」可查詢GCC命令的詳細用法。
接著,GCC將預處理文件編譯為匯編代碼,生成匯編文件。匯編文件包含了核心的匯編代碼,展示了編譯過程中的匯編指令和數據操作。對比32位機器和64位機器匯編代碼的差異,可以發現主要在於寄存器的位寬和指令的位寬不同。
匯編代碼中,.cfi_startproc和.cfi_endproc用於初始化和結束本地數據結構,本地標簽用於分支目的地標記。基本匯編指令如pushl、movl、subl、cmpl、je、addl、sall、ret、movl等,分別用於操作寄存器、存儲數據、進行算術運算和邏輯運算、控制流程等。了解這些基本指令的用途有助於深入理解程序的執行流程。
使用GCC的-c選項編譯源代碼為機器代碼,通過-o選項指定輸出文件名。可以使用as命令得到機器語言,通過objmp指令查看目標文件的機器碼,反匯編指令幫助理解機器碼的含義。在程序中發現符號定義沖突時,可以使用nm命令列出目標文件中的符號,快速定位問題。
最後,鏈接器(ld)將編譯生成的目標文件鏈接為可執行文件。鏈接過程中,鏈接器解析未定義的符號引用,將目標文件中的佔位符替換為實際的符號地址。如果缺少必要的CRT文件,ld會生成警告。可通過查詢/usr/lib/x86_64-linux-gnu路徑找到CRT文件。C運行時文件(CRT)包含程序入口函數_start,負責調用__libc_start_main初始化libc,並調用main函數;_init函數在main函數前運行;_fini函數在main函數後運行。鏈接時使用-lc選項鏈接C標准庫。
Clang 是一個基於LLVM的C/C++編譯器,提供C/C++/Objective C/Objective C++語言的編譯支持,旨在超越GCC。Clang預處理、生成匯編代碼、生成目標文件、得到可執行文件的過程與GCC類似,但Clang提供了更多的特性,如更快的編譯速度、更好的錯誤診斷和更先進的類型推斷能力。使用Clang替代GCC進行C程序編譯時,可以體驗到這些額外的優勢。
Clang編譯過程包含預處理、生成32位和64位機器匯編代碼、生成目標文件和得到可執行文件等步驟。使用Clang編譯後的匯編代碼、目標文件和可執行文件與GCC編譯結果一致,但Clang在性能和語言支持方面可能具有優勢。
❷ GCC安全編譯選項
各種安全選擇的編譯參數如下:
NX:-z execstack / -z noexecstack (關閉 / 開啟)
Canary:-fno-stack-protector /-fstack-protector / -fstack-protector-all (關閉 / 開啟 / 全開啟)
PIE:-no-pie / -pie (關閉 / 開啟)
RELRO:-z norelro / -z lazy / -z now (關閉 / 部分開啟 / 完全開啟)