汇编(四) — bl&ret

x30寄存器存放的是函数的返回地址.当ret指令执行时刻,会寻找x30寄存器保存的地址值!

注意:在函数嵌套调用的时候.需要将x30入栈!

代码演练

写一个sum函数

然后在sum处打断点,run起来。

bl跳转到sum函数中去,跳到sum函数中后一旦返回,就要返回到 0x102d16974 : mov w1, #0x0 处,需要需要把 0x102d16974 存到lr(x30)中。

下面我们来改写lr(x30),先进入sun函数,然后走到 add sp, sp, #0x10 处,然后输入

register write lr 0x102d16934
register read lr

复制代码

然后继续往下走

果然走到我们改写的位置,没有出退出sum函数。ret指令其实就是找lr(x30)。

接下来再写一段汇编:

.text
.global _A,_B

_A:
    mov    x0, #0xaaaa
    bl     _B
    mov    x0, #0xbbbb
    ret


_B:
    mov    x0, #0xcccc
    ret

复制代码

然后调用A(),我们发现只打印了A。

我们调试试试看

此时lr是
0x00000001025ba948 ,bl下一个指令地址是
0x1025ba950

,接下来输入s,进入A函数。

lr变成了0x00000001025ba950,也就是指令 adrp x0, 1 的地址。 现在要进入B函数,那么lr要保存bl下一条指令的地址,也就是 mov x0, #0xbbbb 的地址 0x1025babe4 ,接下来进入B函数。

然后不断往下走就又回到A函数,lr仍然是 0x1025babe4 ,我们一直单步往下走,会发现死循环。

接下来再写两个函数C和D:

然后打断点

此时不要s,按住control键,点击箭头进入D函数

stp x29, x30, [sp, #-0x10]! 这条指令是进过优化的简写, [sp, #-0x10]! 是sp减去0x10然后赋值给sp,这段代码相当于

sub    sp,   sp,   #0x10
stp    x29,  x30,  [sp]
复制代码

我们可以看到现在sp是 0x000000016d7eb8e0 ,然后我们ni,发现sp确实是变成了 0x000000016d7eb8d0 ( 0x000000016d7eb8e0 减去0x10)。

stp x29, x30, [sp, #-0x10]! 我们看到在函数最开始先把x29和写x30的值放入栈中。 ldp x29, x30, [sp], #0x10 然后在函数在函数调用完毕之前把栈中数据又放入x29和x30中,这样就保护了x29和x30两个寄存器。

完善之前汇编代码

.text
.global _A,_B

_A:
    mov    x0, #0xaaaa
    str    x30, [sp, #-0x10]!
    bl     _B
    mov    x0, #0xbbbb
    ldr    x30, [sp], #0x10
    ret


_B:
    mov    x0, #0xcccc
    ret
复制代码

注:我们只需要保存x30,不需要保存x29。这里需要再次说明,0x10不能用0x8,8个字节,之前讲过对栈的操作是16个字节对齐,所以可以用0x20,0x30……

运行代码,打印AB,说明这次没有问题了。

###参考: bl&ret

汇编(五)