我正在考虑尝试一些jit compilataion(仅仅是为了学习),并且让它在跨平台上工作会很好,因为我在家里运行所有主要的三个(windows,os x,linux).考虑到这一点,我想知道是否有任何方法可以摆脱使用虚拟内存窗口函数来分配具有执行权限的内存.很高兴只使用malloc或new并将处理器指向这样的块.
有小费吗?
DEP只是关闭每个非代码内存页面的执行权限.应用程序代码被加载到具有执行权限的内存中; 即使DEP处于活动状态,也有很多JIT可以在Windows/Linux/MacOSX中运行.这是因为有一种方法可以动态分配具有所需权限集的内存.
通常,不应使用普通malloc,因为权限是每页的.将malloced内存与页面对齐仍然可能以一些开销为代价.如果你不使用malloc,一些自定义内存管理(仅适用于可执行代码).自定义管理是执行JIT的常用方法.
Chromium项目有一个解决方案,它使用JIT for javascript V8 VM,它是跨平台的.为了跨平台,所需的功能在几个文件中实现,并在编译时选择它们.
Linux:(chromium src/v8/src/platform-linux.cc)标志是mmap()的PROT_EXEC.
void* OS::Allocate(const size_t requested, size_t* allocated, bool is_executable) { const size_t msize = RoundUp(requested, AllocateAlignment()); int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); void* addr = OS::GetRandomMmapAddr(); void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mbase == MAP_FAILED) { /** handle error */ return NULL; } *allocated = msize; UpdateAllocatedSpaceLimits(mbase, msize); return mbase; }
Win32(src/v8/src/platform-win32.cc):标志是VirtualAlloc的PAGE_EXECUTE_READWRITE
void* OS::Allocate(const size_t requested, size_t* allocated, bool is_executable) { // The address range used to randomize RWX allocations in OS::Allocate // Try not to map pages into the default range that windows loads DLLs // Use a multiple of 64k to prevent committing unused memory. // Note: This does not guarantee RWX regions will be within the // range kAllocationRandomAddressMin to kAllocationRandomAddressMax #ifdef V8_HOST_ARCH_64_BIT static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000; static const intptr_t kAllocationRandomAddressMax = 0x000003FFFFFF0000; #else static const intptr_t kAllocationRandomAddressMin = 0x04000000; static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000; #endif // VirtualAlloc rounds allocated size to page size automatically. size_t msize = RoundUp(requested, static_cast(GetPageSize())); intptr_t address = 0; // Windows XP SP2 allows Data Excution Prevention (DEP). int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; // For exectutable pages try and randomize the allocation address if (prot == PAGE_EXECUTE_READWRITE && msize >= static_cast (Page::kPageSize)) { address = (V8::RandomPrivate(Isolate::Current()) << kPageSizeBits) | kAllocationRandomAddressMin; address &= kAllocationRandomAddressMax; } LPVOID mbase = VirtualAlloc(reinterpret_cast (address), msize, MEM_COMMIT | MEM_RESERVE, prot); if (mbase == NULL && address != 0) mbase = VirtualAlloc(NULL, msize, MEM_COMMIT | MEM_RESERVE, prot); if (mbase == NULL) { LOG(ISOLATE, StringEvent("OS::Allocate", "VirtualAlloc failed")); return NULL; } ASSERT(IsAligned(reinterpret_cast (mbase), OS::AllocateAlignment())); *allocated = msize; UpdateAllocatedSpaceLimits(mbase, static_cast (msize)); return mbase; }
MacOS(src/v8/src/platform-macos.cc):flag是mmap的PROT_EXEC,就像Linux或其他posix一样.
void* OS::Allocate(const size_t requested, size_t* allocated, bool is_executable) { const size_t msize = RoundUp(requested, getpagesize()); int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); void* mbase = mmap(OS::GetRandomMmapAddr(), msize, prot, MAP_PRIVATE | MAP_ANON, kMmapFd, kMmapFdOffset); if (mbase == MAP_FAILED) { LOG(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed")); return NULL; } *allocated = msize; UpdateAllocatedSpaceLimits(mbase, msize); return mbase; }
我还要注意,bcdedit.exe
类似的方式应该只用于非常旧的程序,它在内存中创建新的可执行代码,但不在此页面上设置Exec属性.对于较新的程序,如firefox或Chrome/Chromium,或任何现代JIT,DEP应该是活动的,JIT将以细粒度的方式管理内存权限.