http://blog.csdn.net/zhanglianpin
我在學習嵌入式作業系統UCOS時,有很大的迷茫之處,在於我不知道我用的工具到底幫我幹了些神馬工作。下面就說一下我當時的疑問:
1:編譯器和連結器幹了些神馬?我現在也還沒能力知道它是怎麼乾的這些工作。以後慢慢來唄。但當時我連它幹了神馬都不清楚。
2:它輸入的是來源程式,那他輸出的是什嗎?有格式嗎?是什麼樣子那?
重重疑問。
ADS編譯器的目的是怎麼把進階語言編程能在ARM平台上直接啟動並執行東東。
而我們的目的是看看他幹了些神馬,讓我們瞭解一下它以及他連結出來的東東是什麼樣子。對嵌入式整個工程開發做了哪些貢獻,這樣我們就能更好的使用它了。是不是。
關於ARM嵌入式開發的要點和步驟請參考基於ARM的嵌入式系統程式開發要點.pdf,這裡不做詳細介紹。
UCOS啟動並執行硬體環境是ARM晶片我們已經在晶片知識章節介紹了,那UCOS 絕大多數代碼是用C語言寫的,那C語言怎麼變成在ARM晶片上啟動並執行代碼也是一個問題,因為我們要講解的是UCOS在具體平台上的具體運行效果。
先說一下編譯、連結後ARM程式的組成:
ARM程式的組成
此處所說的“ARM程式”是指在ARM系統中正在執行的程式,而非儲存在ROM中的bin映像(image)檔案,這一點清注意區別。
一個ARM程式包含3部分:RO,RW和ZI
RO是程式中的指令和常量
RW是程式中的已初始設定變數
ZI是程式中的未初始化的變數
由以上3點說明可以理解為:
RO就是readonly,
RW就是read/write,即已初始化的變數
ZI就是zero,程式員未初始化的變數,一般預設初始化為0
(2)
ARM映像檔案的組成
所謂ARM映像檔案就是指燒錄到ROM中的bin檔案,也成為image檔案。以下用Image檔案來稱呼它。
Image檔案包含了RO和RW資料。
之所以Image檔案不包含ZI資料,是因為ZI資料都是0,沒必要包含,只要程式運行之前將ZI資料所在的地區一律清零即可。包含進去反而浪費儲存空間。
Q:為什麼Image中必須包含RO和RW?
A:因為RO中的指令和常量以及RW中初始化過的變數是不能像ZI那樣“無中生有”的。
(3)
ARM程式的執行過程
從以上兩點可以知道,燒錄到ROM中的image檔案與實際運行時的ARM程式之間並不是完全一樣的。因此就有必要瞭解ARM程式是如何從ROM中的image到達實際運行狀態的。
實際上,ROM中的指令至少應該有這樣的功能:
1. 將RW從ROM中搬到RAM中,因為RW是變數,變數不能存在ROM中。
2. 將ZI所在的RAM地區全部清零,因為ZI地區並不在Image中,所以需要程式根據編譯器給出的ZI地址及大小來將相應得RAM地區清零。ZI中也是變數,同理:變數不能存在ROM中
在程式啟動並執行最初階段,RO中的指令完成了這兩項工作後C程式才能正常訪問變數。否則只能運行不含變數的代碼。
也就是說經過編譯器編譯出來的可執行程式在記憶體中有兩種狀態,一種是剛裝載到記憶體時的狀態,即按照你寫的函數調用的順序組織成的連續的二進位代碼。另一種是程式運行時的狀態。但一般程式真正執行時要把我們寫的程式分為程式碼片段、資料區段<包括已經初始化的變數和沒有初始化的變數>。你想程式碼片段放到記憶體之後,什麼也不用提前做,只等著被取出執行就行。但資料區段就不一樣了,你想一想,資料區段要有本身的屬性的(初始化為具體數值或者0),那這些屬性必須在程式沒有執行前賦給這些變數。怎麼賦予它們這些屬性那,一般是由編譯器產生一些專門負責這些工作的代碼來完成。現在大家理解為什麼說編譯器編譯出來的可執行程式在記憶體中有兩種狀態了吧。一個是LOAD時的狀態,一個是運行時的狀態。
我們現在知道了ADS編譯出來的程式的樣子了,以及編譯好的代碼要想真正運行還需要做的工作。但問題又出現了,我們怎麼利用ADS這個工具使其產生我們所需要的代碼的樣子以及怎樣讓他變成真正運行時的樣子那?
編譯器把源檔案編譯成ELF的目標檔案,然後經連接器最終產生.bin 檔案,這種檔案可以用於直接燒錄到flash中。也即去除了ELF那些格式。
你可以通過簡單地配置進行設定編譯出的代碼的裝載地址和運行地址。你也可以用分散負載檔案來實現比較複雜的記憶體映射。有關編譯器編譯出的代碼的裝載和運行地址的相關概念請參考:ARM開發工具ADS原理與應用的第十章。