# 翻译原理 ## 是一个进程,不是一台机器 Champollion 只做用户态。客户程序不是被模拟出来的机器,它就是宿主进程本身。`rvrun` 加载 ELF, 把各个段映射进预留的客户地址空间,然后从入口点开始执行。当程序发起系统调用时,由运行时负责 处理——能直接实现的用 Rust 实现,无法实现的则翻译给宿主。 ## 从文件到第一条指令 加载器解析 ELF,把各个段映射进客户内存(预留 256 GiB;在 macOS 上客户地址是相对该区域的固定 偏移,在 arm64 Linux 上客户地址就是宿主地址),构建初始的 Linux 栈,并为静态 PIE 映像做重 定位。程序若是动态链接的,它的 `PT_INTERP`——musl 的 `ld-musl-riscv64.so.1`——会先运行, 从 `-L` 指定的 sysroot 中解析。 ## 两个引擎,一个基准 解释器逐条取指、译码、执行,它是永久的基准:其他引擎均以它为准进行比对,而不是反过来被信任。快速引擎 维护解码块缓存,块与它最后两个后继链接在一起,另有每线程的软件 TLB,覆盖客户内存的 4 KiB 页面。 内存映射发生变化或客户执行 `fence.i` 时,缓存会被丢弃。 `riscv-tests` 测试套件会让快速引擎和 JIT 与解释器逐例比对,要求每个用例结束时状态完全一致。 ## JIT 基本块由手写的编码器翻译成 arm64,它的输出在测试中与汇编器逐字节核对。生成的代码把客户地址 掩码进预留区域,并通过跳转缓存找到已翻译的后继块。翻译结果按可执行文件保存在持久化代码缓存中 (默认在 `~/Library/Caches/champollion`),因此程序第二次运行时就已经是翻译好的; `RVRUN_CACHE=0` 可以关掉,`RVRUN_JIT=0` 则退回解释器。 ## 系统调用与信号 客户程序的系统调用遵循 Linux ABI。一部分由运行时自己处理(`mmap`、`brk` 等);其余的通过 一层极薄的 `HostOs` 接口过界。它的 macOS 后端负责翻译差异——`open` 的标志位、errno 取值、 信号、`flock`;它的 Linux 后端则把调用直接传给内核,内存布局为恒等映射。两种情况下错误都以 Linux errno 返回,并由一个宿主线程向客户程序投递信号。