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_);
    }

    // 以下略

これでファイルをメンバにする必要はなくなりました。