読者です 読者をやめる 読者になる 読者になる

解説: Victor Yurkovsky さんのトークンスレッデッドコードインタプリタのサンプル実装

実装は https://github.com/metanest/SWTMi386-poc に置いてあります。
Victor Yurkovsky さんが 1. A new CPU の最後で示している、トークンスレッデッドコードマシンの proof-of-concept のインタプリタを、実際に動かせるようにしたものです。
アセンブラに NASM を使っています。FreeBSD 以外ではリンクに修正が必要だと思われます。FreeBSD IA-32 用ですが、FreeBSD AMD64 環境でクロスビルドして実行できることを確認しています。

-m32 と mmap の話

このインタプリタは、バイト列でできたスレッデッドコードをアドレス 0x40000000~、テーブルを 0x10000000~ に置いて実行する必要があります。面倒を避けるためにスタティックリンクのバイナリを作ると、これらの場所は空いているので、最初の試作では mmap に MAP_FIXED を指定して OS からページをもらってきて、プログラム中から書き込むようにしていました。
ところが、使用している環境( FreeBSD AMD64 )で gcc に -m32 を指定して 32 ビットバイナリをクロスビルドすると、出来たコードを実行しても mmap が必ず失敗するのです。調べたところ、メイリングリストで議論しているのが見つかったのですが http://lists.freebsd.org/pipermail/freebsd-current/2010-May/017350.html 結論としては -m32 は不完全で mmap は動かない、32 ビット環境を作って chroot するとかしろ、ということのようでした。
(追記: -m32 を指定しても、コンパイル時に参照されるヘッダファイル(のうち、long 型のサイズ変更の影響を受けるもの)が 32 ビット用に切り替わらず、そのまま 64 ビット用のものを参照してしまっているのが原因で、正しくライブラリを呼び出せないことがある、ということのようです)
そんなわけで chroot のための 32 ビット環境をこの実験のために作りました。
(公開している版では mmap を使っておらず、とりあえず動くようなので、64 ビット環境でのクロスビルドを前提にしています)

カスタムリンカスクリプトの話

ELF ファイルにセグメントを追加して、0x10000000 に置くテーブルと、0x40000000 に置くテーブルを、実行可能ファイルの中に最初から用意するようにしました。
実行可能 ELF ファイルにセグメントを追加するには、リンカのふるまいを制御するリンカスクリプトに手を加える必要があります。
まず ld -melf_i386_fbsd -Bstatic --verbose としてデフォルトのリンカスクリプトを手に入れます。
しかし、これに単純にセグメントを追加しようとしても、リンクを実行すると not enough room for program headers というエラーになります。
実行可能 ELF ファイルでは、先頭(正確には ELF ヘッダの次)にプログラムヘッダがあり、そこにセグメントのテーブルが置かれているわけですが、そのサイズが通常は固定のためファイルにセグメントが追加できないわけです。
なので、リンカスクリプトに PHDRS コマンドを自分で追加して、プログラムヘッダにセグメントの記述を追加するわけですが、ここでデフォルトのふるまいと全く同じように動作するリンカスクリプトがどうしても作れませんでした(問題なく動作するものは作れる)。
検索してみると ld --verbose の出力するスクリプトに PHDRS を含めるべき、とか、そうしても 99.9% のユーザーには有用でない、という議論があったりします。
ともあれ、なんとか無事にセグメントが追加された実行可能 ELF ファイルが生成できるようになりました。

その他

頑張って libc と同居させていますが、必要な write(2) とかだけを自前で用意して libc はすっぱり諦めたほうが話が簡単かもしれません。