arm b bl 地址无关码_ARM汇编语言入门(六)
Part 6:條件狀態(tài)和分支
在探討CPSR時我們已經(jīng)接觸了條件狀態(tài)。我們通過跳轉(zhuǎn)(分支)或者一些只有滿足特定條件才執(zhí)行的指令來控制程序在運行時的執(zhí)行流。通過CPSR寄存器中的特定bit位來表示條件狀態(tài)。這些位根據(jù)指令每次執(zhí)行的結(jié)果而不斷變化。例如,比較運算時如果兩個數(shù)相等,那么就置CPSR中的Zero位(Z=1),實際上是因為:a - b = 0,這種情況下就是相等狀態(tài)。如果第一個數(shù)大,那么就是大于狀態(tài)。如果第二個數(shù)大,就是小于狀態(tài)。除此之外,還有小于等于、大于等于等等。
下面的表格列出了可用的條件狀態(tài)碼,描述和標志位:
在下面代碼片段中看一下執(zhí)行條件加法時的實際用法L:
.global main ? main:mov r0, #2 /* 初始化變量 */cmp r0, #3 /* 將R0中的值與3比較,負數(shù)位置1 */addlt r0, r0, #1 /* 如果上一條比較結(jié)果是小于(查看CPSR),則將R0加1 */cmp r0, #3 /* 將R0中的值再與3比較, 零位置1,同時負數(shù)位重置為0 */addlt r0, r0, #1 /* 如果上一條比較結(jié)果是小于(查看CPSR),則將R0加1 */bx lr第一條cmp指令結(jié)果導(dǎo)致CPSR中的負數(shù)位置1(2- 3 = -1)意思是R0小于R3。因為滿足小于條件(CPSR中的溢出位不等于負數(shù)位V != N)所以接下來的ADDLT指令執(zhí)行。在執(zhí)行下一條cmp指令時,R0 = 3。所以清除負數(shù)位(3 - 3 = 0,負數(shù)位清零),零位置位(Z = 1)。現(xiàn)在溢出位是0,負數(shù)位是0,不滿足小于條件。所以最后一條ADDLT指令不執(zhí)行,R0值保持3不變。
Thumb模式下的條件執(zhí)行
我們在介紹指令集的章節(jié)討論了Thumb狀態(tài)下的不同。具體而言是Thumb-2版本支持條件執(zhí)行。某些 ARM 處理器版本支持"IT"指令,允許在 Thumb 狀態(tài)下支持多達4個條件執(zhí)行指令。參考:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/BABIJDIC.html。
語法:IT{x{y{z}}} cond
- cond 指定 IT 塊的第一個指令的條件。
- x 指定 IT 塊中第二個指令的條件開關(guān)。
- y 指定 IT 塊中第三個指令的條件開關(guān)。
- z 指定 IT 塊中第四個指令的條件開關(guān)。
其實IT指令的結(jié)構(gòu)就是“IF-Then-(Else)”,語法都是由字母“T”和“E”構(gòu)成:
- IT:If-Then(下一條指令是條件的);
- ITT:If-Then-Then(后兩條指令是條件的);
- ITE:If-Then-Else(后兩條指令是條件的);
- ITTE:If-Then-Then-Else(后三條指令是條件的);
- ITTEE:If-Then-Then-Else-Else(后四條指令是條件的);
IT塊中的每條指令必須指定相同或邏輯相反的條件后綴。意思是,如果使用ITE,那么前兩個指令必須有相同的后綴,而第三個必須是邏輯相反的后綴。下面是 ARM 參考手冊中的一些示例,說明了這些邏輯:
ITTE NE ; 接下來的3條指令都是有條件的。 ANDNE R0, R0, R1 ; ANDNE不更新條件標志。 ADDSNE R2, R2, #1 ; ADDSNE更新條件標志。 MOVEQ R2, R3 ; 有條件的移動 ? ITE GT ; 接下來的2條指令都是有條件的。 ADDGT R1, R0, #55 ; 條件滿足大于時進行相加。 ADDLE R1, R0, #48 ; 條件不滿足大于時進行相加。 ? ITTEE EQ ; 接下來的4條指令都是有條件的。 MOVEQ R0, R1 ; 有條件的MOV ADDEQ R2, R2, #10 ; 有條件的ADD ANDNE R3, R3, #1 ; 有條件的AND BNE.W dloop ; 分支指令只能在IT塊的最后一個指令中使用。錯誤示例:
IT NE ; 下一條指令是條件的。 ADD R0, R0, R1 ; 語法錯誤,不是有條件的指令。下面是條件代碼和相反代碼:
現(xiàn)在使用以下代碼來測試:
.syntax unified @ 非常重要! .text .global _start ? _start:.code 32add r3, pc, #1 @ PC的值加1并存儲到R3。bx r3 @ 跳轉(zhuǎn)到R3中的地址處,并切換運行模式 ->切換到Thumb模式,因為R3最低有效位(LSB) = 1。 ?.code 16 @ Thumb模式cmp r0, #10 ite eq @ 如果R0等于10...addeq r1, #2 @ ... 那么 R1 = R1 + 2addne r1, #3 @ ... 否則 R1 = R1 + 3bkpt.code 32
示例中的代碼開始在ARM模式下,第一條指令將PC中的地址值加1并存儲到R3,然后bx指令跳轉(zhuǎn)到R3中的地址位置,并且模式切換成Thumb模式,因為R3中的值最低有效位為1(0不切換)。為此使用bx(分支+交換)非常重要。
.code 16
在Thumb模式下,首先比較R0和10,結(jié)果將負數(shù)位N置位(0 - 10 = -10)。之后使用If-Then-Else塊,因為零位Z(Zero)沒有被置位所以ADDEQ指令被跳過,然后因為結(jié)果不相等所以執(zhí)行ADDNE指令。
在 GDB 中單步執(zhí)行此代碼會干擾結(jié)果,因為你要在 ITE 塊中執(zhí)行這兩個指令。 但是,在 GDB 中運行代碼而不設(shè)置斷點并單步執(zhí)行每個指令將生成正確的結(jié)果設(shè)置 R1 = 3。
分支
分支(跳轉(zhuǎn))允許我們跳轉(zhuǎn)到另一個代碼段。當你需要跳過(或者重復(fù))某塊代碼或者跳轉(zhuǎn)到指定的函數(shù)的時候,分支很有用。此類情形中最佳的示例是IF和循環(huán)。先來看看IF案例。
.global main ? main:mov r1, #2 /* 設(shè)置初始變量a */mov r2, #3 /* 設(shè)置初始變量b */cmp r1, r2 /* 比較兩個變量值看哪個更大 */blt r1_lower /* 因為R2更大(N==1),跳轉(zhuǎn)到r1_lower */mov r0, r1 /* 如果沒有跳轉(zhuǎn), 例如R1的值更大(或者相等),則將R1的值存儲到R0 */b end /* 結(jié)束 */ r1_lower:mov r0, r2 /* R1小于R2時跳轉(zhuǎn)到此處, 將R2的值存儲到R0 */b end /* 結(jié)束 */ end:bx lr /* THE END */上面代碼是比較兩個初始值并返回最大值,C語言偽代碼:
int main() {int max = 0;int a = 2;int b = 3;if(a < b) {max = b;}else {max = a;}return max; }現(xiàn)在再看一下怎么使用條件分支實現(xiàn)循環(huán):
.global main ? main:mov r0, #0 /* 設(shè)置初始變量a */ loop:cmp r0, #4 /* 比較a==4 */beq end /* 如果a==4,結(jié)束 */add r0, r0, #1 /* 否則將R0中的值遞增1 */b loop /* 跳轉(zhuǎn)到loop開始位置 */ end:bx lr /* THE END */C語言偽代碼:
int main() {int a = 0;while(a < 4) {a= a+1;}return a; }B、BX、BLX指令
有三種類型的分支指令:
- 普通分支(B)
- 簡單的跳轉(zhuǎn)到一個函數(shù)。
- 帶鏈接的跳轉(zhuǎn)(BL)
- 將PC+4的值保存到LR寄存器,然后跳轉(zhuǎn)。
- 帶狀態(tài)切換的跳轉(zhuǎn)(BX)和帶狀態(tài)切換及鏈接的跳轉(zhuǎn)(BLX)
- 與B和BL一致,只是添加了工作狀態(tài)的切換(ARM模式-Thumb模式)。
- 需要寄存器作為第一個操作數(shù)。
BX、BLX用來切換ARM模式到Thumb模式。
.text .global _start ? _start:.code 32 @ ARM modeadd r2, pc, #1 @ put PC+1 into R2bx r2 @ branch + exchange to R2 ?.code 16 @ Thumb modemov r0, #1這里的技巧是獲得當前PC的值,加1然后保存到一個寄存器,然后跳轉(zhuǎn)(并且切換狀態(tài)模式)到這個寄存器內(nèi)的地址。可以看到加指令(add r2, pc, #1)獲取到有效的PC地址值(當前PC內(nèi)的值+8=0x805C)然后加1(0x805C + 1 = 0x805D)。接下來,我們跳轉(zhuǎn)的地址( 0x805D = 10000000 01011101)最低有效位為1,那么意味著地址不是4字節(jié)(32bit)對齊的。跳轉(zhuǎn)到這樣的地址不會導(dǎo)致非對齊問題。在GDB中運行的樣子(含GEF):
注意上面的gif圖片是在低版本的GEF下創(chuàng)建的,所以你的顯示界面可能不一樣,但是邏輯是一樣的。
條件分支
分支也可以有條件地執(zhí)行,用于在滿足特定條件時跳轉(zhuǎn)到函數(shù)。我們看一個使用BEQ應(yīng)用條件分支的例子,這是一段沒太有用的匯編代碼,只不過是在寄存器等于特定值時將一個值移動到寄存器并跳轉(zhuǎn)到另一個函數(shù)的過程。
.text .global _start ? _start:mov r0, #2mov r1, #2add r0, r0, r1cmp r0, #4beq func1add r1, #5b func2 func1:mov r1, r0bx lr func2:mov r0, r1bx lr<end>
總結(jié)
以上是生活随笔為你收集整理的arm b bl 地址无关码_ARM汇编语言入门(六)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言按照姓名查询员工信息,输入10个职
- 下一篇: github打开前端样式丢失_工具资源系