汇编角度看函数返回大对象原理
Win32
测试代码
C++ struct ABCED { int a; int b; int c; int d; }; ABCED TestABC() { ABCED aa; aa.a = 1; aa.b = 32; return aa; } MVoid TestA() { TestABC(); return; } |
来看下汇编代码
TestA()
C++ 00D70D30 push ebp 00D70D31 mov ebp,esp 00D70D33 sub esp,0F4h 00D70D39 push ebx 00D70D3A push esi 00D70D3B push edi 00D70D3C lea edi,[ebp+FFFFFF0Ch] 00D70D42 mov ecx,3Dh 00D70D47 mov eax,0CCCCCCCCh 00D70D4C rep stos dword ptr es:[edi] 00D70D4E mov eax,dword ptr ds:[023100B4h] 00D70D53 xor eax,ebp 00D70D55 mov dword ptr [ebp-4],eax 00D70D58 lea eax,[ebp+FFFFFF10h] 00D70D5E push eax 00D70D5F call 00D70DB0 00D70D64 add esp,4 00D70D67 mov ecx,dword ptr [eax] 00D70D69 mov dword ptr [ebp+FFFFFF28h],ecx 00D70D6F mov edx,dword ptr [eax+4] 00D70D72 mov dword ptr [ebp+FFFFFF2Ch],edx 00D70D78 mov ecx,dword ptr [eax+8] 00D70D7B mov dword ptr [ebp+FFFFFF30h],ecx 00D70D81 mov edx,dword ptr [eax+0Ch] 00D70D84 mov dword ptr [ebp+FFFFFF34h],edx 00D70D8A pop edi 00D70D8B pop esi 00D70D8C pop ebx |
TestABC()
C++ 00D70DB0 push ebp 00D70DB1 mov ebp,esp 00D70DB3 sub esp,0DCh 00D70DB9 push ebx 00D70DBA push esi 00D70DBB push edi 00D70DBC lea edi,[ebp+FFFFFF24h] 00D70DC2 mov ecx,37h 00D70DC7 mov eax,0CCCCCCCCh 00D70DCC rep stos dword ptr es:[edi];把函数栈空间初始化0xCCCCCCCC 00D70DCE mov eax,dword ptr ds:[023100B4h] 00D70DD3 xor eax,ebp 00D70DD5 mov dword ptr [ebp-4],eax 00D70DD8 mov dword ptr [ebp-18h],1 00D70DDF mov dword ptr [ebp-14h],20h 00D70DE6 mov eax,dword ptr [ebp+8] 00D70DE9 mov ecx,dword ptr [ebp-18h];ecx = 1 00D70DEC mov dword ptr [eax],ecx ;AA.a;[ebp+8]=1 00D70DEE mov edx,dword ptr [ebp-14h] ;edx = 0x20 00D70DF1 mov dword ptr [eax+4],edx ;AA.b;[ebp + 12] = 0x20 00D70DF4 mov ecx,dword ptr [ebp-10h];AA.c 00D70DF7 mov dword ptr [eax+8],ecx ;;[ebp + 16] 00D70DFA mov edx,dword ptr [ebp-0Ch] ;AA.d 00D70DFD mov dword ptr [eax+0Ch],edx ;;[ebp + 20] 00D70E00 mov eax,dword ptr [ebp+8] ;[ebp+8] 也就是TestA ebp+FFFFFF28h也就是 ;也就是TestA ebp-D8, |
这就跟上边联系起来了,虽然TestA()里边什么也没有定义,也可以的出来栈空间还是分配了具体的空间,所以这里建议大家不要轻易返回大空间对象。接下来我们看下arm64的做法是否一致的
arm64
C++ struct ABCED { int a; int b; int c; int d; }; ABCED TestABC() { ABCED aa; aa.a = 1; aa.b = 32; return aa; } void TestA() { TestABC(); return; } |
TestA
Assembly language 0x100003c6c <+0>: sub sp, sp, #0x20 0x100003c70 <+4>: stp x29, x30, [sp, #0x10] 0x100003c74 <+8>: add x29, sp, #0x10 0x100003c78 <+12>: bl 0x100003c48 ; TestABC at main.mm:20 0x100003c7c <+16>: str x0, [sp];返回值ABCED.a, ABCED.b 0x100003c80 <+20>: str x1, [sp, #0x8];返回值ABCED.c, ABCED.d 0x100003c84 <+24>: ldp x29, x30, [sp, #0x10] 0x100003c88 <+28>: add sp, sp, #0x20;恢复栈 0x100003c8c <+32>: ret |
TestABC
Assembly language 0x100003c48 <+0>: sub sp, sp, #0x10 0x100003c4c <+4>: mov w8, #0x1 0x100003c50 <+8>: str w8, [sp];1 保存sp + 0 0x100003c54 <+12>: mov w8, #0x20 0x100003c58 <+16>: str w8, [sp, #0x4]; 32保存在sp + 4 0x100003c5c <+20>: ldr x0, [sp];把 ABCED.a, ABCED.b 保存x0 0x100003c60 <+24>: ldr x1, [sp, #0x8];;把 ABCED.c, ABCED.c 保存x1 0x100003c64 <+28>: add sp, sp, #0x10 ;函数栈返回 0x100003c68 <+32>: ret |
arm64汇编同样也可以看的出来,在TestA并没有分配对象空间,但是汇编代码上看,确实给其返回分配了空间,所以同样建议不要返回大空间对象,如果需要传递可以传递指针