重定位
NOR与NAND:
上电时(2440为例):
- nandflash不能本地运行,需要将前4K内容复制到2440的片内sdram
若程序超过4k时,则前4k需要把全部程序重定位到整个SDRAM上。
所以需要写时(全局变量,静态变量),需要重定位到SDRAM中才能修改其值。
- norflash则可以直接在本地运行(cpu可以直接读flash的值,但是不能直接写,需要一定的写序列才能写)
为了让在norflash启动时,能够修改数据段,所以需要把数据段重定位到SDRAM地址上,查看原理图和手册,可以知道SDRAM的基地址是0x30000000
原始方法:在链接时,指定data段链接在0x3000 0000地址上
原始方法有个很大的弊端,就是编译出来的bin文件特别大,以至于flash不够放,所以需要更改策略。
重定位两种策略:
- 代码段链接地址从0开始,将代码段和数据段靠在一起,然后烧写值norflash,在运行时,将数据段复制到sdram(0x3000 0000),这样就是减少编译出来的bin文件大小,在运行代码是重定位。
- 代码段链接地址从0x3000 0000(sdram基地址)开始,然后烧写至norflash(0地址)上,运行代码时,将整个代码(代码段+数据段)重定位到sdram(0x3000 0000)上。
为什么在flash运行时能够从0开始运行,而与链接地址无关?因为用了位置无关码。
位置无关程序:
- 使用相对跳转指令,例b、bl汇编代码,是相对跳转,即使用偏移地址。
- 重定位之前,不可使用绝对地址,不可访问全局变量/静态变量/有初始值的数组(会使用绝对地址访问,会访问sdram,此时sdram并未重定位值过去)。
使用bl命令时,程序仍然在nor/sram执行,当需要跳到sdram地址时,需要用到绝对地址pc绝对跳转。
重定位过程
- 链接时根据链接脚本确定内存各段在二进制文本中的位置。
如:
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800) //0x30000000表示该段链接的位置,0x800表示处于二进制文本的位置
{
data_load_addr = LOADADDR(.data);
data_start = . ;
*(.data)
data_end = . ;
}
.bss : { *(.bss) *(.COMMON) }
}
上述连接脚本表示文本段在二进制文本的0地址上,然后以此排放只读数据段(.rodata)、数据段(.data),bss段不放在二进制文本中,内存分布如下图。
- 编译出来的二进制可执行文件下载到Flash上,然后可以根据需要重定位某个段或者重定位整个程序,如:
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE
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 clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
其中,
_start
是程序运行时的位置,即下载代码到flash的位置,一般都是0,_TEXT_BASE
则是定义的链接地址,对应宏CONFIG_SYS_TEXT_BASE
,这个宏一般在include/configs/xx.h
或在board/xx/config.ms
里定义。
所以cpu先判断当前地址_start
和链接地址_TEXT_BASE
是否相同,不相同则将程序复制到链接地址上,从而完成重定位。
通过链接程序(.lds)来分析程序内存分布,确定各段链接地址,然后在Flash运行代码时根据链接地址将代码重定位到SDRAM。