【问题标题】:ebp + 6 instead of +8 in a JIT compilerJIT 编译器中的 ebp + 6 而不是 +8
【发布时间】:2011-03-01 20:29:39
【问题描述】:

我正在为好玩而编写的 VM 中实现一个简单的 JIT 编译器(主要是为了了解有关语言设计的更多信息)并且我遇到了一些奇怪的行为,也许有人可以告诉我原因。

首先,我为 C 和 C++ 定义了一个 JIT“原型”:

#ifdef __cplusplus 
    typedef void* (*_JIT_METHOD) (...);
#else
    typedef (*_JIT_METHOD) ();
#endif

我有一个compile() 函数,它会将内容编译到 ASM 中并将其粘贴到内存中的某个位置:

void* compile (void* something)
{
    // grab some memory
    unsigned char* buffer = (unsigned char*) malloc (1024);

    // xor eax, eax
    // inc eax
    // inc eax
    // inc eax
    // ret -> eax should be 3
    /* WORKS!
    buffer[0] = 0x67;
    buffer[1] = 0x31;
    buffer[2] = 0xC0;
    buffer[3] = 0x67;
    buffer[4] = 0x40;
    buffer[5] = 0x67;
    buffer[6] = 0x40;
    buffer[7] = 0x67;
    buffer[8] = 0x40;
    buffer[9] = 0xC3; */

    // xor eax, eax
    // mov eax, 9
    // ret 4 -> eax should be 9
    /* WORKS!
    buffer[0] = 0x67;
    buffer[1] = 0x31;
    buffer[2] = 0xC0;
    buffer[3] = 0x67;
    buffer[4] = 0xB8;
    buffer[5] = 0x09;
    buffer[6] = 0x00;
    buffer[7] = 0x00;
    buffer[8] = 0x00;
    buffer[9] = 0xC3; */


    // push ebp
    // mov ebp, esp
    // mov eax, [ebp + 6] ; wtf? shouldn't this be [ebp + 8]!?
    // mov esp, ebp
    // pop ebp
    // ret -> eax should be the first value sent to the function
    /* WORKS! */
    buffer[0] = 0x66;
    buffer[1] = 0x55;
    buffer[2] = 0x66;
    buffer[3] = 0x89;
    buffer[4] = 0xE5;
    buffer[5] = 0x66;
    buffer[6] = 0x66;
    buffer[7] = 0x8B;
    buffer[8] = 0x45;
    buffer[9] = 0x06;
    buffer[10] = 0x66;
    buffer[11] = 0x89;
    buffer[12] = 0xEC;
    buffer[13] = 0x66;
    buffer[14] = 0x5D;
    buffer[15] = 0xC3;

    // mov eax, 5
    // add eax, ecx
    // ret -> eax should be 50
    /* WORKS!
    buffer[0] = 0x67;
    buffer[1] = 0xB8;
    buffer[2] = 0x05;
    buffer[3] = 0x00;
    buffer[4] = 0x00;
    buffer[5] = 0x00;
    buffer[6] = 0x66;
    buffer[7] = 0x01;
    buffer[8] = 0xC8;
    buffer[9] = 0xC3; */

    return buffer;
}

最后我有了程序的主要部分:

int main (int argc, char **args)
{
    DWORD oldProtect = (DWORD) NULL;
    int i = 667, j = 1, k = 5, l = 0;

    // generate some arbitrary function
    _JIT_METHOD someFunc = (_JIT_METHOD) compile(NULL);

    // windows only
#if defined _WIN64 || defined _WIN32
    // set memory permissions and flush CPU code cache
    VirtualProtect(someFunc,1024,PAGE_EXECUTE_READWRITE, &oldProtect);  
    FlushInstructionCache(GetCurrentProcess(), someFunc, 1024);
#endif

    // this asm just for some debugging/testing purposes
    __asm mov ecx, i

    // run compiled function (from wherever *someFunc is pointing to)
    l = (int)someFunc(i, k);

    // did it work?
    printf("result: %d", l);

    free (someFunc);
    _getch();

    return 0;
}

如您所见,我对compile() 函数进行了几次测试,以确保获得预期的结果,并且几乎一切正常,但我有一个问题...

在大多数教程或文档资源中,要获取传递的函数的第一个值(在整数的情况下),您执行[ebp+8],第二个[ebp+12] 等等。出于某种原因,我必须先做[ebp+6] 然后[ebp+10] 等等。谁能告诉我为什么?

【问题讨论】:

    标签: c++ c assembly jit vm-implementation


    【解决方案1】:

    您的操作码看起来很可疑:它们充满了 0x660x67 地址/数据大小覆盖前缀,它们(在 32 位代码段中)会将 32 位操作转换为 16 位操作。例如

    buffer[0] = 0x66;
    buffer[1] = 0x55;
    buffer[2] = 0x66;
    buffer[3] = 0x89;
    buffer[4] = 0xE5;
    ...
    

    push bp
    mov  bp, sp
    

    而不是

    push ebp
    mov  ebp, esp
    

    (这似乎解释了观察到的行为:推 bp 将堆栈指针递减 2 而不是 4)。

    【讨论】:

    • 谢谢,那我应该用什么来代替0x660x67。此外,ASM 字节码是用 TASM 组装的(可能是 16 位模式)。
    • 我对 TASM 不熟悉...但是您需要在 32 位模式下组装。 (尽管在这些简单的示例中,您可能需要做的只是完全删除 0x660x67 字节:它们只是修改以下操作码含义的前缀字节。例如,push ebp ; mov ebp, esp 只是 0x55 , 0x89, 0xE5 在 32 位模式下。)
    【解决方案2】:

    您的问题是 6667 字节——分别是操作数大小覆盖和地址大小覆盖。

    由于您在 32 位模式下运行此代码,因此这些字节告诉处理器您需要 16 位操作数和地址而不是 32 位操作数和地址。 66 55 反汇编为 PUSH BP,它只推送 2 个字节而不是 4 个字节,因此您的地址减少了 2 个。

    前两个示例中的67 字节也是不必要的,但是因为您只访问寄存器而不是内存,所以它们没有任何效果并且不会破坏任何东西(还)。这些字节也应该被删除。

    看起来您正在使用专为 16 位代码设计的框架,或者也许有一种方法可以告诉它您需要 32 位代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-01
      • 2011-07-01
      • 2020-06-20
      • 1970-01-01
      • 2023-04-07
      • 2010-10-06
      • 2017-05-20
      • 1970-01-01
      相关资源
      最近更新 更多