概述
重定位(relocate)代碼將BootLoader自身由Flash複製到SDRAM,以便跳轉到SDRAM執行。之所以需要進行重定位是因為在Flash中執行速度比較慢,而系統複位後總是從0x00000000地址取指。
重定位代碼,位於/U-Boot/cpu/s3c44b0/start.S :
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
*/
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 address [r2]
*/
ble copy_loop
以上代碼首先判斷是否需要進行重定位,如果需要的話首先確定複製的源基址、源大小和目標基址,然後以r3 ~ r13為媒介,將BootLoader複製到SDRAM中。
分析
copy_loop很容易理解,這裡主要分析relocate處的前兩條指令:
1. adr r0, _start
adr是一條偽指令,彙編器總是試圖為它產生add/sub這樣的指令,(在這裡)以pc為基址裝載目標寄存器。以下是arm-elf-objdump產生的反組譯碼代碼:
c700048: e24f0050 sub r0, pc, #80
; 0x50
e24f0050是指令對應的機器碼,c700048是存放該機器碼的地址(十六進位表示)。這個地址是怎麼來的呢?在/U-Boot/config.mk中有問題的答案:
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
上面的宏指定串連時的命令列參數,-Ttext設定了.text段的地址,而TEXT_BASE在/U-Boot/board/.../config.mk中定義為0x0C700000。這些資訊最後都以硬式編碼方式記錄在程式映像檔案中,程式的入口_start
= TEXT_BASE = 0x0C700000 :
Disassembly of section .text:
0c700000 <_start>:
c700000: ea00000a b c700030 <reset>
...
但是,程式映像是燒寫到Flash中並開始執行的,而Flash的地址從0x00000000開始。於是,程式映像的第一條指令對齊到0x00000000處。相應的,這條adr指令的地址應對齊到0x00000048處,執行後r0等於0。
2. ldr r1, _TEXT_BASE
以下是arm-elf-objdump產生的反組譯碼代碼:
c70004c: e51f1034 ldr r1, [pc, #-52]
; c700020 <_TEXT_BASE>
由此可見,這裡的ldr並不是簡單的將_TEXT_BASE地址處的4位元組裝載到r1,而同樣是以pc為基址計算得到源地址的。這裡的pc = 0x4c + 8 = 0x54,於是該指令把0x54
– 52 = 0x20處的4位元組(即TEXT_BASE,亦即0x0C700000)裝載到r1。
3. 源大小的確定
通過上面的分析,我們已經有了一個概念:程式的實際執行地址與串連時指定的載入地址可能是不一樣的。我們已經得到BootLoader代碼開始的運行時開始地址,存放於r0,還需要計算它的運行時結束位址。運行時結束位址 =
運行時開始地址 + 程式碼片段大小。程式碼片段大小由.bss段的期望開始地址 - .text段的期望開始地址獲得。
小結
通過串連時的-Ttext選項,將.text段的地址寫入程式碼到程式映像中。雖然程式映像在Flash中執行,其實際執行地址與期望執行地址不一致,但在relocate之前,通過以pc為基址進行相對定址,使得這些代碼的執行與其實際裝載的地址無關