AT(ldadr):定义本段存储(加载)的地址。
看一个简单的例子:
SECTIONS{
firtst0x00000000:{head.oinit.o}
second0x30000000:AT(4096){main.o}
}
以上,head.o放在0x00000000地址开始处,init.o放在head.o后面,他们的运行地址也是0x00000000,即连接和存储地址相同(没有AT指定);main.o放在4096(0x1000,是AT指定的,存储地址)开始处,但是它的运行地址在0x30000000,运行之前需要从0x1000(加载处)复制到0x30000000(运行处),此过程也就用到了读取Nandflash。这就是存储地址和连接(运行)地址的不同,称为加载时域和运行时域,可以在.lds连接脚本文件中分别指定。
编写好的.lds文件,在用arm-linux-ld连接命令时带-Tfilename来调用执行,如arm-linux-ld–Tnand.ldsx.oy.o–oxy.o。也用-Ttext参数直接指定连接地址,如arm-linux-ld–Ttext0x30000000x.oy.o–oxy.o。
总之:
连接地址<==>运行地址
存储地址<==>加载地址
既然程序有了两种地址,就涉及到一些跳转指令的区别,下面就来具体看看这些跳转指令。
ARM汇编中,常有两种跳转方法:b跳转指令、ldr指令向PC赋值。
(1)bstep1:b跳转指令是相对跳转,依赖当前PC的值,偏移量是通过该指令本身的bit[23:0]算出来的,这使得使用b指令的程序不依赖于要跳到的代码的位置,只看指令本身。
(2)ldrpc,=step1:该指令是从内存中的某个位置(step1)读出数据并赋给PC,同样依赖当前PC的值,但是偏移量是那个位置(step1)的连接地址(运行时的地址),所以可以用它实现从Flash到RAM的程序跳转。
我们以后会经常用到“存储地址和连接地址不同”(术语上称为加载时域和运行时域)的特性:大多机器上电时是从地址0开始运行的,但是从地址0运行程序在性能方面总有很多限制,(运行地址和加载地址不同时,只能用相对跳转)所以一般在开始的时候,使用与位置无关的指令将程序本身复制到它的连接地址处,然后使用向pc寄存器赋值的方法跳到连接地址开始的内存上去执行剩下的代码。