_armboot_start 內容的疑問

--------------------------------------------------------------------------------"
aaronwong: u-boot中代碼的疑問(_armboot_start與_start)?12Gm
---------------------------=j
我使用的是u-boot-1.3.0-rc2。在cpu/pxa/start.S中,有如下的標號定義: w'
_TEXT_BASE: 7B
.word TEXT_BASE  k&BnQf
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  A%
.globl _armboot_start /'b&%
_armboot_start: 50m B8
.word _start  2inlX
 ~1
 ©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  ?Ud=F}
.globl _bss_start W8
_bss_start: 4V1kfj
.word __bss_start 5`
====================== XM
按照上面的理解,__bss_start是uboot 的bss段起始地址,那麼uboot映像的大小就是__bss_start - _start;在relocate代碼段中計算uboot的大小時,也體現了這一點。 fHK'f0
實際上,_armboot_start並沒有實際意義,它只是在"ldr r2, _armboot_start"中用來尋址_start的值而已,_bss_start也是一樣的道理,真正有意義的應該是_start和 __bss_start本身。 ;{I
但是,令我不解的是,在C入口函數start_armboot()中(對應文件爲lib_arm/board.c),有如下代碼: =-yz!
void start_armboot (void) 6#F[C
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  dCb
......... *=
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); //第一句話 7
.......... xfw,,
monitor_flash_len = _bss_start - _armboot_start; //第二句話 =r1m,
............... =cN^x+
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //第三句話 W
.......... ?
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  u
============================================== v#HG/
按照上面的理解,_armboot_start與_bss_start都是沒有實際意義的,它們只是一個地址,有實際意義的是地址中的內容_start和 __bss_start(雖然也還是地址)。象第一句話,其“意圖”很明顯,是把gd作爲全局數據結構體的指針,並初始化爲“SDRAM中的uboot起始地址(即TEXT_BASE)-CFG_MALLOC_LEN-全局數據結構體大小”。 <BgA
要實現這個“意圖”,應該是寫成:gd = (gd_t*)(_start - CFG_MALLOC_LEN - sizeof(gd_t));或者gd = (gd_t*)(TEXT_BASE- CFG_MALLOC_LEN - sizeof(gd_t));纔對阿?用_armboot_start來作運算應該是沒有任何意義纔對!? #0gYd?
第二句話也是一樣的道理,它的意圖是要計算u-boot映像的大小,應該寫成__bss_start - _start纔對阿? @`PVq
我使用readelf工具查看編譯所得到的uboot映像文件得到信息如下: NK7,G
[aaronwong@localhost build]$ readelf -s u-boot|grep _start G
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start !Qgo}
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start W
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start b9>
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start m[<B2Q
上面我刪除了與該討論無關的包含“_start""t的標號信息。 &:gP
顯然,我前面的理解應該是正確的(_start=TEXT_BASE=0xa170 0000)。那麼u-boot源代碼中的monitor_flash_len=_bss_start - _armboot_start=0xa1700048 - 0xa1700044 = 4,有什麼意義?? p
迷茫中,期盼大蝦指點迷津,謝謝~!!! <
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  M6fJvX
--------------------------------------------------------------------------------%:#-
eltshan: [Re: aaronwong]9o22#P
-----------------Zi
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start D3dY(
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start _mAq>
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start QNr+Pc
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start +=
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  E-Y>
我想: FCNh{M
_start所在的地址是a1700000, eHEsMt
_armboot_start 所在的地址是a1700044, ?
那麼 根據這句: 7.Iy
_armboot_start: .word _start }<U
所以_armboot_start的值應該是a1700000 w34ok:
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  gh/
所以 ©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  ka
monitor_flash_len = _bss_start - _armboot_start = a171b070 - a1700000 = 1b070 ~=w
而不是你說的 = 4 FYxAA@
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  *E*4z
以上個人意見.Q~St
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  ^p`Sc
--------------------------------------------------------------------------------Nkh
aaronwong: [Re: eltshan];DQlk5
-------------------p4
謝謝,eltshan!你的理解是正確的,不過我看了之後還是沒能想得很明白,因爲我在想,按你所說,那麼_start的值應該是多少呢?難道是“b reset”這條指令的機器碼?所以我對ELF格式的u-boot映像文件作了反彙編,分析之後終於找到了癥結所在。以下是部分分析過程,首先是反彙編: 24
arm-iwmmxt-linux-gnueabi-objectdump -D u-boot > u-boot.s *{|(q#
並提取了monitor_flash_len = _bss_start - _armboot_start;這條語句相關的反彙編代碼如下: /
============================== ^o#c7
a1700044 <_armboot_start>: b?
a1700044: a1700000 .word 0xa1700000 e{Zn
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  >
a1700048 <_bss_start>: l#"{w
a1700048: a171b070 .word 0xa171b070 U3sK
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  !.
a171b070 <monitor_flash_len>: R
a171b070: 00000000 .word 0x00000000 Q^$
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  MeE9
..... m4
a1700f40: e59f41d0 ldr r4, [pc, #464] ; a1701118 <start_armboot+0x1dc> lF-4
//r4=[a1701118]=a1700044 2/NL_;
..... EW0Th
a1700f7c: e59f3198 ldr r3, [pc, #408] ; a170111c <start_armboot+0x1e0> [T4Uwy
//r3=[a1700044]=a1700048 D
a1700f80: e5942000 ldr r2, [r4] 2/0N0
//r2=[a1700044]=a1700000 mV
a1700f84: e59f4194 ldr r4, [pc, #404] ; a1701120 <start_armboot+0x1e4> bWFU
//r4=[a1701120]=a1719d24 #Bnq
a1700f88: e5933000 ldr r3, [r3] *
//r3=[a1700048]=a171b070 <?
a1700f8c: e0623003 rsb r3, r2, r3 $e8I:
//r3= r3-r2 = a171b070-a1700000 = 1b070; q|
a1700f90: e59f218c ldr r2, [pc, #396] ; a1701124 <start_armboot+0x1e8> f1XV
//r2=[a1701124]=a171b070 }
a1700f94: e5823000 str r3, [r2] h`lC]
//monitor_flash_len=[r2]=r3=1b070 mJT:HJ
...... =op4
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  9
a1701118: a1700044 .word 0xa1700044 Z0
a170111c: a1700048 .word 0xa1700048 fr3g(
a1701120: a1719d24 .word 0xa1719d24 EpcDe
a1701124: a171b070 .word 0xa171b070 XT&
======================================== :
上面//是我自己的註釋。這表明,你的理解的確是正確的。 :}6
經過這個過程之後,我終於認識到自己的誤解在哪裏了。原來,我是把"彙編語言中LDR僞指令對符號的引用"與"C語言中對彙編程序中符號/常量/變量的引用"搞混淆了。我想說明以下幾點:`[I
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  WY
(1) readelf以及u-boot.map和System.map所給出的符號表中符號的值,實際上是表示符號所在的地址,而不是指符號本身的值。 E?F'R
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  u
(2) 彙編語言中沒有指針的概念,因此對符號的引用是"赤裸裸"的。例如: M"
========== wM
.globl _armboot_start J%
_armboot_start: .word _start d_
ldr r2, _armboot_start Kf
========== ,
實際上反彙編以後是: 466
============ ;/g-oE
a1700044 <_armboot_start>: }b
a1700044: a1700000 .word 0xa1700000 R
a1700074: e51f2038 ldr r2, [pc, #-56] ; a1700044 <_armboot_start> b b}/4
============ [7A
也就是說,_armboot_start是一個地址0xa1700044,其中的內容是0xa1700000,上面對_armboot_start的引用是直接將其替換爲其表示的地址0xa1700044,而非其中的內容0xa1700000。這就是"赤裸裸"的引用。_armboot_start變量的地址值由連接器而定。 m
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  )bR;
(3) C語言則不同,對變量/符號/常量的引用必須要通過地址來尋址,不管是全局變量還是局部變量,不同的是局部變量在生命期結束後,所佔的地址空間會被釋放而已。即使是函數調用時的參數傳遞,雖然是將實參的值"拷貝"給形參,但"拷貝"的過程也是通過實參和形參的地址來對兩者進行訪問的。 p
所以,在C語言中的 "monitor_flash_len = _bss_start - _armboot_start" 這句話中對_armboot_star的引用,實際上是把它用作了指針,把它作爲訪問對象的地址來使用,通過這個地址即a1700044 來訪問對應存儲空間所存放的內容亦即0xa1700000,_bss_start也是同樣的道理。所以這句話實際上是monitor_flash_len =[a1700048]-[0xa1700044]=a171b070-a1700000 = 1b070,這樣就得到了正確的結果。
 eNe#ij
©南開大學嵌入式系統與信息安全實驗室學術論壇 -- 我的論壇,我的天地  MnK-47
現在,我們再回答最前面的問題:_start的值是什麼?_start表示地址0xa1700000 ,在彙編語言中,對_start的"絕對引用"(這裏是與用相對尋址進行跳轉進行區別)就是將其替換爲0xa1700000,但其中存放的內容的的確確就是"b reset"這條指令的機器碼,所以如果在C語言中引用_start,得到的結果反而就是這個指令的機器碼了。其實這個問題很簡單,只是和C語言的引用攪在一起,一些概念被偷換了而已。 *
//*********************************************************************************************//
摘自另一篇博客
u-boot 1.1.6 start.S 代碼學習
/*
參考了別人的一些筆記。
       看完了啓動代碼
本文檔記錄在看代碼時碰到的困難。
       將這些曾經困擾的問題記錄下來。
       以備今後之用。分析時不重要的代碼被刪除了。
*/
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
/*
_start是整個u-boot程序的入口點,即鏈接後。
       該處是整個程序的第一條指令。程序的入口點是由鏈接腳本所指定,比如對於smdk2410的板子(下面都以smdk241爲例),腳本文件位於board/smdk2140/u-boot.lds。在該腳本文件中:ENTRY(_start) 即指定程序的入口地址。globl _start 定義一個外部可以引用的變量,比如說,在其它源代碼文檔中。
       就可以直指引用_start這個變量。如int entry=_start; 那此處entry值將是多少呢?因爲_start相當於一個變量,entry的值就是_start處存儲的值。
       即 b reset機器碼值。關於globl定義的變量得注意的地方。
       後面還有記錄
這段代碼處理中斷向量表。
關於 balignal 16,0xdeadbeef的網上資料:
.align僞操作用於表示對齊方式:通過添加填充字節使當前位置滿足一定的對齊方式。
.balign的作用同.align。
.align {alignment} {,fill} {,max}
其中:alignment用於指定對齊方式,可能的取值爲2的次冪,缺省爲4。fill是填充內容。
       
缺省用0填充。max是填充字節數最大值,如果填充字節數超過max,就不進行對齊.
例如: .align 4, 指定對齊方式爲字對齊
deadbeef一般用來表示沒用的或是已經被釋放掉的內存
*/
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
/*
備註這幾個由.word僞操作符定義變量的作用及其取值
_TEXT_BASE:
.word TEXT_BASE
_TEXT_BASE:此處定義一彙編語言標籤,更好的理解就是:告訴編譯器。
       爲_TEXT_BASE分配存儲空間
       該空間的名字就叫_TEXT_BASE,該空間中存儲的值就是由.word後面確定的TEXT_BASEC(即0x33F80000)。
       相當於C語言中 long _TEXT_BASE=TEXT_BASE; TEXT_BASE定義在board/smdk2410/config.mk文件中。該值的作用是告訴鏈接器,本程序運行的基地址爲TEXT_BASE。U-boot編譯後。
       燒在FLASH的第一個塊中。
       CPU復位上電後,電腦寄存器爲0x0000。怎麼會跑到TEXT_BASE處執行呢?
事實上,CPU上電後。
       從地址0x0000處執行。
       而U-BOOT的最起始代碼
       即本文件中從_start開始的代碼是與地址不相關的。
       這段代碼放在任何空間執行的結果都是一樣(當然不是絕對,假設u-boot代碼段是100K。
       則放在TEXT_BASE-80K處。
       搬運時就會把u-boot代碼後面20K部分覆蓋爲最前面的20K)。
.globl _armboot_start
_armboot_start:
.word _start
定義外部可以引用的變量_armboot_start,。即相當於C long _armboot_start=&_start; _armboot_start值是多少?是TEXT_BASE,即0x33F80000!等價的那條C語句,取的是_start變量地址。
       而不是_start本身。在C語言中。
       定義一變量 int x=100;就是告訴編譯器。給我一個int大小的存儲空間
       該空間存儲的值就是100,這個空間在哪呢?即空間地址是多少呢?我們可以通過&x知道。
在彙編語言中,理解上有點不一樣。上面三行語句。
       
第一句,告訴編譯器。
       向外面輸出變量_armboot_start
第二句,_armboot_start變量在此處,到底在哪。
       要到鏈接時才能確定。
       凡正現在知道有這麼一個變量了。
第三句,_armboot_start變量空間放的數據爲_start標籤的值。這點與C語言的理解有點不一樣了。此處引用的是_start標籤對應處的地址。在彙編語言中,標籤代表的就是那行所在的地址。
圖1是從u-boot編譯後生成的u-boot.map截圖的。從此文件中知道,_armboot_start這個變量地址爲0x33f80044,
*/
圖1
/*
.globl _bss_start
_bss_start:
.word __bss_start
此三句。
       特別要備註的是__bss_start這個符號。
       它的值爲多少?該值定義在board/smdk2410/u-boot.lds文件中
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
上面的__bss_start=.; 表示__bss_start值就是當前位置的值。當前位置是多少呢?從下面一句
.bss:{*(.ss)}知道。緊接該位置後面馬上就是放bss段數據了。所以。
       當然就是bss段的起始地址。_end就是bss段的結束地址。
參考:http://blog.chinaunix.net/u1/58780/showart_462971. html
bss是這個鏈接腳本的最後一個段。start.S就是以這個段的起始地址來計算要搬運u-boot大小的代碼的。即。
       這個段前面的所有數據都將被搬到TEXT_BASE處。然後跑到start_armboot處,即C語言的入口代碼。__bss_start這個值是多少? Smdk2410我編譯後值是0x33f96f20。可以從圖2的map文件中查到,bss段從0x33f96f20開始分配的。
再次驗證一下0x33f96f20就是__bss_start值。
       我們可以從圖1處可以看到有個變量_bss_start該變量就是由下面三條語句所定義,_bss_start處保存的值就是__bss_start:
.globl _bss_start
_bss_start:
.word __bss_start
用UltraEdit打開生成的u-boot.bin,定位到文件偏移0x48(0x48由_bss_start所在地址0x33f80048-TEXT_BASE得到),如圖3,此處值確實是20 6F F9 33(注意大小端)
*/
圖2
圖3
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/*
上面幾行進入SVC模式,通過裝入CPSR到r0。
       修改相應位後再載入到CPSR。
*/
/* turn off the watchdog 關閉WATCH DOG,具體寄存器地址看芯片DATASHEET即知*/
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR default
* 設置中斷屏蔽寄存器相應位。
       關閉中斷。
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
/*
下面是重定位代碼
       即將整個u-boot搬到TEXT_BASE地址處的代碼
/*
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
/*
adr r0, _start這句代碼是將_start標籤處運行時的地址值裝進r0,這條指令到底取一個什麼值呢?假設運行到該指令時pc寄存器的值是X,start標籤相對此本指令有Y的偏移,則r0=X+Y(Y可能是負值).
針對該程序來說。
       當被裝載到0x0000處運行時,r0就是0,假設裝載到TEXT_BASE處運行。
       則r0=TEXT_BASE.
幾條指令分析如下:
求正在運行的程序_start標籤處地址到r0。
       將_TEXT_BASE變量值裝到r1,即將0x33f80000裝到r1,比較r0/r1是否相等。
       這個比較就是確定當前是不是從Flash中執行的,還是這段代碼已裝載到0x33f80000處執行的。如果不等於0x33f80000,則將FLASH中的u-boot代碼搬到0x33f80000處。
具體計算如下:
ldr r2,_armboot_startàr2=0x33f80000
ldr r3,_ss_startàr3=bss段的起始地
*/
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
/*
上面幾行將r0地址處的代碼複製到r1處。
       即從0x0000搬到0x33f80000(假設CPU復位從0x0000執行的)。
*/
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack 設置堆棧 */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
/*
上面代碼中,指定從(TEXT_BASE - CFG_MALLOC_LEN)開始地址。
       大小爲CFG_MALLOC_LEN的內存預留給由malloc函數。因爲u-boot中其它地方調用了malloc函數。
       該空間就是供其動態分配的。
再向低端又預留了CFG_GBL_DATA_SIZE字節的數據(注:爲global_data結構體預留的空間,看board.c)
然後爲IRQ及FIQ中斷預留空間
最後將預留空間的低端地址賦給sp。
       ARM堆棧由低到高增長的??
*/
clear_bss: /*將bss段數據清零*/
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
/*下面一句跳到C語言的入口處start_armboot(函數定義在board.c中) */
ldr pc, _start_armboot
_start_armboot: .word start_armboot
/*關鍵代碼分析結束*/


發佈了16 篇原創文章 · 獲贊 40 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章