Chinaunix首页 | 论坛 | 博客
  • 博客访问: 674375
  • 博文数量: 118
  • 博客积分: 7172
  • 博客等级: 少将
  • 技术积分: 1799
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-12 13:33
文章分类

全部博文(118)

文章存档

2012年(5)

2011年(12)

2010年(18)

2009年(36)

2008年(47)

我的朋友

分类: LINUX

2008-04-15 13:48:19

U-Boot:Startup Code - Relocation

http://blog.chinaunix.net/u1/43047/showart_351142.html

jollen 發表於 February 8, 2007 10:22 PM
延續前一則日記的介紹,我們繼續以 s3c2410 的平臺為例。U-Boot 的 startup code(hardware bring-up code)for s3c2410 位於 cpu/arm920t/start.S,此部份的研究重心如下:
Monitor relocation.
Stack setup.
BSS clearing.
Jump to high-level language.

基本概念分述如後。不過,在開始前,必須具備幾個基本背景知識:
您必須先學會 ARM assembly 後再來閱讀此部份。
必須知道什麼叫「symbol(符號)」以及「memory address」。
能區分 symbol 與 variable 的差異。
一定要能看得懂 symbol table,以 U-Boot 為例,在編譯好 U-Boot 後便會產生檔名為 System.map 的 symbol table。

1. Monitor relocation.

一開始 U-Boot 是在 SMDK2410 的 nor flash 執行,所以 U-Boot 心須把自己由 nor flash 搬到 RAM,才能執行接下來的工作,這個動作就稱為 relocation。相關的程式片斷如下:
#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 */

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
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

程式裡有相當詳細的註解。首先,U-Boot 先把 symbol _start 載到 register r0,再把 symbol _TEXT_BASE 載到 register r1,然後比較 r0 是否等於 r1。如果 r0 等於 r1,表示 U-Boot 目前是在 RAM 裡頭,所以不必做 relocation。

symbol 代表一個記憶體位址(memory address),所以要找出 _start symbol 的 memory address,方式是由 board 的 linker script 來看,所以把 board/smdk2410/u-boot.lds 叫出來瞧瞧:
...
ENTRY(_start)
SECTIONS
{
. = 0x00000000;

. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
...
}

看懂 linker script 是您的功課,不過這個部份其實很直覺。程式的進入點(ENTRY command)定義為 _start symbol,而程式一開始的 value to current address(看到沒,就是 SECTIONS 後的那個小點點!)為 0x00000000,所以 _start symbol 代表的是 memory address 0x0。請不要用 _start 等於 0x0 的方式來解說,因為 _start 壓根兒就是一個 symbol 並不是 variable。

Linker script 的 "." 稱為 location counter,這是一個指定運算子,也就是「assign value to symbol」的意思。因此,_start symbol 為 address 0x0,且 value to _start symbol 為 0x00000000。

所以,總結來看 "adr r0, _start" 把 symbol _start 的 memory address 放到 r0,adr 是 ARM 的 pseudo-instruction;"ldr r1, _TEXT_BASE" 把 symbol _TEXT_BASE 的值放到 r1,ldr 是 ARM 的 memory addressing 指令,這裡用到的 addressing mode 是 direct addressing。

回頭找一下 symbol _TEXT_BASE,_TEXT_BASE 可就不在 linker script 裡頭了。把 start.S 程式移到開頭,就可以找到 _TEXT_BASE:
_TEXT_BASE:
.word TEXT_BASE

這是一個 symbol 的定義與 GNU assembly 的 variable 宣告語法,所以 symbol _TEXT_BASE 所代表的 memory address 之處,放了一個值(value),其值為 TEXT_BASE。TEXT_BASE 便是一個變數,此變數是 linking 階段(GNU ld)所定義的,以 U-Boot for SMDK2410 為例,便是 0x33F80000(board/smdk2410/config.mk):
TEXT_BASE = 0x33F80000

接下來的程式是:
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */

這裡做的是「計算程式長度」的動作。把 _armboot_start 放到 r2、_bss_start 放到 r3,然後 r2 = r3 - r2 算出要 relocation 的長度,此時 r2 放的便是 U-Boot 後面「要 relocate 到 RAM 的程長度」。下一行做 r2 = r0 + r2,很清楚,再把程式尾段的 memory address 算出來,所以 r2 最後放的是「source end address」。

這個時候就要把 symbol table 請出來看了。

先在 start.S 裡找到 _armboot_start:
.globl _armboot_start
_armboot_start:
.word _start

再對照 System.map:
33f80000 t $a
33f80000 T _start
33f80020 t $d
...

不過,_bss_start 可就不是對照 System.map 就能解決的了。概念上,_bss_start 是 linker script 所定義的 symbol,可用來表示程式「實體程式碼」的 end address;概念上來說雖然簡單,不過仍強烈建議了解 ELF 的格式與 .bss section 的整體觀念,畢道這是基本功,不可不練。

可在 U-Boot 程式碼裡頭找到這行宣告:
extern ulong _bss_start; /* code + data end == BSS start */

剩下來的部份就容易了,只要以上這些觀念都能確實掌握,startup code 並沒有什麼困難的地方。

延伸閱讀
Executable and Linking Format: 重要的 ELF 格式介紹
BSS Section Concepts: .bss section 的基本觀念介紹

--jollen


原文地址 http://www.jollen.org/blog/2007/02/u-boot_startup_code_relocation.html


阅读(1501) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~