edubart/riscvm
GitHub: edubart/riscvm
一个极简的 RV64I 解释器,可在沙箱中运行 GCC 编译的 C 程序,实现类似脚本语言的灵活部署与隔离执行。
Stars: 69 | Forks: 5
使用 Nelua 编写的微型 RISC-V 虚拟机(因此也包含 C)。
这是一个最小且简单的 RISC-V ISA 模拟器,实现了 RV64I 指令集的一个子集。
仅实现了运行最小用户态虚拟机所需的指令。
该模拟器允许运行经 GCC 编译为 RISC-V 的 C 文件,在一个最小的沙箱虚拟机中运行。
该虚拟机能够通过系统调用调用宿主机的函数。
本项目受 [libriscv](https://github.com/fwsGonzo/libriscv) 启发。
## 有趣之处在哪里?
这是一个关于如何在沙箱虚拟机中解释执行 C 程序的最小示例。人们可以利用这个概念,在任何安装了该模拟器的系统上运行由 GCC 编译的程序,或者将 C 应用程序隔离在独立的环境中运行,又或者实现应用程序运行时的热重载(这通常由脚本语言完成)。
## 运行示例
使用 Nelua 运行示例:
```
nelua -r riscvm.nelua examples/fib.bin
nelua -r riscvm.nelua examples/ack.bin
nelua -r riscvm.nelua examples/sieve.bin
```
这些示例是从相应的 C 文件编译成 RV64I 二进制文件的。
如果你没有安装 Nelua,也可以:
```
gcc -O2 -o riscvm riscvm.c
./riscvm examples/fib.bin
```
## 编译示例
所有示例都已经编译为 RV64I,但如果你想编辑或运行一个新的示例,
可以使用例如 `make EXAMPLE=fib.c` 命令将 `fib.c` 编译为 `fib.bin`,
这需要 RISC-V elf 工chain。
## 它是如何工作的?
0. 示例使用独立式(freestanding)C 代码编写,
沙箱环境之外的功能(例如打印到终端)
是通过 `lib/syscalls.h` 中的系统调用实现的,
而最小化的 libc 则在 `lib/tinyc.c` 中实现。
1. C 示例被编译为 RISC-V elf 二进制文件,
使用 `lib/start.s` 来正确初始化虚拟机状态,
并通过 `lib/link.ld` 的特殊链接规则来调整指令地址。
2. RISC-V 字节码从编译好的 elf 二进制文件中提取出来,生成字节码二进制文件。
3. 字节码二进制文件通过 `riscvm` 加载并运行,
解释执行 RV64I 指令。
4. 在解释执行过程中,虚拟机可能会
通过系统调用调用宿主机。
5. 一旦调用 `exit` 系统调用或发生任何错误,应用程序就会停止。
## 实现细节
这是通过阅读 [RISC-V 规范](https://riscv.org/technical/specifications/) 实现的
## 基准测试
在同一个系统中,使用 Lua 5.4、riscvm 和原生代码运行等效代码进行了一些示例测试,
实验表明解释执行的代码比原生代码慢 10~20 倍:
| example | lua 5.4 | riscvm | x86_64 |
|---------|----------|--------|--------|
| ack | 1070ms | 1022ms | 47ms |
| sieve | 1077ms | 716ms | 64ms |
注意:此虚拟机不做任何类型的 JIT,且实现非常简单,
还有很大的优化空间。
## 未来改进
这些扩展尚未实现,但会很有用:
* M 扩展 - 乘法和除法
* F 扩展 - 单精度浮点
* D 扩展 - 双精度浮点
此外,更多 C 函数(如 malloc/free/memcpy)也可以作为系统调用实现。
标签:API接口, DNS 反向解析, ELF, GCC, ISA, Nelua, RISC-V, RV64I, 仿真器, 动态执行, 后端技术, 安全沙箱, 客户端加密, 嵌入式, 底层开发, 指令集架构, 沙箱, 热重载, 生成式AI安全, 程序隔离, 系统编程, 脚本语言替代, 虚拟机, 解释器, 轻量级虚拟机