近日某青年似乎研究到x64(AMD64 in history)的参数传递调用约定,这玩意确实和x86上面不一样,于是我决定写点字介绍一下。
x64首先把x86原有的寄存器扩展到了64位,然后更增加了8个通用寄存器:R8~~R15,嗯,确实有点RISC的味道。
- x64上面默认的函数调用约定是fast call,也就是ABI是fast call。
- 前四个参数传递顺序是RCX,RDX,R8,R9,其余的参数通过压栈传递。注意这里有一个细节:前四个参数也是占用栈空间的,或者说,栈需要为前四个参数保留32个字节。
- 小于64位的参数传递时高位并不填充零,大于64位需要按照地址传递。
- 返回值在RAX
- 被调用函数不负责清栈
- RAX,RCX,RDX,R8,R9,R10,R11是“易挥发”的,其余寄存器需要保护。
- 栈需要16字节对齐。
OK,以上是理论上的东西,实际上编译器会把一切都搞糟,具体可以看一下两篇文章:
The history of calling conventions, part 5: amd64
这一切看起来非常的令人迷惑,个人推荐的解决办法有三种:
- 使用ICC或者GCC,这样可以用内联汇编,于是绝大多数问题都解决了。
- 使用编译器 intrinsics 指令,这玩意好像应该翻译成内省指令,还是内醒,总之不管了,知道是这玩意就行。
- 手工代码发射,不过这个最好还是用在API hook的时候,写算法真还不如用MASM,不过MASM免不了还得和参数调用约定捉迷藏。