mcpField
M
make-mips-interpreter

make-mips-interpreter

github.com/letta-ai开发15235 次安装4 周前更新
代码仓库

关于 make-mips-interpreter

为实现可加载并运行 MIPS ELF 二进制文件的解释器或模拟器提供开发指导。内容按阶段讲解 ELF 加载、指令解码、系统调用和测试,强调增量实现。

主要功能

  1. 01分阶段加载 MIPS ELF
  2. 02解码 R、I、J 型指令
  3. 03实现核心运算与内存指令
  4. 04处理系统调用和文件 I/O
  5. 05增量测试与执行跟踪

适用场景

  1. 01构建 MIPS ELF 解释器原型
  2. 02逐步增加指令与系统调用支持
  3. 03定位寄存器、内存及跳转错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/letta-ai/skills --skill make-mips-interpreter

安装后在 Claude Code 中输入/make-mips-interpreter确认已加载。

详细介绍

MIPS 解释器实现

概述

本技能为实现能够加载并执行 MIPS ELF 二进制文件的 MIPS 解释器/模拟器提供指导。核心挑战包括解析 ELF 文件、解码 MIPS 指令、管理虚拟内存以及处理系统调用。

关键方法:增量开发

这项任务最重要的原则是优先采用增量开发,而非全面分析。避免在编写代码之前花费过多时间进行分析。相反,应:

  1. 尽早搭建最精简的可运行框架
  2. 逐步扩展功能
  3. 使用部分实现频繁测试
  4. 根据实际执行结果进行调试和优化

实现阶段

阶段 1:精简 ELF 加载器

首先实现加载可执行文件所需的基本功能:

  1. 解析 ELF 头部并提取:
    • 魔数验证(0x7f、'E'、'L'、'F')
    • 架构(MIPS32)
    • 字节序(通常为小端序)
    • 入口点地址
  2. 解析程序头,识别可加载的段
  3. 将各段加载到指定地址的虚拟内存中
  4. 将程序计数器设为入口点

所需的关键数据结构:

  • 用于虚拟地址空间的内存数组/映射
  • 寄存器数组(32 个通用寄存器 + PC + HI/LO)
阶段 2:核心指令解码

为三种 MIPS 指令格式实现指令解码:

R 型格式(寄存器操作):

  • 位 31-26:opcode(R 型为 0x00)
  • 位 25-21:rs(源寄存器 1)
  • 位 20-16:rt(源寄存器 2)
  • 位 15-11:rd(目标寄存器)
  • 位 10-6:shamt(移位量)
  • 位 5-0:funct(功能码)

I 型格式(立即数操作):

  • 位 31-26:opcode
  • 位 25-21:rs
  • 位 20-16:rt
  • 位 15-0:立即数

J 型格式(跳转操作):

  • 位 31-26:opcode
  • 位 25-0:目标地址
阶段 3:优先实现必要指令

根据典型程序的需求顺序实现指令:

高优先级(优先实现):

  • 算术:ADD、ADDU、ADDI、ADDIU、SUB、SUBU
  • 逻辑:AND、ANDI、OR、ORI、XOR、NOR
  • 移位:SLL、SRL、SRA、SLLV、SRLV、SRAV
  • 比较:SLT、SLTI、SLTU、SLTIU
  • 内存:LW、SW、LB、LBU、SB、LH、LHU、SH
  • 分支:BEQ、BNE、BGTZ、BLEZ、BLTZ、BGEZ
  • 跳转:J、JAL、JR、JALR
  • 加载:LUI

中优先级:

  • 乘除:MULT、MULTU、DIV、DIVU、MFHI、MFLO、MTHI、MTLO

低优先级:

  • 协处理器指令(如有需要)
  • 浮点指令(如有需要)
阶段 4:系统调用处理程序

根据目标环境实现系统调用接口:

  1. 检测 SYSCALL 指令
  2. 从寄存器读取系统调用编号(通常为 $v0 或 $2)
  3. 从寄存器读取参数($a0-$a3 或 $4-$7)
  4. 执行系统调用,并将返回值设在 $v0 中

需要实现的常见系统调用:

  • read(文件描述符、缓冲区、数量)
  • write(文件描述符、缓冲区、数量)
  • open(路径、标志、模式)
  • close(文件描述符)
  • lseek(文件描述符、偏移量、起始位置)
  • exit(状态码)
阶段 5:输入/输出和文件系统

对于需要访问文件的程序:

  • 实现文件描述符表
  • 处理标准流(stdin=0、stdout=1、stderr=2)
  • 支持打开/读取外部文件(例如数据文件)
  • 处理输出文件的创建(例如帧缓冲区、结果)

验证策略

增量测试

每个实现阶段完成后都进行测试:

  1. ELF 加载器测试:验证入口点和内存布局是否符合预期
  2. 指令测试:为每组指令创建简单的测试序列
  3. 系统调用测试:使用已知的输入/输出测试每个系统调用
  4. 集成测试:运行实际的目标二进制文件
调试技巧
  • 添加指令跟踪(PC、指令、寄存器变化)
  • 记录系统调用及其参数
  • 验证是否在预期地址进行内存读写
  • 在检查点将寄存器状态与预期值进行比较
常见验证要点
  • 入口点地址与 ELF 头匹配
  • 正确初始化栈指针
  • 在正确的地址加载内存段
  • 寄存器 $0 始终读为 0
  • 正确处理有符号与无符号运算
  • 正确处理分支延迟槽(如果目标架构适用)

常见陷阱

分析瘫痪

问题:编写代码前花太多时间试图理解每个细节。 解决方案:了解 ELF 基础、入口点和系统调用编号后就开始实现。通过构建不断迭代和学习。

忽略字节序处理

问题:加载指令或数据时字节顺序不正确。 解决方案:检查 ELF 头中的字节序标志,并在读取多字节值时始终一致地应用。

寄存器零硬连线

问题:允许写入寄存器 $0 的值保留下来。 解决方案:读取 $0 时始终返回 0,或忽略对 $0 的写入。

符号扩展错误

问题:对立即数或加载操作进行符号扩展时出错。 解决方案:仔细区分有符号与无符号操作。LB 进行符号扩展,LBU 进行零扩展。

分支/跳转地址计算

问题:目标地址计算不正确。 解决方案:

  • 分支:PC + 4 +(符号扩展后的偏移量 << 2)
  • 跳转:(PC & 0xF0000000)|(target << 2)
内存对齐

问题:未对齐的内存访问导致错误。 解决方案:要么强制对齐,要么根据目标架构正确处理未对齐访问。

系统调用返回值

问题:未正确设置错误码或返回值。 解决方案:将返回值设为 $v0,并一致地处理错误情况。

指令覆盖不完整

问题:缺少指令导致程序静默失败。 解决方案:记录未实现指令及其编码,以便调试。

时间管理策略

对于复杂的解释器任务:

  1. 前 25% 的时间:ELF 加载 + 基本指令循环框架
  2. 接下来 25% 的时间:核心算术/逻辑/内存指令
  3. 接下来 25% 的时间:分支、跳转和系统调用
  4. 最后 25% 的时间:测试、调试和边界情况

优先实现能运行的解释器(即使尚不完整),而不是进行全面分析。能执行代码的部分实现,比没有代码的完整分析能提供更多调试信息。

评论

支持 Markdown