当前位置: 首页 > news >正文

编译器构造:模拟器,汇编与反汇编

前言:

这个秋季,三碗饭会开始更新自己在秋季课程的笔记,主要包括Introduction to ML,可能还会有Complier Construction 以及 Computer Graphics.

本文将介绍Complier Construction中关于模拟器,汇编以及反汇编的知识,在下一章我们会从0写一个简易模拟器,以及汇编,反汇编程序。这门课如果没有兴趣,会非常无聊非常无聊,当然我一开始也觉得挺无聊的,毕竟搞AI Agent比这个有意思多,但我还是会尽量以简单的描述(避免复杂的解释),以及有意思的例子去讲这些知识。
在这里插入图片描述

前言

在浏览博客的时候看到一个挺有意思的思考,我们什么时候会需要去写编译器,或者反编译程序?

当然我要修学分写作业必须写lol

其他呢?
(1)有一种情况,你从网站下载了一个自动清理桌面的exe文件,通过工具分析,你知道这个文件的是用C写的,你觉得这个exe文件其实功能挺不错,但有些缺点,比如自动扫描磁盘效率低,你也找不到这个文件主人,于是你想修改这个文件,于是你用一个反编译工具把这个程序从EXE反编译为其原来汇编语言的伪代码(无法得到一模一样的源代码),这样你就可以对源代码进行修改和完善,然后再重新编译成EXE。
(2)第2个就比较“刑”,我第一次听到也挺感兴趣。有一些软件你下载了运行需要注册,输入序列号才能使用。因为exe是CPU才能看懂的机器代码,这时候你就可以用到反编译技术,将其翻译为我们能看懂的汇编语言,然后你在代码中寻找判断是否注册的语句,进行修改就可以免费使用。相信大家或多或少在生活学习中有遇到这些情况。

1:编译器和反编译器

(1)编译器(Compiler)
编译器的核心用途就是把高级语言(C、Java、Haskell 等)翻译成机器可以执行的低级语言(汇编、目标文件、机器码)。典型场景:

我们自己写程序需要执行,所以要编译。

操作系统内核、驱动程序、数据库等系统级软件,都依赖高效的编译器。

(2)反编译(Decompiler)与反汇编(Disassembler)

反汇编:机器码 → 汇编。工具如 objdump,这是最常见的,因为机器码和汇编是一一对应的。

反编译:机器码 → 高级语言。比如 IDA Pro、Ghidra,结果往往不如源代码整洁,因为编译优化会丢失很多语义。

2:一个C++程序是如何运行的?

2.1 总体流程

源代码.cpp
预处理器
编译器
汇编器
链接器
可执行文件

2.2 预处理阶段

源代码
预处理器
头文件.h
宏定义
预处理后的代码

示例:

// 输入
#include <iostream>
#define MAX 100
int main() { return MAX; }// 输出
// iostream内容展开
int main() { return 100; }

2.3 编译阶段

预处理后代码
词法分析
语法分析
语义分析
中间代码
汇编代码

示例:

// 输入
int add(int a, int b) {return a + b;
}// 输出(汇编代码)
add:push rbpmov  rbp, rspmov  eax, ediadd  eax, esipop  rbpret

2.4 汇编阶段

汇编代码
汇编器
目标文件.o

目标文件内容:

二进制格式:
+-------------+
| ELF头       |
+-------------+
| 代码段      |
+-------------+
| 数据段      |
+-------------+
| 重定位信息  |
+-------------+

2.5 链接阶段

目标文件1.o
链接器
目标文件2.o
库文件.lib
可执行文件

2.6 内存布局

高地址
栈区
...
堆区
BSS段
数据段
代码段
低地址

2.7 运行时状态

程序加载
创建进程
分配内存
初始化数据
执行main

内存使用示例:

int main() {int x = 10;        // 栈区int* p = new int;  // 堆区static int y = 20; // 数据段return 0;
}

2.8 执行过程

取指令
解码
执行
存储结果

这就是C++程序从源代码到运行的主要阶段。每个阶段都有其特定的任务和输出,共同协作完成程序的构建和执行。
看不懂也没关系,我们后面将一一来介绍。

3. 预处理

3.1 移除注释

// 输入代码
int main() {// 这是一个注释int x = 1;  /* 这也是注释 */return x;
}// 预处理后
int main() {int x = 1;  return x;
}

3.2 头文件处理

找到include
没有include
源文件
检查#include
打开头文件
递归处理
替换内容
完成

示例:

// 输入代码
#include <iostream>
#include "myheader.h"// 预处理后(展开的头文件内容)
namespace std { ... }  // iostream的内容
class MyClass { ... } // myheader.h的内容

3.3 宏定义处理

源代码
宏定义收集
宏展开
替换结果

示例:

// 输入代码
#define MAX 100
#define SQUARE(x) ((x) * (x))
int value = SQUARE(MAX);// 预处理后
int value = ((100) * (100));

3.4 条件编译

条件为真
条件为假
源代码
检查条件
保留代码
移除代码
结果代码

示例:

// 输入代码
#ifdef DEBUGvoid log(const char* msg) { printf("%s\n", msg); }
#elsevoid log(const char* msg) {}
#endif// 预处理后(如果DEBUG未定义)
void log(const char* msg) {}

4: 编译阶段

4.1 词法分析(Lexical Analysis)

源代码
分词器
标记流
符号表

源代码 → Source Code

分词器 → Lexer

标记流 → Token Stream

符号表 → Symbol Table

示例1
int main() {int x = 42;return x;
}

对应的Token Stream:

[
Token[0]:  {type: KEYWORD,    lexeme: "int",    line: 1, col: 1}
Token[1]:  {type: IDENTIFIER, lexeme: "main",   line: 1, col: 5}
Token[2]:  {type: SYMBOL,     lexeme: "(",      line: 1, col: 9}
Token[3]:  {type: SYMBOL,     lexeme: ")",      line: 1, col: 10}
Token[4]:  {type: SYMBOL,     lexeme: "{",      line: 1, col: 12}
Token[5]:  {type: KEYWORD,    lexeme: "int",    line: 2, col: 5}
Token[6]:  {type: IDENTIFIER, lexeme: "x",      line: 2, col: 9}
Token[7]:  {type: OPERATOR,   lexeme: "=",      line: 2, col: 11}
Token[8]:  {type: NUMBER,     lexeme: "42",     line: 2, col: 13}
Token[9]:  {type: SYMBOL,     lexeme: ";",      line: 2, col: 15}
Token[10]: {type: KEYWORD,    lexeme: "return", line: 3, col: 5}
Token[11]: {type: IDENTIFIER, lexeme: "x",      line: 3, col: 12}
Token[12]: {type: SYMBOL,     lexeme: ";",      line: 3, col: 13}
Token[13]: {type: SYMBOL,     lexeme: "}",      line: 4, col: 1}
]

词法分析器(Lexer)会把源代码切分成一连串 Token(记号)。
每个 Token 一般包含:

type:类型(比如关键字、标识符、运算符等)

lexeme:原始文本(代码里具体的字符串)

line/col:出现在源代码里的行号和列号

4.2 语法分析(Syntax Analysis)

标记流
语法分析器
语法树
错误处理
语法树示例
Program
FunctionDeclaration:main
ReturnType:int
Body
Declaration
Return
Type:int
Variable:x
Value:42
Variable:x
  1. 函数声明识别

    • Token[0-4]:识别出函数声明的开始
    • 通过Token类型和词素构建FunctionDeclaration节点
  2. 变量声明识别

    • Token[5-9]:识别出变量声明
    • 构建VariableDeclaration节点及其子节点
  3. 返回语句识别

    • Token[10-12]:识别出return语句
    • 构建ReturnStatement节点
  4. 作用域结束识别

    • Token[13]:识别出函数体结束

5. 语义分析(Semantic Analysis)

语法树
类型检查
作用域分析
符号解析
语义树

5.1 检查项目

  1. 类型检查
int x = "hello";  // 错误:类型不匹配
int y = 42;       // 正确:类型匹配
  1. 作用域检查
void func() {int x = 1;{int x = 2;  // 合法:新作用域int y = x;  // 使用内层x}int y = x;      // 使用外层x
}
  1. 符号解析
int func() {return x;  // 错误:x未定义
}

6. 中间代码生成(IR Generation)

工作流程

语义树
IR生成器
优化器
中间代码

示例

// 源代码
int x = a + b * c;// 中间代码
t1 = b * c    // 临时变量t1存储b*c的结果
t2 = a + t1   // 临时变量t2存储最终结果
x = t2        // 将结果赋值给x

7. 目标代码生成(Code Generation)

优化后的IR
指令选择
寄存器分配
指令调度
目标代码

示例

// 中间代码
t1 = b * c
t2 = a + t1
x = t2// x86汇编代码
mov eax, [b]    ; 加载b到eax
imul eax, [c]   ; 乘以c
add eax, [a]    ; 加上a
mov [x], eax    ; 存储结果到x

文章转载自:

http://tooEFAEM.drswd.cn
http://bdHitt3N.drswd.cn
http://fLQjs5JS.drswd.cn
http://Pfhum8h9.drswd.cn
http://HmZEJjpg.drswd.cn
http://0JZ6QNqh.drswd.cn
http://ooFcxJwp.drswd.cn
http://TCJw9yi3.drswd.cn
http://cYplkpHq.drswd.cn
http://ht75Aj30.drswd.cn
http://dPUSiHeo.drswd.cn
http://C9UBamK3.drswd.cn
http://yovhih3Z.drswd.cn
http://Ee1uj3A1.drswd.cn
http://g6bDijef.drswd.cn
http://qsUoVM45.drswd.cn
http://yTBNLEOf.drswd.cn
http://r4D3u1VT.drswd.cn
http://HkTRmgql.drswd.cn
http://nAZzG4Z6.drswd.cn
http://lshhk9zL.drswd.cn
http://tqAG85zM.drswd.cn
http://aCXztPJo.drswd.cn
http://6a68jRnq.drswd.cn
http://dfgJ85Cd.drswd.cn
http://lr83D9TI.drswd.cn
http://VWLA6lmA.drswd.cn
http://nnm857Ke.drswd.cn
http://XpZ87qvA.drswd.cn
http://WMvQS9VY.drswd.cn
http://www.dtcms.com/a/374073.html

相关文章:

  • 自由学习记录(96)
  • Cy5-Tyramide, Cyanine 5 Tyramide;1431148-26-3
  • JMeter接口测试全流程解析
  • ARM处理器的小常识
  • Go语言极速入门与精要指南从零到精通的系统化学习路径
  • RK3576 android14 usb_audio_policy_configuration.xml解析
  • 本地安装部署svn服务,并设置外网远程访问内网svn,含路由器转发和端口映射工具教程
  • idea2025构建springboot项目能运行的样例
  • 【底层机制】std::unordered_map 扩容机制
  • Cpp::STL—位图bitset的使用与模拟实现(39)
  • 链表 (C/C++)
  • WinEdt编译tex文件失败解决办法
  • C语言第12讲
  • commons-email
  • (堆)347. 前 K 个高频元素
  • GitHub Release Monitor部署指南:实时追踪开源项目更新与自动通知
  • 重新定义音频编程:SoundFlow如何以模块化设计革新.NET音频开发生态
  • SQL 注入与防御-第八章:代码层防御
  • Miniflux 安全升级:绑定域名并开启 HTTPS
  • 标准 HTTP 状态码详解
  • STM32开发(创建工程)
  • MFC 图形设备接口详解:小白从入门到掌握
  • APM32芯得 EP.34 | 告别I2C“假死”——APM32F103硬件IIC防锁死设计
  • n8n入门
  • 静态住宅IP的特点
  • 数智之光燃盛景 共同富裕创丰饶
  • colmap+pycolmap带cuda编译
  • Nano-Bananary 搭建 使用 nano banana
  • 前端性能监控与优化:从 Lighthouse 到 APM
  • 浅聊一下微服务的网关模块