uboot rom 拷贝
一、code
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 = _start : current position of code */
ldr r1, _TEXT_BASE /*r1 = _TEST_BASE : test if we run from flash or RAM */
cmp r0, r1 /*r0 - r1 eq jump to stack_setup,nor flash 下面的case说明是ROM*/
beq stack_setup
ldr r2, _armboot_start /*r2 = _armboot*/
ldr r3, _bss_start /*r3 = _bss_start*/
sub r2, r3, r2 /* r2 : size of armboot */
add r2, r0, r2 /* r2 : test 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
二、简要说明
其中,
adr r0, _start
是取得_start行在内存(Nor Flash ROM)的实际位置,_TEXT_BASE则是RAM中的位置。
实际把plain binary格式的u-boot烧写到NOR Flash运行时,需要把Nor Flash中的
代码拷贝到RAM。当u-boot在NorFlash中运行时,adr r0,_start可以取得该行的
实际位置。
_armboot_start的值
_armboot_start:
.word _start
此值在编译时已经决定,为0x33F80000
_bss_start的值
_bss_start:
.word __bss_start
此值在编译时已经决定,即__bss_start的值,在link script中定义。
u-boot代码长度 = _armboot_start - _bss_start
u-boot代码末地址 = r2
ldmia : 每次取8个word
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
直到r0 > r2 ,其中ble表示 <=,即r0<=r2时循环拷贝。
三、简单的例子
test.S
.text
.global _start
_start:
ldr r0, test
adr r0, test
ldr r0, =test
ldr r0, =0x0
ldr r0, =0x3000000
nop
test:
nop
nop
test.lds
STACK_SIZE = 0X200;
OUTPUT_FORMAT(elf32-littlearm)
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS {
. = 0x3000;
.text : { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
makefile
all:test_adr.S
arm-linux-gnueabihf-gcc -c -o test_adr.o test_adr.S
arm-linux-gnueabihf-ld -T test.lds test_adr.o -o test_adr_elf
arm-linux-gnueabihf-objcopy -O binary -S test_adr_elf test_adr.bin
arm-linux-gnueabihf-objdump -DS -m arm test_adr_elf > test_adr.dis
的到的 反汇编文件
Disassembly of section .text:
00003000 <_start>:
3000: e59f0010 ldr r0, [pc, #16] ; 3018 <test>
3004: e28f000c add r0, pc, #12
3008: e59f0010 ldr r0, [pc, #16] ; 3020 <test+0x8>
300c: e3a00000 mov r0, #0
3010: e3a00403 mov r0, #50331648 ; 0x3000000
3014: e320f000 nop {0}
00003018 <test>:
3018: e320f000 nop {0}
301c: e320f000 nop {0}
3020: 00003018 andeq r3, r0, r8, lsl r0
分析
- 先分析第一条指令ldr r0,test被编译成ldr r0, [pc, #16] ; 3018 ,
即到当前PC+16的存储器取值,运行第一条指令时,PC其实已经是8了(流水线决定的)。
那么16+8等于0x18,所以r0等于e320f000 ,此指令的作用就是读取test地址处存放的值。由于此处放了一条nop,即得到nop的机器码, 注意这里 r0值是一个机器码,是一个指令,一般都指令寄存器处理 - 第二条adr r0,test被编译成add r0, pc, #12
这显然是依赖程序执行到此处的PC值。ADR是小范围地址读取伪指令,会将基于PC 相对偏移的地址值读取到寄存器中,此指令在3004地址,PC是3004+8=0x300c再加12,于是r0=0x3018。这个就是地址 - ldr r0,=test被编译成两个字,一个指令,一个文字池
执行到这里PC=0x3008, 0x3008+8+16=0x3020,所以在0x3020地址取值,编译器在此地址处放了00003018 ,00003018 是test的值,假如在Makefile指定连接地址是0x30000000,那么编译器放在这里的就是0x30000018,可见,这个值是编译时确定的。是个地址
最后一行andeq r3, r0, r8, lsl r0大概是编译器的机械动作,把一个数字翻译成了指令。
总结:
adr 相对偏移, 小范围, 就是取标号的地址值
ldr without =,取此标号内容,可能是指令,可能是地址;
ldr with =,就是这个标号的地址值,ldr跟带等于的立即数, 就是把此立即数当做地址值赋值给相关寄存器
四、 指令参考
adr ldr
cmp
ldmia stmia
start.S简析
start.S详细
boot spl 系列, 建议细看