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)(+ ) 吗?FlushInstructionCache在 Linux 上也有类似的东西?
  • 什么是容器,它以正确的方式(即一个一个)可靠地将字节放入内存中?并且还允许获取指向内存块的指针。
4

1 回答 1

2

首先,您需要将代码分配为可执行文件[在 Windows 中使用带有“executable”标志的 VirtualAlloc,并使用“MAP_EXECUTABLE”作为标志之一的 mmap]。分配这种内存的大区域可能要容易得多,然后为您的内容提供“分配功能”。您可以使用 virtualprotect 以及 Linux 中的任何相应功能,但我会说首先分配为可执行文件是更好的选择。我不相信您需要刷新指令缓存,因为内存已经分配为可执行文件 - 至少在 x86 上不是 - 而且由于您的指令是 x86 指令,我想这是一个公平的限制。

其次,您需要创建一个指向代码的函数指针之类的东西。像这样的事情应该这样做:

typedef void (*funcptr)(void); 

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

应该做的伎俩。

于 2013-02-06T16:26:40.007 回答