目录
首先,adr是伪指令,而ldr根据使用的格式可分为ldr指令与ldr伪指令。
指令功能:
格式:adr{cond} Rd,addr
adr基于PC相对偏移的地址值或者基于寄存器相对偏移的地址值,读取到目标寄存器Rd中。
格式:ldr{cond} Rd,addr
将addr这个存储器地址对应的存储单元中的数据,加载到目标寄存器Rd中。
执行效果对比:
直接在Linux内核head.s开启MMU之前和开启MMU之后,分别加入ADR和LDR指令对比。
开启mmu之前,程序运行地址和编译链接的地址不一致;
开启mmu后完成了虚拟地址映射,对cpu来说,程序运行地址和编译链接的地址是一致的。
对照反汇编,gdb调试,
这里内核编译用的链接地址从0x8000000开始,对应到物理地址从0x6000000开始。stex链接地址为0x80008000,运行时物理地址为0x60008000。
反汇编代码可以看到:
- ADR被直接转换为了相对pc指针的计算;
- LDR指令是将值保存为了一个.word放在代码中,LDR通过pc+offset读这个内存中的数据到目标寄存器中;
gdb打印r0和r1寄存器,
运行地址和链接的地址不一致时(启动MMU之前):
ADR指令得到的值为stext在内存中的实际地址0x60008000;
LDR指令得到的值还是0x80008000,为编译时指定的stext的地址;
也就是ADR指令得到了stext正确的地址,而LDR得到的值不对,ADR是地址无关码。当标签离当前pc偏移很远时,ADR会被转换为多条指令,比如先mov立即数,之后add pc。
运行地址和链接的地址一致时(启动MMU之后):
ADR和LDR指令得到的值都为__mmap_swiched的运行地址(也是链接),两条指令结果都对;
总结
ADR指令是地址无关码,程序运行地址和链接的地址不一致时,依然能通过相对偏移,得到正确的标签地址;LDR是地址相关码。