这是一个简单的反汇编代码示例,说明你在说什么
typedef struct
{
int b;
int c;
int d;
int e;
int f;
int g;
char x;
} A;
A foo(int b, int c)
{
A myA = {b, c, 5, 6, 7, 8, 10};
return myA;
}
int main()
{
A myA = foo(5,9);
return 0;
}
这是 foo 函数的反汇编,以及调用它的主函数
主要:
push ebp
mov ebp, esp
and esp, 0FFFFFFF0h
sub esp, 30h
call ___main
lea eax, [esp+20] ; placing the addr of myA in eax
mov dword ptr [esp+8], 9 ; param passing
mov dword ptr [esp+4], 5 ; param passing
mov [esp], eax ; passing myA addr as a param
call _foo
mov eax, 0
leave
retn
富:
push ebp
mov ebp, esp
sub esp, 20h
mov eax, [ebp+12]
mov [ebp-28], eax
mov eax, [ebp+16]
mov [ebp-24], eax
mov dword ptr [ebp-20], 5
mov dword ptr [ebp-16], 6
mov dword ptr [ebp-12], 7
mov dword ptr [ebp-8], 9
mov byte ptr [ebp-4], 0Ah
mov eax, [ebp+8]
mov edx, [ebp-28]
mov [eax], edx
mov edx, [ebp-24]
mov [eax+4], edx
mov edx, [ebp-20]
mov [eax+8], edx
mov edx, [ebp-16]
mov [eax+0Ch], edx
mov edx, [ebp-12]
mov [eax+10h], edx
mov edx, [ebp-8]
mov [eax+14h], edx
mov edx, [ebp-4]
mov [eax+18h], edx
mov eax, [ebp+8]
leave
retn
现在让我们来看看刚刚发生的事情,所以当调用 foo 时,参数按以下方式传递,9 是最高地址,然后是 5,然后是 main 中的 myA 开始的地址
lea eax, [esp+20] ; placing the addr of myA in eax
mov dword ptr [esp+8], 9 ; param passing
mov dword ptr [esp+4], 5 ; param passing
mov [esp], eax ; passing myA addr as a param
在foo内有一些本地的myA存储在栈帧上,由于栈是往下走的,myA的最低地址从[ebp - 28]开始,-28偏移量可能是由结构对齐,所以我猜测结构的大小在这里应该是 28 个字节,而不是预期的 25 个字节。正如我们在foo 中看到的那样,在创建foo 的本地myA 并填充参数和立即值后,它被复制并重新写入从myA 传递的地址main(这个是按值返回的实际含义)
mov eax, [ebp+8]
mov edx, [ebp-28]
[ebp + 8] 是存储main::myA 的地址的位置(内存地址向上,因此 ebp + 旧 ebp(4 个字节)+ 返回地址(4 个字节))在整个 ebp + 8 处到达第一个字节main::myA,如前所述,foo::myA 随着堆栈向下存储在 [ebp-28] 中
mov [eax], edx
将foo::myA.b放在main::myA的第一个数据成员的地址中,即main::myA.b
mov edx, [ebp-24]
mov [eax+4], edx
将位于foo::myA.c 地址中的值放入edx,并将该值放入main::myA.b + 4 个字节的地址中,即main::myA.c
正如你所见,这个过程在整个函数中不断重复
mov edx, [ebp-20]
mov [eax+8], edx
mov edx, [ebp-16]
mov [eax+0Ch], edx
mov edx, [ebp-12]
mov [eax+10h], edx
mov edx, [ebp-8]
mov [eax+14h], edx
mov edx, [ebp-4]
mov [eax+18h], edx
mov eax, [ebp+8]
这基本上证明了当通过val返回一个结构时,不能作为参数放入,发生的情况是返回值应该驻留的地址作为参数传递给函数并在函数内被调用的返回结构的值被复制到作为参数传递的地址中......
希望这个示例可以帮助您更好地想象引擎盖下发生的事情:)
编辑
我希望您注意到我的示例使用的是 32 位汇编程序,并且 我知道您曾询问过有关 x86-64 的问题,但我目前无法在 64 位上反汇编代码机器所以我希望你相信我的话,这个概念对于 64 位和 32 位是完全相同的,并且调用约定几乎相同