自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我們一起分析下BL(B)/LDR指令

系統(tǒng)
如果想讓程序正常的運行,就得使用地址無關(guān)指令。比如在完成將程序復制到內(nèi)存之前想要跳轉(zhuǎn)到一個函數(shù)里,就得使用BL。因為BL跳轉(zhuǎn)依靠的是相對地址,和運行地址無關(guān),所以能完成跳轉(zhuǎn)。
  • 1. BL LDR指令簡介
  • 2. 分析絕對跳轉(zhuǎn)過程
  • 3. BL(B)和LDR跳轉(zhuǎn)范圍是如何規(guī)定的
  • 4. BL執(zhí)行過程分析
  • 5. LDR執(zhí)行過程分析
  • 6. 總結(jié)

1. BL LDR指令簡介

LDR和BL在啟動程序中,都是可以負責pc跳轉(zhuǎn)的指令。

BL是地址無關(guān)指令,即和當前的運行地址無關(guān)。鏈接器腳本中標明了一個運行地址,但是arm中的代碼實際是從地址0開始運行的。這個時候,實際的地址和運行地址是不符的。

如果想讓程序正常的運行,就得使用地址無關(guān)指令。比如在完成將程序復制到內(nèi)存之前想要跳轉(zhuǎn)到一個函數(shù)里,就得使用BL。因為BL跳轉(zhuǎn)依靠的是相對地址,和運行地址無關(guān),所以能完成跳轉(zhuǎn)。

LDR是地址有關(guān)指令。如果這個時候使用“ldr pc,=函數(shù)名”來跳轉(zhuǎn),實際上是跳轉(zhuǎn)到這個函數(shù)在鏈接器腳本中標明的地址上了。所以使用地址相關(guān)指令之前,要把代碼復制到鏈接器腳本中指明的那個地址上,否則的話程序就跑飛了。復制完成之后再使用LDR跳轉(zhuǎn)到內(nèi)存中,使程序繼續(xù)運行。

2. 分析絕對跳轉(zhuǎn)過程

我們以一個例子具體分析下絕對跳轉(zhuǎn)過程。

指令編號 指令功能
指令1 順序執(zhí)行
指令2 順序執(zhí)行
指令3 相對跳轉(zhuǎn)到指令5
指令4 順序執(zhí)行
指令5 順序執(zhí)行
指令6 絕對跳轉(zhuǎn)到指令8
指令7 順序執(zhí)行
指令8 順序執(zhí)行

假設程序被放在0x00000000位置開始執(zhí)行,編譯鏈接后的結(jié)果為:

指令地址 指令編號 指令功能 下條指令地址
0x00000000 順序執(zhí)行 順序執(zhí)行 當前地址+4
0x00000004 順序執(zhí)行 順序執(zhí)行 當前地址+4
0x00000008 跳轉(zhuǎn)到指令5 跳轉(zhuǎn)到指令5 當前地址+8
0x0000000C 順序執(zhí)行 順序執(zhí)行 當前地址+4
0x00000010 順序執(zhí)行 順序執(zhí)行 當前地址+4
0x00000014 跳轉(zhuǎn)到指令8 跳轉(zhuǎn)到指令8 0xC000001C
0x00000018 順序執(zhí)行 順序執(zhí)行 當前地址+4
0x0000001C 順序執(zhí)行 順序執(zhí)行 當前地址+4

絕對跳轉(zhuǎn)分析

當這段程序被放在0xC000000空間(如右圖)時,開始執(zhí)行指令1,然后采用相對尋址的方法就可以運行到指令6,在指令6執(zhí)行時也可以使用絕對尋址的方法從0xC0000014正確跳轉(zhuǎn)到指令8所在的0xC00001C位置,這段代碼運行正常。

當這段代碼被放在0x00000000空間(如左圖)時,開始執(zhí)行指令1,然后采用相對尋址的方法就可以運行到指令6,但在指令6執(zhí)行時使用絕對尋址的方法從0x0000014跳轉(zhuǎn)到了0xC000001C,但0xC000001C空間沒有代碼,這樣程序就跑飛了。

因此,當編譯地址(加載地址)和運行地址相同時,絕對跳轉(zhuǎn)和相對跳轉(zhuǎn)都可以正確執(zhí)行。比如,程序在NORFLASH存儲時。

但是,當編譯地址(加載地址)和運行地址不相同時,相對跳轉(zhuǎn)就會出現(xiàn)問題。比如,代碼存儲在NANDFLASH,由于NANDFLASH并不能運行代碼,所以需要重定位代碼到內(nèi)部的SRAM。

3. BL(B)和LDR跳轉(zhuǎn)范圍是如何規(guī)定的

下圖為B(BL)指令的格式

BL指令編碼格式

BL指令的[23,0]位存放的是要跳轉(zhuǎn)的相對地址,由于指令所在地址必須是4字節(jié)對齊的,因此跳轉(zhuǎn)的地址最低位必然是0。

BL指令[23,0]位保存的是省略這最低2位的地址,如果補全了這2位,BL指令就可以表示26位的跳轉(zhuǎn)地址。在這26位中需要使用1位表示向前跳還是向后跳,那么剩下的25位就可以表示32 MBts的范圍了,225=32M因此,B(BL)指令的跳轉(zhuǎn)范圍為-32MBytes~+32MBytes。

下圖為LDR指令的格式。

LDR指令編碼格式

LDR指令編碼格式

圖中的LDR的跳轉(zhuǎn)范圍計算方式和B指令的類似,其中Rn和Address_mode共同構(gòu)成第二個操作數(shù)的內(nèi)存地址。由Address_mode的9種格式可以知道,Address_mode表示的就是偏移地址的范圍大小,為212=4K。(不理解的可以對比下ldr pc, [pc, #804]和Address_mode的九種格式,很明顯可以看出Address_mode就是當前地址的偏移范圍)

4. BL執(zhí)行過程分析

下圖為B(BL)指令的格式。

BL指令編碼格式

28~31位(cond)是條件碼,就是表明這條語句里是否有大于、等于、非零等的條件判斷,這4位共有16種狀態(tài),分別為:

條件碼

我們以Uboot啟動過程中的這句跳轉(zhuǎn)代碼分析下BL指令具體的執(zhí)行過程。

  1. #ifndef CONFIG_SKIP_LOWLEVEL_INIT 
  2.  bl cpu_init_crit 
  3. #endif 

上述代碼對應的反匯編代碼如下:

  1. 33f000ac: eb000017  bl 33f00110 <cpu_init_crit> 
  1. 33f00110 <cpu_init_crit>: 
  2. 33f00110: e3a00000  mov r0, #0 ; 0x0 
  3. 33f00114: ee070f17  mcr 15, 0, r0, cr7, cr7, {0} 

當指令執(zhí)行到33f000ac時,對應的機器碼為eb000017(1110_1011_0000_0000_0000_0000_0001_0111?),其中[31,28]高四位為條件碼,1110表示無條件執(zhí)行。[25,27]位保留區(qū)域,24位表示是否帶有返回值,1表示帶有返回值,也就是BL指令。[23,0]為指令的操作數(shù),0000_0000_0000_0000_0001_0111。

BL指令的跳轉(zhuǎn)地址是按照如下方式計算:

1、將指令中24位帶符號的補碼立即數(shù)擴展為32位(擴展其符號位)原數(shù)變成  0000_0000_0000_0000_0000_0000_0001_0111。

2、將此數(shù)左移兩位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的地址相等。

4  在算的過程中我們使用的始終是PC的值,假設程序在 0 地址處執(zhí)行,那么計算方法一樣,pc 的值變了,計算出來的結(jié)果也隨之改變。所以 BL 的跳轉(zhuǎn)時是與位置無關(guān)的。

5. LDR執(zhí)行過程分析

下圖為LDR指令的格式。圖片

LDR指令編碼格式

我們以下圖中的代碼作為例子分析下。

  1. ldr pc,=call_board_init_f 

對應的反匯編代碼如下:

  1. 33f000d0: e59ff324  ldr pc, [pc, #804] ; 33f003fc <fiq+0x5c> 
  1. 33f003fc: 33f000d4  .word 0x33f000d4 
  2. ........ 
  3. 33f000d4 <call_board_init_f>: 
  4. 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使用了流水線的原因,所以在執(zhí)行 ldr pc,[ pc, #4 ]的時候 pc 不在這句代碼這里了,而是跑到了 pc+8的地方,這句代碼相當于 pc= *(pc+804+8)=33f000d0+32C=33f003fc ,所以會跳轉(zhuǎn)到33f003fc 地址取33f000d4 ,而 33f000d4 是存在代碼段中的一個常量,并不是計算出來的,不會隨程序的位置而改變,所以無論代碼和pc怎么變 *(pc+804) 的值時不會變的。

6. 總結(jié)

這樣,絕對跳轉(zhuǎn)中的固定地址就很好理解了,要跳轉(zhuǎn)地址的值在鏈接時就已經(jīng)確定了,存在了一塊內(nèi)存中。

相對跳轉(zhuǎn)時,反匯編bl 33f00110中的33f00110是根據(jù)pc計算出來的,當pc改變時,結(jié)果也會改變。所以,稱為相對跳轉(zhuǎn),與當前位置無關(guān)。

本文參考

《ARM體系結(jié)構(gòu)與編程》

https://www.cnblogs.com/dchipnau/p/5256039.html

本文轉(zhuǎn)載自微信公眾號「嵌入式與Linux那些事」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系嵌入式與Linux那些事公眾號。

 

責任編輯:武曉燕 來源: 嵌入式與Linux那些事
相關(guān)推薦

2022-01-17 06:59:40

Grep指令linux

2023-09-26 00:53:37

B端搭建低代碼

2022-07-10 23:15:46

Go語言內(nèi)存

2021-05-31 07:17:42

數(shù)據(jù)分析算法

2023-11-03 12:54:00

KAFKA探索中間件

2023-01-30 23:04:10

B-Treegolang優(yōu)化

2023-08-04 08:20:56

DockerfileDocker工具

2021-01-12 05:08:49

DHCP協(xié)議模型

2021-08-27 07:06:09

DubboDocker技術(shù)

2022-03-31 18:59:43

數(shù)據(jù)庫InnoDBMySQL

2022-05-24 08:21:16

數(shù)據(jù)安全API

2023-08-10 08:28:46

網(wǎng)絡編程通信

2022-10-18 07:33:57

Maven構(gòu)建工具

2023-09-10 21:42:31

2023-06-30 08:18:51

敏捷開發(fā)模式

2021-08-27 07:06:10

IOJava抽象

2024-02-20 21:34:16

循環(huán)GolangGo

2021-12-29 08:27:05

ByteBuffer磁盤服務器

2022-03-08 17:52:58

TCP格式IP

2021-07-28 07:53:20

Github ActiDotnet 應用
點贊
收藏

51CTO技術(shù)棧公眾號