汇编基础(四)函数&返回值

bl和ret指令

bl标号
  • 将下一条指令的地址放入lr(x30)寄存器
  • 转到标号处执行指令

注意:当我们遇到bl指令的时候,是跳转指令,跳转之后我们是怎么回来的?怎么回到bl下面的指令?遇到bl后,CPU会将下一个指令的内存地址存到lr寄存器当中。
验证:

  • 随便写一个函数,在main中调用,run


  • lldb输入跳转指令进入函数内部,查看lr寄存器是否是上图bl下一条指令的内存地址


ret
  • 默认使用lr(x30)寄存器的值,通过底层指令提示CPU此处作为下条指令地址!
ARM64平台的特色指令,它面向硬件做了优化处理apple文档写的

练习题:

.text
.global _Test1,_Test2
_Test1:
    mov x0,#0xaaaa
    mov x0,#0xcccc
    bl _Test2
    mov x0,#0xdddd
    ret

_Test2:
    mov x0,#0xbbbb
    mov x0,#0xffff
    ret

.text
.global _Test1,_Test2

_Test1:
    mov x0,#0xaaaa
    str x30,[sp,#-0x10]!
    bl _Test2
    mov x0,#0xcccc
    ldr x30,[sp],#0x10
    ret

_Test2:
    mov x0,#0xbbbb
    ret
答案
  • 一里面的习题会产生死循环,根据bl跳转之前会将下一条指令的地址付给lr寄存器,这样函数在调用完成 ret以后会跳到lr指向的指令。 出现了死循环
  • 二里面 str 首先开辟一个栈空间,来将ret跳转的指令地址存到栈里面,当bl执行完成以后,在从栈空间读出来存到lr寄存器当中,来跳过死循环。
  • x30就是lr寄存器

我们平时写的函数转换成汇编会变成什么?

int sum(int a, int b){
    return a + b;
}
int main (){
    sum(10,20);
    return 0;
}

汇编中

->  0x1049ba8ac <+0>:  sub    sp, sp, #0x10             ; =0x10 
    0x1049ba8b0 <+4>:  str    w0, [sp, #0xc]
    0x1049ba8b4 <+8>:  str    w1, [sp, #0x8]
    0x1049ba8b8 <+12>: ldr    w0, [sp, #0xc]
    0x1049ba8bc <+16>: ldr    w1, [sp, #0x8]
    0x1049ba8c0 <+20>: add    w0, w0, w1
    0x1049ba8c4 <+24>: add    sp, sp, #0x10             ; =0x10 
    0x1049ba8c8 <+28>: ret  

解读

  • sub sp, sp, #0x10 首先开辟栈空间向下移动16个字节
  • 后面的4句话将w0,w1的值存到栈空间,又从栈空间读到w0,w1,废话
  • add w0, w0, w1 计算两个值的和,最后返回的值是w0
  • add sp, sp, #0x10 因为函数使用完成,需要将该函数占用的栈空间清除,也就是栈平衡

练习题,自己写一个sum函数

int sumA(int a, int b);

int main(int argc, char * argv[]) {

    int b = sumA(10, 20);
    printf("%d",b);
    return 0;
  
}
  • 返回x0
.text
.global _sumA

_sumA:
    add x0,x0,x1
    ret
  • 返回x1
.text
.global _sumA

_sumA:
    add x1,x0,x1
    ret

验证
返回x0寄存器是正确的值,返回x1寄存器的值是10
那么:

通常情况下函数的返回值通常都放在x0 里面!!ARM64下,函数的参数是存放在X0到X7(W0到W7)这8个寄存器里面的.如果超过8个参数,就会入栈.

代码验证:

int map(int a, int b,int c, int d, int e,int f,int g, int h, int i,int j){
    
    return a+b+c+d+e+f+g+h+j+i+j;
}
int main(int argc, char * argv[]) {
    map(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    return 0;
}

汇编代码

0x1005c289c <+0>: sub sp, sp, #0x30 ; =0x30
0x1005c28a0 <+4>: stp x29, x30, [sp, #0x20]
0x1005c28a4 <+8>: add x29, sp, #0x20 ; =0x20
0x1005c28a8 <+12>: orr w8, wzr, #0x1
0x1005c28ac <+16>: orr w9, wzr, #0x2
0x1005c28b0 <+20>: orr w2, wzr, #0x3
0x1005c28b4 <+24>: orr w3, wzr, #0x4
0x1005c28b8 <+28>: mov w4, #0x5
0x1005c28bc <+32>: orr w5, wzr, #0x6
0x1005c28c0 <+36>: orr w6, wzr, #0x7
0x1005c28c4 <+40>: orr w7, wzr, #0x8
0x1005c28c8 <+44>: mov w10, #0x9
0x1005c28cc <+48>: mov w11, #0xa
0x1005c28d0 <+52>: stur wzr, [x29, #-0x4]
0x1005c28d4 <+56>: stur w0, [x29, #-0x8]
0x1005c28d8 <+60>: str x1, [sp, #0x10]
0x1005c28dc <+64>: mov x0, x8
0x1005c28e0 <+68>: mov x1, x9
0x1005c28e4 <+72>: str w10, [sp]
0x1005c28e8 <+76>: str w11, [sp, #0x4]
0x1005c28ec <+80>: bl 0x1005c280c ; map at main.m:21 bl之前是当前的准备工作,开辟栈空间,设置局部变量
0x1005c28f0 <+84>: mov w8, #0x0
0x1005c28f4 <+88>: str w0, [sp, #0xc]
0x1005c28f8 <+92>: mov x0, x8
0x1005c28fc <+96>: ldp x29, x30, [sp, #0x20]
0x1005c2900 <+100>: add sp, sp, #0x30 ; =0x30
0x1005c2904 <+104>: ret

代码分析:

  • sub sp, sp, #0x30 开辟空间,sp向下移动48个字节
  • stp x29, x30, [sp, #0x20] 向sp向上偏移32个字节,开始存储x29,x30. sp + 0x20 = x29 sp + 0x20 + 1 = x30
  • add x29, sp, #0x20 x29 = sp + 0x20
  • orr w8, wzr, #0x1 w8 = wzr | #0x1
  • orr w9, wzr, #0x2
  • orr w2, wzr, #0x3
  • orr w3, wzr, #0x4
  • mov w4, #0x5 w4 = #0x5
  • orr w5, wzr, #0x6
  • orr w6, wzr, #0x7
  • orr w7, wzr, #0x8
  • mov w10, #0x9
  • mov w11, #0xa 这以上不用解释可以类推 这几个赋值就是没有x0 x1,股么的x0,x1有用
  • stur wzr, [x29, #-0x4] 0x00000000 写入到内存 x29 - 0x4
  • stur w0, [x29, #-0x8] 正好8个字节放入到栈内存
  • str x1, [sp, #0x10] 将x1放入到栈内存
  • mov x0, x8 x0 = x8
  • mov x1, x9 x1 = x9
  • str w10, [sp] 将w10 放入到占内存中 sp
  • str w11, [sp, #0x4] 将w11 放入栈内存中 sp,+#0x4
    然后跳转到map函数里面
0x1005c280c <+0>:   sub    sp, sp, #0x30             ; =0x30 
    0x1005c2810 <+4>:   ldr    w8, [sp, #0x34]
    0x1005c2814 <+8>:   ldr    w9, [sp, #0x30]
    0x1005c2818 <+12>:  str    w0, [sp, #0x2c]
    0x1005c281c <+16>:  str    w1, [sp, #0x28]
    0x1005c2820 <+20>:  str    w2, [sp, #0x24]
    0x1005c2824 <+24>:  str    w3, [sp, #0x20]
    0x1005c2828 <+28>:  str    w4, [sp, #0x1c]
    0x1005c282c <+32>:  str    w5, [sp, #0x18]
    0x1005c2830 <+36>:  str    w6, [sp, #0x14]
    0x1005c2834 <+40>:  str    w7, [sp, #0x10]
    0x1005c2838 <+44>:  str    w9, [sp, #0xc]
    0x1005c283c <+48>:  str    w8, [sp, #0x8]
    0x1005c2840 <+52>:  ldr    w8, [sp, #0x2c]
    0x1005c2844 <+56>:  ldr    w9, [sp, #0x28]
    0x1005c2848 <+60>:  add    w8, w8, w9
    0x1005c284c <+64>:  ldr    w9, [sp, #0x24]
    0x1005c2850 <+68>:  add    w8, w8, w9
    0x1005c2854 <+72>:  ldr    w9, [sp, #0x20]
    0x1005c2858 <+76>:  add    w8, w8, w9
    0x1005c285c <+80>:  ldr    w9, [sp, #0x1c]
    0x1005c2860 <+84>:  add    w8, w8, w9
    0x1005c2864 <+88>:  ldr    w9, [sp, #0x18]
    0x1005c2868 <+92>:  add    w8, w8, w9
    0x1005c286c <+96>:  ldr    w9, [sp, #0x14]
    0x1005c2870 <+100>: add    w8, w8, w9
    0x1005c2874 <+104>: ldr    w9, [sp, #0x10]
    0x1005c2878 <+108>: add    w8, w8, w9
    0x1005c287c <+112>: ldr    w9, [sp, #0x8]
    0x1005c2880 <+116>: add    w8, w8, w9
    0x1005c2884 <+120>: ldr    w9, [sp, #0xc]
    0x1005c2888 <+124>: add    w8, w8, w9
    0x1005c288c <+128>: ldr    w9, [sp, #0x8]
    0x1005c2890 <+132>: add    w0, w8, w9
    0x1005c2894 <+136>: add    sp, sp, #0x30             ; =0x30 
    0x1005c2898 <+140>: ret    

这代码我就不分析,可以根据上面的分析进行类推,

  • 稍微解释下,参数分析,一般函数参数,如果不超过8个直接可以用寄存器搞定,如果超过8个需要存到栈空间
  • 到函数里面,汇编分析,如果如果函数参数超过8个,多余的会存到栈内存中,再将寄存器中的参数按照从低到高的顺序存储到内存当中跟上面多余的参数的内存是连续的,那么首先将栈内存当中按照从低到高的顺序去读取两个值,然后add相加给x8,最后得到的值在赋值给x0 返回

练习:

int sum(int a, int b){
    int c = 6;
    int d = 0; 
    return  a + b + c;
}

int main(int argc, char * argv[]) {
    sum(10,20);
    return 0;
}

汇编分配的栈空间

  sub  sp,sp,#0x10
int sum(int a, int b){
    int c = 6;
    int d = 0; 
    int f = 1;  
    return  a + b + c;
}

int main(int argc, char * argv[]) {
    sum(10,20);
    return 0;
}

汇编分配的栈空间

  sub  sp,sp,#0x20

结论
这个应该可以理解哦 ,因为ARM64是16位每次开辟空间都是16为基准,只能是16的倍数, int = 4字节,4* 4正好 4*5 = 20 需要再开辟一个16位的空间 也就是 sp + 0x20

练习

int sum(int a, int b){
    int c = 6;
    printf("%d",c);
    return  a + b + c;
}

int main(int argc, char * argv[]) {
    sum(10,20);
    return 0;
}

汇编分配的栈空间

  sub  sp,sp,#0x30

为什么会多呐? 因为嵌套函数必须保护寄存器x30,lr 回家的路

练习

int sum(int a, int b){
    
    return  a + b ;
}


int funcA(int a, int b){
    
    int d = sum(a, b);
    int e = sum(a, b);
    return e;
    
}
  0x102e62880 <+0>:  sub    sp, sp, #0x20             ; =0x20 
    0x102e62884 <+4>:  stp    x29, x30, [sp, #0x10]
    0x102e62888 <+8>:  add    x29, sp, #0x10            ; =0x10 
    0x102e6288c <+12>: stur   w0, [x29, #-0x4]
    0x102e62890 <+16>: str    w1, [sp, #0x8]
    0x102e62894 <+20>: ldur   w0, [x29, #-0x4]
    0x102e62898 <+24>: ldr    w1, [sp, #0x8]
    0x102e6289c <+28>: bl     0x102e62860               ; sum at main.m:14
    0x102e628a0 <+32>: str    w0, [sp, #0x4]
    0x102e628a4 <+36>: ldur   w0, [x29, #-0x4]
    0x102e628a8 <+40>: ldr    w1, [sp, #0x8]
    0x102e628ac <+44>: bl     0x102e62860               ; sum at main.m:14
    0x102e628b0 <+48>: str    w0, [sp]
    0x102e628b4 <+52>: ldr    w0, [sp]
    0x102e628b8 <+56>: ldp    x29, x30, [sp, #0x10]
    0x102e628bc <+60>: add    sp, sp, #0x20             ; =0x20 
    0x102e628c0 <+64>: ret    

分析

 - 开辟栈空间
- 保护回家的路
- 保存栈底
- 保存w0
- 保存w1
- 读取 w0
- 读取w1
- sum跳转
- 保存w0的值
- 读取a值到x0
- 读取b值到x1
- sum跳转
- 保存w0的的值搭配 栈
- 读取栈内存的值到w0
- 恢复x30的值 准备回家
- 平衡栈空间
- 回家

总结

bl指令: 跳转 将下一条执行的指令放入lr(x30)寄存器
ret指令:返回lr寄存器所保存的地址,执行代码
pc 寄存器指向马上要执行的代码地址
sp 指向了栈
函数调用会开辟一个栈空间,

  • 函数的局部变量、参数、寄存器的保护
  • 这个栈空间属于这个函数自己

函数中的参数放在哪里:
ARM64每一个寄存器都是64位的

  • x0 ~ x7(1.个数有关系;2.数据类型也有关系)
    • 放入栈
    • 或者如果放不下放入浮点、向量寄存器

代码只有一份,内存不只一份
函数嵌套调用:
| - A(开辟)-->B(开辟)--->A(开辟)
| - A<-->A 死的溢出 递归

扩展 小知识

[self viewDidLoad];
OC方法的调用 本质上是不是调用msgSend()
msgSend(self,@selector(viewDidLoad));
x0 x1 寄存器!!  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容

  • 前言:想必大家都知道了,新浪阅读整个业务线砍掉的事情,是的,朕的大清亡了,不过赔偿很到位,也有了幸福感。从北京回到...
    AndyYaWei阅读 703评论 3 5
  • 以arm64为例 xcode调试汇编 1. xcode 查看运行时的汇编代码 debug -> debug wor...
    meryin阅读 2,527评论 1 6
  • 前言 最近准备学习汇编,然后在B站上看到叫iOS小贤的作者发的视频挺不错,打算跟着学,文章是看视频的笔记,最后有原...
    小凉介阅读 5,273评论 0 1
  • 寄存器种类: 通用寄存器: 64位: X0-X30 32位: W0-W30 是低地址 特殊寄存器: 零寄存器:XZ...
    一川烟草i蓑衣阅读 2,866评论 0 0
  • 2017年应该是我人生中最有里程碑意义的一年,这年我和超哥25岁,相识九年,恋爱7年.异地4年,被身边的朋友泼...
    cym1992阅读 285评论 0 0