2025年ldrh指令(ldrb指令)

ldrh指令(ldrb指令)p style text indent 2em 1 BL LDR 指令简介 p LDR 和 BL 在启动程序中 都是可以负责 pc 跳转的指令 p p BL 是地址无关指令 即和当前的运行地址无关 链接器脚本中标明了一个运行地址 但是 arm 中的代码实际是从地址 0 开始运行的 这个时候 实际的地址和运行地址是不符的 p p

大家好,我是讯享网,很高兴认识大家。



 <p style="text-indent:2em;"> 1. BL LDR指令简介</p> 

讯享网

讯享网LDR和BL在启动程序中,都是可以负责pc跳转的指令。</p> 

BL是地址无关指令,即和当前的运行地址无关。链接器脚本中标明了一个运行地址,但是arm中的代码实际是从地址0开始运行的。这个时候,实际的地址和运行地址是不符的。</p> 

讯享网如果想让程序正常的运行,就得使用地址无关指令。比如在完成将程序复制到内存之前想要跳转到一个函数里,就得使用BL。因为BL跳转依靠的是相对地址,和运行地址无关,所以能完成跳转。</p> 

LDR是地址有关指令。如果这个时候使用“ldr pc,=函数名”来跳转,实际上是跳转到这个函数在链接器脚本中标明的地址上了。所以使用地址相关指令之前,要把代码复制到链接器脚本中指明的那个地址上,否则的话程序就跑飞了。复制完成之后再使用LDR跳转到内存中,使程序继续运行。</p> 

讯享网2. 分析绝对跳转过程</p> 

我们以一个例子具体分析下绝对跳转过程。</p> 

讯享网 指令1</td> <td> 顺序执行</td> </tr><tr><td> 指令2</td> <td> 顺序执行</td> </tr><tr><td> 指令3</td> <td> 相对跳转到指令5</td> </tr><tr><td> 指令4</td> <td> 顺序执行</td> </tr><tr><td> 指令5</td> <td> 顺序执行</td> </tr><tr><td> 指令6</td> <td> 绝对跳转到指令8</td> </tr><tr><td> 指令7</td> <td> 顺序执行</td> </tr><tr><td> 指令8</td> <td> 顺序执行</td> </tr></tbody><tbody><tr><th> 指令编号</th> <th> 指令功能</th> </tr></tbody></div><p style="text-indent:2em;"> 假设程序被放在0x00000000位置开始执行,编译链接后的结果为:</p> 

 0x00000000</td> <td> 顺序执行</td> <td> 顺序执行</td> <td> 当前地址+4</td> </tr><tr><td> 0x00000004</td> <td> 顺序执行</td> <td> 顺序执行</td> <td> 当前地址+4</td> </tr><tr><td> 0x00000008</td> <td> 跳转到指令5</td> <td> 跳转到指令5</td> <td> 当前地址+8</td> </tr><tr><td> 0x0000000C</td> <td> 顺序执行</td> <td> 顺序执行</td> <td> 当前地址+4</td> </tr><tr><td> 0x00000010</td> <td> 顺序执行</td> <td> 顺序执行</td> <td> 当前地址+4</td> </tr><tr><td> 0x00000014</td> <td> 跳转到指令8</td> <td> 跳转到指令8</td> <td> 0xC000001C</td> </tr><tr><td> 0x00000018</td> <td> 顺序执行</td> <td> 顺序执行</td> <td> 当前地址+4</td> </tr><tr><td> 0x0000001C</td> <td> 顺序执行</td> <td> 顺序执行</td> <td> 当前地址+4</td> </tr></tbody><tbody><tr><th> 指令地址</th> <th> 指令编号</th> <th> 指令功能</th> <th> 下条指令地址</th> </tr></tbody></div><p style="text-indent:2em;"> 当这段程序被放在0xC000000空间(如右图)时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,在指令6执行时也可以使用绝对寻址的方法从0xC0000014正确跳转到指令8所在的0xC00001C位置,这段代码运行正常。</p> 

讯享网当这段代码被放在0x00000000空间(如左图)时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,但在指令6执行时使用绝对寻址的方法从0x0000014跳转到了0xC000001C,但0xC000001C空间没有代码,这样程序就跑飞了。</p> 

因此,当编译地址(加载地址)和运行地址相同时,绝对跳转和相对跳转都可以正确执行。比如,程序在NORFLASH存储时。</p> 

讯享网但是,当编译地址(加载地址)和运行地址不相同时,相对跳转就会出现问题。比如,代码存储在NANDFLASH,由于NANDFLASH并不能运行代码,所以需要重定位代码到内部的SRAM。</p> 

3. BL(B)和LDR跳转范围是如何规定的</p> 

讯享网下图为B(BL)指令的格式</p> 

<img src='https://file.elecfans.com/web1/M00/F1/B3/o4YBAGC1pxWAREM0AAHdS7Lxoz8516.png' alt='ARM' /></p> 

讯享网BL指令编码格式</p> 

BL指令的[23,0]位存放的是要跳转的相对地址,由于指令所在地址必须是4字节对齐的,因此跳转的地址最低位必然是0。</p> 

讯享网BL指令[23,0]位保存的是省略这最低2位的地址,如果补全了这2位,BL指令就可以表示26位的跳转地址。在这26位中需要使用1位表示向前跳还是向后跳,那么剩下的25位就可以表示32 MBts的范围了,225=32M因此,B(BL)指令的跳转范围为-32MBytes~+32MBytes。</p> 


讯享网

下图为LDR指令的格式。</p> 

讯享网<img src='https://file.elecfans.com/web1/M00/F1/B3/o4YBAGC1pxWAJ__CAAMplLDH2TY883.png' alt='ARM' /></p> 

LDR指令编码格式</p> 

讯享网<img src='https://file.elecfans.com/web1/M00/F1/B3/o4YBAGC1pxWANSjOAAA5witHZ2M218.png' alt='ARM' /></p> 

LDR指令编码格式</p> 

讯享网图中的LDR的跳转范围计算方式和B指令的类似,其中Rn和Address_mode共同构成第二个操作数的内存地址。由Address_mode的9种格式可以知道,Address_mode表示的就是偏移地址的范围大小,为212=4K。(不理解的可以对比下ldrpc, [pc, #804]和Address_mode的九种格式,很明显可以看出Address_mode就是当前地址的偏移范围)</p> 

4. BL执行过程分析</p> 

讯享网下图为B(BL)指令的格式。</p> 

<img src='https://file.elecfans.com/web1/M00/F1/B3/o4YBAGC1pxWAREM0AAHdS7Lxoz8516.png' alt='ARM' /></p> 

讯享网BL指令编码格式</p> 

28~31位(cond)是条件码,就是表明这条语句里是否有大于、等于、非零等的条件判断,这4位共有16种状态,分别为:</p> 

讯享网<img src='https://file.elecfans.com/web1/M00/F1/B3/o4YBAGC1pxWADmq2AAGV9__wOG4406.png' alt='ARM' /></p> 

条件码</p> 

讯享网我们以Uboot启动过程中的这句跳转代码分析下BL指令具体的执行过程。</p> 

#ifndef CONFIG_SKIP_LOWLEVEL_INIT  bl cpu_init_crit #endif</p> 

讯享网上述代码对应的反汇编代码如下:</p> 

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}</p> 

讯享网当指令执行到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。</p> 

BL指令的跳转地址是按照如下方式计算:</p> 

讯享网1、将指令中24位带符号的补码立即数扩展为32位(扩展其符号位)原数变成  0000_0000_0000_0000_0000_0000_0001_0111。</p> 

2、将此数左移两位0000_0000_0000_0000_0000_0010_1000_0000 变成 0000_0000_0000_0000_0000_0000_0101_1100 =  0x0000005c</p> 

讯享网3、将得到的值加到PC寄存器中得到目标地址,由于ARM为3级流水线,此时的 pc = 33f000ac+8 = 33F000B4,pc = 33F000B4 + 0x0000005c = 33F00110‬与图中的cpu_init_crit的地址相等。</p> 

在算的过程中我们使用的始终是PC的值,假设程序在 0 地址处执行,那么计算方法一样,pc 的值变了,计算出来的结果也随之改变。所以 BL 的跳转时是与位置无关的。</p> 

讯享网5. LDR执行过程分析</p> 

下图为LDR指令的格式。</p> 

讯享网<img src='https://file.elecfans.com/web1/M00/F1/B3/o4YBAGC1pxWAAUSiAACUIuLtWfs534.png' alt='ARM' /></p> 

<img src='https://file.elecfans.com/web1/M00/F1/B3/o4YBAGC1pxWAaHSuAACU5PhZVUU558.png' alt='ARM' /></p> 

讯享网LDR指令编码格式</p> 

我们以下图中的代码作为例子分析下。</p> 

讯享网ldr pc,=call_board_init_f</p> 

对应的反汇编代码如下:</p> 

讯享网33f000d0: e59ff324  ldr pc, [pc, #804] ; 33f003fc <fiq+0x5c>33f003fc: 33f000d4  .word 0x33f000d4 ........ 33f000d4 <call_board_init_f>: 33f000d4: e3a00000  mov r0, #0 ; 0x0</p> 

ldrpc, [pc, #804]这条指令为伪指令,编译的时候会将call_board_init_f的链接地址存入一个固定的地址(链接时确定的),对于本条指令,这个地址就是33f000d4 。</p> 

讯享网上面的反汇编出来的 ldr pc,=call_board_init_f就变成了ldrpc, [pc, #804],由于ARM使用了流水线的原因,所以在执行 ldr pc,[ pc, #4 ]的时候 pc 不在这句代码这里了,而是跑到了 pc+8的地方,这句代码相当于 pc= *(pc+804+8)=33f000d0+32C=33f003fc ,所以会跳转到33f003fc 地址取33f000d4 ,而33f000d4 是存在代码段中的一个常量,并不是计算出来的,不会随程序的位置而改变,所以无论代码和pc怎么变 *(pc+804) 的值时不会变的。</p> 

6. 总结</p> 

讯享网这样,绝对跳转中的固定地址就很好理解了,要跳转地址的值在链接时就已经确定了,存在了一块内存中。</p> 

相对跳转时,反汇编bl33f00110中的33f00110是根据pc计算出来的,当pc改变时,结果也会改变。所以,称为相对跳转,与当前位置无关。</p> 

讯享网编辑:技巧</p> 
小讯
上一篇 2025-05-12 17:37
下一篇 2025-05-17 14:38

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/193035.html