程序编码

$ gcc -Og -S mstore.c  # outputs mstore.s
$ gcc -Og -c mstore.c  # outptus mstore.o
$ objdump -d mstore.o

所有以 ‘.’ 开头额行都是指导汇编器和链接器工作额伪指令。

数据格式

C 声明 Intel 数据类型 汇编代码后缀 大小(字节)
char 字节 b 1
short w 2
int 双字 l 4
long 四字 q 8
char* 四字 q 8
float 单精度 l 4
double 双精度 q 8

访问信息

寄存器

一个 x86-64 的中央处理单元(CPU)包含一组 16 个存储 64 位值的 通用目的寄存器

四字 双字 字节 用途
%rax %eax %ax %al 返回值
%rbx %ebx %bx %bl 被调用者保存
%rcx %ecx %cx %cl 第四个参数
%rdx %edx %dx %dl 第三个参数
%rsi %esi %si %sil 第二个参数
%rdi %edi %di %dil 第一个参数
%rbp %ebp %bp %bpl 被调用者保存
%rsp %esp %sp %spl 栈指针
%r8 %r8d %r8w %r8b 第五个参数
%r9 %r9d %r9w %r9b 第六个参数
%r10 %r10d %r10w %r10b 调用者保存
%r11 %r11d %r11w %r11b 调用者保存
%r12 %r12d %r12w %r11b 被调用者保存
%r13 %r13d %r13w %r13b 被调用者保存
%r14 %r14d %r14w %r14 被调用者保存
%r15 %r15d %r15w %r15 被调用者保存

相关规则:

  • 生成 1 字节和 2 字节数字的指令会保持剩下的字节不变
  • 生成 4 字节的数字的指令会把高位 4 个字节设置为 0

寄存器的使用有一组编程规范控制着如何来管理栈、传递参数、从函数返回值,以及存储局部和临时数据。 %rsp 用来指明运行时栈的结束位置。

操作数指示符

分为几类:

  1. 立即数(字面量)
  2. 寄存器
  3. 内存引用(支持多种寻址模式)

假设寄存器是一个数组 R,内存是一个大的字节数组,寻址方式为索引,如 M[0] 就是访问内存的第一个字节。

类型 格式 操作数值 名称 例子
立即数 $Imm Imm 立即数寻址 $0x08
寄存器 \(r_a\) \(R[r_a]\) 寄存器寻址 %rdx
存储器 Imm M[Imm] 绝对寻址 0x80
存储器 (\(r_a\)) M[R[\(r_a\)]] 间接寻址 (%rdx)
存储器 Imm(\(r_b\)) M[Imm+R[\(r_b\)]] (基址 + 偏移量)寻址 0x08(%rdx)
存储器 (\(r_b\),\(r_i\)) M[R[\(r_b\)] + R[\(r_i\)]] 变址寻址 (%rdx,%rdx)
存储器 Imm(\(r_b\),\(r_i\)) M[Imm + R[\(r_b\)] + R[\(r_i\)]] 变址寻址 0x08(%rdx,%rdx)
存储器 (,\(r_i\),s) M[R[\(r_i\)] * s] 比例变址寻址 (,%rdx,0x08)
存储器 Imm(,\(r_i\),s) M[Imm + R[\(r_i\)] * s] 比例变址寻址 0x08(,%rdx,0x08)
存储器 (\(r_b\),\(r_i\),s) M[R[\(r_b\)]+R[\(r_i\)] * s] 比例变址寻址 (%rdx,%rdx,0x08)
存储器 Imm(\(r_b\),\(r_i\),s) M[Imm + R[\(r_b\)]+R[\(r_i\)] * s] 比例变址寻址 0x08(%rdx,%rdx,0x08)

数据传送指令

指令 变体 效果 描述 备注
MOV S,D movb/movw/movl/movq D<-S 传送
MOVABSQ 1,R R<-1 传送绝对的四字
MOVZ S,R movzbw/movzbl/movzwl/movzbq/movzwq R<-零扩展(S) 以零扩展进行传送 目标只能是寄存器,后缀 bw 表示字节传送到字
MOVS S,R movsbw/movsbl/movswl/movsbq/movswq/movslq R<-符号扩展(S) 以符号扩展进行传送 同上
cltq %rax<-符号扩展(%eax) 把 %eax 符号扩展到 %rax

压入和弹出栈数据

指令 效果 描述
pushq S R[%rsp]<-R[%rsp]-0x08; M[R[%rsp]]<-R 将四字压入栈
popq D D<-M[R[%rsp]];R[%rsp]<-R[%rsp]+0x08 将四字弹出栈

算数和逻辑操作

分类 指令 效果 描述
leaq leaq S,D D<-&S 加载有效地址
一元 INC D D <- D + 1 加 1
DEC D D <- D - 1 减 1
NEG D D <- -D 取负
NOT D D <- ~D 取补
二元 ADD S,D D <- D + S
SUB S,D D <- D - S
IMUL S,D D <- D * S
XOR S,D D <- D ^ S
OR S,D D <- D | S
AND S,D D <- D & S
位移 SAL k,D D <- D << k
SHL k,D D <- D << k
SAR k,D D <- D >> k 算数右移
SHR k,D D <- D >> k 逻辑右移
特殊 imulq S R[%rdx]: R[%rax] <- S x R[%rax] 有符号全乘法(支持两个 64 位的数字全 128 位乘积,用两个寄存器表示 8 字)
mulq R[%rdx]: R[%rax] <- S x R[%rax] 无符号全乘法
cqto R[%rdx]: R[%rax] <- 符号扩展(R[%rax]) 转换为 8 字
idivq S R[%rdx] <- R[%rdx]: R[%rax] mod S 有符号除法
R[%rdx] <- (R[%rdx]: R[%rax]) / S
divq S 同上 无符号除法

leaq 将计算出的有效地址写入到目标寄存器,可以根据比例变址寻址进行复杂的数值计算。

long t = x + 4 * y + 12 * z

; x in %rdi, y in %rsi, z in %rdx

scale:
	leaq (%rdi,%rsi,4), %rax   ; x + 4*y
	leaq (%rdx,%rdx,2), %rdx   ; z + 2*z = 3 * z
	leaq (%rax,%rdx,4), %rax   ; (x + 4*y) + 4*(3*z) = x + 4*y + 12*z

控制

测试数据值,然后根据测试的结果来改变控制流或者数据流。jump 指令可以指定控制应该被传递到程序的某个其他部分。

条件码

条件码寄存器存储了单个位的条件码来描述最近的算术或者逻辑操作的属性。常用的条件码有:

  • CF:进位标志。最近的操作使最高位产生了进位。用来检查无符号操作的溢出。
  • ZF:零标志。最近操作得出的结果为 0。
  • SF:符号标志。最近的操作结果为负数。
  • OF:溢出标志。最近的操作导致一个补码溢出 – 正溢出或负溢出。

leaq 不改变任何条件码。