0xAA55 发表于 2015-7-24 04:01:31

【汇编】x64下的汇编与VS

1、寄存器
通用寄存器:

8位al/ahcl/chdl/dhbl/bhsplbplsildilr8br9br10br11br12br13br14br15b
16位axcxdxbxspbpsidir8wr9wr10wr11wr12wr13wr14wr15w
32位eaxecxedxebxespebpesiedir8dr9dr10dr11dr12dr13dr14dr15d
64位raxrcxrdxrbxrsprbprsirdir8r9r10r11r12r13r14r15

64位媒介、浮点寄存器

mmx0/fpr0
mmx1/fpr1
mmx2/fpr2
mmx3/fpr3
mmx4/fpr4
mmx5/fpr5
mmx6/fpr6
mmx7/fpr7

标识寄存器

16位32位64位
flagseflagsrflags

指令指针寄存器

16位32位64位
ipeiprip

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
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快很多。

NiuDeHua 发表于 2015-7-24 07:25:24

:lol 啥时候A5老板 搞一个 x64 下面的 内联汇编帖子 玩玩呗?

wgz001 发表于 2015-7-24 08:24:49

调用约定讲的很详细吖 :)

0xAA55 发表于 2015-7-24 16:14:33

NiuDeHua 发表于 2015-7-24 07:25
啥时候A5老板 搞一个 x64 下面的 内联汇编帖子 玩玩呗?

内联汇编懒得搞。外联的抽空做一个给你玩玩:lol

NiuDeHua 发表于 2015-7-29 07:09:49

0xAA55 发表于 2015-7-24 16:14
内联汇编懒得搞。外联的抽空做一个给你玩玩

:lol只要能“联” 就ok 的,坐等新帖出世、

7KY6 发表于 2018-1-14 16:07:03

可以可以!!

watermelon 发表于 2020-1-30 18:28:28

支持楼主!今天被调用约定搞惨了, 靠着win32asm记忆淌水x64,结果BSoD了一下午:lol

watermelon 发表于 2020-1-30 18:35:29

0xAA55 发表于 2015-7-24 16:14
内联汇编懒得搞。外联的抽空做一个给你玩玩

外联怎么联,也给俺玩玩呗:lol

0xAA55 发表于 2020-2-1 01:38:36

watermelon 发表于 2020-1-30 18:35
外联怎么联,也给俺玩玩呗

用nasm之类的工具写汇编,编译为“-f win64”格式的obj,然后丢给VS的链接器LINK和vs的其它.c、.cpp的obj一起链接就可以了。

不过,vs的.c和.cpp有“全程序优化”,可以跨obj实现函数内联,而你用汇编器单独写的汇编函数,不会被“全程序优化”实现函数内联。

Ayala 发表于 2020-2-1 01:59:10

0xAA55 发表于 2020-2-1 01:38
用nasm之类的工具写汇编,编译为“-f win64”格式的obj,然后丢给VS的链接器LINK和vs的其它.c、.cpp的obj ...

64位下用需要使用汇编的地方已经非常少了:lol 一少部分外联足够了

watermelon 发表于 2020-2-1 13:15:34

0xAA55 发表于 2020-2-1 01:38
用nasm之类的工具写汇编,编译为“-f win64”格式的obj,然后丢给VS的链接器LINK和vs的其它.c、.cpp的obj ...

哦,这样的就也是用了汇编语言的文件,所以算“外联”

watermelon 发表于 2020-2-1 14:32:43

话说xor rcx, rcx这样的清零运算操作会被vs的汇编编译器优化成xor ecx,ecx因为对32位寄存器的写操作和运算操作会对高32位清零,而后者的opcode又比前者短,所以编译器更喜欢。

0xAA55 发表于 2020-2-2 19:58:04

watermelon 发表于 2020-2-1 14:32
话说xor rcx, rcx这样的清零运算操作会被vs的汇编编译器优化成xor ecx,ecx因为对32位寄存器的写操作和运算 ...

nasm也有这样的优化效果,但可以关闭
页: [1]
查看完整版本: 【汇编】x64下的汇编与VS