X64编程总结

来源:互联网 发布:80端口被进程4占用 编辑:程序博客网 时间:2024/06/05 08:56

X64编程和X86还是有一些不同的地方,总结一下日常用到的东西,便于以后查看:

1. 调用约定

        调用约定对于函数调用有影响,不同的函数调用,在汇编中会因为函数调用后栈不平衡而崩溃,在C/C++语言中如果使用函数指针或以函数作为参数,造成调用栈的不平衡而崩溃。函数调用方式,说到底是函数调用栈不同。

        X86的函数调用方式主要有:stdcall / cdecl / fastcall / thiscall / naked call 等几种形式。简单说一下几种调用方式有何不同,以与X64进行区分:

        stdcall :标准调用方式,Windows API / 函数做参数 一般采用这种方式,从右向左依次压栈函数参数,由被调用者清理调用堆栈。

        cdecl:C语言的调用方式,printf 是典型的cdecl调用方式的函数,参数数目可变。从右向左依次压栈,由调用者自己清理调用堆栈。(之所以调用者清栈,才可实现变参数)

        fastcall : 快速调用方式。最初两个参数,放入ecx,edx两个寄存器中,再有多余参数,按照从右向左依次压栈,由被调用者清理参数。

        thiscall :C++的类成员的调用方式,默认使用ecx 传递 this 指针,其他的参数按照stdcall的方式传参和清理调用栈。

        naked call : 这种方式不常用,表示该函数不需要编译器添加压栈与参数处理代码,相当于直接jmp到函数,栈平衡需要自己处理。

        X64的调用方式比较简单,相当于fastcall 的调用方式,没有了其他的调用方式。

        1. RCX / RDX / R8 / R9 作为整数和指针参数的容器,传递前四个参数。XMM0,1,2,3 三个寄存器作为浮点数运算传参使用。更多的参数,按照从右向左依次压栈。由调用者负责清理参数。同时,不同于32位的 fastcall,调用者要为四个使用寄存器传递的参数预留空间(Shadow space)。返回值以rax / XMM0 传递回来。

        2. 函数要返回更大的值,比如结构体,由调用者在栈上分配空间,由RCX 包含返回结构体的指针,要传递的参数一次向后错一个,即第一个参数用RDX传递。最终在寄存器RAX包含返回的结构体的指针。

        3. X64的函数调用栈需要16Bytes 对齐。call 指令会压入8字节的返回地址,因此非终端的函数,需要调整栈到16n+8 的栈空间。

        4. 寄存器RAX,RCX,RDX,R8,R9,R10,R11等寄存器是易变的,需要考虑到在函数调用中会被覆盖掉。RBX,RBP,RDI,RSI,R12,R14,R15在函数中使用时,必须要保存。浮点数寄存器没有调用方式。

        注:在编写的函数内部调用函数时,需要保持栈平衡,以及16bit 栈对齐。

        附带一张微软的调用栈的图:

        

2. 


0 0
原创粉丝点击