CSAPP6——程序机器级表示2
汇编入门(二)
x86-64 架构中的整型寄存器如下图所示(暂时不考虑浮点数的部分)
仔细看看寄存器的分布,我们可以发现有不同的颜色以及不同的寄存器名称,黄色部分是 16 位寄存器,也就是 16 位处理器 8086 的设计,然后绿色部分是 32 位寄存器(这里我是按照比例画的),给 32 位处理器使用,而蓝色部分是为 64 位处理器设计的。这样的设计保证了令人震惊的向下兼容性,几十年前的 x86 代码现在仍然可以运行!
前六个寄存器(%rax, %rbx, %rcx, %rdx, %rsi, %rdi)称为通用寄存器,有其『特定』的用途:
- %rax(%eax) 用于做累加
- %rcx(%ecx) 用于计数
- %rdx(%edx) 用于保存数据
- %rbx(%ebx) 用于做内存查找的基础地址
- %rsi(%esi) 用于保存源索引值
- %rdi(%edi) 用于保存目标索引值
而 %rsp(%esp) 和 %rbp(%ebp) 则是作为栈指针和基指针来使用的。下面我们通过 movq 这个指令来了解操作数的三种基本类型:立即数(Imm)、寄存器值(Reg)和内存值(Mem)。
对于 movq 指令来说,需要源操作数和目标操作数,源操作数可以是立即数、寄存器值或内存值的任意一种,但目标操作数只能是寄存器值或内存值。指令的具体格式可以这样写 movq [Imm|Reg|Mem], [Reg|Mem],第一个是源操作数,第二个是目标操作数,例如:
movq Imm, Reg->mov $0x5, %rax->temp = 0x5;movq Imm, Mem->mov $0x5, (%rax)->*p = 0x5;movq Reg, Reg->mov %rax, %rdx->temp2 = temp1;movq Reg, Mem->mov %rax, (%rdx)->*p = temp;movq Mem, Reg->mov (%rax), %rdx->temp = *p;
这里有一种情况是不存在的,没有 movq Mem, Mem 这个方式,也就是说,我们没有办法用一条指令完成内存间的数据交换。
上面的例子中有些操作数是带括号的,括号的意思就是寻址,这也分两种情况:
- 普通模式,®,相当于
Mem[Reg[R]],也就是说寄存器 R 指定内存地址,类似于 C 语言中的指针,语法为:movq (%rcx), %rax也就是说以 %rcx 寄存器中存储的地址去内存里找对应的数据,存到寄存器 %rax 中 - 移位模式,D®,相当于
Mem[Reg[R]+D],寄存器 R 给出起始的内存地址,然后 D 是偏移量,语法为:movq 8(%rbp),%rdx也就是说以 %rbp 寄存器中存储的地址再加上 8 个偏移量去内存里找对应的数据,存到寄存器 %rdx 中
因为寻址这个内容比较重要,所以多说两句,不然之后接触指针会比较吃力。对于寻址来说,比较通用的格式是 D(Rb, Ri, S) -> Mem[Reg[Rb]+S*Reg[Ri]+D],其中:
D- 常数偏移量Rb- 基寄存器Ri- 索引寄存器,不能是 %rspS- 系数
除此之外,还有如下三种特殊情况
(Rb, Ri)->Mem[Reg[Rb]+Reg[Ri]]D(Rb, Ri)->Mem[Reg[Rb]+Reg[Ri]+D](Rb, Ri, S)->Mem[Reg[Rb]+S*Reg[Ri]]
我们通过具体的例子来巩固一下,这里假设 %rdx 中的存着 0xf000,%rcx 中存着 0x0100,那么
0x8(%rdx)=0xf000+0x8=0xf008(%rdx, %rcx)=0xf000+0x100=0xf100(%rdx, %rcx, 4)=0xf000+4*0x100=0xf4000x80(, %rdx, 2)=2*0xf000+0x80=0x1e080
了解了寻址之后,我们来看看运算指令,这里以 leaq 指令为例子。具体格式为 leaq Src, Dst,其中 Src 是地址的表达式,然后把计算的值存入 Dst 指定的寄存器,也就是说,无须内存引用就可以计算,类似于 p = &x[i];。我们来看一个具体的例子,假设一个 C 函数是:
1 | long m12(long x) |
复制
对应的汇编代码为:
1 | leaq (%rdi, %rdi, 2), %rax # t <- x+x*2 |
复制
可以看到是直接对 %rdi 寄存器中存的数据(地址)进行运算,然后赋值给 %rax。最后给出一些常见的算术运算指令,注意参数的顺序,而且对于有符号和无符号数都是一样的,更多的信息可以参考 Intel 官方文档[3]。
需要两个操作数的指令
addq Src, Dest->Dest = Dest + Srcsubq Src, Dest->Dest = Dest - Srcimulq Src, Dest->Dest = Dest * Srcsalq Src, Dest->Dest = Dest << Srcsarq Src, Dest->Dest = Dest >> Srcshrq Src, Dest->Dest = Dest >> Srcxorq Src, Dest->Dest = Dest ^ Srcandq Src, Dest->Dest = Dest & Srcorq Src, Dest->Dest = Dest | Src
需要一个操作数的指令
incq Dest->Dest = Dest + 1decq Dest->Dest = Dest - 1negq Dest->Dest = -Destnotq Dest->Dest = ~Dest






