0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度剖析ARM跳轉(zhuǎn)指令

汽車電子技術(shù) ? 來源:宅學(xué)部落 ? 作者:王利濤 ? 2023-02-17 09:37 ? 次閱讀

跳轉(zhuǎn)指令

順序、選擇、循環(huán)是構(gòu)建程序的基本結(jié)構(gòu),任何一個(gè)邏輯復(fù)雜的程序基本上都可以由這三種程序結(jié)構(gòu)組合而成。而跳轉(zhuǎn)指令,則在子程序調(diào)用、選擇、循環(huán)程序結(jié)構(gòu)中被大量使用。程序的跳轉(zhuǎn)是如何實(shí)現(xiàn)的呢?在了解這個(gè)機(jī)制之前,我們需要先了解一下程序計(jì)數(shù)器PC。

程序計(jì)數(shù)器PC,是CPU寄存器列表中最重要的一個(gè)寄存器。它就像一桿槍,指哪打哪:你給PC指針賦值哪個(gè)地址,CPU就會(huì)到PC指針指向的這個(gè)地址去取指令、翻譯指令、執(zhí)行指令。一般情況下,當(dāng)你沒有給PC指針賦新地址時(shí),CPU在PC指針指向的地址取完指令后,PC計(jì)數(shù)器會(huì)自動(dòng)加一,指向下一條指令,程序可以自動(dòng)執(zhí)行下去。當(dāng)我們需要跳轉(zhuǎn)時(shí),可以直接給PC指針賦一個(gè)新地址,于是CPU就會(huì)跳轉(zhuǎn)到新地址去執(zhí)行了。

ARM中,常見的跳轉(zhuǎn)指令有B、BL、MOV、LDR等。不同的指令,它們的使用條件、使用場(chǎng)合是不同的,今天就給大家總結(jié)一下它們的區(qū)別及各自使用的場(chǎng)合。

1B跳轉(zhuǎn)指令

B指令是ARM中最基本的跳轉(zhuǎn)指令,它的使用方法如下:

B label

上面語句表示跳轉(zhuǎn)到label的標(biāo)號(hào)處去執(zhí)行。B跳轉(zhuǎn)指令是ARM中最簡(jiǎn)單的指令,只是單純的跳轉(zhuǎn),而且是相對(duì)跳轉(zhuǎn)。它可以跳到以當(dāng)前位置PC為基址,前后32MB的地址空間范圍,所以B指令只是在臨近的代碼塊、標(biāo)號(hào)之間跳轉(zhuǎn)。

B指令跳轉(zhuǎn),大多數(shù)時(shí)候是單向的,跳過去就不再返回來了。但是我們可以通過添加一些標(biāo)號(hào)來實(shí)現(xiàn)一些控制邏輯:比如循環(huán)、選擇程序結(jié)構(gòu):

;循環(huán)結(jié)構(gòu)示例
LOOP
    SUB R0,R0,#1
    ...
    CMP R0,#0
    BNE LOOP
;選擇結(jié)構(gòu)示例
    MOV R1,#10
    MOV R2,#20
    CMP R1,R2
    BEQ HERE
    ...
    B END 
HERE
    ...
END
    ...

在上面的程序中,我們使用B跳轉(zhuǎn)指令實(shí)現(xiàn)了選擇、循環(huán)這兩種基本的程序結(jié)構(gòu)。B指令像ARM的其它指令一樣,可以根據(jù)CPSR狀態(tài)寄存器的標(biāo)志位,有條件的執(zhí)行。這樣,可以減少指令數(shù)目、提高代碼密度和運(yùn)行效率。如BNE、BEQ就是當(dāng)結(jié)果相等、不相等時(shí)的條件跳轉(zhuǎn)。

當(dāng)前程序狀態(tài)寄存器:

圖片

各種各樣的條件碼:

圖片

2BL指令

BL指令跟B不同:在跳轉(zhuǎn)之前,會(huì)先將當(dāng)前指令的下一條指令地址保存到LR寄存器中,然后才跳轉(zhuǎn)到標(biāo)號(hào)執(zhí)行。這樣做的好處是:當(dāng)我們想從標(biāo)號(hào)地方返回時(shí),可以直接將LR寄存器中的返回地址賦值給PC,程序就可以返回到原來的程序中繼續(xù)執(zhí)行了。

BL跳轉(zhuǎn)指令一般用在子程序的調(diào)用中。無論是匯編語言子程序,還是C語言子程序,在跳轉(zhuǎn)到子程序之前,都要將返回地址保存起來。當(dāng)子程序執(zhí)行完畢,將LR寄存器保存的返回地址,重新賦值給PC,處理器就可以返回到主程序繼續(xù)執(zhí)行了。

BEGIN
    MOV R0,#SRC
    MOV R1,#DST
    MOV R2,#100
    BL COPY
    NOP
    ...
 COPY
    SUB R2,R2,#1
    LDR R3,[R0],#1
    STR R3,[R1],#1
    CMP R2,#0
    BNE COPY
    MOV PC,LR

上面的匯編代碼段,我們定義了一個(gè)匯編子程序COPY,實(shí)現(xiàn)了數(shù)據(jù)拷貝的功能。當(dāng)我們使用BL指令調(diào)用這個(gè)子程序COPY時(shí),CPU會(huì)首先將當(dāng)前指令的下一條指令:NOP 的地址保存到LR寄存器中,然后才跳轉(zhuǎn)到COPY子程序去執(zhí)行。在COPY子程序中,處理完數(shù)據(jù)搬運(yùn)后,通過

MOV PC,LR

這條語句,將保存在LR寄存器中的返回地址,重新賦值給PC,這樣我們就可以返回到原來的程序中繼續(xù)執(zhí)行了。

在上面的匯編代碼中,LR,即R14,連接寄存器,常用來存放程序的返回地址;PC,即R15,程序計(jì)數(shù)器,表示當(dāng)前指令地址。LR和PC都是ARM匯編器為了方便程序員編程,預(yù)定義的一些宏。你在程序中使用這些助記符其實(shí)就是相當(dāng)于操作R14和R15寄存器。除此之外,ARM中常用的助記符有:

  • FP:棧幀基址寄存器,即R12
  • SP:棧指針寄存器,即R13
  • LR:鏈接寄存器,即R14
  • PC:程序計(jì)數(shù)器,即R15

同樣,在C語言調(diào)用子函數(shù)的過程中,在跳轉(zhuǎn)子函數(shù)執(zhí)行之前,CPU也會(huì)將當(dāng)前指令的下一條指令地址保存到LR寄存器中,然后再跳轉(zhuǎn)到子函數(shù)中執(zhí)行。因?yàn)樵谧雍瘮?shù)運(yùn)行過程中,也有可能會(huì)用到ARM的一些寄存器,也有可能會(huì)調(diào)用其它的子函數(shù),會(huì)覆蓋掉保存在LR寄存器中的返回地址,所以,我們一般在運(yùn)行子函數(shù)之前,會(huì)首先將LR寄存器壓入子函數(shù)的棧幀,相當(dāng)于將返回地址保存到了棧上。當(dāng)子函數(shù)運(yùn)行結(jié)束時(shí),再通過出棧操作,將保存在棧中的返回地址彈出到PC指針中,這樣程序就成功從子程序中返回了,直接返回到原來的函數(shù)中繼續(xù)執(zhí)行。

int main(void)
{
    func();
    printf("Hello!\\n");
    return 0;
} 
;對(duì)應(yīng)的匯編代碼
main
    BL func
    BL printf
func
    PUSH LR
    ...
    pop pc 
;func子函數(shù)返回

3MOV指令

通過上面的學(xué)習(xí),我們可以看到,無論是B指令、還是BL指令,都是相對(duì)尋址。其本質(zhì)都是以當(dāng)前指令地址PC為基址,然后加上一個(gè)[0,32M]的偏移,達(dá)到修改PC的目的。

除此之外,我們也可以直接給PC指針賦值,達(dá)到跳轉(zhuǎn)的目的。如上面的 func 子程序返回,就是直接通過

MOV PC,LR

這條指令,將LR寄存器中的返回地址,直接賦值給PC,直返回到原來的主函數(shù)去執(zhí)行。

MOV指令主要用來在寄存器之間傳輸數(shù)據(jù),或者將一個(gè)立即數(shù)傳送到寄存器。但是MOV指令有一個(gè)硬傷,就是傳遞的立即數(shù)只能是8位數(shù),有大小的限制。這是為什么呢?很簡(jiǎn)單,ARM是RISC架構(gòu),在一個(gè)32位的ARM中,指令通常都是32位的。而一個(gè)指令中,通常要包括操作碼+操作數(shù),如下圖:

圖片

一條指令,總共有32個(gè)bit空間,MOV這個(gè)操作碼要占幾位吧,Rd寄存器編碼要占據(jù)幾位吧,剩下的留給立即數(shù)的空間就不多了,所以這也就限定了MOV指令能傳遞的立即數(shù)的大小了。而一般的32位程序中,無論是變量還是函數(shù),它們的地址一般都是32位的,如果使用MOV指令,將他們的地址傳送到PC,使用下面的形式:

MOV PC,#0x30008000

你會(huì)發(fā)現(xiàn),立即數(shù)#0X30008000這個(gè)地址就已經(jīng)32位了,在加上MOV指令這個(gè)操作碼,已經(jīng)超過32位了,編譯器是無法翻譯這個(gè)指令的,所以說,當(dāng)一個(gè)變量或函數(shù)地址為32位時(shí),使用MOV指令給PC直接賦值,行不通,那怎么辦呢?

4LDR偽指令

辦法總是有的,比如,我們就可以通過偽指令LDR,直接將一個(gè)32位的立即數(shù)地址,傳送到PC:

LDR PC,=0x30008000

LDR偽指令的功能和MOV一樣,都可以將一個(gè)立即數(shù)傳送到寄存器。唯一區(qū)別的就是,MOV指令只能傳送8位的,而LDR可以傳送一個(gè)32位的立即數(shù)或地址。

這里需要注意一下,立即數(shù) 0X30008000 的前面有一個(gè)等于號(hào)“=”,這表示前面的LDR指令是一個(gè)偽指令。除此之外,在ARM中,LDR還有另外一個(gè)意思,用來將內(nèi)存中的數(shù)據(jù)加載到寄存器。我們知道,ARM是RISC架構(gòu),使用LDR/STR架構(gòu),不能直接修改內(nèi)存中的數(shù)據(jù)。如果我們要修改內(nèi)存中的一個(gè)變量,要首先使用LDR指令將內(nèi)存中的變量加載到寄存器中,接著對(duì)寄存器進(jìn)行操作,最后再使用STR指令將寄存器中的變量回寫到內(nèi)存中。所以,LDR可以看作是一個(gè)偽指令,也可以看做是普通的一個(gè)LDR指令,判定他們的區(qū)別就是看前面的等于號(hào)。

普通的LDR指令主要使用寄存器間接尋址,常用的使用方式如下:

LDR R0,[R1]
LDR R0,0x30008000

這里注意后面一句,是將地址0x30008000地址上的內(nèi)容傳動(dòng)到寄存器R0,而不是直接將這個(gè)地址傳送到R0,這里一定要注意其跟LDR偽指令的區(qū)別,這一點(diǎn)沒有注意到,你在分析程序時(shí)就可能誤入歧途了。

在《C語言嵌入式Linux高級(jí)編程》第二期中,我們已經(jīng)探討了CPU、指令集、偽指令的基本概念,這里就不贅述了。簡(jiǎn)單來說,偽指令并不是真正的ARM指令,并不屬于ARM指令集中的標(biāo)準(zhǔn)指令。它只是編譯器為了方便我們程序員開發(fā)程序,定義的一些助記符。在編譯時(shí),這些偽指令還是會(huì)使用指令集中的標(biāo)準(zhǔn)指令來實(shí)現(xiàn)。

比如上面的LDR偽指令,程序在編譯時(shí),看到這個(gè)偽指令,會(huì)使用ARM指令集中標(biāo)準(zhǔn)的指令實(shí)現(xiàn)。如果LDR偽指令中的立即數(shù)小于8位,它就會(huì)轉(zhuǎn)換為MOV指令來實(shí)現(xiàn):

LDR R0,=200
MOV R0,#200

如果LDR偽指令中,立即數(shù)大于8bit表示的數(shù)據(jù)范圍,比如說是一個(gè)32位的立即數(shù)或地址,那就不能使用MOV指令來實(shí)現(xiàn)了,可以采用文字池的形式,先將這個(gè)地址常量單獨(dú)存放在存儲(chǔ)單元中,然后使用相對(duì)尋址,曲線救國(guó),完成這個(gè)32位地址或立即數(shù)與寄存器之間的傳輸,這些細(xì)節(jié)在教程視頻中都有講到,就不再贅述了。

5小結(jié)

通過上面的學(xué)習(xí),我們基本上理清了ARM系統(tǒng)中常見的幾種跳轉(zhuǎn)指令,以及它們的區(qū)別。只有徹底理解他們的底層機(jī)制及實(shí)現(xiàn)細(xì)節(jié),才有可能在使用反匯編分析程序時(shí),達(dá)到事半功倍的效果,從而大大提高我們的工作效率。否則,這些基本的細(xì)節(jié)和概念搞不清,將會(huì)永遠(yuǎn)成為你學(xué)習(xí)和工作上的障礙。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    c語言深度剖析

    c語言深度剖析
    發(fā)表于 04-02 09:12

    C語言深度剖析

    C語言深度剖析
    發(fā)表于 08-25 09:08

    C語言深度剖析

    C語言深度剖析[完整版].pdfC語言深度剖析[完整版].pdf (919.58 KB )
    發(fā)表于 03-19 05:11

    arm匯編跳轉(zhuǎn)指令總結(jié)

    目前所知道的跳轉(zhuǎn)指令有 b,bl,bep,bne.他們共同點(diǎn)是都是以b開頭,首先從字面上分析:b:是Branch,表示分支。bl:是Branch Link表示帶連接的分支。bep:Branch
    發(fā)表于 04-26 02:39

    請(qǐng)問一下ARM跳轉(zhuǎn)指令的范圍是多少

    , ?]構(gòu)造跳轉(zhuǎn)指令。)B,BL指令保存的是偏移地址,這個(gè)地址的計(jì)算方法是:假設(shè)跳轉(zhuǎn)指令處的地址是A,
    發(fā)表于 04-14 09:30

    請(qǐng)問一下ARM匯編中的B跳轉(zhuǎn)指令和LDR跳轉(zhuǎn)的區(qū)別在哪

    請(qǐng)問一下ARM匯編中的B跳轉(zhuǎn)指令和LDR跳轉(zhuǎn)的區(qū)別有哪些不同之處呢?
    發(fā)表于 07-21 15:57

    ARM匯編語言跳轉(zhuǎn)指令的特殊用法還有嗎

    關(guān)于 ARM匯編語言跳轉(zhuǎn)指令的特殊用法。有如下兩條跳轉(zhuǎn)指令: beq lablefbeq lableb其中 lable 為某段程序的標(biāo)號(hào),b
    發(fā)表于 10-31 15:30

    arm匯編語言跳轉(zhuǎn)指令有何特殊用法呢?

    關(guān)于 ARM 匯編語言跳轉(zhuǎn)指令的特殊用法。有如下兩條跳轉(zhuǎn)指令: beq lablefbeq lableb其中 lable 為某段程序的標(biāo)號(hào),
    發(fā)表于 02-24 15:28

    arm指令集(1)

    arm指令集(1)  ARM跳轉(zhuǎn)指令可以從當(dāng)前指令向前或向后的32MB地址空間
    發(fā)表于 03-02 15:46 ?79次下載

    ARM指令集(2)

    ARM指令集(2)  1.跳轉(zhuǎn)指令   在ARM中有兩種方式可以實(shí)現(xiàn)程序的跳轉(zhuǎn):一種是刀
    發(fā)表于 03-02 15:49 ?68次下載

    Thumb指令集之Thumb跳轉(zhuǎn)指令

    Thumb指令集中的跳轉(zhuǎn)指令分以下6種類型。 ① 無條件跳轉(zhuǎn),其跳轉(zhuǎn)空間為2KB。 ② 條件跳轉(zhuǎn)
    發(fā)表于 10-19 10:04 ?1次下載
    Thumb<b class='flag-5'>指令</b>集之Thumb<b class='flag-5'>跳轉(zhuǎn)</b><b class='flag-5'>指令</b>

    跳轉(zhuǎn)指令B及帶連接的跳轉(zhuǎn)指令BLX上 

    跳轉(zhuǎn)(B)和跳轉(zhuǎn)連接(BL)指令是改變指令執(zhí)行順序的標(biāo)準(zhǔn)方式。ARM一般按照字地址順序執(zhí)行指令,
    發(fā)表于 10-19 10:26 ?2次下載
    <b class='flag-5'>跳轉(zhuǎn)</b><b class='flag-5'>指令</b>B及帶連接的<b class='flag-5'>跳轉(zhuǎn)</b><b class='flag-5'>指令</b>BLX上 

    ARM嵌入式系統(tǒng)的中斷服務(wù)例程跳轉(zhuǎn)

    在32位 ARM 系統(tǒng)中,一般都是在中斷向量表中放置一條分支指令或PC寄存器加載指令,實(shí)現(xiàn) 程序跳轉(zhuǎn) 到 中斷服務(wù) 例程的功能。例如: IRQEntry B HandleIRQ ;
    發(fā)表于 04-10 10:11 ?2810次閱讀

    PLC跳轉(zhuǎn)/標(biāo)號(hào)指令的工作原理及應(yīng)用舉例

    跳轉(zhuǎn)/標(biāo)號(hào)執(zhí)行是用來跳過部分程序使其不執(zhí)行必須用在同一程序塊內(nèi)部實(shí)現(xiàn)跳轉(zhuǎn)。跳轉(zhuǎn)/標(biāo)號(hào)指令有兩條,分別為跳轉(zhuǎn)
    的頭像 發(fā)表于 10-08 09:59 ?5457次閱讀

    西門子博途SCL的GOTO跳轉(zhuǎn)指令

    跳轉(zhuǎn)標(biāo)簽和“跳轉(zhuǎn)指令必須在同一個(gè)塊中。在一個(gè)塊中,跳轉(zhuǎn)標(biāo)簽的名稱只能指定一次。每個(gè)跳轉(zhuǎn)標(biāo)簽可以是多個(gè)
    發(fā)表于 07-03 14:53 ?7853次閱讀