1. 程式人生 > >MDK編譯過程及其中間檔案介紹

MDK編譯過程及其中間檔案介紹

整個工作過程

  • 編譯:
    編譯器是armcc(C檔案程式碼)和armasm(彙編檔案程式碼),它們根據每個c/c++和彙編原始檔編譯成對應的以".o"為字尾名的物件檔案(Object Code,也稱目標檔案),其內容主要是從原始檔編譯得到的機器碼,包含了程式碼、資料以及除錯使用的資訊。但是其中不包含 地址資訊

  • 連結:
    連結器armlink把各個.o檔案及庫檔案根據你在MDK中的晶片選型 地址資訊設定 連結成一個映像檔案".axf"或".elf"。這個".axf"檔案是包含地址資訊的。其中還會提示程式儲存空間分配,這個具體下面再講

Program Size: Code=14636 RO-data=576 RW-data=204 ZI-data=2732  
  • 格式轉換:
    一般來說Windows或Linux系統使用連結器直接生成可執行映像檔案elf後,核心根據該檔案的資訊載入後,就可以執行程式了。但在微控制器平臺上,需要把該檔案的內容載入到晶片上,所以還需要對連結器生成的elf映像檔案利用格式轉換器fromelf轉換成“.bin”或“.hex”檔案,交給下載器下載到晶片的FLASH或ROM中。

程式儲存空間分配

應用程式中所有具有同一性質的資料(包括程式碼)被歸到一個域,程式在儲存或執行的時候,不同的域儲存、讀寫屬性各不相同。主要分為幾大類CODE、 RO、 RW、 ZI Data。在《程式設計師的自我修養》這本書裡面講了很多這種編譯、連結類的知識。下面僅僅是針對於MCU的架構,指的是nor flash+sram的結構

  • CODE:程式碼段,這個是純粹的程式碼,MCU就是執行這些二進位制指令。在儲存時程式碼段存放在nor flash中,在執行時程式碼段仍然在nor flash中,這跟PC等架構不一樣。MCU核心直接從nor flash取指執行,不需要將CODE載入到RAM中

  • RO:只讀資料區,指程式中用到的只讀資料,這些資料被儲存在ROM區,因而程式不能修改其內容。在儲存時只讀資料區在flash中,在執行時仍然在flash中,並不會被載入到sram中。比如char *p="abc";,其實abc字串是存放在rom區的。一般情況下const定義的變數也是屬於只讀的存放在rom flash中。

  • RW:可讀可寫資料段,它指初始化為“非0值”的可讀寫資料,程式剛執行時,這些資料具有非0的初始值,且執行的時候它們會常駐在RAM區,因而應用程式可以修改其內容。RW段在儲存時放在flash中,在執行時被載入到sram中。如定義的全域性變數(不初始化為0的)。

  • ZI Data:一般又稱為BSS段。即0初始化資料(Zero Initialied Data),它指初始化為“0值”的可讀寫資料域,它與RW-data的區別是程式剛執行時這些資料初始值全都為0,而後續執行過程與RW-data的性質一樣,它們也常駐在RAM區,因而應用程式可以更改其內容。在儲存的時候並不佔用flash中的資源,因為反正都是0,只需要記錄有多少個0即可,在執行的時候會在SRAM中分配相應的大小的記憶體。

  • 堆疊段:在儲存的時候是不佔用空間的(或者說就沒有儲存),在執行的時候有著自己的一套規則。

程式狀態與區域 組成
程式執行時的只讀區域(RO) Code + RO data
程式執行時的可讀寫區域(RW) RW data + ZI data
程式儲存時佔用的ROM區 Code + RO data + RW data

MDK工具鏈

MDK就是將一系列工具如armcc、armlink、armar、 fromelf整合並做了友好的GUI介面,實際幹活的還是這些軟體工具。
在這裡插入圖片描述
下面介紹這些軟體跟MDK設定介面的關係
首先新增環境變數,方便後續操作:如果是按MDK預設安裝路徑來的話,需要將fromelf的路徑加入到環境變數,即C:\Keil_v5\ARM\ARMCC\bin加入環境變數。

  1. armcc :就是實際幹活的編譯器
    在cmd視窗中輸入armcc會提示一串用法,在MDK的Options For Targets->C/C+±>Compiler control string 視窗的一串字串其實就是armcc編譯時的選項,只不過這些選項是由MDK內建指令碼自動生成的而已。
    輸入armcc --cpu list可以看到當前版本armcc支援哪些核心的CPU

  2. armlink :就是實際幹活的編譯器
    在cmd視窗中輸入armlink會提示一串用法,在MDK的Options For Targets->Linker>Linker control string視窗的一串字串其實就是armlink連結時的選項,只不過這些選項是由MDK內建指令碼自動生成的而已。
    連結器根據晶片的地址資訊 和 armcc生成的.o檔案來生成elf格式的axf可執行檔案。
    那麼這個晶片的地址資訊是哪裡來的?原來在我們給晶片選型之後,MDK自動生成了一個.sct檔案,這個也叫載入檔案,連結器也就是根據.sct檔案來確定連結地址的,不同晶片選型會生成不同的.sct檔案。還有一篇文章將分散載入的部落格,其中主要就是講.sct檔案的。

  3. armar:是用於將工程檔案打包成庫檔案的一個工具
    在MDK中有Options for Targets->Output->Create Library選項,就是利用armar來生成庫檔案的。在你不想給對方原始碼,只想提供API介面的時候可以使用這種方法。

  4. fromelf:可以根據elf格式的axf檔案生成HEX、BIN檔案
    但是僅僅集成了hex選項,可以在Options for Targets->Output中看到。如果你想生成BIN檔案怎麼操作呢?當然第一種方法你可以在cmd命令列中根據已經生成的.axf檔案利用fromelf工具來生成BIN檔案;第二種方法是MDK中有Options for Targets->User視窗,裡面就是讓使用者根據編譯過程來新增自己的指令碼命令的,其中分為了三個階段,編譯前、連結前、連結後,如果我們想利用.axf檔案僅僅需要在連線後的裡面新增指令碼fromelf.exe --bin -o ..\OBJ\LED.bin ..\OBJ\LED.axf即可

MDK工程檔案型別詳解

字尾 檔案型別
*.lib 庫檔案
*.dep 整個工程的依賴檔案
*.d 描述了對應.o的依賴的檔案
*.crf 交叉引用檔案, 包含了瀏覽資訊(定義、 引用及識別符號)
*.o 可重定位的物件檔案(目標檔案)
*.bin 二進位制格式的映像檔案, 是純粹的FLASH映像, 不含任何額外資訊
*.hex Intel Hex格式的映像檔案, 可理解為帶儲存地址描述格式的bin檔案
*.elf 由GCC編譯生成的檔案, 功能跟axf檔案一樣, 該檔案不可重定位
*.axf 由ARMCC編譯生成的可執行物件檔案, 可用於除錯, 該檔案不可重定位
*.sct 連結器控制檔案(分散載入)
*.scr 連結器產生的分散載入檔案
*.lnp MDK生成的連結輸入檔案, 用於呼叫連結器時的命令輸入
*.htm 連結器生成的靜態呼叫圖檔案
*.build_log.htm 構建工程的日誌記錄檔案
*.lst C及彙編編譯器產生的列表檔案
*.map 連結器生成的列表檔案, 包含儲存器映像分佈
*.ini 模擬、 下載器的指令碼檔案
*.uvprojx 記錄了整個工程的結構,如晶片型別、工程包含了哪些原始檔等內容
*.uvoptx 記錄了工程的配置選項,如下載器的型別、變數跟蹤配置、斷點位置以及當前已開啟的檔案
*.uvguix 記錄了MDK軟體的GUI佈局,如程式碼編輯區視窗的大小、編譯輸出提示視窗的位置

HEX與BIN區別和聯絡

HEX檔案是包括地址資訊的,HEX檔案都是由記錄(RECORD)組成的。在HEX檔案裡面,每一行代表一個記錄,而BIN檔案格式只包括了資料本身,純粹的二進位制資料連大小端都沒有,任何輔助資訊都沒有。HEX檔案是用ASCII來表示二進位制的數值。例如一般8-BIT的二進位制數值0x5E,用ASCII來表示就需要分別表示字元’5’和字元’E’,每個字元需要一個BYTE,所以HEX檔案需要 > 2倍的空間。對一個BIN檔案而言,你檢視檔案的大小就可以知道檔案包括的資料的實際大小。而對HEX檔案而言,你看到的檔案 大小並不是實際的資料的大小。一是因為HEX檔案是用ASCII來表示資料,二是因為HEX檔案本身還包括別的附加資訊,如地址資訊。
在燒寫或下載HEX檔案的時候,一般都不需要使用者指定地址,因為HEX檔案內部的資訊已經包括了地址。而燒寫BIN檔案的時候,使用者是一定需要指定地址資訊的(MDK下載的就是HEX檔案,ESP8266自己編譯韌體產生bin檔案下載的時候需要指定flash地址)。看圖
STM32生成下載檔案
MDK截圖

ESP8266生成檔案跟下載方式Eclipse 和 ESP8266下載工具截圖

HEX檔案詳解

下面是一個hex檔案前幾行

:020000040800F2
:10000000780B0020E1080008C9020008AB150008C1
:10001000CB020008CF020008D30200080000000055
:10002000000000000000000000000000D7020008EF
:10003000D90200080000000033150008AB360008A4
:10004000FB080008FB080008FB080008FB08000884
:10005000FB080008FB080008FB080008FB08000874
:10006000FB080008FB080008FB080008FB08000864

每行是以:開始,代表一條記錄,一條記錄的基本格式是:llaaaatt[dd…]cc

  • : :冒號是一條記錄開始
  • ll:以16進位制數表示這條記錄的主體資料區的長度
  • aaaa:表示這條記錄中的內容應存放到FLASH中的起始地址
  • tt:表示這條記錄的型別,它包含中的各種型別
tt 資料型別
00 資料記錄
01 本檔案結束記錄
02 擴充套件地址記錄
04 擴充套件線性地址記錄(表示後面的記錄按個這地址遞增)
05 表示一個線性地址記錄的起始(只適用於ARM)
  • dd:表示一個位元組的資料,一條記錄中可以有多個位元組資料
  • cc:表示本條記錄的校驗和,它是前面所有16進位制資料 (除冒號外,兩個為一組)的和對256取模運算的結果的補碼