Rustで実行可能なメモリを確保

最近JITコンパイラを書いていて、実行可能なメモリ領域に命令列を直接書き込んで実行、ということがしたかった。

cならmmapなどで、PROT_EXECフラグを立ててメモリを確保するか、mprotectでPROT_EXECフラグを立ててやればいいという認識。

まず思いついたのは単純にhttps://crates.io/crates/libcを使う方法で、それは普通に思ったとおりにできて、最終的にそれ以外の方法はわからなかった。

こんなイメージ

Rust Playground

Rust 側で操作ができないと不便なので、 raw pointer として u8 で変数を宣言しておく。

で、別途 mmap でメモリ領域を確保して、 https://doc.rust-lang.org/std/mem/fn.transmute.html で u8 の pointer に coerce する。こうすると u8 を書き込める。

memmove や memcpy 相当の操作として copy_to(nonoverlapping) や copy_from(nonoverlapping) がある。 (cf. pointer - Rust )

https://doc.rust-lang.org/beta/std/primitive.slice.html#method.as_ptrでポインタを取り出して、確保した領域に u8 の配列に書き込んだ命令列をコピーする。

そしてまた transmute を使って命令列を書き込んだメモリ領域を関数ポインタに変換して、関数として呼び出す。 transmute, https://doc.rust-lang.org/nomicon/transmutes.htmlであり、危険で強力。

まぁlibc使うので当然だが、なんとも想定通りな感じ。

で、ちょうど Rust で JIT についての記事であり、メモリを確保して実行するもっとおしゃれでいい感じな方法を書いた記事がこちらにあったので、詳しくはこちら。

Building a simple JIT in Rust