一个x86-64中央处理器单元包含16个64位的通用寄存器。如下图所示
EFLAGS标志寄存器包含有状态标志位,控制标志位及系统标志位。处理器初始化时将其赋值为 0x00000002。
下图描绘了EFLAGS标志寄存器各位的功能,其中的第1、3、5、15以及22~31位保留未使用。由于64位模式不再支持VM和NT标志位,所以处理器不应该再置位这两个标志位。
TIPs:在64位模式中,EFLAGS标志寄存器已从32位扩展为64位,被称作RFLAGS寄存器。其中高32位保留未使用,低32位与EFLAGS相同。
状态标志:
缩写 | 全称 | 名称 | 位置 | 描述 |
---|---|---|---|---|
CF | Carry Flag | 进位标志 | 0 | 当运算中,最高位产生了进位或者借位,CF位都会置1。可用于检测无符号整数运算结果是否溢出。也可用于多精度运算中。 |
PF | Parity Flag | 奇偶标志 | 2 | 用于标记结果低8位中1的个数,如果为偶数, PF位为1,否则为0 。校验经常用于数据传输开始时和结束后的对比,判断传输过程中是否出现错误。 |
AF | Auxiliary Carry | 辅助进位标志 | 4 | 辅助进位标志,用来记录运算结果低4位的进、借位情况,即若低半字节有进、借位,AF为1,否则为0。 |
ZF | Zero Flag | 零值标志 | 6 | 若计算结果为0,此标志位置1,否则为0。 |
SF | Sign Flag | 符号标志 | 7 | 若运算结果为负,则SF位为1,否则为0。 |
OF | Overflow Flag | 溢出标志 | 11 | 用来标识计算的结果是否超过了数据类型可以表示的范围,若OF为1,表示有溢出,为0则未溢出。 |
x86-64架构,拥有6个16位段寄存器(CS、DS、SS、ES、FS和GS),用于保存16位段选择子。
intel处理器共拥有6个控制寄存器(CR0、CR1、CR2、CR3、CR4、CR8),她们有若干标志位。
CR0:
标志位 | 描述 | 位置 |
---|---|---|
PE | Protection Enable | 0 |
MP | Monitor coProcessor/Math Present | 1 |
EM | Emulation | 2 |
TS | Task Switched | 3 |
ET | Extention Type | 4 |
NE | Numeric Error | 5 |
WP | Write Protect | 16 |
AM | Alignment Mask | 18 |
NW | Not Writethrough | 29 |
CD | Cache Disable | 30 |
PG | Paging | 31 |
RIP/EIP寄存器,即指令指针寄存器,有时称为程序计数器。指令指针(RIP/EIP)寄存器包含当前代码段中要执行的下一条指令的地址。
操作数分为三种类型:
segment 可以是 x86 架构的任意段寄存器。segment 是可选的,如果指定的话,后面要跟上冒号(“:”)来与offset隔离开;如果未指定指定的话,默认为数据段寄存器–%ds。
offset 是一个立即数偏移量,是可选的。
base表示基址寄存器,可以是16个通用寄存器中的任意一个。
index表示变址寄存器,可以是16个通用寄存器中的任意一个。
scale表示比例因子,scale会与index相乘再加上base来表示内存地址。比例因子必须是1、2、4、或者8,若果比例因子未指定,默认为1。
由于是从16位体系结构扩展成32位的,Intel用术语“字(word)”表示16位数据类型。因此,称32位为“双字(double words)”,称64位数为“四字(quad words)”。
汇编代码后缀 | 大小(字节) |
---|---|
b | 1 |
w | 2 |
l/s/d | 4 |
q | 8 |
最简单形式的数据传送指令是MOV类。这些指令把数据从源位置复制到目的位置,不做任何变化。
指令 | 效果 | 描述 |
---|---|---|
mov s,d | s -> d | 传送 |
movb | 传送字节 | |
movw | 传送字 | |
movl | 传送双字 | |
movq | 传送四字 | |
movabsq l,r | l -> r | 传送绝对的四字 |
x86-64加了一条限制,传送指令的两个操作数不能都指向内存位置。大多数情况下,MOV指令只会更新目的操作数指定的那些寄存器字节或内存位置。唯一的例外是movl指令以寄存器作为目的时,它会把该寄存器的高位4字节设置为0。
movabsq指令是处理64位立即数数据的。常规的movq指令只能以表示为32位补码数字的立即数作为源操作数,然后把这个值符号扩展得到64位的值,放到目的位置。movabsq指令能够以任意64位立即数作为源操作数,并且只能以寄存器作为目的。
理解数据传送如何改变目的寄存器:
1 movabsq $0x0011223344556677, %rax # %rax = 0x0011223344556677
2 movb $-1, %al # %rax = 0x00112233445566FF
3 movw $-1, %ax # %rax = 0x001122334455FFFF
4 movl $-1, %eax # %rax = 0x00000000FFFFFFFF
5 movq $-1, %rax # %rax = 0xFFFFFFFFFFFFFFFF
算术运算指令如下
指令 | 效果 | 描述 |
---|---|---|
inc D | D+1 → D | 加 1 |
dec D | D-1 → D | 减 1 |
neg D | -D → D | 取负 |
add S, D | D + S → D | 加 |
sub S, D | D - S → D | 减 |
imul S, D | D * S → D | 乘 |
逻辑运算指令如下:
指令 | 效果 | 描述 |
---|---|---|
not D | ~D → D | 逻辑非 |
or S, D | D | S → D | 逻辑或 |
and S, D | D & S → D | 逻辑与 |
xor S, D | D ^ S → D | 逻辑异或 |
移位运算指令如下:
指令 | 效果 | 描述 |
sal k, D | D << k → D | 左移 |
shl k, D | D << k → D | 左移(等同于asl) |
sar k, D | D >>_A k → D | 算术右移(填充符号位) |
shr k, D | D >>_L k → D | 逻辑右移(填充0) |
cmp和test指令只更改ZF标志位。如果运算结果为零,则将ZF位置为1。
指令 | 基于 | 描述 |
---|---|---|
CMP S1, S2 | S2 - S1 | 比较 |
cmpb | 比较字节 | |
cmpw | 比较字 | |
cmpl | 比较双字 | |
cmpq | 比较四字 | |
TEST S1, S2 | S1 & S2 | 测试 |
testb | 测试字节 | |
testw | 测试字 | |
testl | 测试双字 | |
testq | 测试四字 |
无条件跳转指令 jmp,可以是直接跳转,即跳转目标是作为指令一部分编码的;也可以是间接跳转,即跳转目标是从寄存器或内存位置中读取的。
######################### 直接跳转示例 ##########################
movq $0, %rax
jmp .L1 # 直接跳转
movq (%rax), %rdx
.L1:
popq %rdx
######################### 间接跳转示例 ##########################
jmp *%rax # 用寄存器 %rax 中的值作为跳转目的
jmp *(%rax) # 以 %rax中的值作为读取地址,从内存中读出跳转目标
有条件跳转指令:
指令 | 同义指令 | 跳转条件 | 描述 |
---|---|---|---|
je Label | jz | ZF | 相等/零 |
jne Lable | jnz | ~ZF | 不相等/非零 |
js Label | SF | 负数 | |
jns Label | ~SF | 非负数 | |
jg Label | jnle | ~(SF ^ OF) & ~ZF | 大于(有符号 > ) |
jge Label | jnl | ~(SF ^ OF) | 大于等于(有符号 >= ) |
jl Label | jnge | SF ^ OF | 小于(有符号小于 < ) |
jle Label | jng | (SF ^ OF) | ZF | 小于等于(有符号 <= ) |
ja Label | jnbe | ~CF & ~ZF | 超过(无符号 > ) |
jae Label | jnb | ~CF | 超过或相等(无符号 >=) |
jb Label | jnae | CF | 低于(无符号 < ) |
jbe Label | jna | CF | ZF | 低于或相等(无符号 <=) |
x86-64中,最多允许 6 个参数通过寄存器来传递,多出的参数需要通过栈来传递,正如 2.5.1 节描述的那样;传递参数时,参数的顺序与寄存器的关系对应如下:
操作数大小(位) | 参数1 | 参数2 | 参数3 | 参数4 | 参数5 | 参数6 |
---|---|---|---|---|---|---|
64 | %rdi | %rsi | %rdx | %rcx | %r8 | %r9 |
32 | %edi | %esi | %edx | %ecx | %r8d | %r9d |
16 | %di | %si | %dx | %cx | %r8w | %r9w |
8 | %dil | %sil | %dl | %cl | %r8b | %r9b |
如果一个函数 Q 有 n (n > 6)个整数参数,如果过程 P 调用过程 Q,需要把参数 1 ~ 6复制到对应的寄存器,把参数 7 ~ n放到栈上,而参数 7 位于栈顶。通过栈传递参数时,所有的数据大小都向 8 的倍数对齐。参数到位以后,程序就可以指向 call 指令将控制转移到 Q 了。
被调用函数返回时,把返回结果放入 %rax中,供调用函数来获取。
在计算机体系结构中,被调用者保存寄存器(callee-saved registers)和调用者保存寄存器(caller-saved registers)是指在函数调用过程中需要保存和恢复的寄存器状态。
被调用者保存寄存器是由被调用的函数负责保存和恢复的寄存器。这些寄存器保存了在函数中使用的临时值和计算结果。被调用者保存寄存器的内容在函数调用前后必须保持一致,以确保函数调用的正确性。被调用者保存寄存器通常包括一些通用寄存器,如RBP(基址指针)、RBX(基址寄存器)、R12、R13、R14和R15等。
调用者保存寄存器是由调用函数负责保存和恢复的寄存器。这些寄存器保存了在调用函数中需要保留的值。调用者保存寄存器的内容在函数调用前后可能会被修改,因此在调用函数结束后需要恢复为调用前的状态。调用者保存寄存器通常包括一些通用寄存器,如RAX(累加器寄存器)、RCX(计数器寄存器)、RDX(数据寄存器)、RSI(源变址寄存器)和RDI(目的变址寄存器)等
过程调用时,通过以下指令来进行调用及返回:
指令 | 描述 |
---|---|
call Label | 过程调用 |
call *Operand | 过程调用 |
ret | 从过程调用返回 |