@[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跳转到指令80xC000001C
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    ; 0x033f00114:    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学习材料。