x86汇编常见语法(GNU格式)

x86汇编常见语法(GNU格式)

目录

  • x86汇编常见语法(GNU格式)
    • 1.寄存器
      • 1.1通用寄存器
      • 1.2标志寄存器EFLAGS(32位)
      • 1.3段寄存器
      • 1.4控制寄存器
      • 1.5 指令指针寄存器
    • 2.指令集
      • 2.1操作数
      • 2.2指令后缀
      • 2.3数据传送指令
      • 2.4 算数和逻辑运算指令
      • 2.5cmp 和 test 指令
      • 2.6跳转指令
    • 3.过程调用
      • 3.1参数传递
      • 3.2返回值
      • 3.3寄存器的其他约定
      • 3.4控制转移

1.寄存器

1.1通用寄存器

一个x86-64中央处理器单元包含16个64位的通用寄存器。如下图所示

x86汇编常见语法(GNU格式)_第1张图片

1.2标志寄存器EFLAGS(32位)

EFLAGS标志寄存器包含有状态标志位,控制标志位及系统标志位。处理器初始化时将其赋值为 0x00000002。

下图描绘了EFLAGS标志寄存器各位的功能,其中的第1、3、5、15以及22~31位保留未使用。由于64位模式不再支持VM和NT标志位,所以处理器不应该再置位这两个标志位。

x86汇编常见语法(GNU格式)_第2张图片

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则未溢出。

1.3段寄存器

x86-64架构,拥有6个16位段寄存器(CS、DS、SS、ES、FS和GS),用于保存16位段选择子。

1.4控制寄存器

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

1.5 指令指针寄存器

RIP/EIP寄存器,即指令指针寄存器,有时称为程序计数器。指令指针(RIP/EIP)寄存器包含当前代码段中要执行的下一条指令的地址。

2.指令集

2.1操作数

操作数分为三种类型:

  • 立即数,用来表示常数。立即数通过在整数前加一个“$”来表示。
  • 寄存器,表示某个寄存器的内容,通过在寄存器名声前加上“%”来表示。
  • 内存引用,它会根据计算出来的地址(通常称为有效地址)访问某个内存位置。内存引用的语法:segment:offset(base, index, scale)

segment 可以是 x86 架构的任意段寄存器。segment 是可选的,如果指定的话,后面要跟上冒号(“:”)来与offset隔离开;如果未指定指定的话,默认为数据段寄存器–%ds。

offset 是一个立即数偏移量,是可选的。

base表示基址寄存器,可以是16个通用寄存器中的任意一个。

index表示变址寄存器,可以是16个通用寄存器中的任意一个。

scale表示比例因子,scale会与index相乘再加上base来表示内存地址。比例因子必须是1、2、4、或者8,若果比例因子未指定,默认为1。

2.2指令后缀

由于是从16位体系结构扩展成32位的,Intel用术语“字(word)”表示16位数据类型。因此,称32位为“双字(double words)”,称64位数为“四字(quad words)”。

汇编代码后缀 大小(字节)
b 1
w 2
l/s/d 4
q 8

2.3数据传送指令

最简单形式的数据传送指令是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

2.4 算数和逻辑运算指令

算术运算指令如下

指令 效果 描述
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)

2.5cmp 和 test 指令

cmp和test指令只更改ZF标志位。如果运算结果为零,则将ZF位置为1。

指令 基于 描述
CMP S1, S2 S2 - S1 比较
cmpb 比较字节
cmpw 比较字
cmpl 比较双字
cmpq 比较四字
TEST S1, S2 S1 & S2 测试
testb 测试字节
testw 测试字
testl 测试双字
testq 测试四字

2.6跳转指令

无条件跳转指令 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 低于或相等(无符号 <=)

3.过程调用

3.1参数传递

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 了。

3.2返回值

被调用函数返回时,把返回结果放入 %rax中,供调用函数来获取。

3.3寄存器的其他约定

​ 在计算机体系结构中,被调用者保存寄存器(callee-saved registers)和调用者保存寄存器(caller-saved registers)是指在函数调用过程中需要保存和恢复的寄存器状态。
​ 被调用者保存寄存器是由被调用的函数负责保存和恢复的寄存器。这些寄存器保存了在函数中使用的临时值和计算结果。被调用者保存寄存器的内容在函数调用前后必须保持一致,以确保函数调用的正确性。被调用者保存寄存器通常包括一些通用寄存器,如RBP(基址指针)、RBX(基址寄存器)、R12、R13、R14和R15等。

​ 调用者保存寄存器是由调用函数负责保存和恢复的寄存器。这些寄存器保存了在调用函数中需要保留的值。调用者保存寄存器的内容在函数调用前后可能会被修改,因此在调用函数结束后需要恢复为调用前的状态。调用者保存寄存器通常包括一些通用寄存器,如RAX(累加器寄存器)、RCX(计数器寄存器)、RDX(数据寄存器)、RSI(源变址寄存器)和RDI(目的变址寄存器)等

3.4控制转移

过程调用时,通过以下指令来进行调用及返回:

指令 描述
call Label 过程调用
call *Operand 过程调用
ret 从过程调用返回

你可能感兴趣的:(x86‘汇编,汇编,gnu)