🎨 颜色标记说明

  • 粉色:特别重要、主线必掌握、优先复盘的内容。
  • 蓝色:核心概念、术语、分类名。
  • 橙色:需要关注的边界、易错点、注意事项。
  • 绿色:解释类内容、帮助理解知识点。

返回:全书总览 · 上一章:启动函数 · 下一章:流程控制

从数据流恢复表达式

机器代码不会保留括号和源码排版。恢复表达式时要跟踪每个值的来源、运算和写回目标。

1
2
3
4
mov eax, [a]
imul eax, [b]
add eax, [c]
mov [result], eax

候选语义是 result = a * b + c,但还需结合宽度、符号和调用上下文确认。

算术与赋值

源码语义 常见指令
加减 ADDSUBLEA
乘法 IMULMUL、移位与加法组合
除法 IDIVDIV、乘倒数优化
取负 NEG
自增自减 INC/DECADD/SUB 1

复合赋值通常直接对原位置读改写:

1
add dword ptr [value], 5

LEA 不访问其计算出的目标内存,常被编译器用于整数运算:

1
lea eax, [ecx+ecx*4]  ; eax = ecx * 5

溢出与截断

不同宽度的临时值会发生截断或扩展。分析时记录:

  • 运算发生在哪个宽度。
  • 写回时保留多少位。
  • 后续使用有符号还是无符号语义。

关系运算

CMP a, b 近似执行 a - b,只更新标志位,不保存结果。

语义 有符号跳转 无符号跳转
相等 JE JE
不等 JNE JNE
小于 JL JB
大于 JG JA
小于等于 JLE JBE
大于等于 JGE JAE

逻辑短路

1
if (a != 0 && b / a > 2) { ... }

编译器会先检查 a,条件失败便跳过第二个表达式。这不仅提高效率,也避免除零。

1
2
3
4
判断 a ─失败→ 结束
│成功

计算 b/a → 判断结果

条件表达式

三目运算可能编译成分支,也可能使用 CMOVcc 或位运算实现无分支选择。不能只凭是否出现跳转判断源码是否使用三目运算。

位运算

典型语义:

  • AND:清位、掩码、对齐。
  • OR:置位、组合标志。
  • XOR:翻转、比较、清零寄存器。
  • TEST:按位与后只更新标志位。
  • SHL/SHR/SAR:乘除 2 的幂、字段提取。
1
and eax, 0FFFFFFF0h  ; 向下按 16 字节对齐

优化如何改变表达式

常见优化包括:

  • 常量折叠与常量传播。
  • 公共子表达式消除。
  • 强度削弱,例如乘法改为移位或 LEA。
  • 无用代码删除。
  • 条件移动与分支消除。
  • 循环不变量外提。

优化后,机器代码更接近“等价语义”,不一定保留源码逐项求值顺序。

恢复表达式的步骤

  1. 给寄存器标注当前语义,不沿用硬件名称思考。
  2. 找出每次内存读取和最终写回。
  3. 按依赖关系组成表达式树。
  4. 判断符号、宽度和溢出语义。
  5. 在调用点或测试输入中验证。

学习检查清单

检查项 状态
能从数据依赖恢复简单算术表达式 待复盘
能区分有符号和无符号条件跳转 待复盘
能识别逻辑短路形成的提前跳转 待复盘
能解释 LEA 用于整数运算的情况 待复盘
能列举至少四种编译优化 待复盘