我们知道,Bootloader在启动后,刚开始在FLASH中运行,后来复制到内存中,在内存中进行运行,FLASH在系统中的起始地址通常为0x0,内存在系统中的位置确是另外的地址。我们还知道,Bootloader中有很多函数的调用,因此程序中会有很多跳转指令。那么这些跳转指令是如何统一这个地址问题的呢?
现在Bootloader功能越来越丰富,也就是说越来越多的代码需要在内存中运行。Bootloader按自身在内存中的定位来编译连接代码,就解决了在内存中运行碰到的地址问题。但 这样,在系统刚启动的时候,最开始执行的汇编代码是如何来解决这个地址问题的呢?
我们先看一段XSCALE系统的Bootloader代码的反汇编。
命令#arm-linux-objdump –D x-boot255-elf,首先看入口第一条代码:
Disassembly of section .text:
a3f80000 <_ld_text_start>:
a3f80000: eaffffff b a3f80004 <reset>
这就是跳转指令,地址a3f80000其实是Bootloader搬移到SDRAM后的地址。那现在的问题是:系统复位时,执行的是在FLASH里面的代码,即0x0地址处的代码。那么这个第一条指令(b a3f80004)会把我们带到哪里去呢?
如果真把我们带到0xa3f80004的地址,那么系统也就挂了?因为这个地址是内存地址,这是,内存根本就没有初始化。也没有代码数据,那跳转过去,不就是自寻短见吗?
只要有正常的思维,都知道上面的假设不可能发生。可机器代码写的一清二楚,这里面的奥秘在哪呢?
首先去看看ARM里面的跳转指令。B指令的格式如下(这里不能显示图片,请查阅“ARM体系结构与编程”)
其中: cond是条件码,
L决定是否保存返回地址。
Signed_immed_24是个有符号数,它表示跳转目标与当前PC指针值之间的偏移值的4倍。也就是说把Signed_immed_24左移2位就是实际的与PC指针的偏移值。
指令eaffffff的具体含义如下:
条件码cond: 0xe
L的值为0。0xa = 1010B
有符号数Signed_immed_24的补码为:ffffff,其值为:-1,再左移两位,就是数值-4。这里是不是比较奇怪了?
其实只有了解ARM的体系结构,就会知道:ARM是流水线结构,存在着指令预取的问题,XSCALE中PC指针的值就是当前执行指令的后两条,每条指令是32位(4字节)。也就是说在执行第一条指令的时候,PC的值为8,减掉4,还有4。也就是说跳转到偏移为4的地址中去。
那么为什么反汇编的时候,显示的指令是b a3f80004,而不是b 00000004呢?这是因为Bootloader代码的设置为a3f80000开始的缘故。
那么当系统把代码拷贝到内存后,如何把代码从0x0基地址的区域跳转到0xa3f80000为基地址的区域呢?
看代码从汇编跳转到C语言主函数的跳转吧。反汇编如下:
a3f80040: e59fd24c ldr sp, [pc, #588] ; a3f80294 <copy_loop+0x24>
a3f80044: e59f024c ldr r0, [pc, #588] ; a3f80298 <copy_loop+0x28>
a3f80048: e1a0f000 mov pc, r0
a3f80294: a4000000 strge r0, [r0]
a3f80298: a3f823d8 mvnges r2, #1610612739 ; 0x60000003
a3f823d8 <c_main>:
a3f823d8: e3a02001 mov r2, #1 ; 0x1
从代码可以看到,
设置堆栈值为: 0xa4000000
然后通过使用ldr指令和mov指令,让程序跳转到0xa3f823d8。这样程序从汇编程序跳转到了C语言程序,也从FLASH中(0x0开始的区域)跳转到内存中(0xa3f823d8)。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/liuqx/archive/2008/10/18/3094763.aspx