这是一个特别有意思,有难度的指令,理解起来确实比较绕。
ADR——小范围的地址读取伪指令,该指令将基于PC的地址或基于寄存器的地址值读取到寄存器中。
这里特别注意,是“基于PC”,不是什么链接地址、绝对地址啥的,很固定,一定是基于PC.
语法格式: ADR {cond} reg,expr
其中:cond——可选的指令执行条件。
reg——目标寄存器。
expr——基于PC或基于寄存器的地址表达式。
使用说明:
在汇编编译器处理源程序时,ADR伪指令会被编译器替换成一条合适的指令,通常,编译器会用一条ADD指令或SUB指令来实现该ADR伪指令的功能,如果不能用一条指令来实现ADR伪指令的功能,编译器将会报错。
因为ADR伪指令中的地址是基于PC或基于寄存器的,所以ADR读取到的地址是一个与位置无关的地址,当ADR伪指令中的地址是基于PC时,该地址与ADR伪指令必须在同一个代码段中,因为ADR寻址范围有限,只能小范围。
示例1:
start:
MOV r0,#10
ADR r4,start
程序在运行到ADR r4,start时,由于ARM三、五级流水线的架构,此时PC = 执行地址 + 8,而start是在该条指令的前面,所以此时该条命令的目的是要读取start相对于PC的地址,很明显,start的地址是相对于PC - 8 -4 = PC - 12 = PC - 0x0C .所以本ADR伪指令将被编译器替换成 SUB r4,pc,#0x0C
示例2:
ADR r4,start
start:
MOV r0,#10
程序在运行到ADR r4,start时,同样的道理,此时PC = 执行地址 + 8,而start此时是在紧跟在这条指令的后面,所以start的地址是 “执行地址 + 4”,所以相对于 PC,start的地址为 PC - 4,所以本ADR伪指令将被编译器替换成 SUB r4,pc,#0x04
示例3:
ADR r4,start
nop;
nop;
nop;
nop;
start:
MOV r0,#10
程序在执行到ADR r4,start时,同样,PC = 执行地址 + 8,而start此时在该条指令的下面,所以start的地址为“执行地址 + 4*4(nop)”,也就是start地址为“执行地址 + 16”,所以start相对于PC 的地址为“16 - 8”,也就是 PC + 8,所以本ADR伪指令将被编译器替换成ADD r4,pc,#0x08
前面说了那么多,可能会觉得不以为然,这tmd那么绕,有什么用啊?有用,有大用,这里相当于告诉程序员一个容易忽略的好用功能,也就是ADR可以自动的鉴别当前相对于PC的地址(感觉还是废话),这个用在BootLoader启动,如u-boot启动的代码重新加载上会有意想不到的作用,因为ARM的CPU在上电瞬间,不是直接在ram中运行的,而是在Nor flash等外围flash中,那么PC可能是从0开始,但是最终肯定是希望代码在RAM中运行,所以就涉及到将代码搬到RAM中运行,这个代码的“搬迁”,就可以巧妙的使用ADR命令,判断当前是在RAM中运行,还是在Nor Flash运行,这个在后面的博客中,会单独介绍这个“巧招”在relocate中的使用。