笔记 | x86系统调用约定

解释x86架构的系统调用约定:

(1)函数压栈的顺序

(2)谁来清理栈、负责栈平衡

调用约定基本概念

调用约定(calling convention)是为了简化函数间数据传递的复杂性而设计的,它设定了函数之间相互作用的规则。它们是应用程序二进制接口(Application Binary Interface,ABI)的一部分,ABI是对代码交互方式的最底层定义[1]。

调用约定必须定义一些规则[1],包括:

(1)参数位置:调用者将参数从哪里传递给被调用者(是栈还是寄存器)?

(2)参数排序:参数将如何排列,是在栈上还是在寄存器中?

(3)栈清理:如果使用了栈,哪个函数负责从栈中清除值(是调用者负责,还是被调用者负责)?

(4)寄存器访问:调用者可以使用哪些寄存器,而无须备份原值并在返回之前恢复它们?

(5)返回值:调用者将如何以及在哪里从被调用者那里获取返回值?

调用约定可能会基于以下几个因素而有所不同[1]:

(1)架构(x86还是ARM)

(2)操作系统(UN*X还是Windows)

(3)编程语言(C还是Java)

(4)编译器(GCC还是Microsoft)

常用的三种调用约定

调用约定

参数压栈顺序

栈的清理

寄存器使用

适用场景

__cdecl

从右往左压栈

调用方

不使用寄存器

通用函数

__stdcall

从右往左压栈

被调用方

不使用寄存器

Windows API 函数

__fastcall

ECX/EDX传递前两个参数

剩下的从右往左依次入栈

被调用方

使用特定寄存器

性能要求高的函数

__cdecl

  • 参数传递:参数从右到左压入栈中。
  • 栈的清理:由调用者负责清理栈。
  • 寄存器使用:通常不使用寄存器传递参数,所有参数都通过栈传递。
  • 特点:适用于大多数 C/C++ 标准库函数;由调用者负责清理栈。

__stdcall

  • 参数传递:参数从右到左压入栈中。
  • 栈的清理:由被调用者负责清理栈。
  • 寄存器使用:通常不使用寄存器传递参数,所有参数都通过栈传递。
  • 特点:适用于 Windows API 函数;由被调用者负责清理栈。

__fastcall

  • 参数传递:前两个参数(通常是整数或指针)通过寄存器传递,其余参数从右到左压入栈中。
  • 栈的清理:由调用者负责清理栈。
  • 寄存器使用:使用特定的寄存器(如 ECXEDX)传递前两个参数,其余参数通过栈传递。
  • 特点:适用于对性能要求较高的函数;由于使用寄存器传递参数,函数调用速度较快。

如何确定程序的调用约定

1. 查看编译器文档:编译器通常有默认的调用约定。例如,GCC默认使用__cdecl,而MSVC在某些情况下默认使用__stdcall;

2. 查看代码:在代码中,函数的调用约定可以显式声明(未声明时编译器使用默认的调用约定);

int __cdecl add(int a, int b){
    return a + b;
}

3. 反汇编查看:通过反汇编代码中堆栈的清理方式进行区分。

参考引用

[1] 《x86 汇编与逆向工程:软件破解与防护的艺术》

[2]  自变量传递和命定 | Microsoft Learn

[3] 【汇编 C】C语言常用的三种函数调用约定:__cdecl、__stdcall、__fastcall_cdecl stdcl-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值