美文网首页
函数调用约定

函数调用约定

作者: Prothean | 来源:发表于2018-11-27 12:02 被阅读0次

最近在研究反汇编相关的东西,看到了一些汇编中函数调用的约定,顺便记录下来。


cdecl
x86体系结构的许多C编译器使用的默认调用约定叫做C调用约定(c declaration)。cdecl调用约定规定:调用方按从右到左的顺序将函数参数放入 栈中,在被调用的函数完成其操作时,调用方(而不是被调用方)负责从栈中清除参数。从右到左在栈中放入参数的一个结果是,如果函数被调用,最左边的(第一个)参数将始终 位于栈顶。这样,无论该函数需要多少个参数,我们都可轻易找到第一个参数。因此,cdecl调用约定非常适用于那些参数数量可变的函数(如printf)。
要求调用函数从栈中删除参数,意味着你将经常看到:指令在由被调用的函数返回后,会立即对程序栈指针进行调整。如果函数能够接受数量可变的参数,则调用方非常适于进行这种调整, 因为它清楚地知道,它向函数传递了多少个参数,因而能够轻松做出正确的调整。而被调用的函数事先无法知道自己会收到多少个参数,因而很难对栈做出必要的调整。
例如我们调用一个拥有以下原型的函数:

void demo_cdecl(int w, int x, int y, int z);

默认情况下,这个函数将使用cdecl调用约定,并希望你按从右到左的顺序压入4个参数,同 时要求调用方清除栈中的参数。编译器可能会为这个函数的调用生成以下代码:

; demo_cdecl(1, 2, 3, 4);
push     4                    ;  push parameter z
push     3                    ;  push parameter y
push     2                    ;  push parameter x
push     1                    ;  push parameter w
call     demo_cdecl           ;  call the function
add      esp, 16              ;  改变 esp,因为栈是从高地址到低地址增长的

在调用函数时,栈指针都会指向最左边的参数。

stdcall
这种约定在函 数声明中使用了修饰符_stdcall,如下所示:

void _stdcall demo_stdcall(int w, int x, int y);

和cdecl调用约定一样,stdcall调用约定按从右到左的顺序将函数参数放在程序栈上。使用stdcall调用约定的区别在于:函数结束执行时,应由被调用的函数负责删除栈中的函数参数。对被调用的函数而言,要完成这个任务,它必须清楚知道栈中有多少个参数,这只有在函数接受的参数数量固定不变时才有可能。因此,printf这种接受数量可变的参数的函数不能使 用stdcall调用约定。例如,demo_stdcall函数需要3个整数参数,在栈上共占用12个字节(在 32位体系结构上为3*sizeof(int))的空间。x86编译器能够使用RET指令的一种特殊形式,同时 从栈顶提取返回地址,并给栈指针加上12,以清除函数参数。demo_stdcall可能会使用以下指 令返回到调用方:

ret 12  ;  return and clear 12 bytes from the stack

使用stdcall的主要优点在于,在每次函数调用之后,不需要通过代码从栈中清除参数,因而能够生成体积稍小、速度稍快的程序。

fastcall
fastcall约定是stdcall约定的一个变体,它向CPU寄存器(而非程序栈)最多传递两个参数。 Microsoft Visual C/C++ 和GNU gcc/g++(3.4及更低版本)编译器能够识别函数声明中的fastcall 修饰符。如果指定使用fastcall约定,则传递给函数的前两个参数将分别位于ECX和EDX寄存器 中。剩余的其他参数则以类似于stdcall约定的方式从右到左放入栈上。同样与stdcall约定类似 的是,在返回其调用方时,fastcall函数负责从栈中删除参数。下面的声明中即使用了fastcall 修饰符:

void fastcall demo_fastcall(int w, int x, int y, int z);

为调用demo_fastcall,编译器可能会生成以下代码:

; demo_fastcall(1, 2, 3, 4);
push     4                    ;  move parameter z to the second position on stack
push     3                    ;  move parameter y to the top position on stack
mov      edx, 2               ;  move parameter x to edx
mov      ecx, 1               ;  move parameter w to ecx
call     demo_fastcall        ;  call the function

注意,调用demo_fastcall返回后,并不需要调整栈,因为demo_fastcall负责在返回到调用 方时从栈中清除参数y和z。由于有两个参数被传递到寄存器中,被调用的函数仅仅需要从栈中清除8字节,即使该函数拥有4个参数也是如此,理解这一点很重要。

相关文章

  • C函数的调用过程

    函数调用过程 当主函数调用子函数的时候: 在主函数中,将子函数的参数按照一定调用约定(参考调用约定),一般是从右向...

  • 函数调用约定

    援引《C++ Primer(Fifth Edition)》4.1.3节: Order of operand eva...

  • 函数调用约定

    函数调用约定 在C语言中,假设我们有这样的一个函数: int function(int a,int b) 调用时只...

  • 函数调用约定

    1. c调用约定 _cdecl 调用方将参数从右面到左压栈,被调用函数完成后,调用方负责从栈中清除参数。 2. s...

  • 函数调用约定

    最近在研究反汇编相关的东西,看到了一些汇编中函数调用的约定,顺便记录下来。 cdeclx86体系结构的许多C编译器...

  • 11-27函数

    函数 函数的重点是识别调用约定、参数类型和返回值类型 调用约定: cdecl调用方平栈 stdcall被调用方平栈...

  • 各主流处理器与系统平台的ABI和函数调用约定

    System V ABI AMD64在System V ABI上的函数调用约定 MSVC上x64模式的函数调用约定...

  • 常见函数调用约定

    _cdecl 传参顺序:从右到左 参数栈维护:调用者 __stdcall 传参顺序:从右到左 参数栈维护:被调用者...

  • 函数的调用约定

    __cdecl __stdcall

  • 函数调用约定(Calling convention)

    一.基本介绍 函数调用约定,是指当一个函数被调用时,函数的[参数]会被传递给被调用的函数和[返回值]会被返回给调用...

网友评论

      本文标题:函数调用约定

      本文链接:https://www.haomeiwen.com/subject/bjcgqqtx.html