汇编指令总结
referance: CSAPP第三章笔记
数据格式
C声明 | 汇编码后缀 | 大小(字节) |
---|---|---|
char | b | 1 |
short | w | 2 |
int | i | 4 |
long | q | 8 |
char* | q | 8 |
float | s | 4 |
double | l | 8 |
寄存器
63 | 31 | 15 | 7 | 0(寄存器末端位置) |
---|---|---|---|---|
%rax | %eax | %ax | %al | 返回值 |
%rbx | %ebx | %bx | %bl | |
%rcx | %ecx | %cx | %cl | 第4个参数 |
%rdx | %edx | %dx | %dl | 第3个参数 |
%rsi | %esi | %si | %sil | 第2个参数 |
%rdi | %edi | %di | %dil | 第1个参数 |
%rbp | %ebp | %bp | %bpl | |
%rsp | %esp | %sp | %spl | 栈指针 |
%r8 | 第5个参数 | |||
%r9 | 第6个参数 | |||
%r10 | ||||
%r11 | ||||
%r12 | ||||
%r13 | ||||
%r14 | ||||
%r15 |
数据表示
下面R[$r_a$]表示取寄存器$r_a$中的值,
M_b[Addr]表示到内存中找Addr地址开始的b个字节的引用,b可省略。
格式 | 含义 | 名称 | |
---|---|---|---|
立即数 | $Imm | Imm | 立即数寻址 |
寄存器 | $r_a$ | R[$r_a$] | 寄存器寻址 |
存储器 | Imm | M[Imm] | 绝对寻址 |
($r_a$) | M[R[$r_a$]] | 间接寻址 | |
($r_b$, $r_i$) | M[R[$r_b$]+R[$r_i$]] | ||
(,$r_i$, s) | M[R[$r_i$] * s] | 比例变址 | |
Imm($r_b, r_i, s$) | M[Imm+R[$r_b$]+R[$r_i$]*s] | 完全写法 | |
ATT格式与Intel格式
- ATT格式中movq a b是将a给b,intel格式中mov a b是将b给a。操作数顺序相反
- intel格式操作没有后缀b/w/q等
- intel格式寄存器前没有%符号
- intel使用
QWORD PTR [rbx]
而不是(%rbx)
表示内存取值。
gcc和objdump使用ATT格式。
move类指令
mov
复制。
ATT中有两个操作数时,第一个是source,第二个是destination。
movb imm/r8, r/m8
movb m8, r8
movw imm/r16, r/m16
movw m16, r16
movl imm/r32, r/m32
movl m32, r32
movq imm32/r64, r/m64
movq m64, r64
movabsq imm64, r64
两个操作数不能同时是memory,特别地,movq不接受imm64
movz
将高位补0后复制。
movzbw r/m8, r16
movzbl r/m8, r32
movzwl r/m16, r32
movzbq r/m8, r64
movzwq r/m16, r64
没有 movzlq
这条指令,因为将寄存器的值修改为一个 double word 时就会将高位清零,所以使用 movl
就可以了。
虽然有 movzbq
和 movzwq
,但一般会用 movzbl
和 movzwl
实现相应的功能。
movs
将高位补符号位后复制。
movsbw r/m8, r16
movsbl r/m8, r32
movsbq r/m8, r64
movswl r/m16, r32
movswq r/m16, r64
movslq r/m32, r64
cltq
: 和movslq %eax, %rax
效果相同(但编码更短)
push/pop
pushq imm32/r/m64
: 将R[%rsp]
减八,然后将 operand 复制到M[R[%rsp]]
(PUSH
指令不支持 imm64,会将 imm32 高位补符号位)popq r/m64
: 将M[R[%rsp]]
复制到 operand,然后将R[%rsp]
加八
%rsp向低地址增长。
原因:由于 memory layout 和 memory 曾经很小的历史原因,stack 和 heap 会往不同的方向增长,总会有一个在增长时减小地址,而 x86-64 选择了 push stack 时减小地址,这也使得访问非栈顶元素不需要给 offset 加负号。
leaq
leaq m, r64
: 将 source operand 的地址复制到 destination operand(只计算 source operand 的地址,与其指向的 memory 中存储的值无关)
LEA 可以用来优化一些简单的算术,例如:
1 | long scale(long x, long y, long z) |
1 | scale: |
这里三个 LEA 分别计算了x+4y,z+2z 和 (x+4y)+4(z+2z)。
一元运算
每种一元运算都有 b/w/l/q 四个类型,接受一个相应类型的 r/m,将这个 operand 计算后的结果存入这个 operand:
INC
: 加一DEC
: 减一NEG
: 取反 (negate)NOT
: 按位取反 (complement)
二元运算
每种二元运算都有 b/w/l/q 四个类型,接受相应类型的 imm/r/m 作为 source(除了 imm64),相应类型的 r/m 作为 destination(source 和 destination 不能同时为 memory),效果为将 source“作用于”destination,将运算结果存入 destination。
ADD
: 加SUB
: destination 减去 sourceIMUL
: 乘XOR
: 按位异或OR
: 按位或AND
: 按位与
特别地,类似 xorl %rdx, %rdx
的代码可以用来优化 movl $0, %rdx
位移
位移有 b/w/l/q 四个类型,source 只能是 imm8 或者 %cl
,destination 是相应类型的 r/m。
SAL
/SHL
: 左移SAR
: 算术右移SHR
: 逻辑右移
结果是 operand 两倍长度的运算
128 位的整数叫做 oct word,需要存在两个寄存器中,在指令中一般高位放在 R[%rdx]
低位放在 R[%rax]
。
虽然截去高位时是否有符号对编码层面的乘法没有影响,不截去高位时就需要对 signed 和 unsigned 使用不同的指令了:
imulq r/m64
: 计算 operand 和R[%rax]
作为 signed integer 相乘而不截去高位的结果,存在R[%rdx]:R[%rax]
中。(如果有两个 operand 就是上面的 二元运算 了。)mulq r/m64
: 计算 operand 和R[%rax]
作为 unsigned integer 相乘而不截去高位的结果,存在R[%rdx]:R[%rax]
中。
除法以及取模:
cqto
/cqtd
: 将R[%rax]
高位填符号位放在R[%rdx]:R[%rax]
(也就是用R[%rax]
的符号位填满R[%rdx]
)。idivq
: 计算R[%rdx]:R[%rax]
有符号地除以 operand,商放在R[%rax]
,余数放在R[%rdx]
。divq
: 计算R[%rdx]:R[%rax]
无符号地除以 operand,商放在R[%rax]
,余数放在R[%rdx]
。
得到的商都是向 0 取整,所以被除数为负时余数非正。
若商溢出了,则会触发 divide error 异常。所以被除数一般会是 64 位整数(在 idivq
之前用 cqto
来设置 R[%rdx]
,在 divq
之前将 R[%rdx]
置为全零),否则很可能溢出而触发异常。
这些运算也有 operand 为 32 位,结果为 64 位的版本:imull
、mull
、cltd
、idivl
、divl
。它们以 %edx
和 %eax
来代替 128 位运算中的 %rdx
和 %rax
。
未完…
- Title: 汇编指令总结
- Author: Endless_daydream
- Created at : 2024-03-07 10:14:45
- Updated at : 2024-03-07 11:28:53
- Link: https://endless_daydream.gitee.io/2024/03/07/CSAPP/汇编指令总结/
- License: This work is licensed under CC BY-NC-SA 4.0.