程序的机器级表示
基础
机器语言
- 机器语言只是一个字节序列 (一系列指令的编码);
- 对产生该指令序列的源代码一无所知;
// c 语言
long mult2(long, long);
void multstore(long x, long y, long *dest) {
long t = mult2(x, y);
*dest = t;
}
// 汇编语言
multstore:
pushq %rbx
movq %rdx, %rbx
call mult2
movq %rax, (%rbx)
popq %rbx
ret
// 机器语言
53 48 89 d3 e8 00 00 00 00 48 89 03 5b c3
反汇编器
- 将机器语言转换为类似汇编语言的格式;
通用目的寄存器
- 一个 CPU 包含一组 16 个存储 64 位值的通用目的寄存器;
- 存储整数数据和指针;
指令
- 指定寄存器的行为;
- 数据传输指令;
- 入栈/出栈指令;
- 加载指令;
- 算术运算指令;
- 跳转指令;
控制
条件码寄存器
- 保存算术运算和逻辑操作信息;
- 执行条件分支指令;
跳转指令
- 跳转到程序的其他部分;
使用条件控制实现条件分支
- 使用条件控制实现条件分支;
- 使用条件码和跳转指令;
- 条件满足跳转到某一部分,反之跳转到另一部分;
t = test-expr;
if Ct)
goto true;
else-statement
goto done;
true :
then-statement
done :
使用条件传送实现条件分支
- 通过数据的条件传送实现条件分支;
- 使用条件传送指令;
- 同时计算一个条件操作的两种结果;
- 根据条件是否满足,使用条件传送指令返回不用的对应结果;
CPU 条件预测
- CPU 遇到条件跳转后,预先猜测会执行的分支并执行;
- 确保 CPU 指令流水线充满指令,以提高性能;
- 若猜测错误就丢弃执行的分支并重新执行正确分支;
- 猜测错误具有很高的乘法;
- 使用条件传送实现条件分支性能高于条件控制;
- 条件传送不依赖于数据,使用条件传送指令,不使用跳转指令;
- CPU 无需预测就可以执行;
- 确保 CPU 流水线是满的;
v = then-expr;
ve = else-expr;
t = test-expr;
if (!t) v = ve;
循环语句
- 将循环转换为条件分支;
// do while
loop:
body-statement
t = test-expr;
if (t)
goto loop;
// while
t = test-expr;
if (! t)
goto done;
loop:
body-statement
t = test-expr;
if (t)
goto loop;
done:
// for
init-expr;
t = test-expr;
if (! t)
goto done;
loop :
body-statement
update-expr;
t = test-expr;
if (t)
goto loop;
done:
switch 语句
- 使用跳转表数据结构:代码段地址构成的哈希表;
- 将 switch 转换为跳转表 + 条件分支;
过程
基础
过程
- 一种代码封装方式;
- 用一组参数和可选的返回值实现某种功能;
- 即编程中的函数;
动作
- 过程 P 调用 Q;
- Q 执行后返回 P;
运行时栈
运行时栈
- 基于栈后进先出的机制,实现过程调用机制;
- P 调用 Q 时,对应信息压入栈;
- 返回 P 时,对应信息弹出栈;
栈帧
- 过程需要的存储空间超出寄存器存储的大小时,将会在运行时栈分配空间;
- 该过程在运行时栈上的占用空间称为栈帧;
- 当前执行的过程的帧总是在运行时栈顶;
栈帧的适用场景
- 过程对应数据首先存储在寄存器中;
- 出现以下情况分配栈帧;
- 寄存器不足够存放所有数据;
- 局部变量使用地址运算符;
- 局部变量为数组或结构;
递归过程
- 存储当前过程结果值至栈顶;
- 调用递归函数, 添加对应信息值栈顶;
- 若递归完成, 依次释放栈顶空间;
- 若递归未完成, 跳转至 1;
数组分配和访问
一维数组
- 使用两个寄存器分别存储数组的起始地址和偏移量;
多维数组
- 计算偏移量;
- 对于一个 m*n 的数组
- D[i][j]=x+L(n*i+j);
- L 为数组元素类型;
- 使用两个寄存器分别存储数组的起始地址和偏移量;
定长数组
- 编译时确定数组维度;
变长数组
- 运行时确定数组维度;
异构的数据结构
结构
- 不同类型对象聚合到一个对象中;
- 编译器维护结构第一个字节的地址和每个字段的偏移;
联合
- 一个对象使用多种类型之一;
- 联合的大小为其最大字段的大小;
数据对齐
- 类型对象的地址必须是 K (2,4,8) 的倍数;
- 用于提高内存访问的性能;
- 实际应用;
- 结构中使用数据对齐;
指针
指针
- 指针对应一个类型;
- 指针对应一个类型的内存地址;
内存越界引用和缓冲区溢出
内存越界引用
- 访问不属于对应类型的内存地址,常见于数组越界;
缓冲区溢出
- 栈分配一定空间给某类型;
- 类型实际使用空间超过分配空间;
- 常见于字符串;
对抗缓冲区溢出攻击
- 栈随机化:栈地址在程序每次运行发生变化;
- 栈破坏检测;
- 栈帧中的缓冲区和栈状态之间存储一个随机产生的特殊值 (金丝雀值);
- 通过检测金丝雀值是否发生变化检测;
- 限制可执行代码区域;
- 虚拟内存空间逻辑上分成 "页";
- 限制程序可以访问的页;
变长栈帧
- 寄存器存储 "帧指针" 用于存储需要分配的内存空间;
浮点代码
任务
- 如何存储和访问浮点值;
- 浮点值指令;
- 函数传递浮点数参数和返回浮点数结果;
- 函数调用过程中寄存器的行为;
指令集
- SSE;
- AVX;
寄存器
- XMM 寄存器:256 位;
- YMM 寄存器:128 位;