详解汇编语言B和LDR指令与相对跳转和绝对跳转的关系
@
为什么要有相对跳转和绝对跳转?
顺序执行:指令一条一条按照顺序往下执行,比如变量的定义和赋值都是按照顺序执行的。
跳转执行:当指令执行到当前位置后跳转到其他位置执行。比如,在主函数中调用其他函数就是典型的跳转执行。其中跳转又分为绝对跳转和相对跳转。
绝对跳转:直接跳转到一个固定的,实实在在的地址。
相对跳转:相对于当前pc值的一个跳转,跳转到pc+offset的地址。
我们清楚了上面几个概念,就知道了为什么要有相对跳转和绝对跳转。各种指令相互配合才能使得cpu有更高的处理效率。正是因为有了顺序和跳转指令,我们的cpu才可以处理各种复杂的计算。
在程序中只有相对跳转/绝对跳转是否可以?
答案肯定是不可以的。我们以一个例子具体分析。
| 指令编号 | 指令功能 |
|---|---|
| 指令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 |

当这段程序被放在0xC000000空间时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,在指令6执行时也可以使用绝对寻址的方法从0xC0000014正确跳转到指令8所在的0xC00001C位置,这段代码运行正常。
当这段代码被放在0x00000000空间时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,但在指令6执行时使用绝对寻址的方法从0x0000014跳转到了0xC000001C,但0xC000001C空间没有代码,这样程序就跑飞了。
因此,当编译地址(加载地址)和运行地址相同时,绝对跳转和相对跳转都可以正确执行。比如,程序在NORFLASH存储时。但是,当编译地址(加载地址)和运行地址不相同时,相对跳转都就会出现问题。比如,代码存储在NANDFLASH,由于NANDFLASH并不能运行代码,所以需要重定位代码到内部的SRAM。关于NANDFLASH和NORFLASH可以看这篇文章S3C2440从NAND Flash启动和NOR FLASH启动的问题
。
B(BL)和LDR指令具体怎么执行的?
我们以下图中的这句跳转代码分析下指令具体的执行过程。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
上述代码对应的反汇编代码如下:
33f000ac: eb000017 bl 33f00110 <cpu_init_crit>
33f00110 <cpu_init_crit>:
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。
按照如下计算方式:
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 的跳转时与位置无关的。
下图为B(BL)指令的格式
28~31bts(cond)是条件码,就是表明这条语句里是否有大于、等于、非零等的条件判断,这4bts共有16种状态,分别为:

下图为LDR指令的格式


我们以下图中的第一句话作为例子分析下
ldr pc,=call_board_init_f
对应的反汇编代码如下:
33f000d0: e59ff324 ldr pc, [pc, #804] ; 33f003fc <fiq+0x5c>
33f003fc: 33f000d4 .word 0x33f000d4
........
33f000d4 <call_board_init_f>:
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) 的值时不会变的。
这样,绝对跳转中的固定地址就很好理解了,要跳转地址的值在链接时就已经确定了,存在了一块内存中。而相对跳转时,反汇编bl 33f00110中的33f00110是根据pc计算出来的,当pc改变时,结果也会改变,所以,称为相对跳转,与当前位置无关。
B(BL)和LDR跳转范围是如何规定的?
下图为B(BL)指令的格式
BL指令的[23,0]bits存放的是要跳转的相对地址,由于指令所在地址必须是4字节对齐的,因此跳转的地址最低bits必然是0,因此BL指令[23,0]bits保存的是省略这最低2bts的地址,如果补全了这2bits,BL指令就可以表示26bits的跳转地址。在这26bits中需要使用1bit表示向前跳还是向后跳,那么剩下的25bits就可以表示32 MBts的范围了,225=32M因此,B(BL)指令的跳转范围为-32MBytes~+32MBytes。
下图为LDR指令的格式


图中的LDR的跳转范围计算方式和B指令的类似,其中Rn和Address_mode共同构成第二个操作数的内存地址,由Address_mode的9种格式可以直到,Address_mode表示的就是偏移地址的范围大小,为212=4K。(不理解的可以对比下ldr pc, [pc, #804]和Address_mode的九种格式,很明显可以看出Address_mode就是当前地址的偏移范围)
大家的鼓励是我继续创作的动力,如果觉得写的不错,欢迎关注,点赞,收藏,转发,谢谢!
如遇到排版错乱的问题,可以通过以下链接访问我的CSDN。
CSDN:CSDN搜索“嵌入式与Linux那些事”
欢迎欢迎关注我的公众号:嵌入式与Linux那些事,领取秋招笔试面试大礼包(华为小米等大厂面经,嵌入式知识点总结,笔试题目,简历模版等)和2000G学习资料。
详解汇编语言B和LDR指令与相对跳转和绝对跳转的关系的更多相关文章
- BIOS详解:什么是BIOS ?BIOS的作用?CMOS及其与BIOS的关系?
1.什么是BIOS ? BIOS是英文Basic Input Output System的缩略语,直译过来后中文名称就是基本输入输出系统.它的全称应该是ROM-BIOS,意思是只读存储器基本输入输出系 ...
- #pragma 预处理指令详解
源地址:http://blog.csdn.net/jx_kingwei/article/details/367312 #pragma 预处理指令详解 在所有的预处理指令中, ...
- C++预处理详解
本文在参考ISO/IEC 14882:2003和cppreference.com的C++ Preprocessor的基础上,对C++预处理做一个全面的总结讲解.如果没有特殊说明,所列内容均依据C++9 ...
- Java类文件结构详解
概述: Class文件结构是了解虚拟机的重要基础之一,如果想深入的了解虚拟机,Class文件结构是不能不了解的.Class文件是一组以8位字节为基础单位的二进制流,各项数据项目严格按照顺序紧凑地排列在 ...
- 2-4、nginx特性及基础概念-nginx web服务配置详解
Nginx Nginx:engine X 调用了libevent:高性能的网络库 epoll():基于事件驱动event的网络库文件 Nginx的特性: 模块化设计.较好扩展性(不支持模块动态装卸载, ...
- Apache2 httpd.conf 配置详解
Apache2 httpd.conf 配置详解 <第一部分> 常用配置指令说明 1. ServerRoot:服务器的基础目录,一般来说它将包含conf/和logs/子目录,其它配置文件的相 ...
- 最强Java并发编程详解:知识点梳理,BAT面试题等
本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...
- hibernate集合映射inverse和cascade详解
hibernate集合映射inverse和cascade详解 1.到底在哪用cascade="..."? cascade属性并不是多对多关系一定要用的,有了它只是让我们在插入或 ...
- 《TCP-IP详解卷2:实现》【PDF】下载
<TCP-IP详解卷2:实现>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062539 内容简介 <TCP/IP详解·卷2 ...
随机推荐
- BUUCTF 不一样的flag writeup
感谢BUUCTF提供的学习平台 https://buuoj.cn 题目:不一样的flag 工具:x64dbg 这是一道内存的迷宫题,迷宫是402000处的字符串 根据经验,这应该(a行*b列)的字符, ...
- day96:flask:flask-migrate&flask-session&蓝图Blueprint&蓝图的运行机制
目录 1.flask-migrate 2.flask-session 3.蓝图:Blueprint 4.蓝图的运行机制 1.数据库迁移:flask-migrate 1.Flask的数据库迁移 在开发过 ...
- Mac小白用户都能体验Windows应用的轻量级软件
近期,苹果在WWDC大会上表示Mac电脑将转向ARM架构,这意味着为iPhone手机.iPad平板和Mac电脑应用APP提供了统一的可能性.也就是说,iPhone手机.iPad平板电脑的应用可能在Ma ...
- 通过Camtasia来添加各种各样的光标效果
在十几二十年前的时候,我们想要学习新的知识需要到学校和培训班才行,但是现在只要有一台电脑.一部手机或者平板,我们在家里也能找到我们喜欢的课程来学习了,微课也因此而生. 同样的,有了想要学习知识的学生, ...
- go学习路线资料
编辑器 JetBrains公司出品的,goland go初步学习路线 Go 指南 如何使用Go编程 实效Go编程 Go by Example 中文版 参考: Go 语言学习资料与社区索引 Go入门指南 ...
- CentOS升级参考
CentOS生产系统升级策略: 1)升级前评估 a)确认kernel或包bug. b)用评估工具 c) 测试验证 2)确认升级内容 a)单独升级kernel b)单独升级包 c)都升级 4)确认升级方 ...
- Spring源码解析之BeanFactoryPostProcessor(二)
上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDef ...
- [整理]qbxt集训10场考试 大 杂 烩 (前篇)
Contest 1 A 计算 \(n!\mod 2^{32}\) .发现数一大答案就为 \(0\) ,直接输出即可. B 一个 \(n\times m\) 的网格,网格中的数都在 \([1,nm]\) ...
- kali修改配置文件后并保存
vim是进入配置文件并修改 修改完按Esc进入控制模式,再:w保存 :wq是保存并退出
- Nebula Flink Connector 的原理和实践
摘要:本文所介绍 Nebula Graph 连接器 Nebula Flink Connector,采用类似 Flink 提供的 Flink Connector 形式,支持 Flink 读写分布式图数据 ...