JIT技术是如何实现的,这个问题困扰我很久

155 阅读3分钟

我们知道,编译程序编译源码后产生的数据是在数据段的,如何使一段在数据段的数据成为一段代码段的代码呢?因为操作系统的限制,cpu是无法直接运行数据段的数据的。

然而,在某些特定的环境和场景下(例如操作系统的开发,嵌入式系统的编程,或者其他对低级别硬件控制有需求的场景),有可能需要把某段数据当做代码来处理和执行。在像C这种较为底层的语言中,你可以通过下面的方式来达成:

// 待执行的二进制代码
unsigned char code[] = { ... };

// 创建一个函数指针,然后将其指向我们的代码
void (*func)();
func = (void (*)()) code;

// 调用这个函数
func();

但是,现代的操作系统大多数已经启用了数据执行防止(DEP)这样的安全策略,这会禁止非代码段的数据作为执行代码。所以在这种环境下,以上的代码有可能会抛出一个安全异常。

为了绕开这个限制,你可能需要使用操作系统层面的API来调整内存的保护设置。例如在 Posix 兼容的系统中,你可以使用 mprotect 来完成:

#include <sys/mman.h>
#include <string.h>

...

// 通过mmap分配一块内存
void *buffer = mmap(0, sizeof(code), PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);

// 将我们的代码复制到那个内存区域
memcpy(buffer, code, sizeof(code));

// 进行内存权限的调整,使其具有可执行的权限
mprotect(buffer, sizeof(code), PROT_READ | PROT_EXEC);

// 创建一个函数指针并调用
void (*func)() = buffer;
func();

这样我们就能够创建一个在数据段的代码并执行它。但是需要注意,这种操作可能引发安全和可移植性问题,在实际开发过程中要慎重考虑其后果。

有什么操作,可以让一段数据段的数据成为一个代码段,在windows上如何实现?

在Windows上,你可以使用VirtualProtect函数来修改内存区域的保护属性,使其可执行。以下是一个使用Windows API并用C++编写的示例,该示例创建一个简单的二进制代码并执行:

#include <windows.h>

int main() {
    // 待执行的二进制代码,例如一段简单的x86代码
    unsigned char code[] = { ... };

    DWORD oldProtect;
    // 更改内存保护属性以使其可写、可执行
    if (VirtualProtect(code, sizeof(code), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        // 创建一个函数指针,然后将其指向我们的代码
        void (*func)();
        func = (void (*)()) code;

        // 调用这个函数
        func();

        // 恢复内存的保护属性
        VirtualProtect(code, sizeof(code), oldProtect, &oldProtect);
    }

    return 0;
}

注意:这是一个低级别的操作,并涉及某些可能的安全风险,特别是在未来试图运行未知的、不信任的或不安全的代码时。

JIT技术生成机器码后,应该就是用这个技术把一段数据变成一段代码,让计算机执行的。