汇编指令总结
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/m8movb m8, r8movw imm/r16, r/m16movw m16, r16movl imm/r32, r/m32movl m32, r32movq imm32/r64, r/m64movq m64, r64movabsq imm64, r64
两个操作数不能同时是memory,特别地,movq不接受imm64
movz
将高位补0后复制。
movzbw r/m8, r16movzbl r/m8, r32movzwl r/m16, r32movzbq r/m8, r64movzwq r/m16, r64
没有 movzlq 这条指令,因为将寄存器的值修改为一个 double word 时就会将高位清零,所以使用 movl 就可以了。
虽然有 movzbq 和 movzwq,但一般会用 movzbl 和 movzwl 实现相应的功能。
movs
将高位补符号位后复制。
movsbw r/m8, r16movsbl r/m8, r32movsbq r/m8, r64movswl r/m16, r32movswq r/m16, r64movslq r/m32, r64cltq: 和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.