既然 complier 能帮助我们将 code 转换为 low-level language 我们为什么还要学习呢?
程序员学习机器代码的需求已经转变为阅读和理解编译器生成的代码。
读汇编的过程实际上是一个 a form of reverse engineering(逆向工程的一种形式)----->试着通过研究系统和向后工作来理解系统创建的过程
当我们写完 c program 我们可以compile通过
linux> gcc -Og -o p p1.c p2.c
计算机系统有两种重要的抽象
第一种 指令集: 机器级程序的格式和行为由指令集体系结构(ISA)定义
(1)描述处理器状态
(2)指令的格式
(3)每条指令执行之后的status
第二种 Virtual Memory(虚拟内存)
一些在C代码中隐藏的细节,可以在汇编代码中看到
(1)program counter(计数器)在x86-64中被叫做 %rip
(2)integer registee file(整数注册文件):可以存储 地址/指针;integer data;存储某些程序重要状态
(3)Condition Code Registers(条件代码寄存器):实现对于不同指令状态的记录, 比如跳转啥的
(4)A set of vector registers 存储 interger / floating point: 一组整数/浮点数向量寄存器
linux> gcc -OG -S mstore.c
-S:表明告诉编译器 to generate assembly file and go no further(以生成程序集文件,并没有进一步)
long mult2(long, long);void multstore(long x, long y, long* dest){long t = mult2(x, y);*dest = t;
}
assembly:
mulstore:pushq %rbx //将rbx中内容 push 到 program stackmovq %rdx, %rbxcall mult2moveq %rax, (%rbx)popq %rbxret
如果我们选择了
linux> gcc -Og -c mstore.c
这样会generate an object-codee file mstore.c:
类似
53 48 86 d3 e8 00 00 00 00 48 5b c3
想知道binary code 代表什么意思,可以使用disassemblers
linux 系统中有 OBJDUMP 可以 实现
linux> objdump -d mstore.c
对于机器表示的内容
pushq %rbx
can start with byte value 53一般我们采用
linux> gcc -Og -S mstore.c
由于历史设计的原因
Intel uses “word” to descript 16-bit data type
X86-64 CPU 包含了 16个 64位 寄存器
指令中几种不同的操作数:
为啥要有这么复杂的 内存reference格式?
它们在引用数组和结构元素时非常有用
Move class, 最简单的形式, 就是从一个地方拷贝到另外一个地方
注意这个图片, 如果 move bytes 那么只动后面两个
MOVZ class 会将dest 的剩余数字全部填充为 0
MOVS class 会将剩下的数字填充 based on sign extension
C code
long exchange(long* xp, long y){long x = *xp;*xp = y;return x;
}
Assembly Code
对上述code进行两点剖析
在 C中的pointer 实际上 deferencing a pointer involves
(1)Copying that pointer into a register(将该指针复制到寄 存器中)
(2)use this register in a memory reference(在内存引用中使用此寄存器)
local variables such as X are often kept in registers rathre than stored in memory locations(局部变量(如X)通常保存在寄存器中,而不是存储在内存中)
(1)因为 Register Access 通常比 Memory Locations 快很多
The stack plays a vital role in the handling of procedure calls(堆栈在过程调用的处理中起着至关重要的作用)
注意:
pushq % rbp 和 下面两步相同
popq %rax 和下面两步相同
蛮多汇编指令有 variant (变体)在 word size 比如:
当然load指令也有一些用处 当实施简单的加减法
long scale(long x, long y, long z){long t = x + 4 * y + 12 * z;return t;
}
第二组指令是 只有一个操作数 的指令
incq(%rsp) //这条指令让 stack 顶的元素 + 1
类似与x ++,y –
第三组指令是
subq %rax, %rdx
实际上的作用是 %rdx - %rax
英文语义是 subtract %rax from %rdx
同时对于 MOV 指令来说, 两个 oprand 不能同时是 memory location