X86_64 ABI调用约定

cdecl(代表C声明)是一种调用约定,它起源于C编程语言,许多C编译器都将它用于x86体系结构。在cdecl中,子例程参数在堆栈中传递。在EAX寄存器中返回整数值和内存地址,在ST0 x87寄存器中返回浮点值。寄存器EAX、ECX和EDX被调用保存,其余的被调用保存。当调用新函数时,x87浮点寄存器ST0到ST7必须为空(弹出或释放),而ST1到ST7在退出函数时必须为空。ST0在不用于返回值时也必须为空。

在C编程语言的上下文中,函数参数按从右到左的顺序被推送到堆栈上,也就是说,最后一个参数首先被推送。在Linux中,GCC为调用约定设置了事实上的标准。由于GCC版本4.5,调用函数时堆栈必须对齐到16字节的边界(以前的版本只需要4字节的对齐)。

考虑以下源代码:

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

在X86架构编译器上,他可能会产生以下的汇编代码:

caller:
        push    ebp       ; 
        mov     ebp, esp  ;        
        push    3
        push    2
        push    1
        call    callee    
        add     eax, 5                                 

       mov     esp, ebp 
       pop     ebp       
       ret               

函数调用返回后,调用方清理堆栈。
cdecl的解释有一些变化,特别是在如何返回值方面。因此,为不同的操作系统平台和/或由不同的编译器编译的x86程序可能不兼容,即使它们都使用“cdecl”约定且不调用底层环境。在寄存器对EAX:EDX中,一些编译器返回长度为2个寄存器或更短的简单数据结构,并且在内存中返回需要异常处理程序(例如,定义的构造函数、析构函数或赋值)特殊处理的更大的结构和类对象。要传递“in memory”,调用者分配内存,并将指针作为隐藏的第一个参数传递给内存;被调用方填充内存并返回指针,在返回时弹出隐藏指针。
在Linux/GCC中,双/浮点值应该通过x87伪栈推送到堆栈上。像这样:

sub     esp, 8          
	fld     [ebp + x]       
	fstp    [esp]           
	call    funct
	add     esp, 8

使用此方法可以确保以正确的格式将其推送到堆栈上。
cdecl调用约定通常是x86 C编译器的默认调用约定,尽管许多编译器提供选项来自动更改所使用的调用约定。要手动定义cdecl函数,一些函数支持以下语法:

return_type _cdecl func_name();

你可能感兴趣的:(网络安全,网络空间安全)