您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center 汽车系统工程   模型库  
会员   
   
软件架构设计方法、案例与实践
10月15日-16日 北京+线上
车载系统功能开发方法与实践
10月25日-26日 北京+线上
SysML和EA进行系统设计与建模
11月19-20日 北京+线上
     
   
 订阅
编译器构造:模拟器,汇编与反汇编
 
 

  41   次浏览      4 次
2025-9-15
 
编辑推荐:
本文本文将介绍Complier Construction中关于模拟器,汇编以及反汇编的知识,希望对您的学习有所帮助 。
本文来自于AI知识物语,由火龙果软件Alice编辑、推荐。

前言:

这个秋季,三碗饭会开始更新自己在秋季课程的笔记,主要包括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 总体流程

2.2 预处理阶段

示例:

// 输入
#include<iostream>
#define MAX 100
intmain()return MAX; }

// 输出
// iostream内容展开
intmain()return100; }

2.3 编译阶段

示例:

// 输入
intadd(int a, int b){
return a + b;
}

// 输出(汇编代码)
add:
    push rbp
    mov  rbp, rsp
    mov  eax, edi
    add  eax, esi
    pop  rbp
    ret

2.4 汇编阶段

目标文件内容:

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

2.5 链接阶段

2.6 内存布局

2.7运行时状态

内存使用示例:

intmain(){
int x = 10;        // 栈区
int* p = newint;  // 堆区
staticint y = 20// 数据段
return0;
}

2.8 执行过程

这就是C++程序从源代码到运行的主要阶段。每个阶段都有其特定的任务和输出,共同协作完成程序的构建和执行。

看不懂也没关系,我们后面将一一来介绍。

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

3. 预处理

3.1 移除注释

// 输入代码
intmain(){
// 这是一个注释
int x = 1;  /* 这也是注释 */
return x;
}

// 预处理后
intmain(){

int x = 1;  
return x;
}

3.2 头文件处理

示例:

// 输入代码
#include<iostream>
#include"myheader.h"

// 预处理后(展开的头文件内容)
namespacestd { ... }  // iostream的内容
classMyClass { ... } // myheader.h的内容

3.3 宏定义处理

示例:

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

// 预处理后
int value = ((100) * (100));

3.4 条件编译

示例:

// 输入代码
#ifdef DEBUG
voidlog(constchar* msg)printf("%s\n", msg); }
#else
voidlog(constchar* msg){}
#endif

// 预处理后(如果DEBUG未定义)
voidlog(constchar* msg){}

4: 编译阶段

4.1 词法分析(Lexical Analysis)

源代码 → Source Code

分词器 → Lexer

标记流 → Token Stream

符号表 → Symbol Table

示例

intmain(){
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)

语法树示例

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;       // 正确:类型匹配

    2.作用域检查 :

    voidfunc(){
    int x = 1;
        {
    int x = 2;  // 合法:新作用域
    int y = x;  // 使用内层x
        }
    int y = x;      // 使用外层x
    }

    3.符号解析 :

    intfunc(){
    return x;  // 错误:x未定义
    }

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

    工作流程

    示例

    // 源代码
    int x = a + b * c;

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

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

    示例

     // 中间代码 

    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

       
    41   次浏览       4 次
    相关文章

    编译原理--C语言C文件和头文件的关系
    用 C 语言开发一门编程语言 — 抽象语法树
    C语言 | 嵌入式C语言编程规范
    详解C语言数组越界及其避免方法
     
    相关文档

    C语言-指针讲解
    详解C语言中的回调函数
    ARM下C语言编程解析
    设计模式的C语言实现
    相关课程

    C++高级编程
    C++并行编程与操作
    C++ 11,14,17,20新特性
    C/C++开发基础

    最新活动计划
    软件架构设计方法、案例实践 10-15[北京]
    数据架构、数据治理数据运营 10-17[北京]
    车载系统功能开发方法与实践 10-25[北京]
    SysML和EA系统设计与建模 11-19[北京]
    AI辅助软件测试方法与实践 10-26[北京]
    OCSMP 认证培训课程 11-18[北京]
     
     
    最新文章
    编译原理--C语言C文件和头文件的关系
    用 C 语言开发一门编程语言 — 抽象语法树
    C语言 | 嵌入式C语言编程规范
    详解C语言数组越界及其避免方法
    最新课程
    C++高级编程
    C++并行编程与操作
    C++ 11,14,17,20新特性
    C/C++开发基础
    成功案例
    某航天科工单位 C++新特性与开发进阶
    北京 C#高级开发技术
    四方电气集团 嵌入式高级C语言编程
    北大方正 C语言单元测试实践