这个x86调用约定的原因是什么?

时间:2014-08-21 05:26:52

标签: macos assembly x86 llvm-clang

我试图减少原始问题(下面)中的混乱,但我担心这会让人难以理解。所以这里是原始来源以及IDA的反汇编。

我的问题仍然是:为什么getStruct()会弹出返回参数而只返回堆栈中的return参数? (它调用ret 4代替ret而不是参数,或者为所有三个参数调用12)。

#include <iostream>
struct SomeStruct {
    char m_buff[0x1000];
};

SomeStruct getStruct(uint32_t someArg1, uint32_t someArg2)
{
    return SomeStruct();
}

int main(int argc, const char * argv[])
{
    SomeStruct myLocalStruct = getStruct(0x20,0x30);

    return 0;
}

; _DWORD __stdcall getStruct(unsigned int, unsigned int)
                public getStruct(unsigned int, unsigned int)
getStruct(unsigned int, unsigned int) proc near ; CODE XREF: _main+4Dp

var_8           = dword ptr -8
var_4           = dword ptr -4
arg_0           = dword ptr  8
arg_4           = dword ptr  0Ch
arg_8           = dword ptr  10h

                push    ebp
                mov     ebp, esp
                sub     esp, 18h
                mov     eax, [ebp+arg_8]
                mov     ecx, [ebp+arg_4]
                mov     edx, [ebp+arg_0]
                mov     [ebp+var_4], ecx
                mov     [ebp+var_8], eax
                mov     eax, esp
                mov     [eax], edx
                mov     dword ptr [eax+4], 1000h
                call    ___bzero
                add     esp, 18h
                pop     ebp
                retn    4
getStruct(unsigned int, unsigned int) endp

; ---------------------------------------------------------------------------
                align 10h

; =============== S U B R O U T I N E =======================================

; Attributes: bp-based frame

; int __cdecl main(int argc, const char **argv, const char **envp)
                public _main
_main           proc near

var_1020        = dword ptr -1020h
var_101C        = dword ptr -101Ch
var_1018        = dword ptr -1018h
var_14          = dword ptr -14h
var_10          = dword ptr -10h
var_C           = dword ptr -0Ch
argc            = dword ptr  8
argv            = dword ptr  0Ch
envp            = dword ptr  10h

                push    ebp
                mov     ebp, esp
                push    edi
                push    esi
                sub     esp, 1030h
                mov     eax, [ebp+argv]
                mov     ecx, [ebp+argc]
                lea     edx, [ebp+var_1018]
                mov     esi, 20h
                mov     edi, 30h
                mov     [ebp+var_C], 0
                mov     [ebp+var_10], ecx
                mov     [ebp+var_14], eax
                mov     [esp], edx      ; ptr to destination
                mov     dword ptr [esp+4], 20h ; unsigned int
                mov     dword ptr [esp+8], 30h
                mov     [ebp+var_101C], esi
                mov     [ebp+var_1020], edi
                call    getStruct(uint,uint)
                sub     esp, 4
                mov     eax, 0
                add     esp, 1030h
                pop     esi
                pop     edi
                pop     ebp
                retn
_main           endp

以下原始问题:

我有一些函数,声明如下:

SomeStruct getStruct(uint32_t someArg1, uint32_t someArg2);

正在调用getStruct:

myLocalStruct = getStruct(someArg1,someArg2);

在x86上使用clang编译时,调用代码看起来大致如下:

lea esi, [ebp-myLocalStructOffset] 
mov [esp], esi
mov [esp+4], someArg1
mov [esp+8], someArg2
call getStruct;
sub esp, 4;

因此调用者在调用后恢复其堆栈指针。果然,getStruct的实现以ret 4结束,有效地弹出了结构指针。

这看起来部分是cdecl,调用者负责堆栈清理,并且部分stdcall与被调用者删除参数。我无法弄清楚这种方法的原因是什么。为什么不将所有清理工作留给来电者?这有什么好处吗?

1 个答案:

答案 0 :(得分:2)

看起来好像忘记在引用的部分之上引用几行汇编程序。我假设有类似的东西:

sub esp,12

高于你引用的地方。调用约定看起来像纯stdcall,并且返回值实际上是作为隐藏指针参数传递的,即代码实际上是编译,就像您声明了一样:

void __stdcall getStruct(SomeStruct *returnValue, uint32_t someArg1, uint32_t someArg2);