
2025/12/13 7:02
Show HN: Tiny VM sandbox in C with apps in Rust, C and Zig
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
uvm32 は、単一の C ファイルで書かれたミニマリストで依存関係を持たない仮想マシンサンドボックスです。
STM32L0 のような非常に小型のマイクロコントローラ上で動作し、4 KB 未満のフラッシュと 1 KB の RAM を使用します。静的割り当てのみで非同期設計となっています。
この VM は RISC‑V のサブセットを実装しており、軽量な管理インタフェースを公開しています。「if‑this‑then‑that」ロジックのために Lua、Duktape、MicroPython などの軽量スクリプトエンジンを置き換え、信頼できないコンポーネントや不安定な部品をサンドボックス化し、ターゲットコンパイラなしでモダン言語の「一度書けばどこでも実行できる」スクリプトを書けるようにすることが目的です。
主な特徴:
- バイトコードアプリは C、Zig、Rust、またはアセンブリで記述可能。
- 非ブロッキングでシンプルな実行モデル。安全かつ最小限の型付けを備えた FFI を使用し、ホスト IO(stdio/ネットワーク)は想定していません。
- デザインは高速よりも安全性を優先しています。
- すべてのソースコードは
ディレクトリにあります。最小限のホスト例はuvm32/
にあり、より高度なホストはhost‑mini
、host/
、host-parallel
にあります。host-arduino
サンプルアプリケーションは VM の機能を示しています(C: helloworld, heap, conio, lissajous, maze, fib, sketch; Zig: zig‑mandel, zigtris, zigalloc, zigdoom; Rust: rust‑hello; アセンブリ: hello‑asm)。
ビルドとテスト用の Dockerfile が提供されており、
make dockerbuild、make dockershell で構築・起動し、その後 make を実行してサンプルをコンパイル・実行します。ドキュメントはヘッダファイル uvm32/uvm32.h と doc/README.md にあります。本プロジェクトは MIT ライセンスで公開されています。本文
uvm32(ユーヴィーエムサンティ)
uvm32 は、マイクロコントローラやリソースが限られたデバイス向けに設計された、最小構成・依存関係のない仮想マシンサンドボックスです。単一の C ファイルで構成され、動的メモリアロケーションは行わず、非同期設計を採用し、純粋な C99 で実装されています。
STM32L0(ARM Cortex‑M0+)上では、必要となるフットプリントは 4 KB 以下のフラッシュ / 1 KB の RAM に収まります。
uvm32 は管理インタフェースを備えた RISC‑V エミュレータで、内部で効率的に動作するコードをビルドできるツール一式が同梱されています。
何のためにあるか?
- Lua・Duktape・MicroPython 等の埋め込みスクリプトエンジンのシンプルな代替として
- 信頼性の低いコンポーネントを分離するサンドボックスとして
- ターゲット用コンパイラが無い場合でも、モダンなシステム言語(例:Rust)で開発できるように
- 「一度書けばどこでも実行」―複数のソフトウェアバリアントを維持する手間を省く
主な機能
| 機能 | 内容 |
|---|---|
| バイトコード例アプリ(C・Zig・Rust・アセンブリ) | さまざまな言語で書かれたサンプルが含まれる |
| 非ブロッキング設計 | VM が不正に動作してもホストは停止しない |
ホスト IO 能力の前提なし( 等を想定しない) | |
| 単純で意図が明確な実行モデル | |
| 安全かつ最小限の型付き FFI | |
| 「if this then that」程度のスクリプト/プラグインに十分なサイズ | それ以上も可能 |
| スピードより安全性を重視 | VM 内で悪質なコードがホストをクラッシュさせないようにする |
uvm32 はハードウェアシミュレーションではなく、カスタムスクリプトロジックを実行する CPU エミュレータです。
代替との比較
| 目的 | uvm32 |
|---|---|
| 小さいフットプリント(組み込み機器・ゲーム・アプリ) | ✔︎ |
| VM コード用に知られた言語と品質の高い開発ツール | ✔︎ |
| 既存ソフトウェアへの統合容易性 | ✔︎ |
| イベント駆動・ポーリング・マルチプロセッサ等、多様なパラダイム | ✔︎ |
| VM コードが不正に動作しても耐久性 | ✔︎ |
uvm32 が目指さないこと
- 直接的な FFI(ホスト–VM 間の関数呼び出し)を簡素化すること
- 最大限の効率化
- スクリプト経験がほとんど無い人向けのサポート(開発・コンパイル・実行は手動で行う)
- 「バッテリー付き」ライブラリ(
、ネットワーク等)は提供しないstdio
リポジトリ構成
全てのコードは
uvm32 ディレクトリにあります。単一の実行ファイルを動かす最小ホストは
host‑mini に置かれています。他のホストやサンプルアプリはそれぞれ別フォルダに入っています。
例 – シンプル VM ホスト(host‑mini
)
host‑mini#include <stdio.h> #include <string.h> #include <stdlib.h> #include "uvm32.h" #include "uvm32_common_custom.h" uint8_t rom[] = { /* mandel.bin */ 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0c0, 0x00, 0xb7, 0x08, 0x00, 0x01 /* … */ }; int main(int argc, char *argv[]) { uvm32_state_t vmst; uvm32_evt_t evt; bool isrunning = true; uvm32_init(&vmst); uvm32_load(&vmst, rom, sizeof(rom)); while (isrunning) { uvm32_run(&vmst, &evt, 100); /* VM が動作しているとみなす周期数 */ switch (evt.typ) { case UVM32_EVT_END: isrunning = false; break; case UVM32_EVT_SYSCALL: /* VM がシステムコールを呼んだ */ switch (evt.data.syscall.code) { case UVM32_SYSCALL_PUTC: printf("%c", uvm32_arg_getval(&vmst, &evt, ARG0)); break; case UVM32_SYSCALL_PRINTLN: const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0); printf("%s\n", str); break; case UVM32_SYSCALL_YIELD: /* 何もしない */ break; default: printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code); } break; case UVM32_EVT_ERR: printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode); break; default: break; } } return 0; }
サンプルアプリ
| カテゴリ | 例 |
|---|---|
| C | 、、、、、、 |
| Rust | (公式 Rust インストーラを使用し、Homebrew ではない) |
| Zig | 、、、 |
| アセンブリ | |
VM ホスト
– バイナリをロードして完了まで実行し、複数のシステムコールに対応host
– 上記の最小ホスト(バイトコードは組み込み済み)host‑mini
– 複数 VM を同時に走らせる並列ホストhost-parallel
– QEMU で AVR コードをテストする Arduino スケッチhost-arduino
クイックスタート(Docker)
make dockerbuild make dockershell
シェル内で
make ./hosts/host/host apps/helloworld/helloworld.bin
host -h でオプション一覧を確認してください。
さらに詳しく
- ヘッダーファイル:
uvm32/uvm32.h - テスト・ドキュメント:
doc/README.md
ライセンス
本プロジェクトは MIT ライセンスの下で配布されています。研究・製品・組み込みデバイスに自由にご利用ください。