同一個編譯腳本下 arm 交叉編譯 生成文件大小不一致的問題分析

最近在工作中需要編譯一個庫文件,使用gcc的交叉編譯環境。遇到了一個令人迷惑的問題:同樣的編譯環境,同樣的代碼,只是因爲在硬盤上存放的位置不同,編譯出來的.o文件大小就會產生差異。此刻,終於搞明白了原因。記錄一下。
背景知識:先了解下gcc編譯選項:(以下內容均載自網絡,有的翻譯可能有點拗口,湊合看吧)
參考鏈接https://www.cnblogs.com/lsgxeva/p/7605141.html
GCC的-g選項用於配置GDB調試級別
如果不打開-g或者-ggdb(GDB專用)調試開關,GCC編譯時不會加入調試信息,因爲這會增大生成代碼的體積。GCC採用了分級調試,通過在-g選項
後附加數字1、2或3來指定在代碼中加入調試信息量。默認的級別是2(-g2),此時調試信息包括擴展的符號表、行號、局部或外部變量信息。
級別3(-g3)包含級別2中的調試信息和源代碼中定義的宏。
級別1(-g1)不包含局部變量和與行號有關的調試信息,只能用於回溯跟蹤和堆棧轉儲之用。
回溯跟蹤指的是監視程序在運行過程中的函數調用歷史,堆棧轉儲則是一種以原始的十六進制格式保存程序執行環境的方法,兩者都是經常用到的調試手段。

順別學習下GCC的-O選項,這是用來優化代碼的(優化指的是速度和體積)
-O0:這個等級(字母“O”後面跟個零)關閉所有優化選項,也是CFLAGS或CXXFLAGS中沒有設置-O等級時的默認等級。這樣就不會優化代碼,這通常不是我們想要的。
-O1:這是最基本的優化等級。編譯器會在不花費太多編譯時間的同時試圖生成更快更小的代碼。這些優化是非常基礎的,但一般這些任務肯定能順利完成。
-O2:-O1的進階。這是推薦的優化等級,除非你有特殊的需求。-O2會比-O1啓用多一些標記。設置了-O2後,編譯器會試圖提高代碼性能而不會增大體積和大量佔用的編譯時間。
-O3:這是最高最危險的優化等級。用這個選項會延長編譯代碼的時間,並且在使用gcc4.x的系統裏不應全局啓用。自從3.x版本以來gcc的行爲已經有了極大地改變。在3.x,-O3生成的代碼也只是比-O2快一點點而已,而gcc4.x中還未必更快。用-O3來編譯所有的軟件包將產生更大體積更耗內存的二進制文件,大大增加編譯失敗的機會或不可預知的程序行爲(包括錯誤)。這樣做將得不償失,記住過猶不及。在gcc 4.x.中使用-O3是不推薦的。
-Os:這個等級用來優化代碼尺寸。其中啓用了-O2中不會增加磁盤空間佔用的代碼生成選項。這對於磁盤空間極其緊張或者CPU緩存較小的機器非常有用。但也可能產生些許問題,因此軟件樹中的大部分ebuild都過濾掉這個等級的優化。使用-Os是不推薦的。

GCC提供選項控制代碼的優化等級:
這些選項可以實現不同程度的優化。
沒有任何優化選項的話,編譯時的目標是降低編譯成本,並使調試產生預期的結果。語句是獨立的:如果你使程序停止在語句之間設置的斷點處,你可以改變任何變量的值或者改變程序計數器的值使程序執行任意函數中的語句並得到源代碼中期望的結果。
打開優化標誌使編譯器嘗試以編譯時間和可能的調試程序的能力爲代價來改進性能和/或代碼大小。
編譯器根據程序的信息執行優化。 一次編譯多個文件生成單個可執行文件的模式允許編譯器在編譯每個文件時使用從所有文件中獲取的信息。
並非所有優化都由標誌直接控制。以下僅列出具有標誌的優化。
只有在命令行上設置-O或其他等級的優化選項時,纔會啓用大多數優化。 否則,即使指定了單獨的優化標誌,它們也會被禁用。
根據目標和GCC的配置方式,可以在每個-O級別啓用略有不同的優化集。

-O0:減少編譯時間並使調試可以產生預期的結果,這也是默認的優化等級。

-O、-O1:優化編譯需要更多時間,並且大型函數需要更多內存。使用-O選項,編譯器會嘗試減小代碼尺寸減少執行時間,不執行任何需要大量編譯時間的優化。
-O選項打開了如下優化標誌:
-fauto-inc-dec -fbranch-count-reg -fcombine-stack-adjustments -fcompare-elim -fcprop-registers -fdce -fdefer-pop
-fdelayed-branch -fdse -fforward-propagate -fguess-branch-probability -fif-conversion2 -fif-conversion
-finline-functions-called-once -fipa-pure-const -fipa-profile -fipa-reference -fmerge-constants -fmove-loop-invariants
-fshrink-wrap -fsplit-wide-types -ftree-bit-ccp -ftree-ccp -fssa-phiopt -ftree-ch -ftree-copy-prop -ftree-copyrename -ftree-dce
-ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre -ftree-phiprop -ftree-sink -ftree-slsr -ftree-sra -ftree-pta
-ftree-ter -funit-at-a-time

-O2:相對-O優化更多。 GCC幾乎執行所有支持的優化,但不涉及空速權衡。 與-O相比,此選項增加了編譯時間和生成代碼的性能。
-O2除了打開所有-O指定的優化標誌,還打開了如下優化標誌:
-fthread-jumps
-falign-functions  -falign-jumps -falign-loops  -falign-labels -fcaller-saves -fcrossjumping -fcse-follow-jumps
-fcse-skip-blocks -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively -fexpensive-optimizations -fgcse
-fgcse-lm -fhoist-adjacent-loads -finline-small-functions -findirect-inlining -fipa-cp -fipa-cp-alignment -fipa-sra -fipa-icf
-fisolate-erroneous-paths-dereference -flra-remat -foptimize-sibling-calls -foptimize-strlen -fpartial-inlining -fpeephole2
-freorder-blocks -freorder-blocks-and-partition -freorder-functions -frerun-cse-after-loop -fsched-interblock  -fsched-spec
-fschedule-insns  -fschedule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-builtin-call-dce -ftree-switch-conversion
-ftree-tail-merge -ftree-pre -ftree-vrp -fipa-ra

-O3:相對-O2優化更多。
-O3除了打開所有-O2指定的優化標誌,還打開了如下優化標誌:
-finline-functions, -funswitch-loops,
-fpredictive-commoning, -fgcse-after-reload, -ftree-loop-vectorize, -ftree-loop-distribute-patterns, -ftree-slp-vectorize,
-fvect-cost-model, -ftree-partial-pre and -fipa-cp-clone

-Os:優化尺寸。 -Os啓用所有通常不會增加代碼大小的-O2優化。 它還執行旨在減少代碼大小的進一步優化。
-Os關閉如下優化標誌:
-falign-functions  -falign-jumps  -falign-loops -falign-labels  -freorder-blocks
-freorder-blocks-and-partition -fprefetch-loop-arrays

-Ofast:無視嚴格的標準合規性。 -Ofast啓用所有-O3優化。 它還打開並非對所有符合標準的程序有效的優化。 它打開
-ffast-math和Fortran特定的-fno-protect-parens和-fstack-arrays。

-Og:優化調試體驗。 -Og啓用不會干擾調試的優化。 它是標準編輯 - 編譯 - 調試周期可以選擇的優化級別,提供合理的優化級別,同時保持快速編譯和良好的調試體驗。
如果使用多個-O選項(包含或不包含級別編號),則最後一個選項是有效的選項。

背景知識瞭解後,問題顯而易見

引起上述問題的原因就是GDB調試級別。因爲編譯腳本使用了-g選項,因此導致生成的二進制文件包含了適應本地環境的調試信息,諸如代碼的完整路徑。這會導致同樣的編譯腳本在不同的目錄中進行編譯時所生成的二進制文件的大小會有差異。要避免這個差異,只要將-g選項刪掉即可。或者寫成-g0也可以。
貼出我的Makefile腳本
=============================
TOOLCHAIN_PREFIX = arm-none-eabi-
TARGET = LIBRARY
CC = $(TOOLCHAIN_PREFIX)gcc
CXX = $(TOOLCHAIN_PREFIX)g++
AR = $(TOOLCHAIN_PREFIX)ar
OUT_FILE_NAME = lib$(TARGET).a
COMMON_FLAGS =  -mcpu=cortex-m33 -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 -Og -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -Wall  -g0(原先這裏是-g3) -T
CFLAGS = -std=c11
CXXFLASG = -std=c++14
INC = -Iinclude
OBJS = $(patsubst %.cpp,%.cpp.o,$(wildcard *.cpp)) $(patsubst %.c,%.c.o,$(wildcard *.c))
DEPS = $(patsubst %.o,%.d,$(OBJS))
# Enumerating of every *.cpp as *.o and using that as dependency
$(OUT_FILE_NAME): $(OBJS)
    $(AR) -r -o $@ $^
%.cpp.o: %.cpp
    $(CXX) -c $(INC) $(COMMON_FLAGS) $(CXXFLAGS) -o $@  $<
%.c.o: %.c
    $(CC) -c $(INC) $(COMMON_FLAGS) $(CFLAGS) -o $@  $<
clean:
    rm -f $(OBJS) $(DEPS) $(OUT_FILE_NAME)
check:
    @echo $(OBJS)
    @echo $(DEPS)
==================================

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章