官方QQ群收藏本站

百问linux嵌入式论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2205|回复: 0

【教程】对比理解adr,ldr指令

[复制链接]

189

主题

1760

帖子

6145

积分

超级版主

答疑助手

Rank: 8Rank: 8

积分
6145
QQ
发表于 2017-8-9 15:56:41 | 显示全部楼层 |阅读模式
很多人在写简单的裸机代码或分析uboot时,常常遇到adr ldr指令。却分不清这2者的区别,今天就来谈谈adr与ldr指令。
参照韦老师的代码和Makefile写了test_adr.S
.text
.globl _start
_start:
    ldr r0, test
    adr r0, test
    ldr r0, =test
    nop
test:
nop
Makefile
all:test_adr.S
       arm-linux-gcc -c -o test_adr.o test_adr.S
         arm-linux-ld -Ttext 0x00000000 -gtest_adr.o -o test_adr_elf
        arm-linux-objcopy -O binary -S test_adr_elf test_adr.bin
        arm-linux-objdump -D -m arm test_adr_elf test_adr.dis
clean:
        rm -ftest_adr.dis test_adr.bin test_adr_elf *.o
反汇编test_adr.S得到test_adr.dis
test_adr_elf:
file format elf32-littlearm
Disassembly of section .text:
00000000 _start:
0: e59f0008 ldr r0, [pc, #8]; 10 test
4: e28f0004 add r0, pc, #4; 0x4
8: e59f0004 ldr r0, [pc, #4]; 14.text+0x14
c: e1a00000 nop (mov r0,r0)
00000010 test:
10:e1a00000 nop (mov r0,r0)
14:00000010 andeq r0, r0, r0, lsl r0
很显然,ldr获取的是内存的值(至于这个内存存的是数据还是地址,不是问题重点),像指针一样间接寻址(看到了[]符号咯),而adr是得到一个与PC有关的值,必定是个地址。
韦老师举了个例子:
adr r0, _start,r0就是_start对应指令当前的地址
对于“_start对应指令当前的地址”,我理解了很久,终于想清楚,比如在uboot中,_start标号对应的指令(即b reset)的链接地址是0x33f80000确凿无疑。
如果从NOR Flash启动,b reset被烧在NOR Flash 0地址,那么b reset相对于此时的PC来说,它的地址就是0。
如果u-boot被直接下载到SDRAM的0x33f80000处运行,那么b reset自然处在SDRAM的0x33f80000。
所谓“当前”---是以运行时的PC为参照。
下面基于以上理解,分析test_adr.dis
00000000 _start:
0: e59f0008 ldr r0, [pc, #8]; 10 test
4: e28f0004 add r0, pc, #4; 0x4
8: e59f0004 ldr r0, [pc, #4]; 14.text+0x14
c: e1a00000 nop (mov r0,r0)
00000010 test:
10:e1a00000 nop (mov r0,r0)
14:00000010 andeq r0, r0, r0, lsl r0
1、先分析第一条指令ldr r0,test被编译成ldr
r0, [pc, #8],即到当前PC+8的存储器取值,运行第一条指令时,PC其实已经是8了(流水线决定的)。
那么8+8等于0x10,所以r0等于e1a00000,此指令的作用就是读取test地址处存放的值。由于此处放了一条nop,即得到nop的机器码。
2、第二条adr r0,test被编译成add r0, pc, #4
这显然是依赖程序执行到此处的PC值。
ADR是小范围地址读取伪指令,会将基于PC 相对偏移的地址值读取到寄存器中,此指令在4地址,PC是4+8=0xc再加4,于是r0=0x10。


从结果上来看,test自身的值(标号值),被读到了r0,这个值是以PC为参考的,也就是test对应的指令(第二个nop)当前的地址。r0=(标号test的地址与此指令的距离差)+(此指令的地址)=((0x10-0x4=12)+(4))=16=0x10。
假如在0x30000000以上运行,r0=((12)+(0x30000004))= 0x30000010。
3、ldr r0,=test被编译成两个字,一个指令,一个文字池。执行到这里PC=8, 8+8+4=0x14,所以在14地址取值,编译器在14地址处放了0x00000010,0x00000010是test的值,假如在Makefile指定连接地址是0x30000000,那么编译器放在这里的就是0x30000010,可见,这个值是编译时确定的。
最后一行andeq r0, r0, r0, lsl r0大概是编译器的机械动作,把一个数字翻译成了指令。
总结
ADR是小范围的地址读取伪指令,它将基于PC 相对偏移的地址值读取到寄存器中。而ldr获取的是内存的值,像指针一样间接寻址。
新手如何向我们反馈有效的信息,以便解决问题,见此贴:
http://www.100ask.org/bbs/forum. ... id=10914&extra=
回复

使用道具 举报

技术支持
在线咨询
咨询热线
0755-86200561
微信扫一扫
获取更多资讯!

Archiver|小黑屋|百问linux嵌入式论坛     

GMT+8, 2019-9-20 12:24 , Processed in 0.045280 second(s), 12 queries , File On.

Powered by Discuz! X3.3 Licensed

© 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

快速回复 返回顶部 返回列表