一段程序说明C语言不同调用约定的区别

7 篇文章 0 订阅

x86 程序的调用约定很多,常用调用约定由:cdecl, stdcall, fastcall。
x86-64 程序的调用约定只有两种:Microsoft x64 calling convention, System V AMD64 ABI。

x86 程序的调用约定

编译方法

64位平台编译32位汇编的方法如下

gcc -m32 -S test.c

主要区别

区别cdeclstdcallfastcall
寄存器ECX, EDX
入栈全部参数全部参数从第三个参数开始
调用者清理
被调用者清理

通过具体汇编代码进行说明

以下汇编均通过 Compiler Explorer 生成。使用 x86-64 gcc 11.1

cdecl

cdecl 是 x86 程序和库的默认调用约定。

int callee(int,int,int);
int caller(void) 
{
    return callee(1,2,3) + 5;
}
  • 参数从右到左进行压栈
  • 调用者清理参数
caller:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp		; reserves 8 bytes for local variables
        subl    $4, %esp		; for alignment
        pushl   $3
        pushl   $2
        pushl   $1
        call    callee
        addl    $16, %esp		; clean parameters (3 paras + 1 alignment)
        addl    $5, %eax
        leave
        ret

stdcall

stdcall 主要用于 32位的 windows。

__attribute__((stdcall)) int callee(int,int,int);
int caller(void) 
{
    return callee(1,2,3) + 5;
}
  • 参数从右到左进行压栈
  • 被调用者清理参数
caller:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $4, %esp		; for alignment
        pushl   $3
        pushl   $2
        pushl   $1
        call    callee
        addl    $4, %esp		; only clean the alignment
        addl    $5, %eax
        leave
        ret

fastcall

__attribute__((fastcall)) int callee(int,int,int);
int caller(void) 
{
    return callee(1,2,3) + 5;
}
  • 前两个参数由 ECX, EDX 进行传递
  • 后面到参数自右向左进行压栈
  • 被调用者清理
caller:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $12, %esp		; for alignment
        pushl   $3
        movl    $2, %edx		; second parameter
        movl    $1, %ecx		; first parameter
        call    callee
        addl    $12, %esp		; only clean the alignment
        addl    $5, %eax
        leave
        ret

x86-64 调用约定

x86-64 调用约定主要使用更多的寄存器来传递参数。

int callee(int,int,int,int,int,int,int);
int caller(void)
{
    return callee(1,2,3,4,5,6,7) + 8;
}

Microsoft x64 calling convention

微软x64调用约定

  • 使用RCX, RDX, R8, R9四个寄存器用于存储函数调用时的4个参数(从左到右),
  • 使用XMM0, XMM1, XMM2, XMM3来传递浮点变量
  • 其他的参数直接入栈(从右至左)
  • 整型返回值放置在RAX中,浮点返回值在XMM0中。

汇编

使用 x64 msvc v19.10 (WINE)

caller  PROC
$LN3:
       sub     rsp, 72                             ; 00000048H
       mov     DWORD PTR [rsp+48], 7
       mov     DWORD PTR [rsp+40], 6
       mov     DWORD PTR [rsp+32], 5		; other paras on stack
       mov     r9d, 4
       mov     r8d, 3
       mov     edx, 2
       mov     ecx, 1
       call    callee
       add     eax, 8
       add     rsp, 72                             ; 00000048H
       ret     0
caller  ENDP

System V AMD64 ABI

此约定主要在Solaris,GNU/Linux,FreeBSD和其他非微软OS上使用

  • 使用寄存器RDI, RSI, RDX, RCX, R8和R9存储函数调用时的前6个参数(从左到右)
  • 使用XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 和 XMM7 用来放置浮点变量
  • 其他的参数直接入栈(从右至左)
  • 整型返回值放置在RAX中,浮点返回值在XMM0中。

汇编

使用 x86-64 gcc 11.1

caller:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $8, %rsp
        pushq   $7				; other paras on stack
        movl    $6, %r9d
        movl    $5, %r8d
        movl    $4, %ecx
        movl    $3, %edx
        movl    $2, %esi
        movl    $1, %edi
        call    callee
        addq    $16, %rsp
        addl    $8, %eax
        leave
        ret

参考:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lylhw13_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值