【iOS内功】ARM黑魔法—栈桢的入栈和出栈

2023-12-26 20:20

本文主要是介绍【iOS内功】ARM黑魔法—栈桢的入栈和出栈,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

栈桢之谜

调用一个子函数,在内存上会入一个新的栈桢。子函数执行完了,当前栈桢会出栈。在运行时,栈桢的出栈和入栈的逻辑是怎么实现的呢?

这是一个很有趣的问题,也是一个重要的知识点,它是排查疑难Crash的必备技能。

ARM64特殊寄存器

栈桢的入栈和出栈依赖于3个特殊寄存器,它们是fp、lr、sp,在ARM汇编里对应的是X29、X30、x31

特殊寄存器作用
LR (X30)link register 链接寄存器,保存返回上一层调用函数的地址
FP (X29)Frame point 指向栈底,保存栈桢的地址
SP (x31)Stack point 指向栈顶, 可以用来寻址
PC指向当前执行的代码的地址,我们无法访问PC寄存器
CPSR状态寄存器。不同于编程语言里面的if else.在汇编中就需要根据状态寄存器中的一些状态来控制分支的执行。

案例分析

下面基于一个Demo来分析

void func2(int c) {
}void func1() {int c = 7+18;func2(c);
}int main(int argc, char * argv[]) {func1();
}

调试汇编代码:

XCode设置Debug->Debug Workflow->Always Show Disassembly,然后真机调试运行Demo,就可以查看到每一个方法的ARM64汇编指令。

main函数汇编代码

OCSimpleTest`main:0x104a5a008 <+0>:  sub    sp, sp, #0x20             ; =0x20 0x104a5a00c <+4>:  stp    x29, x30, [sp, #0x10]0x104a5a010 <+8>:  add    x29, sp, #0x10            ; =0x10 0x104a5a014 <+12>: stur   w0, [x29, #-0x4]0x104a5a018 <+16>: str    x1, [sp]0x104a5a01c <+20>: mov    w0, #0x50x104a5a020 <+24>: mov    w1, #0x70x104a5a024 <+28>: mov    w2, #0x9
->  0x104a5a028 <+32>: bl     0x104a59fd4               ; func1 at main.m:600x104a5a02c <+36>: mov    w8, #0x00x104a5a030 <+40>: mov    x0, x80x104a5a034 <+44>: ldp    x29, x30, [sp, #0x10]0x104a5a038 <+48>: add    sp, sp, #0x20             ; =0x20 0x104a5a03c <+52>: ret    

main函数指令解析

第5行到第7行

mov指令是给寄存器赋值,main函数调用func1时会传递3个参数,因此跳转func1前,要先将3个参数存储到寄存器w0,w1,w2.(w寄存器只占32位,也就是4个字节)

第八行

bl 0x102a41fd8 ; func1 at main.m:60

bl是跳转指令,从main函数跳转到下一个函数func1

函数A汇编代码

OCSimpleTest`func1:0x10428dfb8 <+0>:  sub    sp, sp, #0x30             ; =0x30 0x10428dfbc <+4>:  stp    x29, x30, [sp, #0x20]0x10428dfc0 <+8>:  add    x29, sp, #0x20            ; =0x20 0x10428dfc4 <+12>: stur   w0, [x29, #-0x4]0x10428dfc8 <+16>: stur   w1, [x29, #-0x8]0x10428dfcc <+20>: stur   w2, [x29, #-0xc]0x10428dfd0 <+24>: mov    w8, #0x19
->  0x10428dfd4 <+28>: str    w8, [sp, #0x10]0x10428dfd8 <+32>: ldur   w8, [x29, #-0x4]0x10428dfdc <+36>: ldur   w9, [x29, #-0x8]0x10428dfe0 <+40>: add    w8, w8, w90x10428dfe4 <+44>: ldur   w9, [x29, #-0xc]0x10428dfe8 <+48>: add    w8, w8, w90x10428dfec <+52>: str    w8, [sp, #0xc]0x10428dff0 <+56>: bl     0x10428dfb4               ; func2 at main.m:58:10x10428dff4 <+60>: ldr    w0, [sp, #0xc]0x10428dff8 <+64>: ldp    x29, x30, [sp, #0x20]0x10428dffc <+68>: add    sp, sp, #0x30             ; =0x30 0x10428e000 <+72>: ret       

函数A指令解析

第1行

sub sp, sp, #0x30 ; =0x30

sub是减法指令。 SP寄存器的值向低地址偏移48个字节(0x30)。这时候SP已经指向新栈桢的顶部。

第2行

stp x29, x30, [sp, #0x20]

stp是存值指令,存2个值 存储上一个栈桢fp寄存器(x29)和lr寄存器(x30)的值,存储的位置是sp寄存器地址向高地址偏移32个字节(0x20)。

这里存储上一个栈桢fp和lr的值是一个重要的设计,下一个函数执行完,读取这两个值就可以回到原来的逻辑。

偏移的方向和大小(知识点)
因为栈是从高地址向低地址生长,所以入栈时地址偏移都是负向的。ARM64里寄存器是64位,也就是8个字节,这里要存储fp和lr两个寄存器,所以偏移量是16个字节。
思考:fp_A和lrA存储时哪个在前面,哪个在后面,为什么?

第3行

add x29, sp, #0x20 ; =0x20

add是加法指令。 设置fp(x29)寄存器,将其指向sp寄存器向高地址偏移32个字节的位置(0x20)。

此时函数A的栈桢已经布局完成,fp_A指向栈底,sp_A指向栈顶,占了16个字节。上一个栈桢的fp和lr的指针存储在栈桢A之前,也占了16个字节。

思考:为什么栈桢A的空间只有32个字节?fp到sp之间的内存,主要用来存储寄存器带过来的入参、函数内的局部变量。函数A有3个入参,每个入参占4个字节。2个局部变量,每个4字节,总共20字节。内存有字节对齐,所以总共申请了32个字节的空间。
思考:如果函数A有10几个入参,入参类型除了int,还有其他的类型,这个时候栈桢的空间会是多少呢?

第16行

ldr w0, [sp, #0xc]

ldr是取值指令。 将sp向高地址偏移12个字节(0xc)的值读出来,存储到w0寄存器。sp+0xc存的是“a + b + c”的结果,是函数A要返回的结果y。

第17行

ldp x29, x30, [sp, #0x20]

ldp是取值指令,取2个值 将sp向高地址偏移32个字节的两个值,取出来存储到fp寄存器(x29)和lr寄存器(x30)。

这里和第二行命令是一一对应的,取回main函数的fp和lr

第18行

ret

函数A栈桢出栈,执行lr寄存器指向的指令地址,也就是main函数跳转到fun_A的下一行命令。

小结

main函数调用函数A入栈过程

  1. 将传递给函数A的参数,存储到w0开始的寄存器中
  2. 保存main函数栈底指针fp和返回地址lr。
  3. 对fp和sp指针进行偏移,开辟函数A的栈桢空间

函数A执行完出栈过程

  1. 从内存中取出返回值,储存到w0寄存器里
  2. 从内存中取出main函数的fp和lr
  3. 执行lr的指令

总结

这里有一个iOS交流圈:891 488 181 不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

本文主要介绍调用栈的内存布局,已经ARM汇编如果使用指令进行出栈和入栈。为了方便读者理解,前面还介绍了栈的基础概念,栈在内存中的布局。

作者:Blacktea
链接:https://juejin.cn/post/6897975519048892423

这篇关于【iOS内功】ARM黑魔法—栈桢的入栈和出栈的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/540665

相关文章

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

bash: arm-linux-gcc: No such file or directory

ubuntu出故障重装了系统,一直用着的gcc使用不了,提示bash: arm-linux-gcc: No such file or directorywhich找到的命令所在的目录 在google上翻了一阵发现此类问题的帖子不多,后来在Freescale的的LTIB环境配置文档中发现有这么一段:     # Packages required for 64-bit Ubuntu

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte

Cortex-A7:ARM官方推荐的嵌套中断实现机制

0 参考资料 ARM Cortex-A(armV7)编程手册V4.0.pdf ARM体系结构与编程第2版 1 前言 Cortex-M系列内核MCU中断硬件原生支持嵌套中断,开发者不需要为了实现嵌套中断而进行额外的工作。但在Cortex-A7中,硬件原生是不支持嵌套中断的,这从Cortex-A7中断向量表中仅为外部中断设置了一个中断向量可以看出。本文介绍ARM官方推荐使用的嵌套中断实现机

ARM 虚拟化介绍

0.目录 文章目录 0.目录1.概述 1.1 Before you begin 2.虚拟化介绍 2.1 虚拟化为什么重要2.2 hypervisors的两种类型2.3 全虚拟化和半虚拟化2.4 虚拟机和虚拟CPUs 3.AArch64中的虚拟化4.stage 2 转换 4.1 什么是stage 2 转换4.2 VMIDs4.3 VMID vs ASID4.4 属性整合和覆盖4.5模拟

iOS剪贴板同步到Windows剪贴板(无需安装软件的方案)

摘要 剪贴板同步能够提高很多的效率,免去复制、发送、复制、粘贴的步骤,只需要在手机上复制,就可以直接在电脑上 ctrl+v 粘贴,这方面在 Apple 设备中是做的非常好的,Apple 设备之间的剪贴板同步功能(Universal Clipboard)确实非常方便,它可以在 iPhone、iPad 和 Mac 之间无缝传输剪贴板内容,从而大大提高工作效率。 但是,iPhone 如何和 Wind

iOS项目发布提交出现invalid code signing entitlements错误。

1、进入开发者账号,选择App IDs,找到自己项目对应的AppId,点击进去编辑, 2、看下错误提示出现  --Specifically, value "CVYZ6723728.*" for key "com.apple.developer.ubiquity-container-identifiers" in XX is not supported.-- 这样的错误提示 将ubiquity

我的第一次份实习工作-iOS实习生-第三个月

第三个月 这个月有一个考核项目,是一个电子书阅读器,组长说很重要,是我的实习考核项目。 我的项目XTReader,这是我参考网上的一些代码,和模仿咪咕阅读做的,功能还不完善,数据的部分是用聚合数据做的。要收费的。   还有阅读页面,基本功能实现了一下。使用了autolayout,自适应布局,也是第一次用网络,第一次用数据库,第一次用自动布局。还有很多不足。 做了一周多,有个问题一直没

我的第一次份实习工作-iOS实习生-公司使用过的软件

bittorrentsync 素材,文件同步软件 cornerstone svn 软件开发合作 mark man 测量坐标的软件 SQLLite Manager 数据库操作软件