【问题标题】:JIT compilation and DEPJIT 编译和 DEP
【发布时间】:2009-02-20 16:32:20
【问题描述】:

我正在考虑尝试一些 jit 编译(只是为了学习),让它跨平台工作会很好,因为我在家里运行所有主要的三个(windows、os x、linux) . 考虑到这一点,我想知道是否有任何方法可以摆脱使用虚拟内存窗口函数来分配具有执行权限的内存。最好只使用 malloc 或 new 并将处理器指向这样的块。

有什么建议吗?

【问题讨论】:

  • VirtualProtectEx 有什么问题? “我想做 VirtualProtectEx 做的事情,但我不想使用 VirtualProtectEx?”嗯?
  • 如果您仔细阅读我的问题,您会发现我希望代码跨平台。这意味着如果我能提供帮助,我不想使用 Windows API 中的任何东西。唯一依赖于平台的东西最好是 x86 指令集。
  • 这不会发生。编译器(与解释器相反)是特定于平台的。即使在 x86 中,您也必须处理 Linux 中与位置无关的代码之类的事情。更不用说执行权限,它们是硬件辅助的,但存在于操作系统中。
  • 我要补充一点,类似于依赖注入的东西可能是隔离您需要使用的特定于平台的代码的好方法。
  • 我没想到有人会回答我关于执行保护的问题,但我认为以防万一问一下也无妨。

标签: jit dep


【解决方案1】:

DEP 只是关闭了内存中每个非代码页的执行权限。应用程序代码加载到有执行权限的内存中;并且有很多 JIT 可以在 Windows/Linux/MacOSX 中运行,即使 DEP 处于活动状态也是如此。这是因为有一种方法可以根据所需的权限集动态分配内存。

通常不应使用普通的 malloc,因为权限是每页的。以一些开销为代价,仍然可以将分配的内存与页面对齐。如果你不会使用malloc,一些自定义的内存管理(只针对可执行代码)。自定义管理是一种常见的 JIT 方式。

Chromium 项目有一个解决方案,它为 javascript V8 VM 使用 JIT 并且是跨平台的。为了跨平台,需要的功能在多个文件中实现,并在编译时选择。

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): flag 是 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<int>(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<size_t>(Page::kPageSize)) {
    address = (V8::RandomPrivate(Isolate::Current()) << kPageSizeBits)
      | kAllocationRandomAddressMin;
    address &= kAllocationRandomAddressMax;
  }

  LPVOID mbase = VirtualAlloc(reinterpret_cast<void *>(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<size_t>(mbase), OS::AllocateAlignment()));

  *allocated = msize;
  UpdateAllocatedSpaceLimits(mbase, static_cast<int>(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-like 方式应该只用于非常旧的程序,它会在内存中创建新的可执行代码,但不会在此页面上设置 Exec 属性。对于较新的程序,如 firefox 或 Chrome/Chromium,或任何现代 JIT,DEP 应该处于活动状态,并且 JIT 将以细粒度的方式管理内存权限。

【讨论】:

  • 这是 正确的 方法,但在问题作者的评论中,他说:“我不想使用 Windows API 中的任何东西",所以是基于堆栈的方法。
  • 或在 Windows 中添加一个 POSIX 层(SFU 或 cygwin)并在 POSIX API 中执行此操作。
  • @Gigi 你提到的基于堆栈的方法是什么?
  • @Brent,您好,Gigi 对此问题有另一个答案,8 年前(2012 年)被他删除。他确实建议将指令放入堆栈分配的空间;但堆栈空间通常也是不可执行的。所以他另外建议在 linux (man7.org/linux/man-pages/man8/execstack.8.html) 上链接-z execstack 或依赖于 Windows 的数据执行保护设置。 (但我评论说 technet.microsoft.com/en-us/library/cc738483%28WS.10%29.aspx 说:“在 32 位版本的 Windows 上,默认情况下将 DEP 应用于堆栈。”)。
【解决方案2】:

一种可能性是要求运行您的程序的 Windows 安装配置为 DEP AlwaysOff(坏主意)或 DEP OptOut(更好的主意)。

这可以通过更改 boot.ini 文件来进行配置(至少在 WinXp SP2+ 和 Win2k3 SP1+ 下):

/noexecute=OptOut

然后通过选择(在 XP 下)将您的个人程序配置为退出:

Start button
    Control Panel
        System
            Advanced tab
                Performance Settings button
                    Data Execution Prevention tab

这应该允许您在程序中执行在 malloc() 块中动态创建的代码。

请记住,这会使您的程序更容易受到 DEP 旨在防止的攻击。

看起来这在 Windows 2008 中也可以使用以下命令:

bcdedit.exe /set {current} nx OptOut

但是,老实说,如果您只是想最小化依赖于平台的代码,只需将代码隔离到一个函数中即可轻松实现,例如:

void *MallocWithoutDep(size_t sz) {
    #if defined _IS_WINDOWS
        return VirtualMalloc(sz, OPT_DEP_OFF); // or whatever
    #elif defined IS_LINUX
        // Do linuxy thing
    #elif defined IS_MACOS
        // Do something almost certainly inexplicable
    #endif
}

如果您将所有与平台相关的函数都放在它们自己的文件中,那么您的其余代码将自动与平台无关。

【讨论】:

  • 请说:JavaVM、Firefox、Chrome 中的 JIT 如何在每台计算机上工作,即使启用了 DEP?
  • @osgx,为什么当您发布的答案涵盖了更多足够详细的内容时,我会这样做吗?但是,我想提一下,这个问题特别要求使用 标准的、非平台特定的 函数,“我想知道是否有任何方法可以摆脱使用虚拟内存窗口函数来分配具有执行权限的内存。最好只使用 malloc 或 new 并将处理器指向这样的块“。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-04
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多