#转载内容
Ldr和bl在启动程序中,都是可以负责pc跳转的指令。
1)bl是地址无关指令,和当前的运行地址无关,链接器脚本中标明了一个运行地址,但是arm中的代码实际是从地址0开始运行的。这个时候实际的地址和运行地址不相符,如果想让程序正常的运行,就必须使用地址无关指令。比如在完成将程序复制到内存之前想要跳转到一个函数里,就必须使用bl,因为bl跳转依靠的是相对地址,和运行地址无关,所以能完成跳转。
2)Ldr是地址有关指令,如果这个时候使用“ldr pc,=函数名”来跳转,实际上是跳转到这个函数在链接器脚本中标明的地址上了。所以使用地址相关指令之前,要把代码复制到链接器脚本中指明的那个地址上,否则程序就跑飞了。复制完成之后再使用ldr跳转到内存中,使程序继续运行。
#转载结束
测试代码出现的问题:
/*load program to SDRAM and clear bss */
bl copy_sdram
bl clean_bss
/*init uart port 0 and test*/
ldr pc , =test_uart
/*LED Flush indicate code error*/
halt:
bl led
b halt
程序说明:代码重定位到SDRAM后,跳转到SDRAM中的test_uart空函数位置执行,然后返回执行led函数点亮LED,运行后LED不亮,查看反汇编代码如下:
30000050: e59ff010 ldr pc, [pc, #16] ; 30000068 <.text+0x68>
30000068: 30000664 andcc r0, r0, r4, ror #12
30000664
30000664: e1a0c00d mov ip, sp
30000668: e92dd800 stmdb sp!, {fp, ip, lr, pc}
3000066c: e24cb004 sub fp, ip, #4 ; 0x4
30000670: e89da800 ldmia sp, {fp, sp, pc}
test_uart执行后lr寄存器存入栈中,程序结束lr赋值给pc,但是lr的值并不是下一条指令地址,ldr指令不是bl指令,没有保存pc到lr的操作,所以程序跑飞了。
以后编程需要注意,LDR PC,=函数名的函数内部需要设置死循环,否则返回指令后会跑飞。或者按如下代码,强行给lr寄存器赋值下一条指令地址:
mov lr ,pc
add lr ,lr, #10
ldr pc , =test_uart
因为arm流水线,下一条指令是pc+8,再根据ldr pc , =test_uart是mov lr ,pc执行后的第二个指令,lr=lr+(8+2),就能正常返回。
或者:
ldr lr ,=halt //halt是跳转地址
ldr pc , =test_uart