架構編譯器的感悟

所謂的編譯器就是將c語言代碼編譯爲機器代碼的,先將C編譯爲彙編代碼,再由彙編器將彙編代碼編譯爲機器代碼,CPU執行的是機器代碼

 

突然發覺好像很多書都這麼說,很多人也這麼說,於是很自然的記住了,但是,我突然想起了,這可是隱藏着一些道理。

 

1,C編譯爲彙編,對於這個過程,應該是平臺無關的,具體是怎麼實現的?這個是由C編譯器開發商來處理,總之,如果我用IAR ARM的話,那麼同樣的main函數,編譯出來應該是得到 ARM 格式的彙編代碼,也就是說,使用的是 ARM 指令集,同樣道理,如果是 GCC,也產生類似的,不過因爲他們的內部實現並不一樣,所以出來的結果可能有差異,不過最後的結果是一致的,也就是得到對應平臺的彙編代碼。

 

2,彙編器將彙編代碼翻譯爲機器代碼,其實這個過程比較簡單的,有點像查表,一般廠家都會免費提供這麼一個編譯器,否則MCU就不用賣了,一般C編譯都會自己實現一個。這讓我想起了,什麼是架構 Architecture,一個CPU怎麼尋址,指令集是什麼,堆棧指針是怎麼變化的,PC程序指針是怎麼變化的,怎麼取指令,這些東西都是每個CPU區別於其他CPU所特有的東西,也就是一個CPU設計的時候所面臨的內容。這就是架構,還記得學校學MCS-51的時候說得最多的是什麼麼?對,尋址和指令集,這個就是架構,老師想你學51這種架構,而不單純是學怎麼用這個芯片。

 

3,再說回上層的,ARM是一個核心,是一個架構,他制定了指令集,他指定了SP指針的訪問方式,他指定了怎麼尋址,所以說“ARM架構”

然後很多廠家根據ARM架構生產的芯片,因爲你就一個核心沒用啊?他就會執行指令,其他什麼都不懂,我們需要外設,需要很多外圍器件,例如I2C,USB什麼的,和ARM這個核心一同構成一個完整意義上的芯片,我們叫 SOC (system on chip)單片系統,因爲這個系統已經具備了基本跑起來的條件了,再在外圍加上適當的復位和晶振電路,那基本就沒有什麼問題了。

 

從這我又想到了,那既然是同一個核心出來的,每個廠家生產芯片有什麼不同呢?其實說白了就是外圍器件的不同,例如ARM9芯片,三星的S3C2440,核心是 920T集成了LCD控制器,NAND FLASH 控制器,然而 Atmel的 AT91RM9200,核心同樣是 ARM架構的 920T,但是他沒有LCD控制器,卻集成了網絡方面的功能,這樣看來,在一個架構上面不同廠家添加不同的功能器件,而得到了各種各樣的芯片,也就是SOC了。

 

繼續聯想,不同的SOC之間其實還真的沒有什麼本質性的差別,第1,2點也說明了,C是萬國語言,他到底也是通用的,所以同一段MAIN函數,如果你什麼都不寫,那麼編譯出來,在每一個soc都跑得通。而爲什麼基於這個芯片開發的程序就不能用到拿個芯片呢?其實一個重要的問題就是地址的訪問,尋址的問題。ARM 32位總線固定了 4GB的尋址空間,但是ARM並沒有硬性的規定哪個地址應該做什麼,哪個地址不能做什麼,具體怎麼做,是SOC廠商說了算,所以產生的差異就是,可能這個soc裏面的uart模塊地址爲 0x50000000 ,而另外一個廠商的soc的uart模塊地址爲 0x40000000 ,C語言的器件編程,說白了不就是對某個地址放某些數據,例如我要設置UART,很簡單,將正確的數據送到正確的地址,也就是UART模塊所在的地址,那麼一切都完工了。

 

這裏體現了很重要的一點,就是所謂的編程就是在什麼地址放什麼數據或者要什麼數據。從這個抽象層面來看,編譯器可以很輕鬆的編譯同一個核心的不同SOC,因爲不同核心,那牽涉到架構的問題,要修改編譯器內部的實現代碼,但是同一個架構同一個核心的,對不同芯片的訪問的區別,就在於不同的地址!!

 

如果對S3C2440的編程,核心是 ARM 920T ,我們應該重點學習什麼?

應該學習通用的,如果理解上面的內容,就知道什麼是通用的了。那就是架構,搞懂了ARM 920T的架構,那麼你使用的幾個芯片又有什麼差別呢?最多就是對着那個新的功能模塊研究一下怎麼設置。

 

4,再談談編譯器

其實編譯器實現的目的都是一個,機器代碼,只是實現的辦法和過程並不相同而已。那麼對於衆多的編譯器,我們應該選擇哪個?其實選擇哪個都沒有所謂,他們都同樣的強大,ARMCC, GCC, IAR KEIL-MDK都同樣強大,只要你學會了用,總能滿足你的需要。但是,是不是每個編譯器就各自爲政呢?那倒不一定,所以呢,學習,是需要抓住核心

什麼是核心?有幾點

1)main函數執行之前究竟做了些什麼

可能在學校很多老師都教,C語言的入口在main函數,所有的代碼從這裏開始!但是真的嗎?其實並不是的,main函數執行之前需要必要的環境,這個環境由誰來提供?其實,在你的C語言程序連接的時候,一個初始化的模塊(一般是叫 crt0.o ,也就是 c running time的縮寫)已經悄悄的被連接進去了。這是每個編譯器都會做的,關鍵是,這個crt0 的模塊是編譯器默認提供的還是你自己去實現。如果是編譯器自帶的,那麼你應該怎麼去做部分修改以達到自己的目的,如果是自己寫,那又應該怎麼去寫?這就是學習的重點了。

初始化模塊主要的工作:設置棧,設置堆(heap,主要是爲C庫的malloc服務),.data的搬移(如果需要的話),.bss段的初始化,跳轉到 main函數。這個就是共性,無論什麼編譯器,都是在做這幾步而已,不同的只是用各個編譯器各自的語法去實現,但是本質也是不變的,當然,你也可以自己去實現,在連接的時候連接進來就OK了。

2)怎麼處理連接(link)

編譯的過程基本不需要去關心,編譯器都做得好好的,你也沒有干預的餘地,最多就是傳遞一些不同的編譯參數,這個稍微看看自帶的文檔,或者乾脆用到的時候再翻,也問題不大。真正的大問題出在連接。

編譯的過程得到的obj文件,是完全和地址無關的,例如,每個源文件對應的obj文件,地址都是從0開始的,它真正連接的時候才由連接器linker分配真正運行時候的地址,所以,你要想處理每個具體的代碼段運行的時候應該在什麼地方,就要注意學習link了。

其中指導linker工作的就是對應的腳本 linker scrpit,連接腳本,同樣,這是一個共性,每個編譯器都肯定有,只是實現辦法的不同,腳本具體的編寫的語法不同,所以這個是應該重點研究的,讓你的代碼聽聽話話,就花多點功夫去研究這個吧。

 

所以,每遇到一個編譯器,先了解他怎麼初始化,然後研究它怎麼連接,瞭解了之後基本上你就會用這個編譯器了,剩下的就不用說了,上課老師也說得夠多了,怎麼從main函數開始寫,云云。

 

轉自:https://my.oschina.net/xyh12344/blog/543085

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