共计 3300 个字符,预计需要花费 9 分钟才能阅读完成。
@[TOC]
为什么要有绝对跳转和相对跳转?
程序执行 :指令一条一条依照程序往下执行,比方变量的定义和赋值都是依照程序执行的。
跳转执行 :当指令执行到以后地位后跳转到其余地位执行。比方,在主函数中调用其余函数就是典型的跳转执行。其中跳转又分为相对跳转和绝对跳转。
相对跳转 :间接跳转到一个固定的,实实在在的地址。
绝对跳转:绝对于以后 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 示意的就是偏移地址的范畴大小,为 2 12=4K。(不了解的能够比照下 ldr pc, [pc, #804] 和 Address_mode 的九种格局,很显著能够看出 Address_mode 就是以后地址的偏移范畴)
大家的激励是我持续创作的能源,如果感觉写的不错,欢送关注,点赞,珍藏,转发,谢谢!
如遇到排版错乱的问题,能够通过以下链接拜访我的 CSDN。
CSDN:CSDN 搜寻“嵌入式与 Linux 那些事”
欢送欢送关注我的公众号:嵌入式与 Linux 那些事,支付秋招口试面试大礼包(华为小米等大厂面经,嵌入式知识点总结,口试题目,简历模版等)和 2000G 学习材料。