汇编中 adr 指令 和 ldr 指令的区别
首先看下面的例子:
.global _start
_start:
ldr r0,=0x12345678 /* 将 无效立即数0x12345678 赋值寄存器r0 */
ldr r1,=label /* 将 标签所表示的地址值 赋值寄存器r1 */
ldr r2,label /* 将 标签所指地址处的内容 赋值寄存器r2 */
adr r3,label /* 将 标签所表示的地址值 赋值寄存器r3 */
stop:
b stop
label:
.word 0xdeadbeef
上面的代码在实际运行时,执行的是:
我们看到:
- 对于
ldr r1,=label
和adr r3,label
,两条指令的功能都是获取标签所表示的地址值,但是编译器对两条伪指令的解释是不一样的。
对于指令ldr r1,=label
,在编译的时候,根据链接地址,算出label标签对应的地址值(也就是说,其值是链接的时候决定的),将其放在文字池 (a portion of memory embedded in the code to hold constant values)中;运行时从文字池中取值,将值赋值给寄存器 r1。 Click here for more
对于指令adr r3,label
:adr
是小范围地址加载指令,通过当前的PC的值 +/- 一个偏移量得到地址。很明显,这是一个位置有关码。标签必须位于当前代码段;应用超出范围的标签会出现错误 Click here for more。通常,编译器用一条 add
指令或 sub
指令来实现该 adr
伪指令的功能。所以,adr
指令得到的是运行时地址。
-
编译器的对于伪指令
ldr r0,=0x12345678
的处理方式。将一个无效立即数通过文字池的方式赋值给寄存器。 -
由于流水线的缘故,PC的值总是指向正在预取的指令。
如何确定代码的运行位置
/* 方法一 */
.global _start
_start:
adr r0,_start
ldr r1,=_start
cmp r0,r1
/* 方法二 */
adr r0,label
ldr r1,[r0]
cmp r0,r1
label:
.word .
/* 方法三 Uboot 里的代码 */
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0xff000fff
bic r1, pc, r0 /* r1 <- current base addr of code */
ldr r2, _TEXT_BASE /* r2 <- original base addr in ram */
bic r2, r2, r0 /* r2 <- current base addr of code */
cmp r1, r2 /* compare r1, r2 */
beq after_copy /* r1 == r2 then skip flash copy */
_TEXT_BASE:
.word text_base_addr /* text_base_addr 的值在链接脚本中确定 */
这是U-boot中的代码,比较当前PC值和 .text段基地址中的bit[12:23]是否相同。当前的PC值相对于.text段基地址已经运行了一段代码,但这段代码位于U-boot代码中比较靠前的位置,所以屏蔽了一些位,代码运行不会超过这个范围,可以通过这个比较得知当前代码是在iRAM中还是RAM中运行.
汇编中调用函数
方法一
ldr pc,label_one
label_one:
.word lable_two
label_two:
:
:
:
方法二
ldr pc,label_one
label_one:
.word func_name /* define in C file */
void func_name(void)
{
...
}