一篇雞肋似得,關於u-boot中 _start 和 _armboot_start關係的帖子

以下內容來自筆者在中國Linux論壇Linux嵌 入技術討論區的張貼:


aaronwong: u-boot中代碼的疑問(_armboot_start與_start)? 
---------------------------
我 使用的是u-boot-1.3.0-rc2。在cpu/pxa/start.S中,有如下的標號定義: 
_TEXT_BASE: 
.word TEXT_BASE /*uboot映像在SDRAM中的重定位地址,我 設置爲0xa170 0000 */ 

.globl _armboot_start 
_armboot_start: 
.word _start /*_start是程序入口,鏈接完 畢它的值應該是0xa170 0000=TEXT_BASE*/ 
/* 這句話的意思應該是在_armboot_start標號處,保存了_start的值,也就是說,_armboot_start是存放_start的地址, 該地址對應的存儲單元內容是0xa170 0000*/ 
/* 
* These are defined in the board-specific linker script. 下面的定義與上面應該是一個意思。 
*/ 
.globl _bss_start 
_bss_start: 
.word __bss_start 
====================== 
按照上面的理解,__bss_start是uboot 的bss段起始地址,那麼uboot映像的大小就是__bss_start - _start;在relocate代碼段中計算uboot的大小時,也體現了這一點。 
實際上,_armboot_start並沒有實際意義,它 只是在"ldr r2, _armboot_start"中用來尋址_start的值而已,_bss_start也是一樣的道理,真正有意義的應該是_start和 __bss_start本身。 
但是,令我不解的是,在C入口函數start_armboot()中(對應文件爲 lib_arm/board.c),有如下代碼: 
void start_armboot (void) 

......... 
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); //第一句話 
.......... 
monitor_flash_len = _bss_start - _armboot_start; //第 二句話 
............... 
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //第三句話 
.......... 

============================================== 
按照上面的理 解,_armboot_start與_bss_start都是沒有實際意義的,它們只是一個地址,有實際意義的是地址中的內容_start和 __bss_start(雖然也還是地址)。象第一句話,其“意圖”很明顯,是把gd作爲全局數據結構體的指針,並初始化爲“SDRAM中的uboot起 始地址(即TEXT_BASE)-CFG_MALLOC_LEN-全局數據結構體大小”。 
要實現這個“意圖”,應該是寫成:gd = (gd_t*)(_start - CFG_MALLOC_LEN - sizeof(gd_t));或者gd = (gd_t*)(TEXT_BASE- CFG_MALLOC_LEN - sizeof(gd_t));纔對阿?用_armboot_start來作運算應該是沒有任何意義纔對!? 
第二句話也是一樣的道理,它的意圖是 要計算u-boot映像的大小,應該寫成__bss_start - _start纔對阿? 
我使用readelf工具查看編譯所得到的 uboot映像文件得到信息如下: 
[aaronwong@localhost build]$ readelf -s u-boot|grep _start 
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start 
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start 
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start 
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 
上面我刪除了與該討論無關的包含“_start""t的標號信息。 
顯然,我前面的理 解應該是正確的(_start=TEXT_BASE=0xa170 0000)。那麼u-boot源代碼中的monitor_flash_len=_bss_start - _armboot_start=0xa1700048 - 0xa1700044 = 4,有什麼意義?? 
迷茫中,期盼大蝦指點迷津,謝 謝~!!!


eltshan: [Re: aaronwong] 
-----------------
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start 
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start 
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start 
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 

我想: 
_start 所在的地址是a1700000, 
_armboot_start 所在的地址是a1700044, 
那麼 根據這句: 
_armboot_start: .word _start 
所以_armboot_start的值應該是a1700000 

所以 
monitor_flash_len = _bss_start - _armboot_start = a171b070 - a1700000 = 1b070 
而不是你說的 = 4 

以上個人意見.


aaronwong: [Re: eltshan] 
-------------------
謝謝,eltshan!你的理解是正確的,不過我看 了之後還是沒能想得很明白,因爲我在想,按你所說,那麼_start的值應該是多少呢?難道是“b reset”這條指令的機器碼?所以我對ELF格式的u-boot映像文件作了反彙編,分析之後終於找到了癥結所在。以下是部分分析過程,首先是反彙編: 
arm-iwmmxt-linux-gnueabi-objectdump -D u-boot > u-boot.s 
並提取 了monitor_flash_len = _bss_start - _armboot_start;這條語句相關的反彙編代碼如下: 
============================== 
a1700044 <_armboot_start>: 
a1700044: a1700000 .word 0xa1700000 

a1700048 <_bss_start>: 
a1700048: a171b070 .word 0xa171b070 

a171b070 <monitor_flash_len>: 
a171b070: 00000000 .word 0x00000000 

..... 
a1700f40: e59f41d0 ldr r4, [pc, #464] ; a1701118 <start_armboot+0x1dc> 
//r4=[a1701118]=a1700044 
..... 
a1700f7c: e59f3198 ldr r3, [pc, #408] ; a170111c <start_armboot+0x1e0> 
//r3=[a1700044]=a1700048 
a1700f80: e5942000 ldr r2, [r4] 
//r2=[a1700044]=a1700000 
a1700f84: e59f4194 ldr r4, [pc, #404] ; a1701120 <start_armboot+0x1e4> 
//r4=[a1701120]=a1719d24 
a1700f88: e5933000 ldr r3, [r3] 
//r3=[a1700048]=a171b070 
a1700f8c: e0623003 rsb r3, r2, r3 
//r3= r3-r2 = a171b070-a1700000 = 1b070; 
a1700f90: e59f218c ldr r2, [pc, #396] ; a1701124 <start_armboot+0x1e8> 
//r2=[a1701124]=a171b070 
a1700f94: e5823000 str r3, [r2] 
//monitor_flash_len=[r2]=r3=1b070 
...... 

a1701118: a1700044 .word 0xa1700044 
a170111c: a1700048 .word 0xa1700048 
a1701120: a1719d24 .word 0xa1719d24 
a1701124: a171b070 .word 0xa171b070 
======================================== 
上 面//是我自己的註釋。這表明,你的理解的確是正確的。 
經過這個過程之後,我終於認識到自己的誤解在哪裏了。原來,我是把"彙編語言中LDR僞 指令對符號的引用"與"C語言中對彙編程序中符號/常量/變量的引用"搞混淆了。我想說明以下幾點:

(1) readelf以及u-boot.map和System.map所給出的符號表中符號的值,實際上是表示符號所在的地址,而不是指符號本身的值。 

(2) 彙編語言中沒有指針的概念,因此對符號的引用是"赤裸裸"的。 例如: 
========== 
.globl _armboot_start 
_armboot_start: .word _start 
ldr r2, _armboot_start 
========== 
實際上反彙編以後是: 
============ 
a1700044 <_armboot_start>: 
a1700044: a1700000 .word 0xa1700000 
a1700074: e51f2038 ldr r2, [pc, #-56] ; a1700044 <_armboot_start> 
============ 
也就是說,_armboot_start是一個地址0xa1700044,其中的內容是0xa1700000,上面對 _armboot_start的引用是直接將其替換爲其表示的地址0xa1700044,而非其中的內容0xa1700000。這就是"赤裸裸"的引用。 

(3) C語言則不同,對變量/符號/常量的引用必須要通過地址來尋址,不管是全局變量還是局部變量,不同的是局部變量在生命期結束後,所佔的地址空間會被釋放而 已。即使是函數調用時的參數傳遞,雖然是將實參的值"拷貝"給形參,但"拷貝"的過程也是通過實參和形參的地址來對兩者進行訪問的。 
所 以,在C語言中的 "monitor_flash_len = _bss_start - _armboot_start" 這句話中對_armboot_star的引用,實際上是把它用作了指針,把它作爲訪問對象的地址來使用,通過這個地址即a1700044 來訪問對應存儲空間所存放的內容亦即0xa1700000,_bss_start也是同樣的道理。所以這句話實際上是monitor_flash_len =[a1700048]-[0xa1700044]=a171b070-a1700000 = 1b070,這樣就得到了正確的結果。 

現 在,我們再回答最前面的問題:_start的值是什麼? _start 表示地址0xa1700000 ,在彙編語言中,對_start的"絕對引用"(這裏是與用相對尋址進行跳轉進行區別)就是將其替換爲0xa1700000,但其中存放的內容的的確確就 是"b reset"這條指令的機器碼,所以如果在C語言中引用_start,得到的結果反而就是這個指令的機器碼了。其實這個問題很簡單,只是和C語言的引用攪 在一起,一些概念被偷換了而已。 

以上是我的個人理解,有什麼不對,還請指正。 

再次感謝eltshan的提點。

 

 

 

 

樓主,你還是沒有完全理解對,在(2)



========== 



.globl _armboot_start 



_armboot_start: .word _start 



ldr r2, _armboot_start 



========== 



實際上反彙編以後是: 



============ 



a1700044 <_armboot_start>: 



a1700044: a1700000 .word 0xa1700000 



a1700074: e51f2038 ldr r2, [pc, #-56] ; a1700044 <_armboot_start> 



============ 



_armboot_start是一個地址0xa1700044,它的值爲0xa1700000,上面這句不是你理解的對_armboot_start的引用是直接將其替換爲其表示的地址0xa170044,應該是從地址爲0xa1700044的地方把值(0xa1700000)讀入。不要被GNU反匯編出來的註釋麻痹了,呵呵,它是幫你指明pc,#-56指向了哪個地址。



這個道理跟你自己前面作的註釋一樣,你看看你自己註釋的那句:



//r4=[a1701118]=a1700044



它前面那條語句最後的反彙編註釋。呵呵,你前面都明白了,爲什麼最後還要糊塗?



ldr r0, _start   ;從內存地址_start的地方把值讀入r0,與位置無關,對於_start這個符號表示的地址是通過相對pc的位移來實現,但ldr的結果是讀值。



ldr r0,=_start  ;這個是僞指令,讀取_start的絕對地址到r0。這個跟位置有關,編譯後一般是通過把表示_start地址的值存入一個位置(地址),然後再通過對這個位置的與位置無關的ldr來實現對_start地址的讀取。不管程序在哪裏執行,讀取的值一定是一樣的。



還有一個adr r0, _start的僞指令,是通過add來實現的相對pc的位移讀取_start的地址到r0,與位置就無關了。





總的來說,在c代碼中如果應用第一階段彙編代碼中定義的符號,變量或常量時實際上是引用的該符號處存放的內容,而不是該符號的地址。所以對於在start_armboot函數中出現的:


gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); 

monitor_flash_len = _bss_start - _armboot_start; 




mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); 

裏面的_armboot_start和_bss_start符號就不難理解拉,在C函數中這些符號代表着他們裏面存放着的內容也就是_start和__bss_start值,而不是這兩者的絕對地址拉。



而在彙編代碼中對於符號的運用也有兩種情況:

對於

.globl _armboot_start 

_armboot_start: .word _start 

ldr r2, _armboot_start //對於這種形式的ldr指令,我們可以看到反匯編出來的代碼,實際上就是將_armboot_start存放的內容取出來賦值給r2



而對於

ldr r2,=_armboot_start//對於這種形式的ldr 指令,將_armboot_start的實際絕對地址賦值給r2





其實上面兩種說發,表示的是一個意思,只不過深入的程度不同,第一種說法中提到的在彙編代碼中對符號赤裸裸的引用,意思是直接將符號的絕對地址拿出來去取值,也就是反彙編後括號[ ]裏面存放的直接地址,而第二種說話,只不過就是將從這種地址取到的值賦值出來。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章