x86-64 汇编基础 ---- 记读 《CS: APP》
通常情况下, 使用现代的优化编译器产生的代码至少与一个熟练的汇编语言程序员手工编写的代码一样有效
1. 看懂汇编码
1). 汇编码的格式
· ATT格式
这是GCC/ OBJDUMP和其它一些工具的常用格式, 由AT&T公司命名
使用命令gcc -S <file.c>
输出的汇编码就是这种格式.
一段示例代码如下所示:
movl (%rdi), %eax
imull %eax, %eax
movl %eax, (%rdi)
ret
在《CS: APP》一书中, 使用ATT格式书写汇编码, 因此, 无特殊说明时, 下文的所有汇编码也是ATT格式.
· Intel格式
Intel和Microsoft的文档中经常会出现这种格式的代码, 它个ATT格式的代码有些许不同, 如下示例:
mov eax, DWORD PTR [rdi]
imul eax, eax
mov DWORD PTR [rdi], eax
ret
使用命令gcc -S -masm=intel <file.c>
以输出这种格式的代码
Introduction to x64 Assembly | Intel® Software
注意, 上述代码中的源操作数和目的操作数的位置发生了变化, 因此在分析代码时应注意汇编码的格式问题.
使用GCC编译器输出的汇编码中, 以 “.” 开头的行是 伪指令 , 可以适当忽略.
2) 数据格式
在C语言中, 变量具有其"数据类型", 但是汇编码中的"变量"没有此概念(或者说, 没有"变量"的概念). 但是C语言中数据类型的概念在汇编语言中必须有与其相对应的机制, 在x86-64汇编中, 控制指令操作数的长度, 与C语言中的数据类型概念对应.
下表是摘自书中的C语言数据类型在x86-64中的大小:
C声明 | Intel 数据类型 | 汇编代码后缀 | 大小(字节) |
---|---|---|---|
char | 字节 | b | 1 |
short | 字 | w | 2 |
int | 双字 | l | 4 |
long | 四字 | q | 8 |
char * | 四字 | q | 8 |
float | 单精度 | s | 4 |
double | 双精度 | l | 8 |
在汇编码(ATT格式)中, 通过在指令后加"后缀"表明操作数的大小. (后缀见上表)
3) 操作数指示符
几乎每一条指令都有对应的操作数, 操作数的值有几种形式: 立即数/ 寄存器/ 内存, 其中, 立即数和寄存器可以直接参与运算, 但存放于内存中的数参与运算时, 需要访存操作(此时立即数或寄存器中的值是内存地址).
最完整的一种地址形式是 I m m ( r a , r i , s ) Imm(r_a, r_i, s) Imm(ra,ri,s) , 它的运算后有效地址为 I m m + R [ r a ] + R [ r i ] ⋅ s Imm + R[r_a] + R[r_i] \cdot s Imm+