1. BL LDR指令簡(jiǎn)介
2. 分析絕對(duì)跳轉(zhuǎn)過(guò)程
3. BL(B)和LDR跳轉(zhuǎn)范圍是如何規(guī)定的
4. BL執(zhí)行過(guò)程分析
5. LDR執(zhí)行過(guò)程分析
6. 總結(jié)
1. BL LDR指令簡(jiǎn)介
??LDR和BL在啟動(dòng)程序中,都是可以負(fù)責(zé)pc跳轉(zhuǎn)的指令。
??BL是地址無(wú)關(guān)指令,即和當(dāng)前的運(yùn)行地址無(wú)關(guān)。鏈接器腳本中標(biāo)明了一個(gè)運(yùn)行地址,但是arm中的代碼實(shí)際是從地址0開(kāi)始運(yùn)行的。這個(gè)時(shí)候,實(shí)際的地址和運(yùn)行地址是不符的。
??如果想讓程序正常的運(yùn)行,就得使用地址無(wú)關(guān)指令。比如在完成將程序復(fù)制到內(nèi)存之前想要跳轉(zhuǎn)到一個(gè)函數(shù)里,就得使用BL。因?yàn)锽L跳轉(zhuǎn)依靠的是相對(duì)地址,和運(yùn)行地址無(wú)關(guān),所以能完成跳轉(zhuǎn)。
??LDR是地址有關(guān)指令。如果這個(gè)時(shí)候使用“l(fā)dr pc,=函數(shù)名”來(lái)跳轉(zhuǎn),實(shí)際上是跳轉(zhuǎn)到這個(gè)函數(shù)在鏈接器腳本中標(biāo)明的地址上了。所以使用地址相關(guān)指令之前,要把代碼復(fù)制到鏈接器腳本中指明的那個(gè)地址上,否則的話程序就跑飛了。復(fù)制完成之后再使用LDR跳轉(zhuǎn)到內(nèi)存中,使程序繼續(xù)運(yùn)行。
2. 分析絕對(duì)跳轉(zhuǎn)過(guò)程
??我們以一個(gè)例子具體分析下絕對(duì)跳轉(zhuǎn)過(guò)程。
指令編號(hào) | 指令功能 |
---|---|
指令1 | 順序執(zhí)行 |
指令2 | 順序執(zhí)行 |
指令3 | 相對(duì)跳轉(zhuǎn)到指令5 |
指令4 | 順序執(zhí)行 |
指令5 | 順序執(zhí)行 |
指令6 | 絕對(duì)跳轉(zhuǎn)到指令8 |
指令7 | 順序執(zhí)行 |
指令8 | 順序執(zhí)行 |
??假設(shè)程序被放在0x00000000位置開(kāi)始執(zhí)行,編譯鏈接后的結(jié)果為:
指令地址 | 指令編號(hào) | 指令功能 | 下條指令地址 |
---|---|---|---|
0x00000000 | 順序執(zhí)行 | 順序執(zhí)行 | 當(dāng)前地址+4 |
0x00000004 | 順序執(zhí)行 | 順序執(zhí)行 | 當(dāng)前地址+4 |
0x00000008 | 跳轉(zhuǎn)到指令5 | 跳轉(zhuǎn)到指令5 | 當(dāng)前地址+8 |
0x0000000C | 順序執(zhí)行 | 順序執(zhí)行 | 當(dāng)前地址+4 |
0x00000010 | 順序執(zhí)行 | 順序執(zhí)行 | 當(dāng)前地址+4 |
0x00000014 | 跳轉(zhuǎn)到指令8 | 跳轉(zhuǎn)到指令8 | 0xC000001C |
0x00000018 | 順序執(zhí)行 | 順序執(zhí)行 | 當(dāng)前地址+4 |
0x0000001C | 順序執(zhí)行 | 順序執(zhí)行 | 當(dāng)前地址+4 |
絕對(duì)跳轉(zhuǎn)分析
??當(dāng)這段程序被放在0xC000000空間(如右圖)時(shí),開(kāi)始執(zhí)行指令1,然后采用相對(duì)尋址的方法就可以運(yùn)行到指令6,在指令6執(zhí)行時(shí)也可以使用絕對(duì)尋址的方法從0xC0000014正確跳轉(zhuǎn)到指令8所在的0xC00001C位置,這段代碼運(yùn)行正常。
??當(dāng)這段代碼被放在0x00000000空間(如左圖)時(shí),開(kāi)始執(zhí)行指令1,然后采用相對(duì)尋址的方法就可以運(yùn)行到指令6,但在指令6執(zhí)行時(shí)使用絕對(duì)尋址的方法從0x0000014跳轉(zhuǎn)到了0xC000001C,但0xC000001C空間沒(méi)有代碼,這樣程序就跑飛了。
??因此,當(dāng)編譯地址(加載地址)和運(yùn)行地址相同時(shí),絕對(duì)跳轉(zhuǎn)和相對(duì)跳轉(zhuǎn)都可以正確執(zhí)行。比如,程序在NORFLASH存儲(chǔ)時(shí)。
??但是,當(dāng)編譯地址(加載地址)和運(yùn)行地址不相同時(shí),相對(duì)跳轉(zhuǎn)就會(huì)出現(xiàn)問(wèn)題。比如,代碼存儲(chǔ)在NANDFLASH,由于NANDFLASH并不能運(yùn)行代碼,所以需要重定位代碼到內(nèi)部的SRAM。
3. BL(B)和LDR跳轉(zhuǎn)范圍是如何規(guī)定的
??下圖為B(BL)指令的格式
BL指令編碼格式
??BL指令的[23,0]位存放的是要跳轉(zhuǎn)的相對(duì)地址,由于指令所在地址必須是4字節(jié)對(duì)齊的,因此跳轉(zhuǎn)的地址最低位必然是0。
??BL指令[23,0]位保存的是省略這最低2位的地址,如果補(bǔ)全了這2位,BL指令就可以表示26位的跳轉(zhuǎn)地址。在這26位中需要使用1位表示向前跳還是向后跳,那么剩下的25位就可以表示32 MBts的范圍了,225=32M因此,B(BL)指令的跳轉(zhuǎn)范圍為-32MBytes~+32MBytes。
??下圖為L(zhǎng)DR指令的格式。
LDR指令編碼格式 LDR指令編碼格式
??圖中的LDR的跳轉(zhuǎn)范圍計(jì)算方式和B指令的類(lèi)似,其中Rn和Address_mode共同構(gòu)成第二個(gè)操作數(shù)的內(nèi)存地址。由Address_mode的9種格式可以知道,Address_mode表示的就是偏移地址的范圍大小,為212=4K。(不理解的可以對(duì)比下ldrpc, [pc, #804]和Address_mode的九種格式,很明顯可以看出Address_mode就是當(dāng)前地址的偏移范圍)
4. BL執(zhí)行過(guò)程分析
??下圖為B(BL)指令的格式。
BL指令編碼格式
??28~31位(cond)是條件碼,就是表明這條語(yǔ)句里是否有大于、等于、非零等的條件判斷,這4位共有16種狀態(tài),分別為:
條件碼
??我們以Uboot啟動(dòng)過(guò)程中的這句跳轉(zhuǎn)代碼分析下BL指令具體的執(zhí)行過(guò)程。
#ifndefCONFIG_SKIP_LOWLEVEL_INIT blcpu_init_crit #endif
??上述代碼對(duì)應(yīng)的反匯編代碼如下:
33f000ac:eb000017bl33f00110
33f00110: 33f00110:e3a00000movr0,#0;0x0 33f00114:ee070f17mcr15,0,r0,cr7,cr7,{0}
??當(dāng)指令執(zhí)行到33f000ac時(shí),對(duì)應(yīng)的機(jī)器碼為eb000017(1110_1011_0000_0000_0000_0000_0001_0111?),其中[31,28]高四位為條件碼,1110表示無(wú)條件執(zhí)行。[25,27]位保留區(qū)域,24位表示是否帶有返回值,1表示帶有返回值,也就是BL指令。[23,0]為指令的操作數(shù),0000_0000_0000_0000_0001_0111。
??BL指令的跳轉(zhuǎn)地址是按照如下方式計(jì)算:
??1、將指令中24位帶符號(hào)的補(bǔ)碼立即數(shù)擴(kuò)展為32位(擴(kuò)展其符號(hào)位)原數(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寄存器中得到目標(biāo)地址,由于ARM為3級(jí)流水線,此時(shí)的 pc = 33f000ac+8 = 33F000B4,pc = 33F000B4 + 0x0000005c = 33F00110?與圖中的cpu_init_crit的地址相等。
?? 在算的過(guò)程中我們使用的始終是PC的值,假設(shè)程序在 0 地址處執(zhí)行,那么計(jì)算方法一樣,pc 的值變了,計(jì)算出來(lái)的結(jié)果也隨之改變。所以 BL 的跳轉(zhuǎn)時(shí)是與位置無(wú)關(guān)的。
5. LDR執(zhí)行過(guò)程分析
??下圖為L(zhǎng)DR指令的格式。
LDR指令編碼格式
??我們以下圖中的代碼作為例子分析下。
ldrpc,=call_board_init_f
??對(duì)應(yīng)的反匯編代碼如下:
33f000d0:e59ff324ldrpc,[pc,#804];33f003fc
33f003fc:33f000d4.word0x33f000d4 ........ 33f000d4: 33f000d4:e3a00000movr0,#0;0x0
?? ldrpc, [pc, #804]這條指令為偽指令,編譯的時(shí)候會(huì)將call_board_init_f的鏈接地址存入一個(gè)固定的地址(鏈接時(shí)確定的),對(duì)于本條指令,這個(gè)地址就是33f000d4 。
??上面的反匯編出來(lái)的 ldr pc,=call_board_init_f就變成了ldrpc, [pc, #804],由于ARM使用了流水線的原因,所以在執(zhí)行 ldr pc,[ pc, #4 ]的時(shí)候 pc 不在這句代碼這里了,而是跑到了 pc+8的地方,這句代碼相當(dāng)于 pc= *(pc+804+8)=33f000d0+32C=33f003fc ,所以會(huì)跳轉(zhuǎn)到33f003fc 地址取33f000d4 ,而 33f000d4 是存在代碼段中的一個(gè)常量,并不是計(jì)算出來(lái)的,不會(huì)隨程序的位置而改變,所以無(wú)論代碼和pc怎么變 *(pc+804) 的值時(shí)不會(huì)變的。
6. 總結(jié)
??這樣,絕對(duì)跳轉(zhuǎn)中的固定地址就很好理解了,要跳轉(zhuǎn)地址的值在鏈接時(shí)就已經(jīng)確定了,存在了一塊內(nèi)存中。
相對(duì)跳轉(zhuǎn)時(shí),反匯編bl33f00110中的33f00110是根據(jù)pc計(jì)算出來(lái)的,當(dāng)pc改變時(shí),結(jié)果也會(huì)改變。所以,稱(chēng)為相對(duì)跳轉(zhuǎn),與當(dāng)前位置無(wú)關(guān)。
原文標(biāo)題:分析下BL(B)/LDR指令
文章出處:【微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
寄存器
+關(guān)注
關(guān)注
31文章
5294瀏覽量
119814 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4277瀏覽量
62323 -
代碼
+關(guān)注
關(guān)注
30文章
4722瀏覽量
68229
原文標(biāo)題:分析下BL(B)/LDR指令
文章出處:【微信號(hào):zhuyandz,微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論