从反汇编来看U-boot在PPC架构下的PIC(位置无关程序)设计
Sailor_forever sailing_9806#163.com 转载请注明
http://blog.csdn.net/sailor_8318/archive/2009/11/14/4811785.aspx
【摘要】本文详细分析了U-boot1.3.4在PPC架构下的位置无关程序设计的细节。首先介绍了PIC的相关概念,接着从汇编指令的角度分析了如何访问函数及全局变量。最后介绍了U-boot为了在PPC下实现位置无关程序所做的一系列调整。
【关键字】PPC MPC8270 PIC GOT
目录
1 PIC(位置无关程序)的基本概念 1
2 如何访问函数 2
2.1 相对跳转 2
2.2 绝对跳转 4
3 如何访问全局变量 5
3.1 汇编代码 6
3.2 C代码 8
4 U-boot如何实现PIC 9
1 PIC(位置无关程序)的基本概念
程序经过编译链接后,每一个符号(函数、全局变量及汇编程序里的标号)都将对应一个特定的地址,计算机程序见到的符号就是这个地址。
PIC(位置无关程序)指当程序的加载位置和程序的链接地址不等时,程序仍然可以正确访问函数及变量。否则即为位置相关程序,程序必须加载到其链接地址运行,这种相关性对于嵌入式bootloader阶段的有很大的限制,因为各种各样的原因,在启动阶段程序大多数时候不是运行在链接地址上的。
对于具备操作系统的application程序而言,其都是PIC(位置无关程序),具备动态加载的能力。试想,各个应用程序在内存中都占据了一个特定的地址,并且必须是不同的,但是你在编译应用程序时指定过链接脚本么?执行时指定了加载到某个位置么?没有!!既然application开发者未指定程序的编译链接及运行地址,那是谁捣的鬼呢?那只能是操作系统了,在application执行前,操作系统会根据内存分布情况自动申请一块可用的地址空间,然后对application映像进行重地位,并对函数及变量的访问进行修正,从而正确解析。
但是在嵌入式bootloader的启动阶段是没有OS的,这种位置无关的特性就需要开发者自己实现了。要深刻的理解PIC(位置无关程序),首先我们从反汇编的角度来看看,程序是如何访问函数及全局变量的。
2 如何访问函数
首先,我们获得源代码和反汇编代码的对应关系。
ppc_82xx-objdump -S u-boot > u-boot_mix.S
ppc_82xx-objdump -d u-boot > u-boot.S
u-boot为ELF格式的映像,其包含了各种符号调用的相关信息,和application的格式一样。但是嵌入式平台启动阶段没有加载器,烧录到flash中的是BIN格式的二进制映像。
2.1 相对跳转
相对跳转指令包括BL和B,其都是基于PC来寻址的。
40000100 <_start>:
40000100: 3a a0 00 01 li r21,1
40000104: 60 00 00 00 nop
40000108: 48 00 00 10 b 40000118 <boot_cold>
4000010c: 00 00 00 00 .long 0x0
40000110 <_start_warm>:
40000110: 3a a0 00 02 li r21,2
40000114: 48 00 00 04 b 40000118 <boot_cold>
40000118 <boot_cold>:
40000118: 7c a0 00 a6 mfmsr r5
4000011c: 48 00 30 5d bl 40003178 <init_8260_core>
b boot_cold指令对应的地址为40000108,boot_cold函数对应的地址标号为40000118,差值为40000118-40000108=0x10
b boot_cold对应的汇编指令为48 00 00 10,其中48为操作数,表示跳转指令,10为操作码,为下一条指令相对于当前PC的偏移量,其可正可负。
在程序映像中,b boot_cold和boot_cold函数的距离为0x10,无论执行b boot_cold语句时当前PC值为多少,下一条待取指的指令地址为PC+0x10,也就对应着boot_cold函数所在的位置。这种就是位置无关代码。
40000118 <boot_cold>:
40000118: 7c a0 00 a6 mfmsr r5
bl init_8260_core
4000011c: 48 00 30 5d bl 40003178 <init_8260_core>
40003178 <init_8260_core>:
40003178: 38 60 20 02 li r3,8194
4000317c: 50 a3 06 72 rlwimi r3,r5,0,25,25
40003180: 50 a3 05 6c rlwimi r3,r5,0,21,22
函数调用bl init_8260_core的汇编指令为48 00 30 5d,指令地址为4000011c,init_8260_core函数对应的指令地址为40003178,与指令bl init_8260_core的距离为40003178 - 4000011c = 0x305d,正好为指令的48 00 30 5d操作码30 5d。原理同上,不管执行语句bl init_8260_core时的PC为何值,总能够正确寻址到函数init_8260_core。这种情况属于PC+的寻址。
B指令仅存在于手动编写的汇编代码中,而对应c代码中的函数调用,汇编指令是BL。
void board_init_f (ulong bootflag)
{
。。。
memcpy (id, (void *)gd, sizeof (gd_t));
400058bc: 7c 40 13 78 mr r0,r2
400058c0: 80 7f 00 0c lwz r3,12(r31)
400058c4: 7c 04 03 78 mr r4,r0
400058c8: 38 a0 00 4c li r5,76
400058cc: 4b ff f7 25 bl 40004ff0 <memcpy>
。。
}
40004ff0 <memcpy>:
40004ff0: 54 a7 e8 ff rlwinm. r7,r5,29,3,31
40004ff4: 38 c3 ff fc addi r6,r3,-4
40004ff8: 38 84 ff fc addi r4,r4,-4
40004ffc: 41 82 00 28 beq- 40005024 <memcpy+0x34>
函数调用memcpy(id, (void *)gd, sizeof (gd_t))最终对应的跳转语句为bl 40004ff0 <memcpy>,汇编指令为4b ff f7 25,指令地址为400058cc ,memcpy函数对应的指令地址为40004ff0,在指令bl 40004ff0 <memcpy>前方,因此属于PC-寻址。距离为FFFFFF + 1 -(400058cc-40004ff0)= ff f7 24
其中FFFFFF为操作码的最大值,而指令4b ff f7 25从操作数为ff f7 25,我们知道RISC指令集都是4字节编码,对于PC来说,其值必须是4字节对齐的,此处的5非对齐,可能表示PC-寻址。
2.2 绝对跳转
绝对跳转指令通常是对PC直接进行赋值的,可达到此效果的指令包括BLR、RFI
40000100 <_start>:
. = EXC_OFF_SYS_RESET
.globl _start
_start:
li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH*/
40000100: 3a a0 00 01 li r21,1
。。。。。。。。。
lis r3, CFG_MONITOR_BASE@h
40000134: 3c 60 40 00 lis r3,16384
ori r3, r3, CFG_MONITOR_BASE@l
40000138: 60 63 00 00 ori r3,r3,0
addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET
4000013c: 38 63 01 48 addi r3,r3,328
mtlr r3
40000140: 7c 68 03 a6 mtlr r3
blr
40000144: 4e 80 00 20 blr
40000148 <in_flash>:
in_flash:
/* initialize some things that are hard to access from C */
/*--------------------------------------------------------------*/
lis r3, CFG_IMMR@h /* set up stack in internal DPRAM */
待跳转的指令地址为r3,其计算过程如下:
addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET
4000013c: 38 63 01 48 addi r3,r3,328
其中CFG_MONITOR_BASE为40000000,EXC_OFF_SYS_RESET为0x100
40000148 - 40000100 + 0x100+ 40000000 = 40000148
即为in_flash标号的编译链接地址,blr会将LR链接寄存器中保存的40000148赋值给PC,实现绝对跳转
中断返回指令rfi可以同时更新MSR和PC,SRR1中的位0、5~9和16~31被放在MSR中对应的位置,SRR0[0-29]赋值给PC,通常为中断前下一条指令的地址
int_return:
。。。。。
lwz r0,_MSR(r1)
40003154: 80 01 00 94 lwz r0,148(r1)
mtspr SRR0,r2
40003158: 7c 5a 03 a6 mtsrr0 r2
mtspr SRR1,r0
4000315c: 7c 1b 03 a6 mtsrr1 r0
lwz r0,GPR0(r1)
40003160: 80 01 00 10 lwz r0,16(r1)
lwz r2,GPR2(r1)
40003164: 80 41 00 18 lwz r2,24(r1)
lwz r1,GPR1(r1)
40003168: 80 21 00 14 lwz r1,20(r1)
SYNC
4000316c: 7c 00 04 ac sync
40003170: 4c 00 01 2c isync
rfi
40003174: 4c 00 00 64 rfi
另外RFI指令在进行MMU映射时也有特定应用,在Uboot中未涉及。
3 如何访问全局变量
我们知道对于全局变量的访问,首先要获取该变量的全局地址,而这个地址是编译链接时生成的一个地址,对于没有-fpic选项编译的程序,此值是一个静态的地址,当程序从flash重新定位到ram后,访问的地址仍然在Flash中,那么如何读写呢??
这就需要一种机制来保证获取的全局变量的地址是一个动态的值,也是说当程序重定位到RAM中后,这个地址也需要随之修正。而U-boot正是将所有全局符号的地址保存在一个表中GOT,程序访问变量时先从这个表中动态获取该符号的地址,而这个地址在重定位时已经修正过了,从而保证能从重定位后的地址处正确访问全局变量。
链接脚本中定义了一个.got2段
.reloc :
{
*(.got)
_GOT2_TABLE_ = .;
*(.got2)
_FIXUP_TABLE_ = .;
*(.fixup)
}
__got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2;
所有全局符号的地址都保存在*(.got2)中,每个符号的地址占4个字节,_GOT2_TABLE_是该段在代码段中的起始地址,__got2_entries是全局符号的个数。
以下是U-boot.map中*(.got2)的详细列表
.reloc 0x4003f000 0x1318
*(.got)
0x4003f000 _GOT2_TABLE_ = .
*(.got2)
.got2 0x4003f000 0x24 cpu/mpc8270/start.o
.got2 0x4003f024 0x4c cpu/mpc8270/libmpc8270.a(traps.o)
.got2 0x4003f070 0x20 cpu/mpc8270/libmpc8270.a(cpu_init.o)
。。。。。。。。。。
.got2 0x40040174 0x18 lib_generic/libgeneric.a(vsprintf.o)
.got2 0x4004018c 0x8c lib_generic/libgeneric.a(zlib.o)
.got2 0x40040218 0x0 lib_generic/libgeneric.a(ctype.o)
.got2 0x40040218 0x4 cpu/mpc8270/libmpc8270.a(serial_smc.o)
.got2 0x4004021c 0x50 cpu/mpc8270/libmpc8270.a(cpu.o)
.got2 0x4004026c 0x30 cpu/mpc8270/libmpc8270.a(speed.o)
.got2 0x4004029c 0x48 cpu/mpc8270/libmpc8270.a(ether_fcc.o)
.got2 0x400402e4 0x28 lib_ppc/libppc.a(bootm.o)
.got2 0x4004030c 0x0 lib_ppc/libppc.a(cache.o)
.got2 0x4004030c 0x8 drivers/rtc/librtc.a(date.o)
.got2 0x40040314 0x4 common/libcommon.a(miiphybb.o)
0x40040318 _FIXUP_TABLE_ = .
*(.fixup)
0x000004c6 __got2_entries = ((_FIXUP_TABLE_ - _GOT2_TABLE_) >> 0x2)
0x00000000 __fixup_entries = ((. - _FIXUP_TABLE_) >> 0x2)
由上述表可知*(.got)和*(.fixup)为空
3.1 汇编代码
在PPC架构的start.S中定义了如下几个变量
/*
* Set up GOT: Global Offset Table
*
* Use r14 to access the GOT
*/
START_GOT
GOT_ENTRY(_GOT2_TABLE_)
GOT_ENTRY(_FIXUP_TABLE_)
GOT_ENTRY(_start)
GOT_ENTRY(_start_of_vectors)
GOT_ENTRY(_end_of_vectors)
GOT_ENTRY(transfer_to_handler)
GOT_ENTRY(__init_end)
GOT_ENTRY(_end)
GOT_ENTRY(__bss_start)
#if defined(CONFIG_FADS)
GOT_ENTRY(environment)
#endif
END_GOT
相关的宏在./include/ppc_asm.tmpl中定义:
#define START_GOT /
.section ".got2","aw"; /
.LCTOC1 = .+32768
#define END_GOT /
.text
#define GET_GOT /
bl 1f ; /
.text 2 ; /
0: .long .LCTOC1-1f ; /
.text ; /
1: mflr r14 ; /
lwz r0,0b-1b(r14) ; /
add r14,r0,r14 ;
#define GOT_ENTRY(NAME) .L_ ## NAME = . - .LCTOC1 ; .long NAME
#define GOT(NAME) .L_ ## NAME (r14)
关于这些符号的详细解释可以参考http://coryxie.spaces.live.com/Blog/cns!9429CA1F587766E2!208.entry
总体来说,START_GOT用于定义表的开始,END_GOT用于定义表的结束,GOT_ENTRY用于将全局变量NAME的地址写入表中,GOT用于从表中读出NAME的地址,GET_GOT用于将表进行初始化。
START_GOT定义了段"got2",属性为"allocatable and writable",并定义了变量.LCTOC1。如果设表的起始地址为TABLE_START,则.LCTOC1的值为TABLE_START+0x8000。至于为什么是0x8000,因为PPC中立即数的最大值为16位,以TABLE_START+0x8000为中心时即可前后寻址0x8000的空间,因为got2段的最大长度不能超过0x10000
END_GOT定义为子段text 0的开始,也就是结束got2段的定义。
GOT_ENTRY定义了变量.L_NAME,其值为当前表项的地址(.)-.LCTOC1。如果设NAME的表项偏移地址为NAME_OFFSET,那么.L_NAME = . - .LCTOC1 = TABLE_START + NAME_OFFSET - ( TABLE_START + 0x8000 ) = NAME_OFFSET - 0x8000,是一个相对距离。之后将全局变量NAME的地址写入当前表项,这些地址值是在链接的时候确定的。
GET_GOT用于初始化GOT表。首先程序跳转到标号为"1"的地址处(bl 1f),然后将lr的值赋值给r14(此时lr的值为当前实际运行时"1"的地址值)。然后令r0 = 0b - 1b(r14),0b为"0"处的地址值,1b为"1"处的地址值。这样r0就等于"0"处的值,也就是.LCTOC1-1f。最后r14 = r0 + r14 = .LCTOC1 - 1f + r14
当程序当前加载的地址等于链接的地址时,r14 = .LCTOC1,也就是等于编译链接时GOT表的0x8000偏移处。否则r14是动态修正过的当前实际代码GOT表的0x8000偏移处。这就保证了无论哪种情况在汇编代码中都可以争取获得全局变量的地址。
GOT(NAME)的值定义为.L_NAME(r14),这里面r14的值由GET_GOT初始化,也就是.LCTOC1的值。这样GOT(NAME) = .L_NAME + r14 = .L_NAME + .LCTOC1 = NAME_OFFSET - 0x8000 + TABLE_START + 0x8000 = NAME_OFFSET + TABLE_START,也就是NAME所在表项的地址。这样,通过查表,就可以找到当初存储在表中的名字为NAME的offset值。
GOT(NAME)用于在汇编代码中访问全局变量。
3.2 C代码
C文件在-fPIC编译选项的控制下,会自动将全局变量的地址添加到.got2段中,同时在访问全局变量时先获取该变量在.got2段中的位置,从此位置读取修正后的全局变量的地址。
serial_setbrg (void)
{
4003432c: 94 21 ff f0 stwu r1,-16(r1)
40034330: 7c 08 02 a6 mflr r0
40034334: bf c1 00 08 stmw r30,8(r1)
40034338: 90 01 00 14 stw r0,20(r1)
4003433c: 7c 3f 0b 78 mr r31,r1
40034340: 42 9f 00 05 bcl- 20,4*cr7+so,40034344 <serial_setbrg+0x18>
40034344: 7f c8 02 a6 mflr r30
40034348: 80 1e ff e4 lwz r0,-28(r30)
4003434c: 7f c0 f2 14 add r30,r0,r30
#if defined(CONFIG_CONS_USE_EXTC)
m8260_cpm_extcbrg(brg_map[SMC_INDEX], gd->baudrate,
CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL);
#else
m8260_cpm_setbrg(brg_map[SMC_INDEX], gd->baudrate);
40034350: 81 3e 80 00 lwz r9,-32768(r30)
40034354: 88 09 00 01 lbz r0,1(r9)
40034358: 54 00 06 3e clrlwi r0,r0,24
4003435c: 7c 0b 03 78 mr r11,r0
40034360: 7c 49 13 78 mr r9,r2
40034364: 80 09 00 08 lwz r0,8(r9)
40034368: 7d 63 5b 78 mr r3,r11
4003436c: 7c 04 03 78 mr r4,r0
40034370: 4b fd 08 ad bl 40004c1c <m8260_cpm_setbrg>
#endif
}
40034374: 81 61 00 00 lwz r11,0(r1)
40034378: 80 0b 00 04 lwz r0,4(r11)
4003437c: 7c 08 03 a6 mtlr r0
40034380: bb cb ff f8 lmw r30,-8(r11)
40034384: 7d 61 5b 78 mr r1,r11
40034388: 4e 80 00 20 blr
4003434c: 7f c0 f2 14 add r30,r0,r30
此句之后,r30即为本C文件的全局变量在.got段中能够首地址的0x8000偏移处,-32768(r30)即可获得第一个表项在.got段中的地址
40034350: 81 3e 80 00 lwz r9,-32768(r30)
将经过修正过的brg_map的地址加载到r9中。
4 U-boot如何实现PIC
除了上面说到链接脚本中的.got2段和编译选项-fPIC外,实际上还有很多地方需要修正。
/*
* Set up GOT: Global Offset Table
*
* Use r14 to access the GOT
*/
START_GOT
GOT_ENTRY(_GOT2_TABLE_)
GOT_ENTRY(_FIXUP_TABLE_)
GOT_ENTRY(_start)
GOT_ENTRY(_start_of_vectors)
GOT_ENTRY(_end_of_vectors)
GOT_ENTRY(transfer_to_handler)
GOT_ENTRY(__init_end)
GOT_ENTRY(_end)
GOT_ENTRY(__bss_start)
#if defined(CONFIG_HYMOD)
GOT_ENTRY(environment)
#endif
END_GOT
此处定义了几个变量用于重定位时的代码拷贝
#ifndef CFG_RAMBOOT
/* When booting from ROM (Flash or EPROM), clear the */
/* Address Mask in OR0 so ROM appears everywhere */
/*--------------------------------------------------------------*/
lis r3, (CFG_IMMR+IM_REGBASE)@h
lwz r4, IM_OR0@l(r3)
li r5, 0x7fff
and r4, r4, r5
stw r4, IM_OR0@l(r3)
/* Calculate absolute address in FLASH and jump there */
/*--------------------------------------------------------------*/
lis r3, CFG_MONITOR_BASE@h
ori r3, r3, CFG_MONITOR_BASE@l
addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET
mtlr r3
blr
in_flash:
#endif /* CFG_RAMBOOT */
当从Flash中启动时,此处第一次实现绝对跳转,跳转到链接地址处运行
GET_GOT /* initialize GOT access */
初始化r14为当前.got2段的0x8000处的偏移,便于cpu_init_f代码中能正确访问全局变量,但此时程序在Flash中运行,全局变量是只读的。
.rodata 0x4003ccac 0x92 board/eric8270/liberic8270.a(eric8270.o)
0x4003ccac iop_conf_tab
static void config_8260_ioports (volatile immap_t * immr)
{
40003d6c: 94 21 ff b8 stwu r1,-72(r1)
40003d70: 7c 08 02 a6 mflr r0
40003d74: bf c1 00 40 stmw r30,64(r1)
40003d78: 90 01 00 4c stw r0,76(r1)
40003d7c: 7c 3f 0b 78 mr r31,r1
40003d80: 42 9f 00 05 bcl- 20,4*cr7+so,40003d84 <config_8260_ioports+0x18>
40003d84: 7f c8 02 a6 mflr r30
40003d88: 80 1e ff e4 lwz r0,-28(r30)
40003d8c: 7f c0 f2 14 add r30,r0,r30
40003d90: 90 7f 00 38 stw r3,56(r31)
int portnum;
。。。
}
bl board_init_f /* run 1st part of board init code (in Flash)*/
void board_init_f (ulong bootflag)
{
40005610: 94 21 ff c8 stwu r1,-56(r1)
40005614: 7c 08 02 a6 mflr r0
40005618: bf c1 00 30 stmw r30,48(r1)
4000561c: 90 01 00 3c stw r0,60(r1)
40005620: 7c 3f 0b 78 mr r31,r1
40005624: 42 9f 00 05 bcl- 20,4*cr7+so,40005628 <board_init_f+0x18>
40005628: 7f c8 02 a6 mflr r30
4000562c: 80 1e ff e4 lwz r0,-28(r30)
40005630: 7f c0 f2 14 add r30,r0,r30
40005634: 90 7f 00 28 stw r3,40(r31)
。。。。。。
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
40005640: 80 1e 80 2c lwz r0,-32724(r30)
40005644: 90 1f 00 08 stw r0,8(r31)
40005648: 48 00 00 30 b 40005678 <board_init_f+0x68>
if ((*init_fnc_ptr) () != 0) {
4000564c: 81 3f 00 08 lwz r9,8(r31)
40005650: 80 09 00 00 lwz r0,0(r9)
40005654: 7c 09 03 a6 mtctr r0
40005658: 4e 80 04 21 bctrl
4000565c: 7c 60 1b 78 mr r0,r3
40005660: 2f 80 00 00 cmpwi cr7,r0,0
40005664: 41 9e 00 08 beq- cr7,4000566c <board_init_f+0x5c>
hang ();
40005668: 48 00 05 ed bl 40005c54 <hang>
4000566c: 81 3f 00 08 lwz r9,8(r31)
40005670: 38 09 00 04 addi r0,r9,4
40005674: 90 1f 00 08 stw r0,8(r31)
40005678: 81 3f 00 08 lwz r9,8(r31)
4000567c: 80 09 00 00 lwz r0,0(r9)
40005680: 2f 80 00 00 cmpwi cr7,r0,0
40005684: 40 9e ff c8 bne+ cr7,4000564c <board_init_f+0x3c>
}
}
。。。
依次执行init_sequence数组中保存的各个函数,能够正确访问init_sequence数组,但是数组里面保存的函数指针是编译链接时的静态值,当绝对跳转至函数入口时,要求此处必须有程序映像,因此此时对于变量的访问是位置无关的,但是对于函数指针的访问却是位置相关的,这也就意味着在Flash中的运行的代码必须在链接地址处运行。
relocate_code (addr_sp, id, addr);
.globl relocate_code
relocate_code:
mr r1, r3 /* Set new stack pointer */
在PPC架构下r1为栈指针,建立新的栈,此时的SP处于SDRAM中
400033e4: 7c 61 1b 78 mr r1,r3
mr r9, r4 /* Save copy of Global Data pointer */
400033e8: 7c 89 23 78 mr r9,r4
mr r10, r5 /* Save copy of Destination Address */
400033ec: 7c aa 2b 78 mr r10,r5
mr r3, r5 /* Destination Address */
400033f0: 7c a3 2b 78 mr r3,r5
lis r4, CFG_MONITOR_BASE@h /* Source Address */
400033f4: 3c 80 40 00 lis r4,16384
ori r4, r4, CFG_MONITOR_BASE@l
400033f8: 60 84 00 00 ori r4,r4,0
lwz r5, GOT(__init_end)
400033fc: 80 ae 80 18 lwz r5,-32744(r14)
sub r5, r5, r4
40003400: 7c a4 28 50 subf r5,r4,r5
li r6, CFG_CACHELINE_SIZE /* Cache Line Size */
40003404: 38 c0 00 20 li r6,32
从CFG_MONITOR_BASE处拷贝代码,因此链接地址处必须有映像备份
lwz r5, GOT(__init_end)
400033fc: 80 ae 80 18 lwz r5,-32744(r14)
即为在汇编代码中访问全局变量__init_end,__init_end的值在链接脚本中定义,其为程序映像除bss段的末地址
/*
* Fix GOT pointer:
*
* New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address
*
* Offset:
*/
sub r15, r10, r4
/* First our own GOT */
add r14, r14, r15
/* then the one used by the C code */
add r30, r30, r15
当程序重定位到RAM中后,.got2段的首地址也发生变化,对其进行修正,r14用于汇编代码,r30用于C代码
/*
* Now relocate code
*/
。。。。
7: sync /* Wait for all icbi to complete on bus */
Isync
将整个映像拷贝到SDRAM中,此地址是RAM的高端地址,是动态计算出来的
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET
mtlr r0
blr
in_ram:
blr实现绝对跳转,此时程序在RAM中运行
/*
* Relocation Function, r14 point to got2+0x8000
*
* Adjust got2 pointers, no need to check for 0, this code
* already puts a few entries in the table.
*/
li r0,__got2_entries@sectoff@l
400034d4: 38 00 04 c6 li r0,1222
la r3,GOT(_GOT2_TABLE_)
400034d8: 38 6e 80 00 addi r3,r14,-32768
lwz r11,GOT(_GOT2_TABLE_)
400034dc: 81 6e 80 00 lwz r11,-32768(r14)
mtctr r0
400034e0: 7c 09 03 a6 mtctr r0
sub r11,r3,r11
400034e4: 7d 6b 18 50 subf r11,r11,r3
addi r3,r3,-4
1: lwzu r0,4(r3)
add r0,r0,r11
stw r0,0(r3)
bdnz 1b
la r3,GOT(_GOT2_TABLE_)获得重定位后.got2段在RAM中的地址,lwz r11,GOT(_GOT2_TABLE_)获得.got2段链接时候的地址,二者之差即为所有全局符号在Flash和RAM中地址的偏移量,add r0,r0,r11将此偏移量添加到.got2表中,即实现了对所有全局符号地址的修正
/*
* Now adjust the fixups and the pointers to the fixups
* in case we need to move ourselves again.
*/
2: li r0,__fixup_entries@sectoff@l
lwz r3,GOT(_FIXUP_TABLE_)
cmpwi r0,0
mtctr r0
addi r3,r3,-4
beq 4f
3: lwzu r4,4(r3)
lwzux r0,r4,r11
add r0,r0,r11
stw r10,0(r3)
stw r0,0(r4)
bdnz 3b、
对.fixup段进行修正,修正原理同.got2,但实际上在U-boot中,此段为空
4000352c <clear_bss>:
4:
clear_bss:
/*
* Now clear BSS segment
*/
lwz r3,GOT(__bss_start)
4000352c: 80 6e 80 20 lwz r3,-32736(r14)
#if defined(CONFIG_HYMOD)
/*
* For HYMOD - the environment is the very last item in flash.
* The real .bss stops just before environment starts, so only
* clear up to that point.
*
* taken from mods for FADS board
*/
lwz r4,GOT(environment)
#else
lwz r4,GOT(_end)
40003530: 80 8e 80 1c lwz r4,-32740(r14)
#endif
cmplw 0, r3, r4
40003534: 7c 03 20 40 cmplw r3,r4
beq 6f
40003538: 41 82 00 18 beq- 40003550 <clear_bss+0x24>
GOT(__bss_start)和GOT(_end)分别获得bss段在RAM中的首末地址,清除bss段
mr r3, r9 /* Global Data pointer */
mr r4, r10 /* Destination Address */
bl board_init_r
跳转至RAM中的初始化函数
/************************************************************************
*
* This is the next part if the initialization sequence: we are now
* running from RAM and have a "normal" C environment, i. e. global
* data can be written, BSS has been cleared, the stack size in not
* that critical any more, etc.
*
************************************************************************
*/
void board_init_r (gd_t *id, ulong dest_addr)
{
400058e4: 94 21 ff b8 stwu r1,-72(r1)
400058e8: 7c 08 02 a6 mflr r0
400058ec: bf c1 00 40 stmw r30,64(r1)
400058f0: 90 01 00 4c stw r0,76(r1)
400058f4: 7c 3f 0b 78 mr r31,r1
400058f8: 42 9f 00 05 bcl- 20,4*cr7+so,400058fc <board_init_r+0x18>
400058fc: 7f c8 02 a6 mflr r30
40005900: 80 1e ff e4 lwz r0,-28(r30)
40005904: 7f c0 f2 14 add r30,r0,r30
40005908: 90 7f 00 28 stw r3,40(r31)
4000590c: 90 9f 00 2c stw r4,44(r31)
。。。。
debug ("Now running in RAM - U-Boot at: %08lx/n", dest_addr);
40005948: 80 7e 80 50 lwz r3,-32688(r30)
4000594c: 80 9f 00 2c lwz r4,44(r31)
40005950: 4c c6 31 82 crclr 4*cr1+eq
40005954: 48 01 8e d1 bl 4001e824 <printf>
"Now running in RAM - U-Boot at: %08lx/n"字符串的首地址也经过修正了
for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) {
ulong addr;
。。。
命令数组中的指令指针及注释字符串都是编译时获取的静态值,需要修正。当初始化的变量的值为指针及数组首地址时,都需要修正
#ifndef CFG_ENV_IS_NOWHERE
env_name_spec += gd->reloc_off;
#endif
修改环境变量的地址
/*
* Copy exception vector code to low memory
*
* r3: dest_addr
* r7: source address, r8: end address, r9: target address
*/
.globl trap_init
trap_init:
lwz r7, GOT(_start)
lwz r8, GOT(_end_of_vectors)
li r9, 0x100 /* reset vector always at 0x100 */
cmplw 0, r7, r8
bgelr /* return if r7>=r8 - just in case */
mflr r4 /* save link register */
1:
lwz r0, 0(r7)
stw r0, 0(r9)
addi r7, r7, 4
addi r9, r9, 4
cmplw 0, r7, r8
bne 1b
将中断处理相关的代码拷贝到reset vector开始的位置
/*
* relocate `hdlr' and `int_return' entries
*/
li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET
li r8, Alignment - _start + EXC_OFF_SYS_RESET
2:
bl trap_reloc
对中断处理过程中的函数指针进行修正
参考文章
http://coryxie.spaces.live.com/Blog/cns!9429CA1F587766E2!208.entry
http://www.linuxforum.net/forum/showflat.php?Board=embedded&Number=632242
http://blog.sina.com.cn/s/blog_4ae6bb59010005y6.html~type=v5_one&label=rela_prevarticle