引例如下:
先看一段代码:
AREA test,CODE,READONLY
ENTRY
ldr r0,__main
adr r0,__main
ldr r0,=__main
nop
__main
nop
END
反汇编一下后
编译环境:realview 4.12
编译的时候设置 ROM1 :start_addr 0x30000000 、size 0x200000
RAM1 :start_addr 0x30200000 、 size:0x4000000
下面是反汇编的结果:
\\ code pc_current binary code after asmcc
6: ldr r0,__main 0x300000D8 E59F0008 LDR R0,[PC,#0x0008]
7: adr r0,__main 0x300000DC E28F0004 ADD R0,PC,#0x00000004
8: ldr r0,=__main 0x300000E0 E59F0004 LDR R0,[PC,#0x0004]
9: nop
10:
11: EXPORT __main
12: __main 0x300000E4 E1A00000 NOP
13: b __main 0x300000E8 EAFFFFFE B 0x300000E8
0x300000EC 300000E8 ANDCC R0,R0,R8,ROR #1
下面来分析一下这三条指令
1、ldr r0,__main ;这条指令实现将__main位置中的值装入r0中
经过汇编后,__main将被一个相对于PC的表达式取代
__main => [pc,#4] ;应为arm指令是以三级流水线的执行,pc=pc_current+0x08 所以最后地址(pc+4)中的值装入r0中
总结:ldr r0, __main是根据__main对PC(pc_current+8)的相对位置(+4)读取其所在地址的值,因此可以在和___main标号的相对位置不变的情况下移动。
2、adr r0,__main ;小范围的地址读取伪指令
这是一条伪指令,总是会被汇编程序汇编为一个指令。汇编程序尝试产生单个 ADD 或 SUB 指令来装载该地址。如果不能在一个指令中构造该地址,则生成一个错误,并且汇编失败。
在这里是取得标号__main 的地址到 r0,因为地址是相对程序的,因此ADR产生依赖于位置的代码。
本例中会变成:ADD R0,PC,#0x00000004 。因此该代码可以在和标号相对位置不变的情况下移动。假如:这段代码在 0x30000000 运行,那么 adr r0, __main得到 r0 = 0x3000000c;如果在地址 0 运行,就是 0x0000000c 了。
3、ldr r0,=__main ;大范围的地址读取
LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。这个指令取得标号 __main 的绝对地址,这个绝对地址是在 link 的时候确定的,看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,一条是指令,另一条是 __main 的数据(因为在编译的时候不能确定 __main的值,而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需要多出一个空间存放 __main 的真正数据,在这里就是 0x300000EC这个地址,取其中的值300000E8 )。
因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 300000E8 。
完整的总结一下吧:
1、ADR伪指令--- 小范围的地址读取
ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。
ADR伪指令格式 :ADR{cond} register, expr
地址表达式expr的取值范围:
当地址值是字节对齐时,其取指范围为: +255 ~ 255B;
当地址值是字对齐时,其取指范围为: -1020 ~ 1020B;
2、ADRL伪指令----中等范围的地址读取
ADRL伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中,比ADR伪指令可以读取更大范围的地址。
在汇编编译器编译源程序时,ADRL伪指令被编译器替换成两条合适的指令。若不能用两条指令实现,则产生错误,编译失败。
ADRL伪指令格式:ADRL{cond} register, expr
地址表达式expr的取值范围:
当地址值是字节对齐时,其取指范围为: -64K~64K;
当地址值是字对齐时,其取指范围为: -256K~256K;
3、LDR伪指令-----大范围的地址读取
LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。
若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。
====
http://www.cnblogs.com/jack739x/articles/2060762.html