U-Boot:Startup Code - Relocation
来源:岁月联盟
时间:2008-10-23
发表于 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
程式裡有相当详细的注解。首先,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
这裡做的是「计算程式长度」的动作。把 _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 并没有什麽困难的地方。
--jollen