汇编

汇编(五) -- 函数的参数和返回值

2019-08-09  本文已影响0人  小凉介

前言

最近准备学习汇编,然后在B站上看到叫iOS小贤的作者发的视频挺不错,打算跟着学,文章是看视频的笔记,最后有原视频链接,想看视频的可以看看通过链接查看视频。

函数的参数和返回值

我们新写一个sum方法,然后打断点

Screen Shot 2019-08-09 at 2.32.44 PM.png
mov    w0, #0xa
mov    w1, #0x14

这两句是把#0xa(10),#0x14(放入)w0(x0的低32位)和w1(x1的低32位)中。接下来bl 0x10273a94c就进入sum函数。

Screen Shot 2019-08-09 at 2.43.31 PM.png
sub    sp, sp, #0x10 

拉伸栈空间#0x10(16位)。

str    w0, [sp, #0xc]
str    w1, [sp, #0x8]

w0和w1的值放入了栈空间,位置是sp偏移#0xc,sp偏移#0x8。

ldr    w0, [sp, #0xc]
ldr    w1, [sp, #0x8]

把栈空间的值读出来放入寄存器w0,w1。看起来有点滑稽,但是在打包的时候编译器会做优化。

add    w0, w0, w1

w0和w1的值相加然后放入w0。

add    sp, sp, #0x10 
ret

栈平衡并且return。

用汇编手写求和函数

在函数内部没有调用其他函数的函数叫做叶子函数。

叶子函数没有必要开辟栈空间,不移动sp的位置,直接通过sp的偏移,放入sp开始的低地址的区域中,因为接下来不会调用别的函数,就不会有其他函数干扰,所以没问题。而且调用完毕之后这块区域也不需要了,别的函数在用的时候,sp往低地址移动,开辟新的栈空间栈空间会先写再读,所以不会有影响。

Screen Shot 2019-08-09 at 3.09.33 PM.png

这样我们可以写出精简版的sum函数的汇编代码

.text
.global _suma

_suma:
    add x0, x0 , x1
    ret
Screen Shot 2019-08-09 at 8.29.17 PM.png

我们看到输出了正确结果30。

有人可能会问为何结果放入x0中,这是由编译器的,我们试试看放入x1会怎样,结果输出了10。这是因为把结果放入x1,而x0仍然第一个参数是10。


Screen Shot 2019-08-09 at 8.30.28 PM.png

函数参数超过8个

Screen Shot 2019-08-09 at 8.43.40 PM.png
sub    sp, sp, #0x30

sp向低地址拉伸16*3=48个字节,如下图

Screen Shot 2019-08-09 at 8.50.25 PM.png Screen Shot 2019-08-09 at 8.51.09 PM.png
stp    x29, x30, [sp, #0x20]

sp偏移#0x20即向高地址出偏移2*16=32个字节,然后写入x29和x30,前面讲过读写是往高地址,于是之后的内存布局如下图:

Screen Shot 2019-08-09 at 8.59.17 PM.png
add    x29, sp, #0x20

sp加#0x20其实和上面[sp, #0x20]一样,然后赋值给x29。


Screen Shot 2019-08-09 at 9.04.24 PM.png
stur   wzr, [x29, #-0x4]
stur   w0, [x29, #-0x8]
str    x1, [sp, #0x10]

执行这几个命令之后内存图如下:

Screen Shot 2019-08-09 at 9.27.24 PM.png
orr    w0, wzr, #0x1
orr    w1, wzr, #0x2
orr    w2, wzr, #0x3
orr    w3, wzr, #0x4
mov    w4, #0x5
orr    w5, wzr, #0x6
orr    w6, wzr, #0x7
orr    w7, wzr, #0x8
mov    w8, #0x9

orr是或得意思,前面讲过ARM64中

所以是#0x1和0进行或运算,然后赋值给w0,相当于mov w0, #0x1

w8, [sp]

因为函数的参数通常情况下是存放在X0到X7(W0到W7)这8个寄存器里面的,所以w8不能参数,于是直接把w8放入栈中,也就是#0x9,就是参数9。

Screen Shot 2019-08-09 at 10.06.53 PM.png
在调用sum函数bl 0x1050028bc ; sum1 at main.m:16之前,对栈的操作就是这么个过程。
总结:
  1. 就是拉伸了栈空间
  2. 然后往栈中保护了两个寄存器x29,x30
  3. 接下来保护w0和x1寄存器
  4. 然后把参数放入w1到w7
  5. 最后多出来参数放入栈空间

然后进入sum函数

Screen Shot 2019-08-09 at 9.43.19 PM.png
sub    sp, sp, #0x30 

又是拉伸栈空间,拉升了#0x30。

Screen Shot 2019-08-09 at 9.51.45 PM.png
ldr    w8, [sp, #0x30]

[sp, #0x30]是sp加上#0x30,就是刚刚sp的位置,然后向高地址处读取值放入w8中,就是刚刚放的参数,这里读出放入栈中的参数。

str    w0, [sp, #0x2c]
str    w1, [sp, #0x28]
str    w2, [sp, #0x24]
str    w3, [sp, #0x20]
str    w4, [sp, #0x1c]
str    w5, [sp, #0x18]
str    w6, [sp, #0x14]
str    w7, [sp, #0x10]

把w0到w7里的参数入栈。

ldr    w0, [sp, #0x2c]
ldr    w1, [sp, #0x28]
add    w0, w0, w1

从栈中取出来,两两相加。

ldr    w1, [sp, #0x24]
add    w0, w0, w1
ldr    w1, [sp, #0x20]
add    w0, w0, w1
ldr    w1, [sp, #0x1c]
add    w0, w0, w1
ldr    w1, [sp, #0x18]
add    w0, w0, w1
ldr    w1, [sp, #0x14]
add    w0, w0, w1
ldr    w1, [sp, #0x10]
add    w0, w0, w1
ldr    w1, [sp, #0x30]
add    w0, w0, w1

参考

函数的参数和返回值

上一篇 下一篇

猜你喜欢

热点阅读