pc 寄存器
pc保存着当前指令执行的地址,相当于win32汇编里的eip寄存器
lr寄存器 (x30)
lr存储函数的返回地址,每次遇到ret返回指令,都会跳转到lr寄存器里保存的地址,这个地址是函数调用的下一条指令,就是应该要执行的指令,cpu遇到ret之后,会把lr赋值给pc,这样cpu了pc里的地址,就是执行函数调用的下一条指令
ret返回指令
作用是返回,
本质上的操作:执行ret之后,会把lr寄存器里的值赋值给pc,这样cpu就会执行到lr地址里的指令了.
bl 跳转指令,带返回
bl执行跳转之前,会把bl的下一条指令地址,赋值给lr,这样,跳转之后的lr就是跳转之前的下一条指令了.当执行到ret指令的时候,会把lr的值赋值给pc,这样就会跳回到bl的下一条指令.
b 跳转,不带返回
b跳转的时候因为没有给lr赋值,所以跳转之后,lr还是跳转之前的lr值,所以跳转之后遇到ret指令以后会跳转到b之前的 lr的地址.
下面用汇编写的fn1方法里面有b和bl 2种跳转,注释掉其中一种,得到不同的结果
//.h中的声明
void fn1(int *a);
oc中的调用,传入一个int类型的地址
//oc调用
int a = 11;
fn1(&a);
NSLog(@"a=%d",a);
汇编中的函数实现
.text
.global _fn1
_fn1:
str wzr,[x0]
mov x10,lr//保存之前的lr值,这样可以返回到调用fn1()的位置
bl label1 //bl跳转到label1之前,会把lr赋值成下一条指令,这样.遇到label1的ret会返回到下面的指令mov w3,0x10
mov lr,x10 //还原之前的lr地址,这样能返回到调用fn1()的位置
mov w3,0x10
str w3,[x0]
ret
label1:
mov w3,0xf
str w3,[x0]
ret//这句执行完以后会把lr赋值给pc,因为lr已经被bl赋值了bl之后指令的地址,所以会跳转到上面的mov w3,0x10指令,这样,变量a就会赋值成了0x10,打印结果a=16
上面代码注释已经写了在执行到bl之后跳转到label1,然后给变量a赋值0xF,再返回到bl label1的下一条指令,再还原之前的lr值,这样再遇到ret的时候就返回到fn1(&a)的调用位置了
下面的gif图片,录制了这段代码的执行过程,其中re read lr 查看了lr的改变,包括执行完fn1之后a被赋值成了0x10 = 16
b 跳转以后不会改变lr的值,遇到ret以后就会返回之前的lr里的地址里的指令,如下图gif
.text
.global _fn1
_fn1:
str wzr,[x0]
mov x10,lr//保存之前的lr值,这样可以返回到调用fn1()的位置
b label1//b跳转到label1之后,lr没有被赋值,而是保持这之前的值,这样label1执行到遇到ret之后,就会返回到现在lr的值,就是调用_fn1的指令的下一条指令
mov lr,x10 //还原之前的lr地址,这样能返回到调用fn1()的位置
mov w3,0x10
str w3,[x0]
ret
label1:
mov w3,0xf
str w3,[x0]
ret