![](/style/images/good.png)
![](/style/images/bad.png)
ARM64下Indirect Result Location摸索
source link: http://satanwoo.github.io/2017/04/23/ARM64IndirectReturn/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
ARM64下Indirect Result Location摸索
之前学习汇编的时候,大概了解了一些ARM64下寄存器的用途,比如x0 - x7
作为函数传递使用。同时,x0
也可以作为函数返回值时候的寄存器。
但是,今天在研究一些跟返回结构体相关的时候,发现返回值并不是放在X0
寄存器中。上网搜索了一下资料,发现在ARM64下,当一个Callee
函数返回的内容大于16bytes的时候,该内容会被存到一个内存地址当中,然后这个内存地址的值会存入寄存器x8
。后续Caller
函数在使用该返回值的时候,会从X8
寄存器中取出内存地址,并从内存地址取出内容的值
是不是有点绕,还是让我们来看个例子吧。
首先我根据大于16 bytes的要求定义了如下结构体:
typedef struct {
int64_t i;
int64_t j;
int64_t k;
} MYStruct;
在ARM64下,该结构体默认按4 bytes
对齐,每个int64
占用8 bytes
,因此结构体大小24 bytes
我们定义如下函数,用于返回一个该结构体:
- (MYStruct)testIndirectResultLocation:(int64_t)i1 second:(int64_t)i2 th:(int64_t)i3
{
MYStruct s;
s.i = i1;
s.j = i2;
s.k = i3;
return s;
}
这个函数很简单,传入三个值。然后构造个局部变量MYStruct s
,将其对应的成员变量按照刚刚的传入参数赋值,最后返回该结构体。
该函数调用在未优化的前提下的汇编结果如下:
IndirectResultLocation`-[ViewController testIndirectResultLocation:second:th:]:
// 预留空间
<+0>: sub sp, sp, #0x40 ; =0x40
// 存参
<+4>: str x0, [sp, #0x38]
<+8>: str x1, [sp, #0x30]
<+12>: str x2, [sp, #0x28]
<+16>: str x3, [sp, #0x20]
<+20>: str x4, [sp, #0x18]
// 赋值
-> <+24>: ldr x0, [sp, #0x28]
<+28>: str x0, [sp]
<+32>: ldr x0, [sp, #0x20]
<+36>: str x0, [sp, #0x8]
<+40>: ldr x0, [sp, #0x18]
<+44>: str x0, [sp, #0x10]
// 将结构体存到x8寄存器的值代表的地址去
<+48>: ldr x0, [sp]
<+52>: str x0, [x8]
<+56>: ldr x0, [sp, #0x8]
<+60>: str x0, [x8, #0x8]
<+64>: ldr x0, [sp, #0x10]
<+68>: str x0, [x8, #0x10]
// 释放空间
<+72>: add sp, sp, #0x40 ; =0x40
<+76>: ret
第一行:SP即Stack Pointer
,向下减0x40(64 bytes)
的大小,预先分配出函数需要用的栈空间。为什么要预留这么多的大小呢?首先按照Objective-C的函数调用规定,前两个参数必须是self
和selector
,也即会使用到寄存器X0
和X1
。然后该函数有三个形参,使用了X2-X4
的寄存器。
上述这五个,大小占用了self(8 bytes) + selector(8 bytes) + 三个参数(24 bytes) = 40 bytes。那么还有24 bytes
去干嘛了呢?
别忘了,我们在函数中可以声明了一个局部变量MYStruct s
,该结构体大小是24 bytes。而在函数调用中使用到的变量,基本上都在栈区中开辟对应的空间进行暂存。
后续第二行到第六行非常简单易懂,就是把上述5个参数存到实际的栈区中去使用。按照这个存法以后,内存布局如下(注意高地址在上,低地址在下,ARM下的栈是向下增长):
![%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-04-23%20%E4%B8%8A%E5%8D%882.48.35.png?raw=true](https://github.com/SatanWoo/WZRecyclePhotoStackView/blob/master/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-04-23%20%E4%B8%8A%E5%8D%882.48.35.png?raw=true)
将参数都存入到栈以后,我们就要对结构体进行赋值了,这些操作在第七行到第十二行之间。
将1赋值给[SP],2赋值给[SP + #0x8],3赋值给[SP + #0x10]。如果不理解啥意思的话,可以看下我自己转化的伪代码:
void *address = &s;
*(int64_t *)(address) = 1;
*(int64_t *)(address + 8) = 2;
*(int64_t *)(address + 16) = 3;
赋值完以后,我们可以通过内存分布看下数据是否正确:
![%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-04-23%20%E4%B8%8A%E5%8D%882.30.06.png?raw=true](https://github.com/SatanWoo/WZRecyclePhotoStackView/blob/master/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-04-23%20%E4%B8%8A%E5%8D%882.30.06.png?raw=true)
![%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-04-23%20%E4%B8%8A%E5%8D%882.29.53.png?raw=true](https://github.com/SatanWoo/WZRecyclePhotoStackView/blob/master/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-04-23%20%E4%B8%8A%E5%8D%882.29.53.png?raw=true)
当赋值完成后,就要进行结构体的返回了。这里不是简单的mov x0, sp
之类的操作,而是一串和X8
寄存器相关操作。
其实原理差不多,转化成伪代码的话,基本上是这样:
void *toSaveAddress = [x8];
void *valueNowAddress = [sp];
*(int64_t *)(toSaveAddress) = *valueNowAddress;
*(int64_t *)(toSaveAddress + 8) = *(valueNowAddress + 8);
*(int64_t *)(toSaveAddress + 16) = *(valueNowAddress + 16);
操作完成后,释放空间即可。
其实ARM64在汇编层面实现的这么复杂, 我们在编程层面只要按照如下方式理解即可:
some_struct foo(int arg1, int arg2);
some_struct s = foo(1, 2);
会被编译成:
some_struct* foo(some_struct* ret_val, int arg1, int arg2);
some_struct s;
foo(&s, 1, 2);
从本文中我们不难看出,ARM64针对不同大小的返回值都有着对应的Calling Convention。下次我准备来摸索下,处于8 bytes - 16 bytes
之间的返回值究竟是怎么处理的。
Recommend
-
57
摸索一上午,原来这就是渐变映射…觉得更像自动上色黑科技!!!
-
58
0基础,一年自学经验,8个offer,包括头条、去哪儿、猫眼、斗鱼、趣店、趣头条等,总价值180W 掘金的朋友们,大家好! 我是白小白,目前是一名电子科技大学信通学院的大四学生。回想起自己正式涉足前端的学习,已然过去一年又几。这一年里,有困惑、迷茫,也有
-
44
图片来源:视觉中国 2019年春晚,百度作为独家网络互动平台,在2月4日除夕夜发出4轮9亿红包,而在这次盛大的红包活动中,全球观众参与...
-
10
Sonny Angel设计师:做潮玩or保持现状,我们仍在摸索
-
11
录播+直播 一年免费回看,不断巩固知识 建议每周学习 1-2 节课 ...
-
3
自学Go语言过程中摸索出的底层知识 - 程安絮的个人空间 - OSCHINA - 中文开源技术交流社区 本文中出现的概念只是为了方便描述而假设的概念,有些概念在专业术语中并不存在; 并且所有概念在不同硬件、操作系统及编程语言下的实际表现可能会有细节上的不...
-
3
碳链价值APP讯,北京电子工程总体研究所近日发布「虚拟孪生-元宇宙协同建模仿真方法研究」军工需求公告,采购阶段为预研。该消息一出,市场普遍认为元宇宙概念已...
-
5
写作六年,我终于摸索出一套适合自己的高效写作方式 - 少数派
-
3
Android摸索记录--HMS消息推送如果您发现本文排版有问题,可以先点击下面的链接切换至老版...
-
2
Android摸索记录--设置生日提醒如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!Android...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK