mmapとmprotect
またcdecl_thunkをg++に移植した時の話です。
サンクような自己書き換えコードを実行可能にするためには、OSのメモリ保護のモードを変更する必要があります。POSIX環境でこれを実行する関数はmprotect()です。
規格によると、mprotect()はmmap()で確保したメモリに対してのみ動作します。
Linux等ではMAP_ANONYMOUSオプションを使用することでファイルと関連付けないmmap()ができるのですが、このオプションはPOSIX規格にありません。
通常は替わりに/dev/zeroファイルを使用するようです。
さらに、規格を確認したところ、
- MAP_PRIVATEオプションを使用した場合は、元となるファイルは読み込み専用でよい
- ファイルをclose()した後も、munmap()するまでmmap()したメモリを使用できる
ことが分かりました。
これを反映させたコードは次のようになります。
class virtual_memory : boost::noncopyable { public: explicit virtual_memory( std::size_t size, int mode=PROT_READ|PROT_WRITE) : ptr_(::mmap(0, size, mode, MAP_PRIVATE, posix::file("/dev/zero", O_RDONLY).get(), 0)) , size_(size) { if (ptr_ == MAP_FAILED) throw std::runtime_error("cannot allocate virtual memory"); } ~virtual_memory() { ::munmap(ptr_, size_); } // 以下略
これでファイルをメンバにする必要はなくなりました。