常见的有 eax 传递,也有和 edx 联合返回
如果要返回一个大家伙呢?
- 首先调用方在栈上开辟一段空间,将这个空间的一部分作为传递返回值的临时对象,这里称为 tmp
- 将 tmp 对象的地址作为隐藏参数传递给函数
- 函数将数据拷贝给 tmp 对象,并将对象的地址用 eax 传出
- 返回之后,调用方将 eax 指向的 tmp 对象的内容拷贝给真正要赋值的对象 (可以优化成直接在函数内赋值了)
#include <string.h>
typedef struct big_thing {
char buf[128];
} big_thing;
big_thing return_test() {
big_thing b;
memcpy(b.buf, "Hello, World!", 13);
return b;
}
int main() {
big_thing b = return_test();
return 0;
}
_QWORD *__fastcall return_test(_QWORD *a1)
{
__int64 v1; // rbx
__int64 v2; // rbx
__int64 v3; // rbx
__int64 v4; // rbx
__int64 v5; // rbx
__int64 v6; // rbx
__int64 v7; // rbx
__int64 v8; // rbx
char dest[128]; // [rsp+10h] [rbp-90h] BYREF
memcpy(dest, "Hello, World!", 0xDuLL);
v1 = *(_QWORD *)&dest[8];
*a1 = *(_QWORD *)dest;
a1[1] = v1;
v2 = *(_QWORD *)&dest[24];
a1[2] = *(_QWORD *)&dest[16];
a1[3] = v2;
v3 = *(_QWORD *)&dest[40];
a1[4] = *(_QWORD *)&dest[32];
a1[5] = v3;
v4 = *(_QWORD *)&dest[56];
a1[6] = *(_QWORD *)&dest[48];
a1[7] = v4;
v5 = *(_QWORD *)&dest[72];
a1[8] = *(_QWORD *)&dest[64];
a1[9] = v5;
v6 = *(_QWORD *)&dest[88];
a1[10] = *(_QWORD *)&dest[80];
a1[11] = v6;
v7 = *(_QWORD *)&dest[104];
a1[12] = *(_QWORD *)&dest[96];
a1[13] = v7;
v8 = *(_QWORD *)&dest[120];
a1[14] = *(_QWORD *)&dest[112];
a1[15] = v8;
return a1;
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[128]; // [rsp+0h] [rbp-80h] BYREF
return_test(v4);
return 0;
}
细究的话就要涉及到 move 了