1

私のプログラムにデータを実行させるための最良のアプローチは何ですか。たとえば、 x86_64マシン用の(いわゆる)コンパイラを作成しました。

#include <iostream>
#include <vector>

#include <cstdlib>
#include <cstdint>

struct compiler
{

    void op() const { return; }

    template< typename ...ARGS >
    void op(std::uint8_t const _opcode, ARGS && ..._tail)
    {
        code_.push_back(_opcode);
        return op(std::forward< ARGS >(_tail)...);
    }

    void clear() { code_.clear(); }

    long double operator () () const
    {
        // ?
    }

private :

    std::vector< std::uint8_t > code_;

};

int main()
{
    compiler compiler_; // long double (*)();
    compiler_.op(0xD9, 0xEE); // FLDZ
    compiler_.op(0xC3);       // ret
    std::cout << compiler_() << std::endl;
    return EXIT_SUCCESS;
}

operator ()しかし、正しく実装する方法がわかりません。のすべての内容code_を連続したメモリチャンクに入れてから、これにキャストしlong double (*)();て呼び出す必要があるのではないかと思います。しかし、いくつかの困難があります:

  • WindowsでVirtualProtect(Ex)(+ )を使用する必要がありますか?FlushInstructionCacheLinuxでも似たようなものはありますか?
  • バイトを適切な方法で(つまり1つずつ)メモリに確実に配置するコンテナとは何ですか?また、メモリチャンクへのポインタを取得することもできます。
4

1 に答える 1

2

最初に、コードを実行可能として割り当てる必要があります [Windows では VirtualAlloc を使用して "executable" フラグを使用し、mmap では "MAP_EXECUTABLE" をフラグの 1 つとして使用します]。この種のメモリの大きな領域を割り当てて、コンテンツに「割り当て機能」を持たせる方がおそらくはるかに簡単です。Linux で対応する機能が何であれ、virtualprotect を使用することもできますが、最初に実行可能ファイルとして割り当てる方が良い選択だと思います。メモリが既に実行可能として割り当てられている場合、命令キャッシュをフラッシュする必要はないと思います-少なくともx86ではそうではありません-そして、命令はx86命令であるため、それはかなりの制限だと思います。

次に、コードへの関数ポインタのようなものを作成する必要があります。このような何かがそれを行う必要があります:

typedef void (*funcptr)(void); 

funcptr f = reinterpret_cast<funcptr>(&code_[0]); 

トリックを行う必要があります。

于 2013-02-06T16:26:40.007 に答える