Day5:函数调用约定

函数调用约定

函数调用约定

  • 主要有三种:cdecl、stdcall、fastcall。以求和函数为例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //求和
    #include<stdio.h>
    int sum(int a,int b,int c){
    return a+b+c;
    }
    int main(){
    int res=sum(1,2,3);
    printf("%d\n",res);
    return 0;
    }
  • cdecl:完全栈传参,参数从右往左入栈,由调用者清理参数占用的栈空间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    ; int __cdecl main()
    _main proc near ; CODE XREF: j__main↑j

    var_C = byte ptr -0Ch
    res = dword ptr -8

    push ebp
    mov ebp, esp
    sub esp, 0CCh
    push ebx
    push esi
    push edi

    __$EncStackInitStart_2:
    lea edi, [ebp+var_C]
    mov ecx, 3
    mov eax, 0CCCCCCCCh
    rep stosd

    __$EncStackInitEnd_2: ; JMC_flag
    mov ecx, offset _6005466B_entry@cpp
    call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
    nop
    push 3 ; 参数c入栈
    push 2 ; 参数b入栈
    push 1 ; 参数a入栈
    call j_?sum@@YAHHHH@Z ; sum(int,int,int)
    add esp, 0Ch ;这里调用者进行了清理参数占用的栈空间的操作(3个参数×4字节),被调用者不负责
    mov [ebp+res], eax
    mov eax, [ebp+res]
    push eax
    push offset _Format ; "%d\n"
    call j__printf
    add esp, 8
    xor eax, eax
    pop edi
    pop esi
    pop ebx
    add esp, 0CCh
    cmp ebp, esp
    call j___RTC_CheckEsp
    mov esp, ebp
    pop ebp
    retn
    _main endp

    ; int __cdecl sum(int a, int b, int c)
    ?sum@@YAHHHH@Z proc near ; CODE XREF: sum(int,int,int)↑j

    a = dword ptr 8
    b = dword ptr 0Ch
    c = dword ptr 10h

    push ebp
    mov ebp, esp
    sub esp, 0C0h ;sum函数自身的栈空间
    push ebx
    push esi
    push edi

    __$EncStackInitStart:
    mov edi, ebp
    xor ecx, ecx
    mov eax, 0CCCCCCCCh
    rep stosd

    __$EncStackInitEnd: ; JMC_flag
    mov ecx, offset _6005466B_entry@cpp
    call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
    nop
    mov eax, [ebp+a]
    add eax, [ebp+b]
    add eax, [ebp+c]
    pop edi
    pop esi
    pop ebx
    add esp, 0C0h ;清理自身栈空间
    cmp ebp, esp
    call j___RTC_CheckEsp
    mov esp, ebp
    pop ebp
    retn
    ?sum@@YAHHHH@Z endp
  • stdcall:完全栈传参,参数从右往左入栈,由被调用者清理参数占用的栈空间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    ; int __cdecl main()
    _main proc near ; CODE XREF: j__main↑j

    var_C = byte ptr -0Ch
    res = dword ptr -8

    push ebp
    mov ebp, esp
    sub esp, 0CCh
    push ebx
    push esi
    push edi

    __$EncStackInitStart_2:
    lea edi, [ebp+var_C]
    mov ecx, 3
    mov eax, 0CCCCCCCCh
    rep stosd

    __$EncStackInitEnd_2: ; JMC_flag
    mov ecx, offset _6005466B_entry@cpp
    call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
    nop
    push 3 ; 参数c入栈
    push 2 ; 参数b入栈
    push 1 ; 参数a入栈
    call j_?sum@@YGHHHH@Z ; sum(int,int,int)
    mov [ebp+res], eax ;这里直接到把返回值给res了,调用者不负责清理参数占用的栈空间
    mov eax, [ebp+res]
    push eax
    push offset _Format ; "%d\n"
    call j__printf
    add esp, 8
    xor eax, eax
    pop edi
    pop esi
    pop ebx
    add esp, 0CCh
    cmp ebp, esp
    call j___RTC_CheckEsp
    mov esp, ebp
    pop ebp
    retn
    _main endp

    ; int __stdcall sum(int a, int b, int c)
    ?sum@@YGHHHH@Z proc near ; CODE XREF: sum(int,int,int)↑j

    a = dword ptr 8
    b = dword ptr 0Ch
    c = dword ptr 10h

    push ebp
    mov ebp, esp
    sub esp, 0C0h ;sum的栈空间
    push ebx
    push esi
    push edi

    __$EncStackInitStart:
    mov edi, ebp
    xor ecx, ecx
    mov eax, 0CCCCCCCCh
    rep stosd

    __$EncStackInitEnd: ; JMC_flag
    mov ecx, offset _6005466B_entry@cpp
    call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
    nop
    mov eax, [ebp+a]
    add eax, [ebp+b]
    add eax, [ebp+c]
    pop edi
    pop esi
    pop ebx
    add esp, 0C0h ;清理自身栈空间
    cmp ebp, esp
    call j___RTC_CheckEsp
    mov esp, ebp
    pop ebp
    retn 0Ch ;被调用者通过返回指令给esp增加0Ch,清理参数占用的栈空间
    ?sum@@YGHHHH@Z endp
  • fastcall:寄存器+栈传参,优先使用寄存器,剩余参数从右往左入栈,由被调用者清理参数占用的栈空间

    寄存器传参优先级:

    x86:ecx edx 栈

    x64:rcx rdx r8 r9 栈

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    ; int __cdecl main()
    _main proc near ; CODE XREF: j__main↑j

    var_C = byte ptr -0Ch
    res = dword ptr -8

    push ebp
    mov ebp, esp
    sub esp, 0CCh
    push ebx
    push esi
    push edi

    __$EncStackInitStart_2:
    lea edi, [ebp+var_C]
    mov ecx, 3
    mov eax, 0CCCCCCCCh
    rep stosd

    __$EncStackInitEnd_2: ; JMC_flag
    mov ecx, offset _6005466B_entry@cpp
    call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
    nop
    push 3 ; 参数c入栈
    mov edx, 2 ; 参数b存入寄存器
    mov ecx, 1 ; 参数a存入寄存器
    call j_?sum@@YIHHHH@Z ; sum(int,int,int)
    mov [ebp+res], eax ;这里也是没有调用者对参数占用的栈空间的清理
    mov eax, [ebp+res]
    push eax
    push offset _Format ; "%d\n"
    call j__printf
    add esp, 8
    xor eax, eax
    pop edi
    pop esi
    pop ebx
    add esp, 0CCh
    cmp ebp, esp
    call j___RTC_CheckEsp
    mov esp, ebp
    pop ebp
    retn
    _main endp

    ; int __fastcall sum(int a, int b, int c)
    ?sum@@YIHHHH@Z proc near ; CODE XREF: sum(int,int,int)↑j

    var_18 = byte ptr -18h
    b = dword ptr -14h
    a = dword ptr -8
    c = dword ptr 8

    push ebp
    mov ebp, esp
    sub esp, 0D8h ;sum开辟栈空间
    push ebx
    push esi
    push edi
    push ecx

    __$EncStackInitStart:
    lea edi, [ebp+var_18]
    mov ecx, 6
    mov eax, 0CCCCCCCCh
    rep stosd

    __$EncStackInitEnd:
    pop ecx
    mov [ebp+b], edx
    mov [ebp+a], ecx
    mov ecx, offset _6005466B_entry@cpp ; JMC_flag
    call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
    nop
    mov eax, [ebp+a]
    add eax, [ebp+b]
    add eax, [ebp+c]
    pop edi
    pop esi
    pop ebx
    add esp, 0D8h ;sum清理栈空间
    cmp ebp, esp
    call j___RTC_CheckEsp
    mov esp, ebp
    pop ebp
    retn 4 ;由于只有一个参数被push到栈上,所以被调用者返回4,清理参数占用的栈空间
    ?sum@@YIHHHH@Z endp
作者

SydzI

发布于

2025-07-12

更新于

2025-10-03

许可协议

评论