接下来我们看看b.bx.blx
b分支指令 简单地跳向一个函数
bl分支连接指令 将(PC+4)保存到LR中并跳转到函数
BX(分支切换指令)和BLX(分支连接切换指令)
a) 和B/BL+交换指令集相同( ARM <-> Thumb )
b) 需要用寄存器作为第一操作数:BX/BLX+具体的寄存器
BX/BLX用来从ARM指令集切换到Thumb指令集
我们看一个实例
.text
.global _start
_start:
.code 32 @ ARM mode
add r2, pc, #1 @ put PC+1 into R2
bx r2 @ branch + exchange to R2
.code 16 @ Thumb mode
mov r0, #1
获取实际的PC当前值,将其增加1,将结果存储到寄存器中,并将分支存储到该寄存器。
代码在test10.s,还是同样的步骤
run后可以看到将要执行的语句为add r2,pc,#1,注意此时pc为0x10054
我们看到,加法 (add r2, pc, #1) 将简单地获取有效的PC地址(当前PC寄存器的值0x10054+ 8等于0x1005c ),并加1(0x1005c+1=0x1005d)。
nexti之后可以看到此时r2为0x1005d,pc为0x10058,符合我们上面的计算
然后,如果我们分支指令后面的地址的最低有效位(LSB)为1(这里就是这种情况,因为0x1005D=00000000 01011101),意味着地址不是4字节对齐的,则发生状态转换,从ARM切换到了Thumb
我们怎么知道状态转换了呢?
看CPSR,注意此时thumb为小写
nexti之后
thumb为大写,说明此时已经转换到了Thumb
我们接下来学习条件分支指令
分支还可以有条件地执行,如果满足特定条件,则分支到某函数。让我们来看一个非常简单的使用条件分支指令 BEQ的例子。
代码如下
.text
.global _start
_start:
mov r0, #2
mov r1, #2
add r0, r0, r1
cmp r0, #4
beq func1
add r1, #5
b func2
func1:
mov r1, r0
bx lr
func2:
mov r0, r1
bx lr
逻辑就是如果r0等于4,执行func1,否则执行fun2;fun1是将r0赋给r1,然后使用bx lr实现子程序的返回
在test11.s
前三条是赋值
我们使用nexti 3直接执行过去
接下来要执行的是cmp指令,判断r0,4的大小,我们知道是相等的,所以nexti之后,下一条要执行的是func1中的指令
r1此时还是0x2,执行后将被r2赋为0x4
可以看到r1此时确实是0x4,符合推测。
接下来学习栈
一般来说,堆栈是程序/进程内的一块内存区域。创建进程后,会开辟出这样一块内存空间。我们使用堆栈来存储临时数据,例如一些函数的局部变量、环境变量,这些变量可以帮助我们在函数之间转换,等等。我们使用push,pop指令来和栈交互,我们在前面的实验中已经解释过:PUSH和POP是一系列其他的内存相关指令的总称的别名,而不是实际指令,但为了让事情变得简单,我们使用了PUSH和POP。
我们应该知道,堆栈可以用不同的方式来实现。首先,当我们说堆栈生长了,意思是一个项目(32位长度的数据)被压入堆栈了。同样是压入,堆栈可以长大(以堆栈地址降低方式实现)或减小(以堆栈地址升高方式实现)。下个项目(32位)信息将被放置的实际地址由堆栈指针定义,或者确切地说,这个地址是被存储在SP寄存器中的。地址可能会指向堆栈中当前(最新)的项目,或指向当前项的下一项的可用的内存槽。如果SP当前指向堆栈中的最后一个项目(完整堆栈实现),SP将先减少(在堆栈降序排列的情况下)或增加(在栈升序排列的情况下),并且只有这么做后,项目才能被放置在堆栈中。如果SP当前指向栈中的下一个空白内存槽,则数据将首先被放置在这里,并且SP将被减少(降序堆栈)或增加(升序堆栈)。
根本不同类型,涉及到的指令包括
我们看一个例子,这例子中使用的是一个递减的堆栈。
可以看到一开始sp指向0xbefff3a0
下一条指令是
nexti执行
执行这条指令对sp没有影响
注意此时r0为0x2,接着要执行的是push,执行之后会发现:首先,SP的值减少了4(4字节=32位),为0xbefff3a0-0x4=0xbefff39c。然后,R0的内容(0x2)被存储到由SP指定的新地址中。
Nexti之后由下图红框中的内容验证了我们的推测
执行mov r0,#3会发现此时r0的值为0x3,使用nexti后如下
接下来会执行pop指令。当POP被执行时,发生以下情况:首先,从SP中当前地址指向的内存位置(0xBEFFF39c)读取32位长度(4字节)的数据。然后,SP寄存器的值增加4(再次成为0xbefff3a0),寄存器R0被赋0x2。
Nexti后可以看到和我们推测的一样。
azeria-labs 的arm教程
https://azeria-labs.com/writing-arm-assembly-part-1/