- UID
- 1
- 精华
- 积分
- 76361
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
1、寄存器
通用寄存器:
8位 | al/ah | cl/ch | dl/dh | bl/bh | spl | bpl | sil | dil | r8b | r9b | r10b | r11b | r12b | r13b | r14b | r15b | 16位 | ax | cx | dx | bx | sp | bp | si | di | r8w | r9w | r10w | r11w | r12w | r13w | r14w | r15w | 32位 | eax | ecx | edx | ebx | esp | ebp | esi | edi | r8d | r9d | r10d | r11d | r12d | r13d | r14d | r15d | 64位 | rax | rcx | rdx | rbx | rsp | rbp | rsi | rdi | r8 | r9 | r10 | r11 | r12 | r13 | r14 | r15 |
64位媒介、浮点寄存器
mmx0/fpr0 | mmx1/fpr1 | mmx2/fpr2 | mmx3/fpr3 | mmx4/fpr4 | mmx5/fpr5 | mmx6/fpr6 | mmx7/fpr7 |
标识寄存器
16位 | 32位 | 64位 | flags | eflags | rflags |
指令指针寄存器
128位媒介寄存器
xmm0 | xmm1 | xmm2 | xmm3 | xmm4 | xmm5 | xmm6 | xmm7 | xmm8 | xmm9 | xmm10 | xmm11 | xmm12 | xmm13 | xmm14 | xmm15 |
注:al为ax的低8位,ah为ax的高8位,相同的还有cx(cl低,ch高)、dx(dl低,dh高)、bx(bl低,bh高)。spl、bpl、sil、dil分别为sp、bp、si、di的低8位。r8b-r15b分别为r8w-r15w的低8位。
2、调用约定
这里所说的调用约定,是Windows API的调用约定,以及VS编译的x64平台C语言程序中函数的调用约定。
规则如下:- 前四个参数用rcx、rdx、r8、r9存储,但是同时也要留出栈空间。剩下的参数均通过栈传参。
- 调用者维护栈,被调用者返回的时候只需要ret就行了。
来解释一下其中的规则。首先就是参数传递的规则。来用MessageBox举例。
首先如果你链接了x64平台的user32.lib,那么你的整个程序之中就会有__imp_MessageBoxA这个符号,它是个函数指针,指向user32.dll中的MessageBoxA函数的入口。
范例代码(NASM编译通过):- %define MB_OK 0
- segment .text
- start:
- sub rsp,0x28
- mov r9,MB_OK
- mov r8,g_szTitle
- mov rdx,g_szPrompt
- xor rcx,rcx
- call [__imp_MessageBoxA]
- add rsp,0x28
- ret
- segment .data
- g_szTitle db "x64汇编",0
- g_szPrompt db "Hello World!",0
复制代码 其中我们要调用的函数是MessageBoxA,它有四个参数:窗口句柄,提示文本,标题栏文本,样式。
四个参数,每个参数8字节对齐,所有参数总字节数又是16字节对齐的,因此我们需要预留出0x20个字节的栈空间。然后我们还要再留出8个字节用于和call指令压入的返回地址值拼成16字节对齐。
因此预留的栈大小为:8 + 16字节对齐(要调用的函数的参数个数×8)
为什么要预留栈空间?因为,当你使用寄存器传参数的时候,你会发现有时候这个寄存器有时要被用于其它用途(比如调用别的函数、用作数学运算等),但是你又不想丢失寄存器之前存储的数值。因此,你可以把寄存器存储的数值暂存到栈上的预留空间里,以便于后续的读取。
调用者不必帮被调用者存储参数的值——调用者只需要分配好栈空间然后把前四个参数用rcx、rdx、r8、r9来传递(剩下的参数则通过栈传递)。被调用者自己决定是否将参数存储到栈上。
此外,编写x64汇编时,要注意一点:尽可能不要重复使用push和pop,因为这样会降低性能。其中,x64的push如果是要压入立即数的话,可能会产生一个字节数很多的指令,CPU从内存中读取指令是需要时间的。另外就是,push、pop需要做两个操作,先改变rsp的值,然后将数据存入栈顶,这个过程需要消耗额外的CPU时钟周期。而一次性将栈内存分配好,然后用mov来穿参的话,会比使用连续的push和pop快很多。
|
|