电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
分析下B(BL)/LDR指令
分 享
扫描二维码分享
分析下B(BL)/LDR指令
汇编语言
嵌入式软件
ARM体系架构
嵌入式与Linux那些事
关注
发布时间: 2021-03-15
丨
阅读: 718
[TOC] ## 1. BL LDR指令简介 ldr和bl在启动程序中,都是可以负责pc跳转的指令。 bl是地址无关指令,即和当前的运行地址无关。链接器脚本中标明了一个运行地址,但是arm中的代码实际是从地址0开始运行的。这个时候,**实际的地址和运行地址是不符的**。 如果想让程序正常的运行,就得使用地址无关指令。比如在完成将程序复制到内存之前想要跳转到一个函数里,就得使用bl。**因为bl跳转依靠的是相对地址**,**和运行地址无关**,所以能完成跳转。 ldr是地址有关指令。如果这个时候使用“ldr pc,=函数名”来跳转,实际上是跳转到这个**函数在链接器脚本中标明的地址上了**。所以使用地址相关指令之前,要把代码复制到链接器脚本中指明的那个地址上,否则的话程序就跑飞了。复制完成之后再使用ldr跳转到内存中,使程序继续运行。 ## 2. 分析绝对跳转过程 我们以一个例子具体分析下绝对跳转过程。 | 指令编号 | 指令功能 | | :------: | :-------------: | | 指令1 | 顺序执行 | | 指令2 | 顺序执行 | | 指令3 | 相对跳转到指令5 | | 指令4 | 顺序执行 | | 指令5 | 顺序执行 | | 指令6 | 绝对跳转到指令8 | | 指令7 | 顺序执行 | | 指令8 | 顺序执行 | 假设程序被放在0x00000000位置开始执行,编译链接后的结果为: | 指令地址 | 指令编号 | 指令功能 | 下条指令地址 | | :--------: | :---------: | :---------: | :----------: | | 0x00000000 | 顺序执行 | 顺序执行 | 当前地址+4 | | 0x00000004 | 顺序执行 | 顺序执行 | 当前地址+4 | | 0x00000008 | 跳转到指令5 | 跳转到指令5 | 当前地址+8 | | 0x0000000C | 顺序执行 | 顺序执行 | 当前地址+4 | | 0x00000010 | 顺序执行 | 顺序执行 | 当前地址+4 | | 0x00000014 | 跳转到指令8 | 跳转到指令8 | 0xC000001C | | 0x00000018 | 顺序执行 | 顺序执行 | 当前地址+4 | | 0x0000001C | 顺序执行 | 顺序执行 | 当前地址+4 | ![绝对跳转分析](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/%E7%9B%B8%E5%AF%B9/%E7%BB%9D%E5%AF%B9%E8%B7%B3%E8%BD%AC%E8%BF%87%E7%A8%8B%E5%88%86%E6%9E%90.png) 当这段程序被放在**0xC000000空间**(如右图)时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,在指令6执行时也可以使用绝对寻址的方法从0xC0000014正确跳转到指令8所在的0xC00001C位置,这段代码运行正常。 当这段代码被放在**0x00000000空间**(如左图)时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,但在指令6执行时使用绝对寻址的方法从0x0000014跳转到了0xC000001C,但0xC000001C空间没有代码,这样程序就跑飞了。 因此,**当编译地址(加载地址)和运行地址相同时,绝对跳转和相对跳转都可以正确执行**。比如,程序在NORFLASH存储时。 但是,**当编译地址(加载地址)和运行地址不相同时,相对跳转就会出现问题**。比如,代码存储在NANDFLASH,由于NANDFLASH并不能运行代码,所以需要重定位代码到内部的SRAM。 ## 3. BL(B)和LDR跳转范围是如何规定的 下图为B(BL)指令的格式 ![BL指令编码格式](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/BL%E6%8C%87%E4%BB%A4%E8%B7%B3%E8%BD%AC%E8%8C%83%E5%9B%B4%E5%9B%BE%E7%A4%BA.png) BL指令的[23,0]位存放的是要跳转的相对地址,由于指令所在地址必须是4字节对齐的,**因此跳转的地址最低位必然是0**。 BL指令[23,0]位保存的是省略这最低2位的地址,如果补全了这2位,BL指令就可以表示26位的跳转地址。在这26位中需要使用1位表示向前跳还是向后跳,那么剩下的25bits就可以表示32 MBts的范围了,2
25
=32M因此,**B(BL)指令的跳转范围为-32MBytes~+32MBytes**。 下图为LDR指令的格式。 ![LDR指令编码格式](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/LDR%E6%8C%87%E4%BB%A4%E8%B7%B3%E8%BD%AC%E8%8C%83%E5%9B%B4%E5%9B%BE%E7%A4%BA.png) ![LDR指令编码格式](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/LDR%E8%B7%B3%E8%BD%AC%E6%8C%87%E4%BB%A4%E8%8C%83%E5%9B%B4%E5%9B%BE%E7%A4%BA2.png) 图中的LDR的跳转范围计算方式和B指令的类似,其中Rn和Address_mode共同构成第二个操作数的内存地址。由Address_mode的9种格式可以知道,**Address_mode表示的就是偏移地址的范围大小,为2
12
=4K**。(不理解的可以对比下`ldr pc, [pc, #804]`和Address_mode的九种格式,很明显可以看出Address_mode就是当前地址的偏移范围) ## 4. BL执行过程分析 下图为B(BL)指令的格式。 ![BL指令编码格式](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/BL%E6%8C%87%E4%BB%A4%E6%A0%BC%E5%BC%8F.png) 28~31位(cond)是条件码,就是表明这条语句里是否有大于、等于、非零等的条件判断,这4位共有16种状态,分别为: ![条件码](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/BL%E6%8C%87%E4%BB%A4%E6%A0%BC%E5%BC%8F2.png) 我们以Uboot启动过程中的这句跳转代码分析下BL指令具体的执行过程。 ```c #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif ``` 上述代码对应的反汇编代码如下: ```c 33f000ac: eb000017 bl 33f00110
``` ```c 33f00110
: 33f00110: e3a00000 mov r0, #0 ; 0x0 33f00114: ee070f17 mcr 15, 0, r0, cr7, cr7, {0} ``` 当指令执行到33f000ac时,对应的机器码为eb000017(1110_1011_0000_0000_0000_0000_0001_0111),其中[31,28]高四位为条件码,1110表示无条件执行。[25,27]位保留区域,24位表示是否带有返回值,1表示带有返回值,也就是BL指令。[23,0]为指令的操作数,0000_0000_0000_0000_0001_0111。 BL指令的跳转地址是按照如下方式计算: 1、将指令中24位带符号的补码立即数扩展为32位(扩展其符号位)原数变成 0000_0000_0000_0000_0000_0000_0001_0111。 2、将此数左移两位0000_0000_0000_0000_0000_0010_1000_0000 变成 0000_0000_0000_0000_0000_0000_0101_1100 = 0x0000005c 3、将得到的值加到PC寄存器中得到目标地址,由于ARM为3级流水线,此时的 `pc = 33f000ac+8 = 33F000B4`,`pc = 33F000B4 + 0x0000005c = 33F00110`与图中的`cpu_init_crit`的地址相等。 在算的过程中我们使用的始终是PC的值,假设程序在 0 地址处执行,那么计算方法一样,pc 的值变了,计算出来的结果也随之改变。**所以 BL 的跳转时是与位置无关的**。 ## 5. LDR执行过程分析 下图为LDR指令的格式。 ![LDR指令编码格式](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/LDR%E6%8C%87%E4%BB%A4%E6%A0%BC%E5%BC%8F.png) ![LDR指令编码格式](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/Article/2021/%E4%B8%89%E6%9C%88/LDR%E6%8C%87%E4%BB%A4%E6%A0%BC%E5%BC%8F2.png) 我们以下图中的代码作为例子分析下。 ```c ldr pc,=call_board_init_f ``` 对应的反汇编代码如下: ```c 33f000d0: e59ff324 ldr pc, [pc, #804] ; 33f003fc
``` ```c 33f003fc: 33f000d4 .word 0x33f000d4 ........ 33f000d4
: 33f000d4: e3a00000 mov r0, #0 ; 0x0 ``` `ldr pc, [pc, #804]`这条指令为伪指令,编译的时候会将call_board_init_f的链接地址存入一个固定的地址(链接时确定的),对于本条指令这个地址就是33f000d4 。 上面的反汇编出来的 `ldr pc,=call_board_init_f`就变成了`ldr pc, [pc, #804]`,由于ARM使用了流水线的原因,所以在执行 `ldr pc,[ pc, #4 ]`的时候 pc 不在这句代码这里了,而是跑到了 `pc+8`的地方,这句代码相当于 `pc= *(pc+804+8)=33f000d0+32C=33f003fc` ,所以会跳转到33f003fc 地址取33f000d4 ,而 **33f000d4 是存在代码段中的一个常量,并不是计算出来的,不会随程序的位置而改变**,所以无论代码和pc怎么变 `*(pc+804)` 的值时不会变的。 ## 6. 总结 这样,绝对跳转中的固定地址就很好理解了,要跳转地址的值在链接时就已经确定了,**存在了一块内存中**。 相对跳转时,反汇编`bl 33f00110`中的33f00110是**根据pc计算出来的**,当pc改变时,结果也会改变。**所以,称为相对跳转,与当前位置无关。** > 本文参考 > > 《ARM体系结构与编程》 > > https://www.cnblogs.com/dchipnau/p/5256039.html 往期精彩 [9个提高代码运行效率的小技巧你知道几个?](https://mp.weixin.qq.com/s?__biz=Mzg5ODUxNDMxMA==&mid=2247484631&idx=1&sn=cfc2c6aa45c1f10faa8ca1adddf91fb5&chksm=c060283ef717a1285b56534054b983cbb6e212a55f491adf7df61b38294e4f553c5176d6a80d&token=99728457&lang=zh_CN#rd) [24张图7000字详解计算机中的高速缓存](https://mp.weixin.qq.com/s?__biz=Mzg5ODUxNDMxMA==&mid=2247484470&idx=1&sn=98f80777cd8500885da981516213bf7f&chksm=c06028dff717a1c9ea56ae26e9e3352e0dcb82ffdc37711e396df33781605c0eb0b757e270de&token=99728457&lang=zh_CN#rd) [面试官不讲武德,居然让我讲讲蠕虫和金丝雀!](http://mp.weixin.qq.com/s?__biz=Mzg5ODUxNDMxMA==&mid=2247484932&idx=1&sn=56a8cd2f4af474b8908d05ec1643cf69&chksm=c0602aedf717a3fb866edae088e77c161d1b39d503d695b14bb6c8fc6fd28b4cd6ea7d4811bd&mpshare=1&scene=23&srcid=0307LtWpQMqiGTlc6EgUtNk3&sharer_sharetime=1615106866041&sharer_shareid=04d13239c5be2ec55929ede9a8956f27#rd) ![](https://gitee.com/dongxingbo/Picture/raw/master/Wechat/%E5%8A%A8%E6%80%81%E5%BC%95%E5%AF%BC%E5%85%B3%E6%B3%A8%E5%85%AC%E4%BC%97%E5%8F%B7%E5%8F%B7.gif)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
嵌入式与Linux那些事
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交