Bug 1432653: Refactor the DLL interceptor and parameterize its memory operations; r?handyman draft
authorAaron Klotz <aklotz@mozilla.com>
Mon, 09 Apr 2018 13:37:52 -0600
changeset 783812 29d5f8bde9645dc231ea34d6ded1f1d90a0c0ed1
parent 780873 2318adaec61f07eccf66c3f742497aa973b15f2f
push id106787
push useraklotz@mozilla.com
push dateTue, 17 Apr 2018 18:47:36 +0000
reviewershandyman
bugs1432653
milestone61.0a1
Bug 1432653: Refactor the DLL interceptor and parameterize its memory operations; r?handyman MozReview-Commit-ID: EYxVsQ1kicy
dom/plugins/ipc/FunctionHook.cpp
mozglue/misc/interceptor/MMPolicies.h
mozglue/misc/interceptor/PatcherBase.h
mozglue/misc/interceptor/PatcherDetour.h
mozglue/misc/interceptor/PatcherNopSpace.h
mozglue/misc/interceptor/TargetFunction.h
mozglue/misc/interceptor/Trampoline.h
mozglue/misc/interceptor/VMSharingPolicies.h
mozglue/misc/moz.build
mozglue/misc/nsWindowsDllInterceptor.h
mozglue/tests/interceptor/TestDllInterceptor.cpp
mozglue/tests/interceptor/moz.build
mozglue/tests/moz.build
toolkit/xre/test/win/TestDllInterceptor.cpp
toolkit/xre/test/win/moz.build
xpcom/build/PoisonIOInterposerWin.cpp
xpcom/build/moz.build
xpcom/build/nsWindowsDllInterceptor.h
--- a/dom/plugins/ipc/FunctionHook.cpp
+++ b/dom/plugins/ipc/FunctionHook.cpp
@@ -53,29 +53,33 @@ FunctionHook::HookFunctions(int aQuirks)
   }
 }
 
 #if defined(XP_WIN)
 
 // This cache is created when a DLL is registered with a FunctionHook.
 // It is cleared on a call to ClearDllInterceptorCache().  It
 // must be freed before exit to avoid leaks.
-typedef nsClassHashtable<nsCStringHashKey, WindowsDllInterceptor> DllInterceptors;
+typedef nsClassHashtable<nsStringHashKey, WindowsDllInterceptor> DllInterceptors;
 DllInterceptors* sDllInterceptorCache = nullptr;
 
 WindowsDllInterceptor*
 FunctionHook::GetDllInterceptorFor(const char* aModuleName)
 {
   if (!sDllInterceptorCache) {
     sDllInterceptorCache = new DllInterceptors();
   }
 
+  MOZ_ASSERT(NS_IsAscii(aModuleName), "Non-ASCII module names are not supported");
+  NS_ConvertASCIItoUTF16 moduleName(aModuleName);
+
   WindowsDllInterceptor* ret =
-    sDllInterceptorCache->LookupOrAdd(nsCString(aModuleName), aModuleName);
+    sDllInterceptorCache->LookupOrAdd(moduleName);
   MOZ_ASSERT(ret);
+  ret->Init(moduleName.get());
   return ret;
 }
 
 void
 FunctionHook::ClearDllInterceptorCache()
 {
   delete sDllInterceptorCache;
   sDllInterceptorCache = nullptr;
new file mode 100644
--- /dev/null
+++ b/mozglue/misc/interceptor/MMPolicies.h
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_interceptor_MMPolicies_h
+#define mozilla_interceptor_MMPolicies_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Types.h"
+
+#include <windows.h>
+
+namespace mozilla {
+namespace interceptor {
+
+class MMPolicyBase
+{
+public:
+  static DWORD ComputeAllocationSize(const uint32_t aRequestedSize)
+  {
+    MOZ_ASSERT(aRequestedSize);
+    DWORD result = aRequestedSize;
+
+    const uint32_t granularity = GetAllocGranularity();
+
+    uint32_t mod = aRequestedSize % granularity;
+    if (mod) {
+      result += (granularity - mod);
+    }
+
+    return result;
+  }
+
+  static DWORD GetAllocGranularity()
+  {
+    static const DWORD kAllocGranularity = []() -> DWORD {
+      SYSTEM_INFO sysInfo;
+      ::GetSystemInfo(&sysInfo);
+      return sysInfo.dwAllocationGranularity;
+    }();
+
+    return kAllocGranularity;
+  }
+
+  static DWORD GetPageSize()
+  {
+    static const DWORD kPageSize = []() -> DWORD {
+      SYSTEM_INFO sysInfo;
+      ::GetSystemInfo(&sysInfo);
+      return sysInfo.dwPageSize;
+    }();
+
+    return kPageSize;
+  }
+};
+
+class MMPolicyInProcess : public MMPolicyBase
+{
+public:
+  typedef MMPolicyInProcess MMPolicyT;
+
+  explicit MMPolicyInProcess()
+    : mBase(nullptr)
+    , mReservationSize(0)
+    , mCommitOffset(0)
+  {
+  }
+
+  MMPolicyInProcess(const MMPolicyInProcess&) = delete;
+  MMPolicyInProcess& operator=(const MMPolicyInProcess&) = delete;
+
+  MMPolicyInProcess(MMPolicyInProcess&& aOther)
+    : mBase(nullptr)
+    , mReservationSize(0)
+    , mCommitOffset(0)
+  {
+    *this = Move(aOther);
+  }
+
+  MMPolicyInProcess& operator=(MMPolicyInProcess&& aOther)
+  {
+    mBase = aOther.mBase;
+    aOther.mBase = nullptr;
+
+    mCommitOffset = aOther.mCommitOffset;
+    aOther.mCommitOffset = 0;
+
+    mReservationSize = aOther.mReservationSize;
+    aOther.mReservationSize = 0;
+
+    return *this;
+  }
+
+  // We always leak mBase
+  ~MMPolicyInProcess() = default;
+
+  explicit operator bool() const
+  {
+    return !!mBase;
+  }
+
+  /**
+   * Should we unhook everything upon destruction?
+   */
+  bool ShouldUnhookUponDestruction() const
+  {
+    return true;
+  }
+
+  bool Read(void* aToPtr, const void* aFromPtr, size_t aLen) const
+  {
+    ::memcpy(aToPtr, aFromPtr, aLen);
+    return true;
+  }
+
+  bool Write(void* aToPtr, const void* aFromPtr, size_t aLen) const
+  {
+    ::memcpy(aToPtr, aFromPtr, aLen);
+    return true;
+  }
+
+  bool Protect(void* aVAddress, size_t aSize, uint32_t aProtFlags,
+               uint32_t* aPrevProtFlags) const
+  {
+    MOZ_ASSERT(aPrevProtFlags);
+    BOOL ok = ::VirtualProtect(aVAddress, aSize, aProtFlags,
+                               reinterpret_cast<PDWORD>(aPrevProtFlags));
+    MOZ_ASSERT(ok);
+    return !!ok;
+  }
+
+  /**
+   * @return true if the page that hosts aVAddress is accessible.
+   */
+  bool IsPageAccessible(void* aVAddress) const
+  {
+    MEMORY_BASIC_INFORMATION mbi;
+    SIZE_T result = ::VirtualQuery(aVAddress, &mbi, sizeof(mbi));
+
+    return result && mbi.AllocationProtect && mbi.Protect != PAGE_NOACCESS &&
+           mbi.State == MEM_COMMIT;
+  }
+
+  bool FlushInstructionCache() const
+  {
+    return !!::FlushInstructionCache(::GetCurrentProcess(), nullptr, 0);
+  }
+
+protected:
+  uint8_t* GetLocalView() const
+  {
+    return mBase;
+  }
+
+  uintptr_t GetRemoteView() const
+  {
+    // Same as local view for in-process
+    return reinterpret_cast<uintptr_t>(mBase);
+  }
+
+  /**
+   * @return the effective number of bytes reserved, or 0 on failure
+   */
+  uint32_t Reserve(const uint32_t aSize)
+  {
+    if (!aSize) {
+      return 0;
+    }
+
+    if (mBase) {
+      MOZ_ASSERT(mReservationSize >= aSize);
+      return mReservationSize;
+    }
+
+    mReservationSize = ComputeAllocationSize(aSize);
+    mBase = static_cast<uint8_t*>(::VirtualAlloc(nullptr, mReservationSize,
+                                                 MEM_RESERVE, PAGE_NOACCESS));
+    if (!mBase) {
+      return 0;
+    }
+    return mReservationSize;
+  }
+
+  bool MaybeCommitNextPage(const uint32_t aRequestedOffset,
+                           const uint32_t aRequestedLength)
+  {
+    if (!(*this)) {
+      return false;
+    }
+
+    uint32_t limit = aRequestedOffset + aRequestedLength - 1;
+    if (limit < mCommitOffset) {
+      // No commit required
+      return true;
+    }
+
+    MOZ_DIAGNOSTIC_ASSERT(mCommitOffset < mReservationSize);
+    if (mCommitOffset >= mReservationSize) {
+      return false;
+    }
+
+    PVOID local = ::VirtualAlloc(mBase + mCommitOffset, GetPageSize(),
+                                 MEM_COMMIT, PAGE_EXECUTE_READ);
+    if (!local) {
+      return false;
+    }
+
+    mCommitOffset += GetPageSize();
+    return true;
+  }
+
+private:
+  uint8_t*  mBase;
+  uint32_t  mReservationSize;
+  uint32_t  mCommitOffset;
+};
+
+} // namespace interceptor
+} // namespace mozilla
+
+#endif // mozilla_interceptor_MMPolicies_h
+
rename from xpcom/build/nsWindowsDllInterceptor.h
rename to mozglue/misc/interceptor/PatcherBase.h
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/mozglue/misc/interceptor/PatcherBase.h
@@ -1,1495 +1,84 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef NS_WINDOWS_DLL_INTERCEPTOR_H_
-#define NS_WINDOWS_DLL_INTERCEPTOR_H_
-
-#include "mozilla/Assertions.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/UniquePtr.h"
-#include "nsWindowsHelpers.h"
-
-#include <wchar.h>
-#include <windows.h>
-#include <winternl.h>
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
-/*
- * Simple function interception.
- *
- * We have two separate mechanisms for intercepting a function: We can use the
- * built-in nop space, if it exists, or we can create a detour.
- *
- * Using the built-in nop space works as follows: On x86-32, DLL functions
- * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of
- * NOP instructions.
- *
- * When we detect a function with this prelude, we do the following:
- *
- * 1. Write a long jump to our interceptor function into the five bytes of NOPs
- *    before the function.
- *
- * 2. Write a short jump -5 into the two-byte nop at the beginning of the function.
- *
- * This mechanism is nice because it's thread-safe.  It's even safe to do if
- * another thread is currently running the function we're modifying!
- *
- * When the WindowsDllNopSpacePatcher is destroyed, we overwrite the short jump
- * but not the long jump, so re-intercepting the same function won't work,
- * because its prelude won't match.
- *
- *
- * Unfortunately nop space patching doesn't work on functions which don't have
- * this magic prelude (and in particular, x86-64 never has the prelude).  So
- * when we can't use the built-in nop space, we fall back to using a detour,
- * which works as follows:
- *
- * 1. Save first N bytes of OrigFunction to trampoline, where N is a
- *    number of bytes >= 5 that are instruction aligned.
- *
- * 2. Replace first 5 bytes of OrigFunction with a jump to the Hook
- *    function.
- *
- * 3. After N bytes of the trampoline, add a jump to OrigFunction+N to
- *    continue original program flow.
- *
- * 4. Hook function needs to call the trampoline during its execution,
- *    to invoke the original function (so address of trampoline is
- *    returned).
- *
- * When the WindowsDllDetourPatcher object is destructed, OrigFunction is
- * patched again to jump directly to the trampoline instead of going through
- * the hook function. As such, re-intercepting the same function won't work, as
- * jump instructions are not supported.
- *
- * Note that this is not thread-safe.  Sad day.
- *
- */
+#ifndef mozilla_interceptor_PatcherBase_h
+#define mozilla_interceptor_PatcherBase_h
 
-#include <stdint.h>
-
-#define COPY_CODES(NBYTES)  do {    \
-  memcpy(&tramp[nTrampBytes], &origBytes[nOrigBytes], NBYTES);    \
-  nOrigBytes += NBYTES;             \
-  nTrampBytes += NBYTES;            \
-} while (0)
+#include "mozilla/interceptor/TargetFunction.h"
 
 namespace mozilla {
-namespace internal {
-
-class AutoVirtualProtect
-{
-public:
-  AutoVirtualProtect(void* aFunc, size_t aSize, DWORD aProtect)
-    : mFunc(aFunc), mSize(aSize), mNewProtect(aProtect), mOldProtect(0),
-      mSuccess(false)
-  {}
-
-  ~AutoVirtualProtect()
-  {
-    if (mSuccess) {
-      VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, mOldProtect,
-                       &mOldProtect);
-    }
-  }
-
-  bool Protect()
-  {
-    mSuccess = !!VirtualProtectEx(GetCurrentProcess(), mFunc, mSize,
-                                  mNewProtect, &mOldProtect);
-    if (!mSuccess) {
-      // printf("VirtualProtectEx failed! %d\n", GetLastError());
-    }
-    return mSuccess;
-  }
-
-private:
-  void* const mFunc;
-  size_t const mSize;
-  DWORD const mNewProtect;
-  DWORD mOldProtect;
-  bool mSuccess;
-};
-
-class WindowsDllNopSpacePatcher
-{
-  typedef uint8_t* byteptr_t;
-  HMODULE mModule;
-
-  // Dumb array for remembering the addresses of functions we've patched.
-  // (This should be nsTArray, but non-XPCOM code uses this class.)
-  static const size_t maxPatchedFns = 16;
-  byteptr_t mPatchedFns[maxPatchedFns];
-  size_t mPatchedFnsLen;
-
-public:
-  WindowsDllNopSpacePatcher()
-    : mModule(0)
-    , mPatchedFnsLen(0)
-  {}
-
-#if defined(_M_IX86)
-  ~WindowsDllNopSpacePatcher()
-  {
-    // Restore the mov edi, edi to the beginning of each function we patched.
-
-    for (size_t i = 0; i < mPatchedFnsLen; i++) {
-      byteptr_t fn = mPatchedFns[i];
-
-      // Ensure we can write to the code.
-      AutoVirtualProtect protect(fn, 2, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
-        continue;
-      }
-
-      // mov edi, edi
-      *((uint16_t*)fn) = 0xff8b;
-
-      // I don't think this is actually necessary, but it can't hurt.
-      FlushInstructionCache(GetCurrentProcess(),
-                            /* ignored */ nullptr,
-                            /* ignored */ 0);
-    }
-  }
-
-  void Init(const char* aModuleName)
-  {
-    if (!IsCompatible()) {
-#if defined(MOZILLA_INTERNAL_API)
-      NS_WARNING("NOP space patching is unavailable for compatibility reasons");
-#endif
-      return;
-    }
-
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
-      return;
-    }
-  }
+namespace interceptor {
 
-  /**
-   * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions
-   * in our address space. There is a bug in Detours 2.x that causes it to
-   * patch at the wrong address when attempting to detour code that is already
-   * NOP space patched. This function is an effort to detect the presence of
-   * this NVIDIA code in our address space and disable NOP space patching if it
-   * is. We also check AppInit_DLLs since this is the mechanism that the Optimus
-   * drivers use to inject into our process.
-   */
-  static bool IsCompatible()
-  {
-    // These DLLs are known to have bad interactions with this style of patching
-    const wchar_t* kIncompatibleDLLs[] = {
-      L"detoured.dll",
-      L"_etoured.dll",
-      L"nvd3d9wrap.dll",
-      L"nvdxgiwrap.dll"
-    };
-    // See if the infringing DLLs are already loaded
-    for (unsigned int i = 0; i < mozilla::ArrayLength(kIncompatibleDLLs); ++i) {
-      if (GetModuleHandleW(kIncompatibleDLLs[i])) {
-        return false;
-      }
-    }
-    if (GetModuleHandleW(L"user32.dll")) {
-      // user32 is loaded but the infringing DLLs are not, assume we're safe to
-      // proceed.
-      return true;
-    }
-    // If user32 has not loaded yet, check AppInit_DLLs to ensure that Optimus
-    // won't be loaded once user32 is initialized.
-    HKEY hkey = NULL;
-    if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
-          0, KEY_QUERY_VALUE, &hkey)) {
-      nsAutoRegKey key(hkey);
-      DWORD numBytes = 0;
-      const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
-      // Query for required buffer size
-      LONG status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
-                                     nullptr, nullptr, &numBytes);
-      mozilla::UniquePtr<wchar_t[]> data;
-      if (!status) {
-        // Allocate the buffer and query for the actual data
-        data = mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
-        status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
-                                  nullptr, (LPBYTE)data.get(), &numBytes);
-      }
-      if (!status) {
-        // For each token, split up the filename components and then check the
-        // name of the file.
-        const wchar_t kDelimiters[] = L", ";
-        wchar_t* tokenContext = nullptr;
-        wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
-        while (token) {
-          wchar_t fname[_MAX_FNAME] = {0};
-          if (!_wsplitpath_s(token, nullptr, 0, nullptr, 0,
-                             fname, mozilla::ArrayLength(fname),
-                             nullptr, 0)) {
-            // nvinit.dll is responsible for bootstrapping the DLL injection, so
-            // that is the library that we check for here
-            const wchar_t kNvInitName[] = L"nvinit";
-            if (!_wcsnicmp(fname, kNvInitName,
-                           mozilla::ArrayLength(kNvInitName))) {
-              return false;
-            }
-          }
-          token = wcstok_s(nullptr, kDelimiters, &tokenContext);
-        }
-      }
-    }
-    return true;
-  }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    if (!mModule) {
-      return false;
-    }
-
-    if (!IsCompatible()) {
-#if defined(MOZILLA_INTERNAL_API)
-      NS_WARNING("NOP space patching is unavailable for compatibility reasons");
-#endif
-      return false;
-    }
-
-    MOZ_RELEASE_ASSERT(mPatchedFnsLen < maxPatchedFns, "No room for the hook");
-
-    byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName));
-    if (!fn) {
-      //printf ("GetProcAddress failed\n");
-      return false;
-    }
-
-    fn = ResolveRedirectedAddress(fn);
-
-    // Ensure we can read and write starting at fn - 5 (for the long jmp we're
-    // going to write) and ending at fn + 2 (for the short jmp up to the long
-    // jmp). These bytes may span two pages with different protection.
-    AutoVirtualProtect protectBefore(fn - 5, 5, PAGE_EXECUTE_READWRITE);
-    AutoVirtualProtect protectAfter(fn, 2, PAGE_EXECUTE_READWRITE);
-    if (!protectBefore.Protect() || !protectAfter.Protect()) {
-      return false;
-    }
+template <typename VMPolicy>
+class WindowsDllPatcherBase
+{
+protected:
+  typedef typename VMPolicy::MMPolicyT MMPolicyT;
 
-    bool rv = WriteHook(fn, aHookDest, aOrigFunc);
-
-    if (rv) {
-      mPatchedFns[mPatchedFnsLen] = fn;
-      mPatchedFnsLen++;
-    }
-
-    return rv;
-  }
-
-  bool WriteHook(byteptr_t aFn, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Check that the 5 bytes before aFn are NOP's or INT 3's,
-    // and that the 2 bytes after aFn are mov(edi, edi).
-    //
-    // It's safe to read aFn[-5] because we set it to PAGE_EXECUTE_READWRITE
-    // before calling WriteHook.
-
-    for (int i = -5; i <= -1; i++) {
-      if (aFn[i] != 0x90 && aFn[i] != 0xcc) { // nop or int 3
-        return false;
-      }
-    }
-
-    // mov edi, edi.  Yes, there are two ways to encode the same thing:
-    //
-    //   0x89ff == mov r/m, r
-    //   0x8bff == mov r, r/m
-    //
-    // where "r" is register and "r/m" is register or memory.  Windows seems to
-    // use 8bff; I include 89ff out of paranoia.
-    if ((aFn[0] != 0x8b && aFn[0] != 0x89) || aFn[1] != 0xff) {
-      return false;
-    }
-
-    // Write a long jump into the space above the function.
-    aFn[-5] = 0xe9; // jmp
-    *((intptr_t*)(aFn - 4)) = aHookDest - (uintptr_t)(aFn); // target displacement
-
-    // Set aOrigFunc here, because after this point, aHookDest might be called,
-    // and aHookDest might use the aOrigFunc pointer.
-    *aOrigFunc = aFn + 2;
-
-    // Short jump up into our long jump.
-    *((uint16_t*)(aFn)) = 0xf9eb; // jmp $-5
-
-    // I think this routine is safe without this, but it can't hurt.
-    FlushInstructionCache(GetCurrentProcess(),
-                          /* ignored */ nullptr,
-                          /* ignored */ 0);
-
-    return true;
-  }
-
-private:
-  static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
-    // If function entry is jmp rel8 stub to the internal implementation, we
-    // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
-      if (offset <= 0) {
-        // Bail out for negative offset: probably already patched by some
-        // third-party code.
-        return aOriginalFunction;
-      }
-
-      for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
-          // Bail out on insufficient nop space.
-          return aOriginalFunction;
-        }
-      }
-
-      return aOriginalFunction + 2 + offset;
-    }
-
-    // If function entry is jmp [disp32] such as used by kernel32,
-    // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2)));
-    }
-
-    return aOriginalFunction;
-  }
-#else
-  void Init(const char* aModuleName)
-  {
-    // Not implemented except on x86-32.
-  }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Not implemented except on x86-32.
-    return false;
-  }
-#endif
-};
-
-class WindowsDllDetourPatcher
-{
-  typedef unsigned char* byteptr_t;
-public:
-  WindowsDllDetourPatcher()
-    : mModule(0), mHookPage(0), mMaxHooks(0), mCurHooks(0)
+  template <typename... Args>
+  explicit WindowsDllPatcherBase(Args... aArgs)
+    : mVMPolicy(mozilla::Forward<Args>(aArgs)...)
   {
   }
 
-  ~WindowsDllDetourPatcher()
-  {
-    int i;
-    byteptr_t p;
-    for (i = 0, p = mHookPage; i < mCurHooks; i++, p += kHookSize) {
-#if defined(_M_IX86)
-      size_t nBytes = 1 + sizeof(intptr_t);
-#elif defined(_M_X64)
-      size_t nBytes = 2 + sizeof(intptr_t);
-#else
-#error "Unknown processor type"
-#endif
-      byteptr_t origBytes = (byteptr_t)DecodePointer(*((byteptr_t*)p));
-
-      // ensure we can modify the original code
-      AutoVirtualProtect protect(origBytes, nBytes, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
-        continue;
-      }
-
-      // Remove the hook by making the original function jump directly
-      // in the trampoline.
-      intptr_t dest = (intptr_t)(p + sizeof(void*));
-#if defined(_M_IX86)
-      // Ensure the JMP from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0xE9)
-        continue;
-      *((intptr_t*)(origBytes + 1)) =
-        dest - (intptr_t)(origBytes + 5); // target displacement
-#elif defined(_M_X64)
-      // Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0x49 || origBytes[1] != 0xBB)
-        continue;
-      *((intptr_t*)(origBytes + 2)) = dest;
-#else
-#error "Unknown processor type"
-#endif
-    }
-  }
-
-  void Init(const char* aModuleName, int aNumHooks = 0)
-  {
-    if (mModule) {
-      return;
-    }
-
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
-      return;
-    }
-
-    int hooksPerPage = 4096 / kHookSize;
-    if (aNumHooks == 0) {
-      aNumHooks = hooksPerPage;
-    }
-
-    mMaxHooks = aNumHooks + (hooksPerPage % aNumHooks);
-
-    mHookPage = (byteptr_t)VirtualAllocEx(GetCurrentProcess(), nullptr,
-                                          mMaxHooks * kHookSize,
-                                          MEM_COMMIT | MEM_RESERVE,
-                                          PAGE_EXECUTE_READ);
-    if (!mHookPage) {
-      mModule = 0;
-      return;
-    }
-  }
-
-  bool Initialized() { return !!mModule; }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    if (!mModule) {
-      return false;
-    }
-
-    void* pAddr = (void*)GetProcAddress(mModule, aName);
-    if (!pAddr) {
-      //printf ("GetProcAddress failed\n");
-      return false;
-    }
-
-    pAddr = ResolveRedirectedAddress((byteptr_t)pAddr);
-
-    CreateTrampoline(pAddr, aHookDest, aOrigFunc);
-    if (!*aOrigFunc) {
-      //printf ("CreateTrampoline failed\n");
-      return false;
-    }
-
-    return true;
-  }
-
-protected:
-  const static int kPageSize = 4096;
-  const static int kHookSize = 128;
-
-  HMODULE mModule;
-  byteptr_t mHookPage;
-  int mMaxHooks;
-  int mCurHooks;
-
-  // rex bits
-  static const BYTE kMaskHighNibble = 0xF0;
-  static const BYTE kRexOpcode = 0x40;
-  static const BYTE kMaskRexW = 0x08;
-  static const BYTE kMaskRexR = 0x04;
-  static const BYTE kMaskRexX = 0x02;
-  static const BYTE kMaskRexB = 0x01;
-
-  // mod r/m bits
-  static const BYTE kRegFieldShift = 3;
-  static const BYTE kMaskMod = 0xC0;
-  static const BYTE kMaskReg = 0x38;
-  static const BYTE kMaskRm = 0x07;
-  static const BYTE kRmNeedSib = 0x04;
-  static const BYTE kModReg = 0xC0;
-  static const BYTE kModDisp32 = 0x80;
-  static const BYTE kModDisp8 = 0x40;
-  static const BYTE kModNoRegDisp = 0x00;
-  static const BYTE kRmNoRegDispDisp32 = 0x05;
-
-  // sib bits
-  static const BYTE kMaskSibScale = 0xC0;
-  static const BYTE kMaskSibIndex = 0x38;
-  static const BYTE kMaskSibBase = 0x07;
-  static const BYTE kSibBaseEbp = 0x05;
-
-  // Register bit IDs.
-  static const BYTE kRegAx = 0x0;
-  static const BYTE kRegCx = 0x1;
-  static const BYTE kRegDx = 0x2;
-  static const BYTE kRegBx = 0x3;
-  static const BYTE kRegSp = 0x4;
-  static const BYTE kRegBp = 0x5;
-  static const BYTE kRegSi = 0x6;
-  static const BYTE kRegDi = 0x7;
-
-  // Special ModR/M codes.  These indicate operands that cannot be simply
-  // memcpy-ed.
-  // Operand is a 64-bit RIP-relative address.
-  static const int kModOperand64 = -2;
-  // Operand is not yet handled by our trampoline.
-  static const int kModUnknown = -1;
-
-  /**
-   * Returns the number of bytes taken by the ModR/M byte, SIB (if present)
-   * and the instruction's operand.  In special cases, the special MODRM codes
-   * above are returned.
-   * aModRm points to the ModR/M byte of the instruction.
-   * On return, aSubOpcode (if present) is filled with the subopcode/register
-   * code found in the ModR/M byte.
-   */
-  int CountModRmSib(const BYTE *aModRm, BYTE* aSubOpcode = nullptr)
-  {
-    if (!aModRm) {
-      MOZ_ASSERT(aModRm, "Missing ModRM byte");
-      return kModUnknown;
-    }
-    int numBytes = 1; // Start with 1 for mod r/m byte itself
-    switch (*aModRm & kMaskMod) {
-      case kModReg:
-        return numBytes;
-      case kModDisp8:
-        numBytes += 1;
-        break;
-      case kModDisp32:
-        numBytes += 4;
-        break;
-      case kModNoRegDisp:
-        if ((*aModRm & kMaskRm) == kRmNoRegDispDisp32) {
-#if defined(_M_X64)
-          if (aSubOpcode) {
-            *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
-          }
-          return kModOperand64;
-#else
-          // On IA-32, all ModR/M instruction modes address memory relative to 0
-          numBytes += 4;
-#endif
-        } else if (((*aModRm & kMaskRm) == kRmNeedSib &&
-             (*(aModRm + 1) & kMaskSibBase) == kSibBaseEbp)) {
-          numBytes += 4;
-        }
-        break;
-      default:
-        // This should not be reachable
-        MOZ_ASSERT_UNREACHABLE("Impossible value for modr/m byte mod bits");
-        return kModUnknown;
-    }
-    if ((*aModRm & kMaskRm) == kRmNeedSib) {
-      // SIB byte
-      numBytes += 1;
-    }
-    if (aSubOpcode) {
-      *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
-    }
-    return numBytes;
-  }
-
-#if defined(_M_X64)
-  // To patch for JMP and JE
-
-  enum JumpType {
-   Je,
-   Jne,
-   Jmp,
-   Call
-  };
-
-  struct JumpPatch {
-    JumpPatch()
-      : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp)
-    {
-    }
-
-    JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp)
-      : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType)
-    {
-    }
-
-    size_t GenerateJump(uint8_t* aCode)
-    {
-      size_t offset = mHookOffset;
-      if (mType == JumpType::Je) {
-        // JNE RIP+14
-        aCode[offset]     = 0x75;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      } else if (mType == JumpType::Jne) {
-        // JE RIP+14
-        aCode[offset]     = 0x74;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      }
-
-      // Near call/jmp, absolute indirect, address given in r/m32
-      if (mType == JumpType::Call) {
-        // CALL [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x15;
-        // The offset to jump destination -- ie it is placed 2 bytes after the offset.
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 2;
-        aCode[offset + 2 + 4] = 0xeb;    // JMP +8 (jump over mJumpAddress)
-        aCode[offset + 2 + 4 + 1] = 8;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4 + 2) = mJumpAddress;
-        return offset + 2 + 4 + 2 + 8;
-      } else {
-        // JMP [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x25;
-        // The offset to jump destination is 0
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress;
-        return offset + 2 + 4 + 8;
-      }
-    }
-
-    size_t mHookOffset;
-    intptr_t mJumpAddress;
-    JumpType mType;
-  };
-
-#endif
-
-  enum ePrefixGroupBits
-  {
-    eNoPrefixes = 0,
-    ePrefixGroup1 = (1 << 0),
-    ePrefixGroup2 = (1 << 1),
-    ePrefixGroup3 = (1 << 2),
-    ePrefixGroup4 = (1 << 3)
-  };
-
-  int CountPrefixBytes(byteptr_t aBytes, const int aBytesIndex,
-                       unsigned char* aOutGroupBits)
-  {
-    unsigned char& groupBits = *aOutGroupBits;
-    groupBits = eNoPrefixes;
-    int index = aBytesIndex;
-    while (true) {
-      switch (aBytes[index]) {
-        // Group 1
-        case 0xF0: // LOCK
-        case 0xF2: // REPNZ
-        case 0xF3: // REP / REPZ
-          if (groupBits & ePrefixGroup1) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup1;
-          ++index;
-          break;
-
-        // Group 2
-        case 0x2E: // CS override / branch not taken
-        case 0x36: // SS override
-        case 0x3E: // DS override / branch taken
-        case 0x64: // FS override
-        case 0x65: // GS override
-          if (groupBits & ePrefixGroup2) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup2;
-          ++index;
-          break;
-
-        // Group 3
-        case 0x66: // operand size override
-          if (groupBits & ePrefixGroup3) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup3;
-          ++index;
-          break;
-
-        // Group 4
-        case 0x67: // Address size override
-          if (groupBits & ePrefixGroup4) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup4;
-          ++index;
-          break;
-
-        default:
-          return index - aBytesIndex;
-      }
-    }
-  }
-
-  // Return a ModR/M byte made from the 2 Mod bits, the register used for the
-  // reg bits and the register used for the R/M bits.
-  BYTE BuildModRmByte(BYTE aModBits, BYTE aReg, BYTE aRm)
-  {
-    MOZ_ASSERT((aRm & kMaskRm) == aRm);
-    MOZ_ASSERT((aModBits & kMaskMod) == aModBits);
-    MOZ_ASSERT(((aReg << kRegFieldShift) & kMaskReg) == (aReg << kRegFieldShift));
-    return aModBits | (aReg << kRegFieldShift) | aRm;
-  }
-
-  void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp)
+  ReadOnlyTargetFunction<MMPolicyT>
+  ResolveRedirectedAddress(const void* aOriginalFunction)
   {
-    *aOutTramp = nullptr;
-
-    AutoVirtualProtect protectHookPage(mHookPage, mMaxHooks * kHookSize,
-                                       PAGE_EXECUTE_READWRITE);
-    if (!protectHookPage.Protect()) {
-      return;
-    }
-
-    byteptr_t tramp = FindTrampolineSpace();
-    if (!tramp) {
-      return;
-    }
-
-    // We keep the address of the original function in the first bytes of
-    // the trampoline buffer
-    *((void**)tramp) = EncodePointer(aOrigFunction);
-    tramp += sizeof(void*);
-
-    byteptr_t origBytes = (byteptr_t)aOrigFunction;
-
-    // # of bytes of the original function that we can overwrite.
-    int nOrigBytes = 0;
-
-#if defined(_M_IX86)
-    int pJmp32 = -1;
-    while (nOrigBytes < 5) {
-      // Understand some simple instructions that might be found in a
-      // prologue; we might need to extend this as necessary.
-      //
-      // Note!  If we ever need to understand jump instructions, we'll
-      // need to rewrite the displacement argument.
-      unsigned char prefixGroups;
-      int numPrefixBytes = CountPrefixBytes(origBytes, nOrigBytes, &prefixGroups);
-      if (numPrefixBytes < 0 || (prefixGroups & (ePrefixGroup3 | ePrefixGroup4))) {
-        // Either the prefix sequence was bad, or there are prefixes that
-        // we don't currently support (groups 3 and 4)
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-      nOrigBytes += numPrefixBytes;
-      if (origBytes[nOrigBytes] >= 0x88 &&
-          origBytes[nOrigBytes] <= 0x8B) {
-        // various MOVs
-        ++nOrigBytes;
-        int len = CountModRmSib(origBytes + nOrigBytes);
-        if (len < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-          return;
-        }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0x0f &&
-                 (origBytes[nOrigBytes + 1] == 0x10 ||
-                  origBytes[nOrigBytes + 1] == 0x11)) {
-        // SSE: movups xmm, xmm/m128
-        //      movups xmm/m128, xmm
-        nOrigBytes += 2;
-        int len = CountModRmSib(origBytes + nOrigBytes);
-        if (len < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-          return;
-        }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0xA1) {
-        // MOV eax, [seg:offset]
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xB8) {
-        // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x33 &&
-                 (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
-        // XOR r32, r32
-        nOrigBytes += 2;
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0x40) {
-        // INC r32
-        nOrigBytes += 1;
-      } else if (origBytes[nOrigBytes] == 0x83) {
-        // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r/m, imm8
-        unsigned char b = origBytes[nOrigBytes + 1];
-        if ((b & 0xc0) == 0xc0) {
-          // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r, imm8
-          nOrigBytes += 3;
-        } else {
-          // bail
-          MOZ_ASSERT_UNREACHABLE("Unrecognized bit opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x68) {
-        // PUSH with 4-byte operand
-        nOrigBytes += 5;
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-        // 1-byte PUSH/POP
-        nOrigBytes++;
-      } else if (origBytes[nOrigBytes] == 0x6A) {
-        // PUSH imm8
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xe9) {
-        pJmp32 = nOrigBytes;
-        // jmp 32bit offset
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xff &&
-                 origBytes[nOrigBytes + 1] == 0x25) {
-        // jmp [disp32]
-        nOrigBytes += 6;
-      } else if (origBytes[nOrigBytes] == 0xc2) {
-        // ret imm16.  We can't handle this but it happens.  We don't ASSERT but we do fail to hook.
-#if defined(MOZILLA_INTERNAL_API)
-        NS_WARNING("Cannot hook method -- RET opcode found");
-#endif
-        return;
-      } else {
-        //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nOrigBytes]);
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-    }
-
-    // The trampoline is a copy of the instructions that we just traced,
-    // followed by a jump that we add below.
-    memcpy(tramp, aOrigFunction, nOrigBytes);
-#elif defined(_M_X64)
-    // The number of bytes used by the trampoline.
-    int nTrampBytes = 0;
-    bool foundJmp = false;
-
-    while (nOrigBytes < 13) {
-      // If we found JMP 32bit offset, we require that the next bytes must
-      // be NOP or INT3.  There is no reason to copy them.
-      // TODO: This used to trigger for Je as well.  Now that I allow
-      // instructions after CALL and JE, I don't think I need that.
-      // The only real value of this condition is that if code follows a JMP
-      // then its _probably_ the target of a JMP somewhere else and we
-      // will be overwriting it, which would be tragic.  This seems
-      // highly unlikely.
-      if (foundJmp) {
-        if (origBytes[nOrigBytes] == 0x90 || origBytes[nOrigBytes] == 0xcc) {
-          nOrigBytes++;
-          continue;
-        }
-        MOZ_ASSERT_UNREACHABLE("Opcode sequence includes commands after JMP");
-        return;
-      }
-      if (origBytes[nOrigBytes] == 0x0f) {
-        COPY_CODES(1);
-        if (origBytes[nOrigBytes] == 0x1f) {
-          // nop (multibyte)
-          COPY_CODES(1);
-          if ((origBytes[nOrigBytes] & 0xc0) == 0x40 &&
-              (origBytes[nOrigBytes] & 0x7) == 0x04) {
-            COPY_CODES(3);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x05) {
-          // syscall
-          COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] == 0x10 ||
-                   origBytes[nOrigBytes] == 0x11) {
-          // SSE: movups xmm, xmm/m128
-          //      movups xmm/m128, xmm
-          COPY_CODES(1);
-          int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes]);
-          if (nModRmSibBytes < 0) {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          } else {
-            COPY_CODES(nModRmSibBytes);
-          }
-        } else if (origBytes[nOrigBytes] == 0x84) {
-          // je rel32
-          JumpPatch jump(nTrampBytes - 1,  // overwrite the 0x0f we copied above
-                          (intptr_t)(origBytes + nOrigBytes + 5 +
-                                     *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                          JumpType::Je);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 5;
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x40 ||
-                 origBytes[nOrigBytes] == 0x41) {
-        // Plain REX or REX.B
-        COPY_CODES(1);
-        if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-          // push/pop with Rx register
-          COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] >= 0xb8 && origBytes[nOrigBytes] <= 0xbf) {
-          // mov r32, imm32
-          COPY_CODES(5);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x44) {
-        // REX.R
-        COPY_CODES(1);
-
-        // TODO: Combine with the "0x89" case below in the REX.W section
-        if (origBytes[nOrigBytes] == 0x89) {
-          // mov r/m32, r32
-          COPY_CODES(1);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-       } else if (origBytes[nOrigBytes] == 0x45) {
-        // REX.R & REX.B
-        COPY_CODES(1);
-
-        if (origBytes[nOrigBytes] == 0x33) {
-          // xor r32, r32
-          COPY_CODES(2);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if ((origBytes[nOrigBytes] & 0xfa) == 0x48) {
-        // REX.W | REX.WR | REX.WRB | REX.WB
-        COPY_CODES(1);
-
-        if (origBytes[nOrigBytes] == 0x81 &&
-            (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
-          // sub r, dword
-          COPY_CODES(6);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
-          // sub r, byte
-          COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == kModReg) {
-          // add r, byte
-          COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
-          // and [r+d], imm8
-          COPY_CODES(5);
-        } else if (origBytes[nOrigBytes] == 0x2b &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
-          // sub r64, r64
-          COPY_CODES(2);
-        } else if (origBytes[nOrigBytes] == 0x85) {
-          // 85 /r => TEST r/m32, r32
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0xc0) {
-            COPY_CODES(2);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if ((origBytes[nOrigBytes] & 0xfd) == 0x89) {
-          // MOV r/m64, r64 | MOV r64, r/m64
-          BYTE reg;
-          int len = CountModRmSib(origBytes + nOrigBytes + 1, &reg);
-          if (len < 0) {
-            MOZ_ASSERT(len == kModOperand64);
-            if (len != kModOperand64) {
-              return;
-            }
-            nOrigBytes += 2;   // skip the MOV and MOD R/M bytes
-
-            // The instruction MOVs 64-bit data from a RIP-relative memory
-            // address (determined with a 32-bit offset from RIP) into a
-            // 64-bit register.
-            int64_t* absAddr =
-              reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 4 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes));
-            nOrigBytes += 4;
-
-            if (reg == kRegAx) {
-              // Destination is RAX.  Encode instruction as MOVABS with a
-              // 64-bit absolute address as its immediate operand.
-              tramp[nTrampBytes] = 0xa1;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
-            } else {
-              // The MOV must be done in two steps.  First, we MOVABS the
-              // absolute 64-bit address into our target register.
-              // Then, we MOV from that address into the register
-              // using register-indirect addressing.
-              tramp[nTrampBytes] = 0xb8 + reg;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
-              tramp[nTrampBytes] = 0x48;
-              tramp[nTrampBytes+1] = 0x8b;
-              tramp[nTrampBytes+2] = BuildModRmByte(kModNoRegDisp, reg, reg);
-              nTrampBytes += 3;
-            }
-          } else {
-            COPY_CODES(len+1);
-          }
-        } else if (origBytes[nOrigBytes] == 0xc7) {
-          // MOV r/m64, imm32
-          if (origBytes[nOrigBytes + 1] == 0x44) {
-            // MOV [r64+disp8], imm32
-            // ModR/W + SIB + disp8 + imm32
-            COPY_CODES(8);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0xff) {
-          // JMP /4
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & 0x07) == 0x5) {
-            // [rip+disp32]
-            // convert JMP 32bit offset to JMP 64bit direct
-            JumpPatch jump(nTrampBytes - 1,  // overwrite the REX.W/REX.WR we copied above
-                           *reinterpret_cast<intptr_t*>(origBytes + nOrigBytes + 6 +
-                                                        *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2)),
-                           JumpType::Jmp);
-            nTrampBytes = jump.GenerateJump(tramp);
-            nOrigBytes += 6;
-            foundJmp = true;
-          } else {
-            // not support yet!
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x8d) {
-          // LEA reg, addr
-          if ((origBytes[nOrigBytes + 1] & kMaskMod) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & kMaskRm) == 0x5) {
-            // [rip+disp32]
-            // convert 32bit offset to 64bit direct and convert instruction
-            // to a simple 64-bit mov
-            BYTE reg = (origBytes[nOrigBytes + 1] & kMaskReg) >> kRegFieldShift;
-            intptr_t absAddr =
-              reinterpret_cast<intptr_t>(origBytes + nOrigBytes + 6 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-            nOrigBytes += 6;
-            tramp[nTrampBytes] = 0xb8 + reg;    // mov
-            ++nTrampBytes;
-            intptr_t* trampOperandPtr = reinterpret_cast<intptr_t*>(tramp + nTrampBytes);
-            *trampOperandPtr = absAddr;
-            nTrampBytes += 8;
-          } else {
-            // Above we dealt with RIP-relative instructions.  Any other
-            // operand form can simply be copied.
-            int len = CountModRmSib(origBytes + nOrigBytes + 1);
-            // We handled the kModOperand64 -- ie RIP-relative -- case above
-            MOZ_ASSERT(len > 0);
-            COPY_CODES(len + 1);
-          }
-        } else if (origBytes[nOrigBytes] == 0x63 &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
-          // movsxd r64, r32 (move + sign extend)
-          COPY_CODES(2);
-        } else {
-          // not support yet!
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x66) {
-        // operand override prefix
-        COPY_CODES(1);
-        // This is the same as the x86 version
-        if (origBytes[nOrigBytes] >= 0x88 && origBytes[nOrigBytes] <= 0x8B) {
-          // various MOVs
-          unsigned char b = origBytes[nOrigBytes + 1];
-          if (((b & 0xc0) == 0xc0) ||
-              (((b & 0xc0) == 0x00) &&
-               ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05))) {
-            // REG=r, R/M=r or REG=r, R/M=[r]
-            COPY_CODES(2);
-          } else if ((b & 0xc0) == 0x40) {
-            if ((b & 0x07) == 0x04) {
-              // REG=r, R/M=[SIB + disp8]
-              COPY_CODES(4);
-            } else {
-              // REG=r, R/M=[r + disp8]
-              COPY_CODES(3);
-            }
-          } else {
-            // complex MOV, bail
-            MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x44 &&
-                   origBytes[nOrigBytes+1] == 0x89) {
-          // mov word ptr [reg+disp8], reg
-          COPY_CODES(2);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            // no way to support this yet.
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        }
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-        // 1-byte push/pop
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0x65) {
-        // GS prefix
-        //
-        // The entry of GetKeyState on Windows 10 has the following code.
-        // 65 48 8b 04 25 30 00 00 00    mov   rax,qword ptr gs:[30h]
-        // (GS prefix + REX + MOV (0x8b) ...)
-        if (origBytes[nOrigBytes + 1] == 0x48 &&
-            (origBytes[nOrigBytes + 2] >= 0x88 && origBytes[nOrigBytes + 2] <= 0x8b)) {
-          COPY_CODES(3);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            // no way to support this yet.
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x80 &&
-                 origBytes[nOrigBytes + 1] == 0x3d) {
-        // cmp byte ptr [rip-relative address], imm8
-        // We'll compute the absolute address and do the cmp in r11
-
-        // push r11 (to save the old value)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x53;
-        ++nTrampBytes;
-
-        byteptr_t absAddr =
-          reinterpret_cast<byteptr_t>(origBytes + nOrigBytes + 7 +
-                                      *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-        nOrigBytes += 6;
-
-        // mov r11, absolute address
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0xbb;
-        ++nTrampBytes;
-
-        *reinterpret_cast<byteptr_t*>(tramp + nTrampBytes) = absAddr;
-        nTrampBytes += 8;
-
-        // cmp byte ptr [r11],...
-        tramp[nTrampBytes] = 0x41;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x80;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x3b;
-        ++nTrampBytes;
-
-        // ...imm8
-        COPY_CODES(1);
-
-        // pop r11 (doesn't affect the flags from the cmp)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x5b;
-        ++nTrampBytes;
-      } else if (origBytes[nOrigBytes] == 0x90) {
-        // nop
-        COPY_CODES(1);
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0xb8) {
-        // MOV r32, imm32
-        COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0x33) {
-        // xor r32, r/m32
-        COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xf6) {
-        // test r/m8, imm8 (used by ntdll on Windows 10 x64)
-        // (no flags are affected by near jmp since there is no task switch,
-        // so it is ok for a jmp to be written immediately after a test)
-        BYTE subOpcode = 0;
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1], &subOpcode);
-        if (nModRmSibBytes < 0 || subOpcode != 0) {
-          // Unsupported
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(2 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0x85) {
-        // test r/m32, r32
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1]);
-        if (nModRmSibBytes < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(1 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0xd1 &&
-                  (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
-        // bit shifts/rotates : (SA|SH|RO|RC)(R|L) r32
-        // (e.g. 0xd1 0xe0 is SAL, 0xd1 0xc8 is ROR)
-        COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xc3) {
-        // ret
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xcc) {
-        // int 3
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xe8 ||
-                 origBytes[nOrigBytes] == 0xe9) {
-        // CALL (0xe8) or JMP (0xe9) 32bit offset
-        foundJmp = origBytes[nOrigBytes] == 0xe9;
-        JumpPatch jump(nTrampBytes,
-                       (intptr_t)(origBytes + nOrigBytes + 5 +
-                                  *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                       origBytes[nOrigBytes] == 0xe8 ? JumpType::Call : JumpType::Jmp);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x74 || // je rel8 (0x74)
-                 origBytes[nOrigBytes] == 0x75) { // jne rel8 (0x75)
-        char offset = origBytes[nOrigBytes + 1];
-        auto jumpType = JumpType::Je;
-        if (origBytes[nOrigBytes] == 0x75)
-          jumpType = JumpType::Jne;
-        JumpPatch jump(nTrampBytes,
-          (intptr_t)(origBytes + nOrigBytes + 2 + offset), jumpType);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xff) {
-        if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == 0xf0) {
-          // push r64
-          COPY_CODES(2);
-        } else if (origBytes[nOrigBytes + 1] == 0x25) {
-          // jmp absolute indirect m32
-          foundJmp = true;
-          int32_t offset = *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-          int64_t* ptrToJmpDest = reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 6 + offset);
-          intptr_t jmpDest = static_cast<intptr_t>(*ptrToJmpDest);
-          JumpPatch jump(nTrampBytes, jmpDest, JumpType::Jmp);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 6;
-        } else if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == BuildModRmByte(kModReg, 2, 0)) {
-          // CALL reg (ff nn)
-          COPY_CODES(2);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x83 &&
-                 (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
-        // and [r+d], imm8
-        COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0xc6) {
-        // mov [r+d], imm8
-        int len = CountModRmSib(&origBytes[nOrigBytes + 1]);
-        if (len < 0) {
-          // RIP-relative not yet supported
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(len + 1);
-      } else {
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-    }
-#else
-#error "Unknown processor type"
-#endif
-
-    if (nOrigBytes > 100) {
-      //printf ("Too big!");
-      return;
-    }
-
-    // target address of the final jmp instruction in the trampoline
-    byteptr_t trampDest = origBytes + nOrigBytes;
-
-#if defined(_M_IX86)
-    if (pJmp32 >= 0) {
-      // Jump directly to the original target of the jump instead of jumping to the
-      // original function.
-      // Adjust jump target displacement to jump location in the trampoline.
-      *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp;
-    } else {
-      tramp[nOrigBytes] = 0xE9; // jmp
-      *((intptr_t*)(tramp + nOrigBytes + 1)) =
-        (intptr_t)trampDest - (intptr_t)(tramp + nOrigBytes + 5); // target displacement
-    }
-#elif defined(_M_X64)
-    // If the we found a Jmp, we don't need to add another instruction. However,
-    // if we found a _conditional_ jump or a CALL (or no control operations
-    // at all) then we still need to run the rest of aOriginalFunction.
-    if (!foundJmp) {
-      JumpPatch patch(nTrampBytes, reinterpret_cast<intptr_t>(trampDest));
-      patch.GenerateJump(tramp);
-    }
-#endif
-
-    // The trampoline is now valid.
-    *aOutTramp = tramp;
-
-    // ensure we can modify the original code
-    AutoVirtualProtect protect(aOrigFunction, nOrigBytes, PAGE_EXECUTE_READWRITE);
-    if (!protect.Protect()) {
-      return;
-    }
-
-#if defined(_M_IX86)
-    // now modify the original bytes
-    origBytes[0] = 0xE9; // jmp
-    *((intptr_t*)(origBytes + 1)) =
-      aDest - (intptr_t)(origBytes + 5); // target displacement
-#elif defined(_M_X64)
-    // mov r11, address
-    origBytes[0] = 0x49;
-    origBytes[1] = 0xbb;
-
-    *((intptr_t*)(origBytes + 2)) = aDest;
-
-    // jmp r11
-    origBytes[10] = 0x41;
-    origBytes[11] = 0xff;
-    origBytes[12] = 0xe3;
-#endif
-  }
-
-  byteptr_t FindTrampolineSpace()
-  {
-    if (mCurHooks >= mMaxHooks) {
-      return 0;
-    }
-
-    byteptr_t p = mHookPage + mCurHooks * kHookSize;
-
-    mCurHooks++;
-
-    return p;
-  }
-
-  static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
+    ReadOnlyTargetFunction<MMPolicyT> origFn(mVMPolicy, aOriginalFunction);
     // If function entry is jmp rel8 stub to the internal implementation, we
     // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
+    if (origFn[0] == 0xeb) {
+      int8_t offset = (int8_t)(origFn[1]);
       if (offset <= 0) {
         // Bail out for negative offset: probably already patched by some
         // third-party code.
-        return aOriginalFunction;
+        return Move(origFn);
       }
 
       for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
+        if (origFn[2 + i] != 0x90) {
           // Bail out on insufficient nop space.
-          return aOriginalFunction;
+          return Move(origFn);
         }
       }
 
-      return aOriginalFunction + 2 + offset;
+      origFn += 2 + offset;
+      return Move(origFn);
     }
 
 #if defined(_M_IX86)
     // If function entry is jmp [disp32] such as used by kernel32,
     // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (void*)(**((uint32_t**) (aOriginalFunction + 2)));
+    if (origFn[0] == 0xff && origFn[1] == 0x25) {
+      return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy,
+        reinterpret_cast<const void*>((origFn + 2).template ChasePointer<uintptr_t*>()));
     }
 #elif defined(_M_X64)
-    if (aOriginalFunction[0] == 0xe9) {
+    // If function entry is jmp [disp32] such as used by kernel32,
+    // we resolve redirected address from import table.
+    if (origFn[0] == 0x48 && origFn[1] == 0xff && origFn[2] == 0x25) {
+      return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy,
+        reinterpret_cast<const void*>((origFn + 3).ChasePointerFromDisp()));
+    }
+
+    if (origFn[0] == 0xe9) {
       // require for TestDllInterceptor with --disable-optimize
-      int32_t offset = *((int32_t*)(aOriginalFunction + 1));
-      return aOriginalFunction + 5 + offset;
+      uintptr_t abstarget = (origFn + 1).ReadDisp32AsAbsolute();
+      return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, abstarget);
     }
 #endif
 
-    return aOriginalFunction;
-  }
-};
-
-} // namespace internal
-
-class WindowsDllInterceptor
-{
-  internal::WindowsDllNopSpacePatcher mNopSpacePatcher;
-  internal::WindowsDllDetourPatcher mDetourPatcher;
-
-  const char* mModuleName;
-  int mNHooks;
-
-public:
-  explicit WindowsDllInterceptor(const char* aModuleName = nullptr,
-                                 int aNumHooks = 0)
-    : mModuleName(nullptr)
-    , mNHooks(0)
-  {
-    if (aModuleName) {
-      Init(aModuleName, aNumHooks);
-    }
-  }
-
-  void Init(const char* aModuleName, int aNumHooks = 0)
-  {
-    if (mModuleName) {
-      return;
-    }
-
-    mModuleName = aModuleName;
-    mNHooks = aNumHooks;
-    mNopSpacePatcher.Init(aModuleName);
-
-    // Lazily initialize mDetourPatcher, since it allocates memory and we might
-    // not need it.
+    return Move(origFn);
   }
 
-  /**
-   * Hook/detour the method aName from the DLL we set in Init so that it calls
-   * aHookDest instead.  Returns the original method pointer in aOrigFunc
-   * and returns true if successful.
-   *
-   * IMPORTANT: If you use this method, please add your case to the
-   * TestDllInterceptor in order to detect future failures.  Even if this
-   * succeeds now, updates to the hooked DLL could cause it to fail in
-   * the future.
-   */
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Use a nop space patch if possible, otherwise fall back to a detour.
-    // This should be the preferred method for adding hooks.
-
-    if (!mModuleName) {
-      return false;
-    }
-
-    if (mNopSpacePatcher.AddHook(aName, aHookDest, aOrigFunc)) {
-      return true;
-    }
-
-    return AddDetour(aName, aHookDest, aOrigFunc);
-  }
-
-  /**
-   * Detour the method aName from the DLL we set in Init so that it calls
-   * aHookDest instead.  Returns the original method pointer in aOrigFunc
-   * and returns true if successful.
-   *
-   * IMPORTANT: If you use this method, please add your case to the
-   * TestDllInterceptor in order to detect future failures.  Even if this
-   * succeeds now, updates to the detoured DLL could cause it to fail in
-   * the future.
-   */
-  bool AddDetour(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Generally, code should not call this method directly. Use AddHook unless
-    // there is a specific need to avoid nop space patches.
-
-    if (!mModuleName) {
-      return false;
-    }
-
-    if (!mDetourPatcher.Initialized()) {
-      mDetourPatcher.Init(mModuleName, mNHooks);
-    }
-
-    return mDetourPatcher.AddHook(aName, aHookDest, aOrigFunc);
-  }
+protected:
+  VMPolicy  mVMPolicy;
 };
 
+} // namespace interceptor
 } // namespace mozilla
 
-#endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */
+#endif // mozilla_interceptor_PatcherBase_h
copy from xpcom/build/nsWindowsDllInterceptor.h
copy to mozglue/misc/interceptor/PatcherDetour.h
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/mozglue/misc/interceptor/PatcherDetour.h
@@ -1,497 +1,183 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef NS_WINDOWS_DLL_INTERCEPTOR_H_
-#define NS_WINDOWS_DLL_INTERCEPTOR_H_
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/Assertions.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/UniquePtr.h"
-#include "nsWindowsHelpers.h"
-
-#include <wchar.h>
-#include <windows.h>
-#include <winternl.h>
+#ifndef mozilla_interceptor_PatcherDetour_h
+#define mozilla_interceptor_PatcherDetour_h
 
-/*
- * Simple function interception.
- *
- * We have two separate mechanisms for intercepting a function: We can use the
- * built-in nop space, if it exists, or we can create a detour.
- *
- * Using the built-in nop space works as follows: On x86-32, DLL functions
- * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of
- * NOP instructions.
- *
- * When we detect a function with this prelude, we do the following:
- *
- * 1. Write a long jump to our interceptor function into the five bytes of NOPs
- *    before the function.
- *
- * 2. Write a short jump -5 into the two-byte nop at the beginning of the function.
- *
- * This mechanism is nice because it's thread-safe.  It's even safe to do if
- * another thread is currently running the function we're modifying!
- *
- * When the WindowsDllNopSpacePatcher is destroyed, we overwrite the short jump
- * but not the long jump, so re-intercepting the same function won't work,
- * because its prelude won't match.
- *
- *
- * Unfortunately nop space patching doesn't work on functions which don't have
- * this magic prelude (and in particular, x86-64 never has the prelude).  So
- * when we can't use the built-in nop space, we fall back to using a detour,
- * which works as follows:
- *
- * 1. Save first N bytes of OrigFunction to trampoline, where N is a
- *    number of bytes >= 5 that are instruction aligned.
- *
- * 2. Replace first 5 bytes of OrigFunction with a jump to the Hook
- *    function.
- *
- * 3. After N bytes of the trampoline, add a jump to OrigFunction+N to
- *    continue original program flow.
- *
- * 4. Hook function needs to call the trampoline during its execution,
- *    to invoke the original function (so address of trampoline is
- *    returned).
- *
- * When the WindowsDllDetourPatcher object is destructed, OrigFunction is
- * patched again to jump directly to the trampoline instead of going through
- * the hook function. As such, re-intercepting the same function won't work, as
- * jump instructions are not supported.
- *
- * Note that this is not thread-safe.  Sad day.
- *
- */
+#include "mozilla/interceptor/PatcherBase.h"
+#include "mozilla/interceptor/Trampoline.h"
 
-#include <stdint.h>
+#include "mozilla/ScopeExit.h"
 
 #define COPY_CODES(NBYTES)  do {    \
-  memcpy(&tramp[nTrampBytes], &origBytes[nOrigBytes], NBYTES);    \
-  nOrigBytes += NBYTES;             \
-  nTrampBytes += NBYTES;            \
+  tramp.CopyFrom(origBytes.GetAddress(), NBYTES); \
+  origBytes += NBYTES;              \
 } while (0)
 
 namespace mozilla {
-namespace internal {
+namespace interceptor {
 
-class AutoVirtualProtect
+template <typename VMPolicy>
+class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy>
 {
 public:
-  AutoVirtualProtect(void* aFunc, size_t aSize, DWORD aProtect)
-    : mFunc(aFunc), mSize(aSize), mNewProtect(aProtect), mOldProtect(0),
-      mSuccess(false)
-  {}
-
-  ~AutoVirtualProtect()
-  {
-    if (mSuccess) {
-      VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, mOldProtect,
-                       &mOldProtect);
-    }
-  }
-
-  bool Protect()
-  {
-    mSuccess = !!VirtualProtectEx(GetCurrentProcess(), mFunc, mSize,
-                                  mNewProtect, &mOldProtect);
-    if (!mSuccess) {
-      // printf("VirtualProtectEx failed! %d\n", GetLastError());
-    }
-    return mSuccess;
-  }
-
-private:
-  void* const mFunc;
-  size_t const mSize;
-  DWORD const mNewProtect;
-  DWORD mOldProtect;
-  bool mSuccess;
-};
-
-class WindowsDllNopSpacePatcher
-{
-  typedef uint8_t* byteptr_t;
-  HMODULE mModule;
-
-  // Dumb array for remembering the addresses of functions we've patched.
-  // (This should be nsTArray, but non-XPCOM code uses this class.)
-  static const size_t maxPatchedFns = 16;
-  byteptr_t mPatchedFns[maxPatchedFns];
-  size_t mPatchedFnsLen;
-
-public:
-  WindowsDllNopSpacePatcher()
-    : mModule(0)
-    , mPatchedFnsLen(0)
-  {}
-
-#if defined(_M_IX86)
-  ~WindowsDllNopSpacePatcher()
-  {
-    // Restore the mov edi, edi to the beginning of each function we patched.
-
-    for (size_t i = 0; i < mPatchedFnsLen; i++) {
-      byteptr_t fn = mPatchedFns[i];
-
-      // Ensure we can write to the code.
-      AutoVirtualProtect protect(fn, 2, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
-        continue;
-      }
-
-      // mov edi, edi
-      *((uint16_t*)fn) = 0xff8b;
-
-      // I don't think this is actually necessary, but it can't hurt.
-      FlushInstructionCache(GetCurrentProcess(),
-                            /* ignored */ nullptr,
-                            /* ignored */ 0);
-    }
-  }
-
-  void Init(const char* aModuleName)
-  {
-    if (!IsCompatible()) {
-#if defined(MOZILLA_INTERNAL_API)
-      NS_WARNING("NOP space patching is unavailable for compatibility reasons");
-#endif
-      return;
-    }
-
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
-      return;
-    }
-  }
-
-  /**
-   * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions
-   * in our address space. There is a bug in Detours 2.x that causes it to
-   * patch at the wrong address when attempting to detour code that is already
-   * NOP space patched. This function is an effort to detect the presence of
-   * this NVIDIA code in our address space and disable NOP space patching if it
-   * is. We also check AppInit_DLLs since this is the mechanism that the Optimus
-   * drivers use to inject into our process.
-   */
-  static bool IsCompatible()
-  {
-    // These DLLs are known to have bad interactions with this style of patching
-    const wchar_t* kIncompatibleDLLs[] = {
-      L"detoured.dll",
-      L"_etoured.dll",
-      L"nvd3d9wrap.dll",
-      L"nvdxgiwrap.dll"
-    };
-    // See if the infringing DLLs are already loaded
-    for (unsigned int i = 0; i < mozilla::ArrayLength(kIncompatibleDLLs); ++i) {
-      if (GetModuleHandleW(kIncompatibleDLLs[i])) {
-        return false;
-      }
-    }
-    if (GetModuleHandleW(L"user32.dll")) {
-      // user32 is loaded but the infringing DLLs are not, assume we're safe to
-      // proceed.
-      return true;
-    }
-    // If user32 has not loaded yet, check AppInit_DLLs to ensure that Optimus
-    // won't be loaded once user32 is initialized.
-    HKEY hkey = NULL;
-    if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
-          0, KEY_QUERY_VALUE, &hkey)) {
-      nsAutoRegKey key(hkey);
-      DWORD numBytes = 0;
-      const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
-      // Query for required buffer size
-      LONG status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
-                                     nullptr, nullptr, &numBytes);
-      mozilla::UniquePtr<wchar_t[]> data;
-      if (!status) {
-        // Allocate the buffer and query for the actual data
-        data = mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
-        status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
-                                  nullptr, (LPBYTE)data.get(), &numBytes);
-      }
-      if (!status) {
-        // For each token, split up the filename components and then check the
-        // name of the file.
-        const wchar_t kDelimiters[] = L", ";
-        wchar_t* tokenContext = nullptr;
-        wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
-        while (token) {
-          wchar_t fname[_MAX_FNAME] = {0};
-          if (!_wsplitpath_s(token, nullptr, 0, nullptr, 0,
-                             fname, mozilla::ArrayLength(fname),
-                             nullptr, 0)) {
-            // nvinit.dll is responsible for bootstrapping the DLL injection, so
-            // that is the library that we check for here
-            const wchar_t kNvInitName[] = L"nvinit";
-            if (!_wcsnicmp(fname, kNvInitName,
-                           mozilla::ArrayLength(kNvInitName))) {
-              return false;
-            }
-          }
-          token = wcstok_s(nullptr, kDelimiters, &tokenContext);
-        }
-      }
-    }
-    return true;
-  }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    if (!mModule) {
-      return false;
-    }
-
-    if (!IsCompatible()) {
-#if defined(MOZILLA_INTERNAL_API)
-      NS_WARNING("NOP space patching is unavailable for compatibility reasons");
-#endif
-      return false;
-    }
-
-    MOZ_RELEASE_ASSERT(mPatchedFnsLen < maxPatchedFns, "No room for the hook");
-
-    byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName));
-    if (!fn) {
-      //printf ("GetProcAddress failed\n");
-      return false;
-    }
-
-    fn = ResolveRedirectedAddress(fn);
-
-    // Ensure we can read and write starting at fn - 5 (for the long jmp we're
-    // going to write) and ending at fn + 2 (for the short jmp up to the long
-    // jmp). These bytes may span two pages with different protection.
-    AutoVirtualProtect protectBefore(fn - 5, 5, PAGE_EXECUTE_READWRITE);
-    AutoVirtualProtect protectAfter(fn, 2, PAGE_EXECUTE_READWRITE);
-    if (!protectBefore.Protect() || !protectAfter.Protect()) {
-      return false;
-    }
-
-    bool rv = WriteHook(fn, aHookDest, aOrigFunc);
-
-    if (rv) {
-      mPatchedFns[mPatchedFnsLen] = fn;
-      mPatchedFnsLen++;
-    }
-
-    return rv;
-  }
-
-  bool WriteHook(byteptr_t aFn, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Check that the 5 bytes before aFn are NOP's or INT 3's,
-    // and that the 2 bytes after aFn are mov(edi, edi).
-    //
-    // It's safe to read aFn[-5] because we set it to PAGE_EXECUTE_READWRITE
-    // before calling WriteHook.
-
-    for (int i = -5; i <= -1; i++) {
-      if (aFn[i] != 0x90 && aFn[i] != 0xcc) { // nop or int 3
-        return false;
-      }
-    }
-
-    // mov edi, edi.  Yes, there are two ways to encode the same thing:
-    //
-    //   0x89ff == mov r/m, r
-    //   0x8bff == mov r, r/m
-    //
-    // where "r" is register and "r/m" is register or memory.  Windows seems to
-    // use 8bff; I include 89ff out of paranoia.
-    if ((aFn[0] != 0x8b && aFn[0] != 0x89) || aFn[1] != 0xff) {
-      return false;
-    }
-
-    // Write a long jump into the space above the function.
-    aFn[-5] = 0xe9; // jmp
-    *((intptr_t*)(aFn - 4)) = aHookDest - (uintptr_t)(aFn); // target displacement
-
-    // Set aOrigFunc here, because after this point, aHookDest might be called,
-    // and aHookDest might use the aOrigFunc pointer.
-    *aOrigFunc = aFn + 2;
-
-    // Short jump up into our long jump.
-    *((uint16_t*)(aFn)) = 0xf9eb; // jmp $-5
-
-    // I think this routine is safe without this, but it can't hurt.
-    FlushInstructionCache(GetCurrentProcess(),
-                          /* ignored */ nullptr,
-                          /* ignored */ 0);
-
-    return true;
-  }
-
-private:
-  static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
-    // If function entry is jmp rel8 stub to the internal implementation, we
-    // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
-      if (offset <= 0) {
-        // Bail out for negative offset: probably already patched by some
-        // third-party code.
-        return aOriginalFunction;
-      }
-
-      for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
-          // Bail out on insufficient nop space.
-          return aOriginalFunction;
-        }
-      }
-
-      return aOriginalFunction + 2 + offset;
-    }
-
-    // If function entry is jmp [disp32] such as used by kernel32,
-    // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2)));
-    }
-
-    return aOriginalFunction;
-  }
-#else
-  void Init(const char* aModuleName)
-  {
-    // Not implemented except on x86-32.
-  }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Not implemented except on x86-32.
-    return false;
-  }
-#endif
-};
-
-class WindowsDllDetourPatcher
-{
-  typedef unsigned char* byteptr_t;
-public:
-  WindowsDllDetourPatcher()
-    : mModule(0), mHookPage(0), mMaxHooks(0), mCurHooks(0)
+  template <typename... Args>
+  explicit WindowsDllDetourPatcher(Args... aArgs)
+    : WindowsDllPatcherBase<VMPolicy>(mozilla::Forward<Args>(aArgs)...)
   {
   }
 
   ~WindowsDllDetourPatcher()
   {
-    int i;
-    byteptr_t p;
-    for (i = 0, p = mHookPage; i < mCurHooks; i++, p += kHookSize) {
-#if defined(_M_IX86)
-      size_t nBytes = 1 + sizeof(intptr_t);
-#elif defined(_M_X64)
-      size_t nBytes = 2 + sizeof(intptr_t);
-#else
-#error "Unknown processor type"
-#endif
-      byteptr_t origBytes = (byteptr_t)DecodePointer(*((byteptr_t*)p));
+    Clear();
+  }
+
+  WindowsDllDetourPatcher(const WindowsDllDetourPatcher&) = delete;
+  WindowsDllDetourPatcher(WindowsDllDetourPatcher&&) = delete;
+  WindowsDllDetourPatcher& operator=(const WindowsDllDetourPatcher&) = delete;
+  WindowsDllDetourPatcher& operator=(WindowsDllDetourPatcher&&) = delete;
 
-      // ensure we can modify the original code
-      AutoVirtualProtect protect(origBytes, nBytes, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
-        continue;
-      }
+  void Clear()
+  {
+    if (!mVMPolicy.ShouldUnhookUponDestruction()) {
+      return;
+    }
 
-      // Remove the hook by making the original function jump directly
-      // in the trampoline.
-      intptr_t dest = (intptr_t)(p + sizeof(void*));
 #if defined(_M_IX86)
-      // Ensure the JMP from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0xE9)
-        continue;
-      *((intptr_t*)(origBytes + 1)) =
-        dest - (intptr_t)(origBytes + 5); // target displacement
+    size_t nBytes = 1 + sizeof(intptr_t);
 #elif defined(_M_X64)
-      // Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0x49 || origBytes[1] != 0xBB)
-        continue;
-      *((intptr_t*)(origBytes + 2)) = dest;
+    size_t nBytes = 2 + sizeof(intptr_t);
 #else
 #error "Unknown processor type"
 #endif
+
+    const auto& tramps = mVMPolicy.Items();
+    for (auto&& tramp : tramps) {
+      // First we read the pointer to the interceptor instance.
+      Maybe<uintptr_t> instance = tramp.ReadEncodedPointer();
+      if (!instance) {
+        continue;
+      }
+
+      if (instance.value() != reinterpret_cast<uintptr_t>(this)) {
+        // tramp does not belong to this interceptor instance.
+        continue;
+      }
+
+      auto clearInstance = MakeScopeExit([&tramp]() -> void {
+        // Clear the instance pointer so that no future instances with the same
+        // |this| pointer will attempt to reset its hook.
+        tramp.Rewind();
+        tramp.WriteEncodedPointer(nullptr);
+      });
+
+      // Now we read the pointer to the intercepted function.
+      Maybe<uintptr_t> interceptedFn = tramp.ReadEncodedPointer();
+      if (!interceptedFn) {
+        continue;
+      }
+
+      WritableTargetFunction<MMPolicyT> origBytes(mVMPolicy,
+                                                  interceptedFn.value(), nBytes);
+      if (!origBytes) {
+        continue;
+      }
+
+      Maybe<uint8_t> maybeOpcode1 = origBytes.ReadByte();
+      if (!maybeOpcode1) {
+        continue;
+      }
+
+      uint8_t opcode1 = maybeOpcode1.value();
+
+#if defined(_M_IX86)
+      // Ensure the JMP from CreateTrampoline is where we expect it to be.
+      if (opcode1 != 0xE9) {
+        continue;
+      }
+
+      intptr_t startOfTrampInstructions =
+        static_cast<intptr_t>(tramp.GetCurrentRemoteAddress());
+
+      origBytes.WriteDisp32(startOfTrampInstructions);
+      if (!origBytes) {
+        continue;
+      }
+#elif defined(_M_X64)
+      // Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
+      if (opcode1 != 0x49) {
+        continue;
+      }
+
+      Maybe<uint8_t> maybeOpcode2 = origBytes.ReadByte();
+      if (!maybeOpcode2) {
+        continue;
+      }
+
+      uint8_t opcode2 = maybeOpcode2.value();
+      if (opcode2 != 0xBB) {
+        continue;
+      }
+
+      origBytes.WritePointer(tramp.GetCurrentRemoteAddress());
+      if (!origBytes) {
+        continue;
+      }
+#else
+#error "Unknown processor type"
+#endif
+
+      origBytes.Commit();
     }
+
+    mVMPolicy.Clear();
   }
 
-  void Init(const char* aModuleName, int aNumHooks = 0)
+  void Init(int aNumHooks = 0)
   {
-    if (mModule) {
-      return;
-    }
-
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
+    if (Initialized()) {
       return;
     }
 
-    int hooksPerPage = 4096 / kHookSize;
     if (aNumHooks == 0) {
-      aNumHooks = hooksPerPage;
+      // Win32 allocates VM addresses at a 64KiB granularity, so by default we
+      // might as well utilize that entire 64KiB reservation instead of
+      // artifically constraining ourselves to the page size.
+      aNumHooks = mVMPolicy.GetAllocGranularity() / kHookSize;
     }
 
-    mMaxHooks = aNumHooks + (hooksPerPage % aNumHooks);
-
-    mHookPage = (byteptr_t)VirtualAllocEx(GetCurrentProcess(), nullptr,
-                                          mMaxHooks * kHookSize,
-                                          MEM_COMMIT | MEM_RESERVE,
-                                          PAGE_EXECUTE_READ);
-    if (!mHookPage) {
-      mModule = 0;
-      return;
-    }
+    mVMPolicy.Reserve(aNumHooks);
   }
 
-  bool Initialized() { return !!mModule; }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+  bool Initialized() const
   {
-    if (!mModule) {
-      return false;
-    }
+    return !!mVMPolicy;
+  }
 
-    void* pAddr = (void*)GetProcAddress(mModule, aName);
-    if (!pAddr) {
-      //printf ("GetProcAddress failed\n");
-      return false;
-    }
+  bool AddHook(FARPROC aTargetFn, intptr_t aHookDest, void** aOrigFunc)
+  {
+    ReadOnlyTargetFunction<MMPolicyT> target(ResolveRedirectedAddress(aTargetFn));
 
-    pAddr = ResolveRedirectedAddress((byteptr_t)pAddr);
-
-    CreateTrampoline(pAddr, aHookDest, aOrigFunc);
+    CreateTrampoline(target, aHookDest, aOrigFunc);
     if (!*aOrigFunc) {
-      //printf ("CreateTrampoline failed\n");
       return false;
     }
 
     return true;
   }
 
 protected:
   const static int kPageSize = 4096;
   const static int kHookSize = 128;
 
-  HMODULE mModule;
-  byteptr_t mHookPage;
-  int mMaxHooks;
-  int mCurHooks;
-
   // rex bits
   static const BYTE kMaskHighNibble = 0xF0;
   static const BYTE kRexOpcode = 0x40;
   static const BYTE kMaskRexW = 0x08;
   static const BYTE kMaskRexR = 0x04;
   static const BYTE kMaskRexX = 0x02;
   static const BYTE kMaskRexB = 0x01;
 
@@ -533,22 +219,19 @@ protected:
   /**
    * Returns the number of bytes taken by the ModR/M byte, SIB (if present)
    * and the instruction's operand.  In special cases, the special MODRM codes
    * above are returned.
    * aModRm points to the ModR/M byte of the instruction.
    * On return, aSubOpcode (if present) is filled with the subopcode/register
    * code found in the ModR/M byte.
    */
-  int CountModRmSib(const BYTE *aModRm, BYTE* aSubOpcode = nullptr)
+  int CountModRmSib(const ReadOnlyTargetFunction<MMPolicyT>& aModRm,
+                    BYTE* aSubOpcode = nullptr)
   {
-    if (!aModRm) {
-      MOZ_ASSERT(aModRm, "Missing ModRM byte");
-      return kModUnknown;
-    }
     int numBytes = 1; // Start with 1 for mod r/m byte itself
     switch (*aModRm & kMaskMod) {
       case kModReg:
         return numBytes;
       case kModDisp8:
         numBytes += 1;
         break;
       case kModDisp32:
@@ -581,90 +264,74 @@ protected:
     }
     if (aSubOpcode) {
       *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
     }
     return numBytes;
   }
 
 #if defined(_M_X64)
-  // To patch for JMP and JE
-
-  enum JumpType {
+  enum class JumpType
+  {
    Je,
    Jne,
    Jmp,
    Call
   };
 
-  struct JumpPatch {
-    JumpPatch()
-      : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp)
-    {
-    }
-
-    JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp)
-      : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType)
-    {
+  static bool
+  GenerateJump(Trampoline<MMPolicyT>& aTramp, uintptr_t aAbsTargetAddress,
+               const JumpType aType)
+  {
+    // Near call, absolute indirect, address given in r/m32
+    if (aType == JumpType::Call) {
+      // CALL [RIP+0]
+      aTramp.WriteByte(0xff);
+      aTramp.WriteByte(0x15);
+      // The offset to jump destination -- 2 bytes after the current position.
+      aTramp.WriteInteger(2);
+      aTramp.WriteByte(0xeb);       // JMP + 8 (jump over target address)
+      aTramp.WriteByte(8);
+      aTramp.WritePointer(aAbsTargetAddress);
+      return !!aTramp;
     }
 
-    size_t GenerateJump(uint8_t* aCode)
-    {
-      size_t offset = mHookOffset;
-      if (mType == JumpType::Je) {
-        // JNE RIP+14
-        aCode[offset]     = 0x75;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      } else if (mType == JumpType::Jne) {
-        // JE RIP+14
-        aCode[offset]     = 0x74;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      }
-
-      // Near call/jmp, absolute indirect, address given in r/m32
-      if (mType == JumpType::Call) {
-        // CALL [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x15;
-        // The offset to jump destination -- ie it is placed 2 bytes after the offset.
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 2;
-        aCode[offset + 2 + 4] = 0xeb;    // JMP +8 (jump over mJumpAddress)
-        aCode[offset + 2 + 4 + 1] = 8;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4 + 2) = mJumpAddress;
-        return offset + 2 + 4 + 2 + 8;
-      } else {
-        // JMP [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x25;
-        // The offset to jump destination is 0
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress;
-        return offset + 2 + 4 + 8;
-      }
+    if (aType == JumpType::Je) {
+      // JNE RIP+14
+      aTramp.WriteByte(0x75);
+      aTramp.WriteByte(14);
+    } else if (aType == JumpType::Jne) {
+      // JE RIP+14
+      aTramp.WriteByte(0x74);
+      aTramp.WriteByte(14);
     }
 
-    size_t mHookOffset;
-    intptr_t mJumpAddress;
-    JumpType mType;
-  };
+    // Near jmp, absolute indirect, address given in r/m32
+    // JMP [RIP+0]
+    aTramp.WriteByte(0xff);
+    aTramp.WriteByte(0x25);
+    // The offset to jump destination is 0
+    aTramp.WriteInteger(0);
+    aTramp.WritePointer(aAbsTargetAddress);
 
+    return !!aTramp;
+  }
 #endif
 
   enum ePrefixGroupBits
   {
     eNoPrefixes = 0,
     ePrefixGroup1 = (1 << 0),
     ePrefixGroup2 = (1 << 1),
     ePrefixGroup3 = (1 << 2),
     ePrefixGroup4 = (1 << 3)
   };
 
-  int CountPrefixBytes(byteptr_t aBytes, const int aBytesIndex,
+  int CountPrefixBytes(const ReadOnlyTargetFunction<MMPolicyT>& aBytes,
+                       const int aBytesIndex,
                        unsigned char* aOutGroupBits)
   {
     unsigned char& groupBits = *aOutGroupBits;
     groupBits = eNoPrefixes;
     int index = aBytesIndex;
     while (true) {
       switch (aBytes[index]) {
         // Group 1
@@ -720,387 +387,398 @@ protected:
   BYTE BuildModRmByte(BYTE aModBits, BYTE aReg, BYTE aRm)
   {
     MOZ_ASSERT((aRm & kMaskRm) == aRm);
     MOZ_ASSERT((aModBits & kMaskMod) == aModBits);
     MOZ_ASSERT(((aReg << kRegFieldShift) & kMaskReg) == (aReg << kRegFieldShift));
     return aModBits | (aReg << kRegFieldShift) | aRm;
   }
 
-  void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp)
+  void CreateTrampoline(ReadOnlyTargetFunction<MMPolicyT>& origBytes,
+                        intptr_t aDest, void** aOutTramp)
   {
     *aOutTramp = nullptr;
 
-    AutoVirtualProtect protectHookPage(mHookPage, mMaxHooks * kHookSize,
-                                       PAGE_EXECUTE_READWRITE);
-    if (!protectHookPage.Protect()) {
+    Trampoline<MMPolicyT> tramp(mVMPolicy.GetNextTrampoline());
+    if (!tramp) {
       return;
     }
 
-    byteptr_t tramp = FindTrampolineSpace();
+    // The beginning of the trampoline contains two pointer-width slots:
+    // [0]: |this|, so that we know whether the trampoline belongs to us;
+    // [1]: Pointer to original function, so that we can reset the hook upon
+    //      destruction.
+    tramp.WriteEncodedPointer(this);
     if (!tramp) {
       return;
     }
 
-    // We keep the address of the original function in the first bytes of
-    // the trampoline buffer
-    *((void**)tramp) = EncodePointer(aOrigFunction);
-    tramp += sizeof(void*);
+    auto clearInstanceOnFailure = MakeScopeExit([aOutTramp, &tramp]() -> void {
+      // *aOutTramp is not set until CreateTrampoline has completed successfully,
+      // so we can use that to check for success.
+      if (*aOutTramp) {
+        return;
+      }
 
-    byteptr_t origBytes = (byteptr_t)aOrigFunction;
+      // Clear the instance pointer so that we don't try to reset a nonexistent
+      // hook.
+      tramp.Rewind();
+      tramp.WriteEncodedPointer(nullptr);
+    });
 
-    // # of bytes of the original function that we can overwrite.
-    int nOrigBytes = 0;
+    tramp.WritePointer(origBytes.AsEncodedPtr());
+    if (!tramp) {
+      return;
+    }
+
+    tramp.StartExecutableCode();
 
 #if defined(_M_IX86)
     int pJmp32 = -1;
-    while (nOrigBytes < 5) {
+    while (origBytes.GetOffset() < 5) {
       // Understand some simple instructions that might be found in a
       // prologue; we might need to extend this as necessary.
       //
       // Note!  If we ever need to understand jump instructions, we'll
       // need to rewrite the displacement argument.
       unsigned char prefixGroups;
-      int numPrefixBytes = CountPrefixBytes(origBytes, nOrigBytes, &prefixGroups);
+      int numPrefixBytes = CountPrefixBytes(origBytes, origBytes.GetOffset(), &prefixGroups);
       if (numPrefixBytes < 0 || (prefixGroups & (ePrefixGroup3 | ePrefixGroup4))) {
         // Either the prefix sequence was bad, or there are prefixes that
         // we don't currently support (groups 3 and 4)
         MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
         return;
       }
-      nOrigBytes += numPrefixBytes;
-      if (origBytes[nOrigBytes] >= 0x88 &&
-          origBytes[nOrigBytes] <= 0x8B) {
+
+      origBytes += numPrefixBytes;
+      if (*origBytes >= 0x88 && *origBytes <= 0x8B) {
         // various MOVs
-        ++nOrigBytes;
-        int len = CountModRmSib(origBytes + nOrigBytes);
+        ++origBytes;
+        int len = CountModRmSib(origBytes);
         if (len < 0) {
           MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
           return;
         }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0x0f &&
-                 (origBytes[nOrigBytes + 1] == 0x10 ||
-                  origBytes[nOrigBytes + 1] == 0x11)) {
+        origBytes += len;
+      } else if (*origBytes == 0x0f &&
+                 (origBytes[1] == 0x10 ||
+                  origBytes[1] == 0x11)) {
         // SSE: movups xmm, xmm/m128
         //      movups xmm/m128, xmm
-        nOrigBytes += 2;
-        int len = CountModRmSib(origBytes + nOrigBytes);
+        origBytes += 2;
+        int len = CountModRmSib(origBytes);
         if (len < 0) {
           MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
           return;
         }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0xA1) {
+        origBytes += len;
+      } else if (*origBytes == 0xA1) {
         // MOV eax, [seg:offset]
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xB8) {
+        origBytes += 5;
+      } else if (*origBytes == 0xB8) {
         // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x33 &&
-                 (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
+        origBytes += 5;
+      } else if (*origBytes == 0x33 &&
+                 (origBytes[1] & kMaskMod) == kModReg) {
         // XOR r32, r32
-        nOrigBytes += 2;
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0x40) {
+        origBytes += 2;
+      } else if ((*origBytes & 0xf8) == 0x40) {
         // INC r32
-        nOrigBytes += 1;
-      } else if (origBytes[nOrigBytes] == 0x83) {
+        origBytes += 1;
+      } else if (*origBytes == 0x83) {
         // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r/m, imm8
-        unsigned char b = origBytes[nOrigBytes + 1];
+        unsigned char b = origBytes[1];
         if ((b & 0xc0) == 0xc0) {
           // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r, imm8
-          nOrigBytes += 3;
+          origBytes += 3;
         } else {
           // bail
           MOZ_ASSERT_UNREACHABLE("Unrecognized bit opcode sequence");
           return;
         }
-      } else if (origBytes[nOrigBytes] == 0x68) {
+      } else if (*origBytes == 0x68) {
         // PUSH with 4-byte operand
-        nOrigBytes += 5;
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
+        origBytes += 5;
+      } else if ((*origBytes & 0xf0) == 0x50) {
         // 1-byte PUSH/POP
-        nOrigBytes++;
-      } else if (origBytes[nOrigBytes] == 0x6A) {
+        ++origBytes;
+      } else if (*origBytes == 0x6A) {
         // PUSH imm8
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xe9) {
-        pJmp32 = nOrigBytes;
+        origBytes += 2;
+      } else if (*origBytes == 0xe9) {
+        pJmp32 = origBytes.GetOffset();
         // jmp 32bit offset
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xff &&
-                 origBytes[nOrigBytes + 1] == 0x25) {
+        origBytes += 5;
+      } else if (*origBytes == 0xff &&
+                 origBytes[1] == 0x25) {
         // jmp [disp32]
-        nOrigBytes += 6;
-      } else if (origBytes[nOrigBytes] == 0xc2) {
+        origBytes += 6;
+      } else if (*origBytes == 0xc2) {
         // ret imm16.  We can't handle this but it happens.  We don't ASSERT but we do fail to hook.
 #if defined(MOZILLA_INTERNAL_API)
         NS_WARNING("Cannot hook method -- RET opcode found");
 #endif
         return;
       } else {
-        //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nOrigBytes]);
+        //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", *origBytes);
         MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
         return;
       }
     }
 
     // The trampoline is a copy of the instructions that we just traced,
     // followed by a jump that we add below.
-    memcpy(tramp, aOrigFunction, nOrigBytes);
+    tramp.CopyFrom(origBytes.GetBaseAddress(), origBytes.GetOffset());
+    if (!tramp) {
+      return;
+    }
 #elif defined(_M_X64)
-    // The number of bytes used by the trampoline.
-    int nTrampBytes = 0;
     bool foundJmp = false;
 
-    while (nOrigBytes < 13) {
+    while (origBytes.GetOffset() < 13) {
       // If we found JMP 32bit offset, we require that the next bytes must
       // be NOP or INT3.  There is no reason to copy them.
       // TODO: This used to trigger for Je as well.  Now that I allow
       // instructions after CALL and JE, I don't think I need that.
       // The only real value of this condition is that if code follows a JMP
       // then its _probably_ the target of a JMP somewhere else and we
       // will be overwriting it, which would be tragic.  This seems
       // highly unlikely.
       if (foundJmp) {
-        if (origBytes[nOrigBytes] == 0x90 || origBytes[nOrigBytes] == 0xcc) {
-          nOrigBytes++;
+        if (*origBytes == 0x90 || *origBytes == 0xcc) {
+          ++origBytes;
           continue;
         }
         MOZ_ASSERT_UNREACHABLE("Opcode sequence includes commands after JMP");
         return;
       }
-      if (origBytes[nOrigBytes] == 0x0f) {
+      if (*origBytes == 0x0f) {
         COPY_CODES(1);
-        if (origBytes[nOrigBytes] == 0x1f) {
+        if (*origBytes == 0x1f) {
           // nop (multibyte)
           COPY_CODES(1);
-          if ((origBytes[nOrigBytes] & 0xc0) == 0x40 &&
-              (origBytes[nOrigBytes] & 0x7) == 0x04) {
+          if ((*origBytes & 0xc0) == 0x40 &&
+              (*origBytes & 0x7) == 0x04) {
             COPY_CODES(3);
           } else {
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
-        } else if (origBytes[nOrigBytes] == 0x05) {
+        } else if (*origBytes == 0x05) {
           // syscall
           COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] == 0x10 ||
-                   origBytes[nOrigBytes] == 0x11) {
+        } else if (*origBytes == 0x10 ||
+                   *origBytes == 0x11) {
           // SSE: movups xmm, xmm/m128
           //      movups xmm/m128, xmm
           COPY_CODES(1);
-          int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes]);
+          int nModRmSibBytes = CountModRmSib(origBytes);
           if (nModRmSibBytes < 0) {
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           } else {
             COPY_CODES(nModRmSibBytes);
           }
-        } else if (origBytes[nOrigBytes] == 0x84) {
+        } else if (*origBytes == 0x84) {
           // je rel32
-          JumpPatch jump(nTrampBytes - 1,  // overwrite the 0x0f we copied above
-                          (intptr_t)(origBytes + nOrigBytes + 5 +
-                                     *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                          JumpType::Je);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 5;
+          ++origBytes;
+          --tramp; // overwrite the 0x0f we copied above
+
+          if (!GenerateJump(tramp,
+                            origBytes.ReadDisp32AsAbsolute(),
+                            JumpType::Je)) {
+            return;
+          }
         } else {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
-      } else if (origBytes[nOrigBytes] == 0x40 ||
-                 origBytes[nOrigBytes] == 0x41) {
+      } else if (*origBytes >= 0x88 && *origBytes <= 0x8B) {
+        // various 32-bit MOVs
+        COPY_CODES(1);
+        int len = CountModRmSib(origBytes);
+        if (len < 0) {
+          MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
+          return;
+        }
+        COPY_CODES(len);
+      } else if (*origBytes == 0x40 ||
+                 *origBytes == 0x41) {
         // Plain REX or REX.B
         COPY_CODES(1);
-        if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
+        if ((*origBytes & 0xf0) == 0x50) {
           // push/pop with Rx register
           COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] >= 0xb8 && origBytes[nOrigBytes] <= 0xbf) {
+        } else if (*origBytes >= 0xb8 && *origBytes <= 0xbf) {
           // mov r32, imm32
           COPY_CODES(5);
         } else {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
-      } else if (origBytes[nOrigBytes] == 0x44) {
+      } else if (*origBytes == 0x44) {
         // REX.R
         COPY_CODES(1);
 
         // TODO: Combine with the "0x89" case below in the REX.W section
-        if (origBytes[nOrigBytes] == 0x89) {
+        if (*origBytes == 0x89) {
           // mov r/m32, r32
           COPY_CODES(1);
-          int len = CountModRmSib(origBytes + nOrigBytes);
+          int len = CountModRmSib(origBytes);
           if (len < 0) {
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
           COPY_CODES(len);
         } else {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
-       } else if (origBytes[nOrigBytes] == 0x45) {
+       } else if (*origBytes == 0x45) {
         // REX.R & REX.B
         COPY_CODES(1);
 
-        if (origBytes[nOrigBytes] == 0x33) {
+        if (*origBytes == 0x33) {
           // xor r32, r32
           COPY_CODES(2);
         } else {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
-      } else if ((origBytes[nOrigBytes] & 0xfa) == 0x48) {
+      } else if ((*origBytes & 0xfa) == 0x48) {
         // REX.W | REX.WR | REX.WRB | REX.WB
         COPY_CODES(1);
 
-        if (origBytes[nOrigBytes] == 0x81 &&
-            (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
+        if (*origBytes == 0x81 &&
+            (origBytes[1] & 0xf8) == 0xe8) {
           // sub r, dword
           COPY_CODES(6);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
+        } else if (*origBytes == 0x83 &&
+                   (origBytes[1] & 0xf8) == 0xe8) {
           // sub r, byte
           COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == kModReg) {
+        } else if (*origBytes == 0x83 &&
+                   (origBytes[1] & (kMaskMod|kMaskReg)) == kModReg) {
           // add r, byte
           COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
+        } else if (*origBytes == 0x83 &&
+                   (origBytes[1] & 0xf8) == 0x60) {
           // and [r+d], imm8
           COPY_CODES(5);
-        } else if (origBytes[nOrigBytes] == 0x2b &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
+        } else if (*origBytes == 0x2b &&
+                   (origBytes[1] & kMaskMod) == kModReg) {
           // sub r64, r64
           COPY_CODES(2);
-        } else if (origBytes[nOrigBytes] == 0x85) {
+        } else if (*origBytes == 0x85) {
           // 85 /r => TEST r/m32, r32
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0xc0) {
+          if ((origBytes[1] & 0xc0) == 0xc0) {
             COPY_CODES(2);
           } else {
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
-        } else if ((origBytes[nOrigBytes] & 0xfd) == 0x89) {
+        } else if ((*origBytes & 0xfd) == 0x89) {
           // MOV r/m64, r64 | MOV r64, r/m64
           BYTE reg;
-          int len = CountModRmSib(origBytes + nOrigBytes + 1, &reg);
+          int len = CountModRmSib(origBytes + 1, &reg);
           if (len < 0) {
             MOZ_ASSERT(len == kModOperand64);
             if (len != kModOperand64) {
               return;
             }
-            nOrigBytes += 2;   // skip the MOV and MOD R/M bytes
+            origBytes += 2;   // skip the MOV and MOD R/M bytes
 
             // The instruction MOVs 64-bit data from a RIP-relative memory
             // address (determined with a 32-bit offset from RIP) into a
             // 64-bit register.
-            int64_t* absAddr =
-              reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 4 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes));
-            nOrigBytes += 4;
+            uintptr_t absAddr = origBytes.ReadDisp32AsAbsolute();
 
             if (reg == kRegAx) {
               // Destination is RAX.  Encode instruction as MOVABS with a
               // 64-bit absolute address as its immediate operand.
-              tramp[nTrampBytes] = 0xa1;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
+              tramp.WriteByte(0xa1);
+              tramp.WritePointer(absAddr);
             } else {
               // The MOV must be done in two steps.  First, we MOVABS the
               // absolute 64-bit address into our target register.
               // Then, we MOV from that address into the register
               // using register-indirect addressing.
-              tramp[nTrampBytes] = 0xb8 + reg;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
-              tramp[nTrampBytes] = 0x48;
-              tramp[nTrampBytes+1] = 0x8b;
-              tramp[nTrampBytes+2] = BuildModRmByte(kModNoRegDisp, reg, reg);
-              nTrampBytes += 3;
+              tramp.WriteByte(0xb8 + reg);
+              tramp.WritePointer(absAddr);
+              tramp.WriteByte(0x48);
+              tramp.WriteByte(0x8b);
+              tramp.WriteByte(BuildModRmByte(kModNoRegDisp, reg, reg));
             }
           } else {
             COPY_CODES(len+1);
           }
-        } else if (origBytes[nOrigBytes] == 0xc7) {
+        } else if (*origBytes == 0xc7) {
           // MOV r/m64, imm32
-          if (origBytes[nOrigBytes + 1] == 0x44) {
+          if (origBytes[1] == 0x44) {
             // MOV [r64+disp8], imm32
             // ModR/W + SIB + disp8 + imm32
             COPY_CODES(8);
           } else {
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
-        } else if (origBytes[nOrigBytes] == 0xff) {
+        } else if (*origBytes == 0xff) {
           // JMP /4
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & 0x07) == 0x5) {
-            // [rip+disp32]
-            // convert JMP 32bit offset to JMP 64bit direct
-            JumpPatch jump(nTrampBytes - 1,  // overwrite the REX.W/REX.WR we copied above
-                           *reinterpret_cast<intptr_t*>(origBytes + nOrigBytes + 6 +
-                                                        *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2)),
-                           JumpType::Jmp);
-            nTrampBytes = jump.GenerateJump(tramp);
-            nOrigBytes += 6;
+          if ((origBytes[1] & 0xc0) == 0x0 &&
+              (origBytes[1] & 0x07) == 0x5) {
+            origBytes += 2;
+            --tramp; // overwrite the REX.W/REX.RW we copied above
+
+            if (!GenerateJump(tramp, origBytes.ChasePointerFromDisp(),
+                              JumpType::Jmp)) {
+              return;
+            }
+
             foundJmp = true;
           } else {
             // not support yet!
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
-        } else if (origBytes[nOrigBytes] == 0x8d) {
+        } else if (*origBytes == 0x8d) {
           // LEA reg, addr
-          if ((origBytes[nOrigBytes + 1] & kMaskMod) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & kMaskRm) == 0x5) {
+          if ((origBytes[1] & kMaskMod) == 0x0 &&
+              (origBytes[1] & kMaskRm) == 0x5) {
             // [rip+disp32]
             // convert 32bit offset to 64bit direct and convert instruction
             // to a simple 64-bit mov
-            BYTE reg = (origBytes[nOrigBytes + 1] & kMaskReg) >> kRegFieldShift;
-            intptr_t absAddr =
-              reinterpret_cast<intptr_t>(origBytes + nOrigBytes + 6 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-            nOrigBytes += 6;
-            tramp[nTrampBytes] = 0xb8 + reg;    // mov
-            ++nTrampBytes;
-            intptr_t* trampOperandPtr = reinterpret_cast<intptr_t*>(tramp + nTrampBytes);
-            *trampOperandPtr = absAddr;
-            nTrampBytes += 8;
+            BYTE reg = (origBytes[1] & kMaskReg) >> kRegFieldShift;
+            origBytes += 2;
+            uintptr_t absAddr = origBytes.ReadDisp32AsAbsolute();
+            tramp.WriteByte(0xb8 + reg); // move
+            tramp.WritePointer(absAddr);
           } else {
             // Above we dealt with RIP-relative instructions.  Any other
             // operand form can simply be copied.
-            int len = CountModRmSib(origBytes + nOrigBytes + 1);
+            int len = CountModRmSib(origBytes + 1);
             // We handled the kModOperand64 -- ie RIP-relative -- case above
             MOZ_ASSERT(len > 0);
             COPY_CODES(len + 1);
           }
-        } else if (origBytes[nOrigBytes] == 0x63 &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
+        } else if (*origBytes == 0x63 &&
+                   (origBytes[1] & kMaskMod) == kModReg) {
           // movsxd r64, r32 (move + sign extend)
           COPY_CODES(2);
         } else {
           // not support yet!
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
-      } else if (origBytes[nOrigBytes] == 0x66) {
+      } else if (*origBytes == 0x66) {
         // operand override prefix
         COPY_CODES(1);
         // This is the same as the x86 version
-        if (origBytes[nOrigBytes] >= 0x88 && origBytes[nOrigBytes] <= 0x8B) {
+        if (*origBytes >= 0x88 && *origBytes <= 0x8B) {
           // various MOVs
-          unsigned char b = origBytes[nOrigBytes + 1];
+          unsigned char b = origBytes[1];
           if (((b & 0xc0) == 0xc0) ||
               (((b & 0xc0) == 0x00) &&
                ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05))) {
             // REG=r, R/M=r or REG=r, R/M=[r]
             COPY_CODES(2);
           } else if ((b & 0xc0) == 0x40) {
             if ((b & 0x07) == 0x04) {
               // REG=r, R/M=[SIB + disp8]
@@ -1109,387 +787,241 @@ protected:
               // REG=r, R/M=[r + disp8]
               COPY_CODES(3);
             }
           } else {
             // complex MOV, bail
             MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
             return;
           }
-        } else if (origBytes[nOrigBytes] == 0x44 &&
-                   origBytes[nOrigBytes+1] == 0x89) {
+        } else if (*origBytes == 0x44 &&
+                   origBytes[1] == 0x89) {
           // mov word ptr [reg+disp8], reg
           COPY_CODES(2);
-          int len = CountModRmSib(origBytes + nOrigBytes);
+          int len = CountModRmSib(origBytes);
           if (len < 0) {
             // no way to support this yet.
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
           COPY_CODES(len);
         }
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
+      } else if ((*origBytes & 0xf0) == 0x50) {
         // 1-byte push/pop
         COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0x65) {
+      } else if (*origBytes == 0x65) {
         // GS prefix
         //
         // The entry of GetKeyState on Windows 10 has the following code.
         // 65 48 8b 04 25 30 00 00 00    mov   rax,qword ptr gs:[30h]
         // (GS prefix + REX + MOV (0x8b) ...)
-        if (origBytes[nOrigBytes + 1] == 0x48 &&
-            (origBytes[nOrigBytes + 2] >= 0x88 && origBytes[nOrigBytes + 2] <= 0x8b)) {
+        if (origBytes[1] == 0x48 &&
+            (origBytes[2] >= 0x88 && origBytes[2] <= 0x8b)) {
           COPY_CODES(3);
-          int len = CountModRmSib(origBytes + nOrigBytes);
+          int len = CountModRmSib(origBytes);
           if (len < 0) {
             // no way to support this yet.
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
           COPY_CODES(len);
         } else {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
-      } else if (origBytes[nOrigBytes] == 0x80 &&
-                 origBytes[nOrigBytes + 1] == 0x3d) {
+      } else if (*origBytes == 0x80 &&
+                 origBytes[1] == 0x3d) {
+        origBytes += 2;
+
         // cmp byte ptr [rip-relative address], imm8
         // We'll compute the absolute address and do the cmp in r11
 
         // push r11 (to save the old value)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x53;
-        ++nTrampBytes;
+        tramp.WriteByte(0x49);
+        tramp.WriteByte(0x53);
 
-        byteptr_t absAddr =
-          reinterpret_cast<byteptr_t>(origBytes + nOrigBytes + 7 +
-                                      *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-        nOrigBytes += 6;
+        uintptr_t absAddr = origBytes.ReadDisp32AsAbsolute();
 
         // mov r11, absolute address
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0xbb;
-        ++nTrampBytes;
-
-        *reinterpret_cast<byteptr_t*>(tramp + nTrampBytes) = absAddr;
-        nTrampBytes += 8;
+        tramp.WriteByte(0x49);
+        tramp.WriteByte(0xbb);
+        tramp.WritePointer(absAddr);
 
         // cmp byte ptr [r11],...
-        tramp[nTrampBytes] = 0x41;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x80;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x3b;
-        ++nTrampBytes;
+        tramp.WriteByte(0x41);
+        tramp.WriteByte(0x80);
+        tramp.WriteByte(0x3b);
 
         // ...imm8
         COPY_CODES(1);
 
         // pop r11 (doesn't affect the flags from the cmp)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x5b;
-        ++nTrampBytes;
-      } else if (origBytes[nOrigBytes] == 0x90) {
+        tramp.WriteByte(0x49);
+        tramp.WriteByte(0x5b);
+      } else if (*origBytes == 0x90) {
         // nop
         COPY_CODES(1);
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0xb8) {
+      } else if ((*origBytes & 0xf8) == 0xb8) {
         // MOV r32, imm32
         COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0x33) {
+      } else if (*origBytes == 0x33) {
         // xor r32, r/m32
         COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xf6) {
+      } else if (*origBytes == 0xf6) {
         // test r/m8, imm8 (used by ntdll on Windows 10 x64)
         // (no flags are affected by near jmp since there is no task switch,
         // so it is ok for a jmp to be written immediately after a test)
         BYTE subOpcode = 0;
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1], &subOpcode);
+        int nModRmSibBytes = CountModRmSib(origBytes + 1, &subOpcode);
         if (nModRmSibBytes < 0 || subOpcode != 0) {
           // Unsupported
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
         COPY_CODES(2 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0x85) {
+      } else if (*origBytes == 0x85) {
         // test r/m32, r32
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1]);
+        int nModRmSibBytes = CountModRmSib(origBytes + 1);
         if (nModRmSibBytes < 0) {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
         COPY_CODES(1 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0xd1 &&
-                  (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
+      } else if (*origBytes == 0xd1 &&
+                  (origBytes[1] & kMaskMod) == kModReg) {
         // bit shifts/rotates : (SA|SH|RO|RC)(R|L) r32
         // (e.g. 0xd1 0xe0 is SAL, 0xd1 0xc8 is ROR)
         COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xc3) {
+      } else if (*origBytes == 0xc3) {
         // ret
         COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xcc) {
+      } else if (*origBytes == 0xcc) {
         // int 3
         COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xe8 ||
-                 origBytes[nOrigBytes] == 0xe9) {
+      } else if (*origBytes == 0xe8 ||
+                 *origBytes == 0xe9) {
         // CALL (0xe8) or JMP (0xe9) 32bit offset
-        foundJmp = origBytes[nOrigBytes] == 0xe9;
-        JumpPatch jump(nTrampBytes,
-                       (intptr_t)(origBytes + nOrigBytes + 5 +
-                                  *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                       origBytes[nOrigBytes] == 0xe8 ? JumpType::Call : JumpType::Jmp);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x74 || // je rel8 (0x74)
-                 origBytes[nOrigBytes] == 0x75) { // jne rel8 (0x75)
-        char offset = origBytes[nOrigBytes + 1];
+        foundJmp = *origBytes == 0xe9;
+        ++origBytes;
+
+        if (!GenerateJump(tramp, origBytes.ReadDisp32AsAbsolute(),
+                          foundJmp ? JumpType::Jmp : JumpType::Call)) {
+          return;
+        }
+      } else if (*origBytes == 0x74 || // je rel8 (0x74)
+                 *origBytes == 0x75) { // jne rel8 (0x75)
+        uint8_t offset = origBytes[1];
         auto jumpType = JumpType::Je;
-        if (origBytes[nOrigBytes] == 0x75)
+        if (*origBytes == 0x75) {
           jumpType = JumpType::Jne;
-        JumpPatch jump(nTrampBytes,
-          (intptr_t)(origBytes + nOrigBytes + 2 + offset), jumpType);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xff) {
-        if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == 0xf0) {
+        }
+
+        origBytes += 2;
+
+        if (!GenerateJump(tramp, origBytes.OffsetToAbsolute(offset), jumpType)) {
+          return;
+        }
+      } else if (*origBytes == 0xff) {
+        if ((origBytes[1] & (kMaskMod|kMaskReg)) == 0xf0) {
           // push r64
           COPY_CODES(2);
-        } else if (origBytes[nOrigBytes + 1] == 0x25) {
+        } else if (origBytes[1] == 0x25) {
           // jmp absolute indirect m32
           foundJmp = true;
-          int32_t offset = *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-          int64_t* ptrToJmpDest = reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 6 + offset);
-          intptr_t jmpDest = static_cast<intptr_t>(*ptrToJmpDest);
-          JumpPatch jump(nTrampBytes, jmpDest, JumpType::Jmp);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 6;
-        } else if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == BuildModRmByte(kModReg, 2, 0)) {
+
+          origBytes += 2;
+
+          uintptr_t jmpDest = origBytes.ChasePointerFromDisp();
+
+          if (!GenerateJump(tramp, jmpDest, JumpType::Jmp)) {
+            return;
+          }
+        } else if ((origBytes[1] & (kMaskMod|kMaskReg)) == BuildModRmByte(kModReg, 2, 0)) {
           // CALL reg (ff nn)
           COPY_CODES(2);
         } else {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
-      } else if (origBytes[nOrigBytes] == 0x83 &&
-                 (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
+      } else if (*origBytes == 0x83 &&
+                 (origBytes[1] & 0xf8) == 0x60) {
         // and [r+d], imm8
         COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0xc6) {
+      } else if (*origBytes == 0xc6) {
         // mov [r+d], imm8
-        int len = CountModRmSib(&origBytes[nOrigBytes + 1]);
+        int len = CountModRmSib(origBytes + 1);
         if (len < 0) {
           // RIP-relative not yet supported
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
         COPY_CODES(len + 1);
       } else {
         MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
         return;
       }
     }
 #else
 #error "Unknown processor type"
 #endif
 
-    if (nOrigBytes > 100) {
+    if (origBytes.GetOffset() > 100) {
       //printf ("Too big!");
       return;
     }
 
-    // target address of the final jmp instruction in the trampoline
-    byteptr_t trampDest = origBytes + nOrigBytes;
-
 #if defined(_M_IX86)
     if (pJmp32 >= 0) {
       // Jump directly to the original target of the jump instead of jumping to the
       // original function.
       // Adjust jump target displacement to jump location in the trampoline.
-      *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp;
+      tramp.AdjustDisp32AtOffset(pJmp32 + 1, origBytes.GetBaseAddress());
     } else {
-      tramp[nOrigBytes] = 0xE9; // jmp
-      *((intptr_t*)(tramp + nOrigBytes + 1)) =
-        (intptr_t)trampDest - (intptr_t)(tramp + nOrigBytes + 5); // target displacement
+      tramp.WriteByte(0xe9);  // jmp
+      tramp.WriteDisp32(origBytes.GetAddress());
     }
 #elif defined(_M_X64)
     // If the we found a Jmp, we don't need to add another instruction. However,
     // if we found a _conditional_ jump or a CALL (or no control operations
     // at all) then we still need to run the rest of aOriginalFunction.
     if (!foundJmp) {
-      JumpPatch patch(nTrampBytes, reinterpret_cast<intptr_t>(trampDest));
-      patch.GenerateJump(tramp);
+      if (!GenerateJump(tramp, origBytes.GetAddress(), JumpType::Jmp)) {
+        return;
+      }
     }
 #endif
 
-    // The trampoline is now valid.
-    *aOutTramp = tramp;
+    // The trampoline is now complete.
+    *aOutTramp = tramp.EndExecutableCode();
+    if (!(*aOutTramp)) {
+      return;
+    }
 
-    // ensure we can modify the original code
-    AutoVirtualProtect protect(aOrigFunction, nOrigBytes, PAGE_EXECUTE_READWRITE);
-    if (!protect.Protect()) {
+    WritableTargetFunction<MMPolicyT> target(origBytes.Promote());
+    if (!target) {
       return;
     }
 
 #if defined(_M_IX86)
     // now modify the original bytes
-    origBytes[0] = 0xE9; // jmp
-    *((intptr_t*)(origBytes + 1)) =
-      aDest - (intptr_t)(origBytes + 5); // target displacement
+    target.WriteByte(0xe9); //jmp
+    target.WriteDisp32(aDest); // hook displacement
 #elif defined(_M_X64)
     // mov r11, address
-    origBytes[0] = 0x49;
-    origBytes[1] = 0xbb;
-
-    *((intptr_t*)(origBytes + 2)) = aDest;
+    target.WriteByte(0x49);
+    target.WriteByte(0xbb);
+    target.WritePointer(aDest);
 
     // jmp r11
-    origBytes[10] = 0x41;
-    origBytes[11] = 0xff;
-    origBytes[12] = 0xe3;
-#endif
-  }
-
-  byteptr_t FindTrampolineSpace()
-  {
-    if (mCurHooks >= mMaxHooks) {
-      return 0;
-    }
-
-    byteptr_t p = mHookPage + mCurHooks * kHookSize;
-
-    mCurHooks++;
-
-    return p;
-  }
-
-  static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
-    // If function entry is jmp rel8 stub to the internal implementation, we
-    // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
-      if (offset <= 0) {
-        // Bail out for negative offset: probably already patched by some
-        // third-party code.
-        return aOriginalFunction;
-      }
-
-      for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
-          // Bail out on insufficient nop space.
-          return aOriginalFunction;
-        }
-      }
-
-      return aOriginalFunction + 2 + offset;
-    }
-
-#if defined(_M_IX86)
-    // If function entry is jmp [disp32] such as used by kernel32,
-    // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (void*)(**((uint32_t**) (aOriginalFunction + 2)));
-    }
-#elif defined(_M_X64)
-    if (aOriginalFunction[0] == 0xe9) {
-      // require for TestDllInterceptor with --disable-optimize
-      int32_t offset = *((int32_t*)(aOriginalFunction + 1));
-      return aOriginalFunction + 5 + offset;
-    }
+    target.WriteByte(0x41);
+    target.WriteByte(0xff);
+    target.WriteByte(0xe3);
 #endif
 
-    return aOriginalFunction;
+    target.Commit();
   }
 };
 
-} // namespace internal
-
-class WindowsDllInterceptor
-{
-  internal::WindowsDllNopSpacePatcher mNopSpacePatcher;
-  internal::WindowsDllDetourPatcher mDetourPatcher;
-
-  const char* mModuleName;
-  int mNHooks;
-
-public:
-  explicit WindowsDllInterceptor(const char* aModuleName = nullptr,
-                                 int aNumHooks = 0)
-    : mModuleName(nullptr)
-    , mNHooks(0)
-  {
-    if (aModuleName) {
-      Init(aModuleName, aNumHooks);
-    }
-  }
-
-  void Init(const char* aModuleName, int aNumHooks = 0)
-  {
-    if (mModuleName) {
-      return;
-    }
-
-    mModuleName = aModuleName;
-    mNHooks = aNumHooks;
-    mNopSpacePatcher.Init(aModuleName);
-
-    // Lazily initialize mDetourPatcher, since it allocates memory and we might
-    // not need it.
-  }
-
-  /**
-   * Hook/detour the method aName from the DLL we set in Init so that it calls
-   * aHookDest instead.  Returns the original method pointer in aOrigFunc
-   * and returns true if successful.
-   *
-   * IMPORTANT: If you use this method, please add your case to the
-   * TestDllInterceptor in order to detect future failures.  Even if this
-   * succeeds now, updates to the hooked DLL could cause it to fail in
-   * the future.
-   */
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Use a nop space patch if possible, otherwise fall back to a detour.
-    // This should be the preferred method for adding hooks.
-
-    if (!mModuleName) {
-      return false;
-    }
-
-    if (mNopSpacePatcher.AddHook(aName, aHookDest, aOrigFunc)) {
-      return true;
-    }
-
-    return AddDetour(aName, aHookDest, aOrigFunc);
-  }
-
-  /**
-   * Detour the method aName from the DLL we set in Init so that it calls
-   * aHookDest instead.  Returns the original method pointer in aOrigFunc
-   * and returns true if successful.
-   *
-   * IMPORTANT: If you use this method, please add your case to the
-   * TestDllInterceptor in order to detect future failures.  Even if this
-   * succeeds now, updates to the detoured DLL could cause it to fail in
-   * the future.
-   */
-  bool AddDetour(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Generally, code should not call this method directly. Use AddHook unless
-    // there is a specific need to avoid nop space patches.
-
-    if (!mModuleName) {
-      return false;
-    }
-
-    if (!mDetourPatcher.Initialized()) {
-      mDetourPatcher.Init(mModuleName, mNHooks);
-    }
-
-    return mDetourPatcher.AddHook(aName, aHookDest, aOrigFunc);
-  }
-};
-
+} // namespace interceptor
 } // namespace mozilla
 
-#endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */
+#endif // mozilla_interceptor_PatcherDetour_h
+
copy from xpcom/build/nsWindowsDllInterceptor.h
copy to mozglue/misc/interceptor/PatcherNopSpace.h
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/mozglue/misc/interceptor/PatcherNopSpace.h
@@ -1,178 +1,64 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef NS_WINDOWS_DLL_INTERCEPTOR_H_
-#define NS_WINDOWS_DLL_INTERCEPTOR_H_
-
-#include "mozilla/Assertions.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/UniquePtr.h"
-#include "nsWindowsHelpers.h"
-
-#include <wchar.h>
-#include <windows.h>
-#include <winternl.h>
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
-/*
- * Simple function interception.
- *
- * We have two separate mechanisms for intercepting a function: We can use the
- * built-in nop space, if it exists, or we can create a detour.
- *
- * Using the built-in nop space works as follows: On x86-32, DLL functions
- * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of
- * NOP instructions.
- *
- * When we detect a function with this prelude, we do the following:
- *
- * 1. Write a long jump to our interceptor function into the five bytes of NOPs
- *    before the function.
- *
- * 2. Write a short jump -5 into the two-byte nop at the beginning of the function.
- *
- * This mechanism is nice because it's thread-safe.  It's even safe to do if
- * another thread is currently running the function we're modifying!
- *
- * When the WindowsDllNopSpacePatcher is destroyed, we overwrite the short jump
- * but not the long jump, so re-intercepting the same function won't work,
- * because its prelude won't match.
- *
- *
- * Unfortunately nop space patching doesn't work on functions which don't have
- * this magic prelude (and in particular, x86-64 never has the prelude).  So
- * when we can't use the built-in nop space, we fall back to using a detour,
- * which works as follows:
- *
- * 1. Save first N bytes of OrigFunction to trampoline, where N is a
- *    number of bytes >= 5 that are instruction aligned.
- *
- * 2. Replace first 5 bytes of OrigFunction with a jump to the Hook
- *    function.
- *
- * 3. After N bytes of the trampoline, add a jump to OrigFunction+N to
- *    continue original program flow.
- *
- * 4. Hook function needs to call the trampoline during its execution,
- *    to invoke the original function (so address of trampoline is
- *    returned).
- *
- * When the WindowsDllDetourPatcher object is destructed, OrigFunction is
- * patched again to jump directly to the trampoline instead of going through
- * the hook function. As such, re-intercepting the same function won't work, as
- * jump instructions are not supported.
- *
- * Note that this is not thread-safe.  Sad day.
- *
- */
+#ifndef mozilla_interceptor_PatcherNopSpace_h
+#define mozilla_interceptor_PatcherNopSpace_h
 
-#include <stdint.h>
+#if defined(_M_IX86)
 
-#define COPY_CODES(NBYTES)  do {    \
-  memcpy(&tramp[nTrampBytes], &origBytes[nOrigBytes], NBYTES);    \
-  nOrigBytes += NBYTES;             \
-  nTrampBytes += NBYTES;            \
-} while (0)
+#include "mozilla/interceptor/PatcherBase.h"
 
 namespace mozilla {
-namespace internal {
+namespace interceptor {
 
-class AutoVirtualProtect
+template <typename VMPolicy>
+class WindowsDllNopSpacePatcher final : public WindowsDllPatcherBase<VMPolicy>
 {
+  // For remembering the addresses of functions we've patched.
+  mozilla::Vector<void*> mPatchedFns;
+
 public:
-  AutoVirtualProtect(void* aFunc, size_t aSize, DWORD aProtect)
-    : mFunc(aFunc), mSize(aSize), mNewProtect(aProtect), mOldProtect(0),
-      mSuccess(false)
+  template <typename... Args>
+  explicit WindowsDllNopSpacePatcher(Args... aArgs)
+    : WindowsDllPatcherBase<VMPolicy>(mozilla::Forward<Args>(aArgs)...)
   {}
 
-  ~AutoVirtualProtect()
+  ~WindowsDllNopSpacePatcher()
   {
-    if (mSuccess) {
-      VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, mOldProtect,
-                       &mOldProtect);
-    }
-  }
-
-  bool Protect()
-  {
-    mSuccess = !!VirtualProtectEx(GetCurrentProcess(), mFunc, mSize,
-                                  mNewProtect, &mOldProtect);
-    if (!mSuccess) {
-      // printf("VirtualProtectEx failed! %d\n", GetLastError());
-    }
-    return mSuccess;
+    Clear();
   }
 
-private:
-  void* const mFunc;
-  size_t const mSize;
-  DWORD const mNewProtect;
-  DWORD mOldProtect;
-  bool mSuccess;
-};
-
-class WindowsDllNopSpacePatcher
-{
-  typedef uint8_t* byteptr_t;
-  HMODULE mModule;
+  WindowsDllNopSpacePatcher(const WindowsDllNopSpacePatcher&) = delete;
+  WindowsDllNopSpacePatcher(WindowsDllNopSpacePatcher&&) = delete;
+  WindowsDllNopSpacePatcher& operator=(const WindowsDllNopSpacePatcher&) = delete;
+  WindowsDllNopSpacePatcher& operator=(WindowsDllNopSpacePatcher&&) = delete;
 
-  // Dumb array for remembering the addresses of functions we've patched.
-  // (This should be nsTArray, but non-XPCOM code uses this class.)
-  static const size_t maxPatchedFns = 16;
-  byteptr_t mPatchedFns[maxPatchedFns];
-  size_t mPatchedFnsLen;
-
-public:
-  WindowsDllNopSpacePatcher()
-    : mModule(0)
-    , mPatchedFnsLen(0)
-  {}
-
-#if defined(_M_IX86)
-  ~WindowsDllNopSpacePatcher()
+  void Clear()
   {
     // Restore the mov edi, edi to the beginning of each function we patched.
 
-    for (size_t i = 0; i < mPatchedFnsLen; i++) {
-      byteptr_t fn = mPatchedFns[i];
-
-      // Ensure we can write to the code.
-      AutoVirtualProtect protect(fn, 2, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
+    for (auto&& ptr : mPatchedFns) {
+      WritableTargetFunction<MMPolicyT> fn(mVMPolicy,
+                                           reinterpret_cast<uintptr_t>(ptr),
+                                           sizeof(uint16_t));
+      if (!fn) {
         continue;
       }
 
       // mov edi, edi
-      *((uint16_t*)fn) = 0xff8b;
-
-      // I don't think this is actually necessary, but it can't hurt.
-      FlushInstructionCache(GetCurrentProcess(),
-                            /* ignored */ nullptr,
-                            /* ignored */ 0);
-    }
-  }
-
-  void Init(const char* aModuleName)
-  {
-    if (!IsCompatible()) {
-#if defined(MOZILLA_INTERNAL_API)
-      NS_WARNING("NOP space patching is unavailable for compatibility reasons");
-#endif
-      return;
+      fn.WriteShort(0xff8b);
+      fn.Commit();
     }
 
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
-      return;
-    }
+    mPatchedFns.clear();
   }
 
   /**
    * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions
    * in our address space. There is a bug in Detours 2.x that causes it to
    * patch at the wrong address when attempting to detour code that is already
    * NOP space patched. This function is an effort to detect the presence of
    * this NVIDIA code in our address space and disable NOP space patching if it
@@ -209,17 +95,17 @@ public:
       DWORD numBytes = 0;
       const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
       // Query for required buffer size
       LONG status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
                                      nullptr, nullptr, &numBytes);
       mozilla::UniquePtr<wchar_t[]> data;
       if (!status) {
         // Allocate the buffer and query for the actual data
-        data = mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
+        data = mozilla::MakeUnique<wchar_t[]>((numBytes + 1) / sizeof(wchar_t));
         status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
                                   nullptr, (LPBYTE)data.get(), &numBytes);
       }
       if (!status) {
         // For each token, split up the filename components and then check the
         // name of the file.
         const wchar_t kDelimiters[] = L", ";
         wchar_t* tokenContext = nullptr;
@@ -239,1257 +125,93 @@ public:
           }
           token = wcstok_s(nullptr, kDelimiters, &tokenContext);
         }
       }
     }
     return true;
   }
 
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+  bool AddHook(FARPROC aTargetFn, intptr_t aHookDest, void** aOrigFunc)
   {
-    if (!mModule) {
-      return false;
-    }
-
     if (!IsCompatible()) {
 #if defined(MOZILLA_INTERNAL_API)
       NS_WARNING("NOP space patching is unavailable for compatibility reasons");
 #endif
       return false;
     }
 
-    MOZ_RELEASE_ASSERT(mPatchedFnsLen < maxPatchedFns, "No room for the hook");
-
-    byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName));
-    if (!fn) {
-      //printf ("GetProcAddress failed\n");
+    MOZ_ASSERT(aTargetFn);
+    if (!aTargetFn) {
       return false;
     }
 
-    fn = ResolveRedirectedAddress(fn);
+    ReadOnlyTargetFunction<MMPolicyT> readOnlyTargetFn(
+      ResolveRedirectedAddress(aTargetFn));
 
-    // Ensure we can read and write starting at fn - 5 (for the long jmp we're
-    // going to write) and ending at fn + 2 (for the short jmp up to the long
-    // jmp). These bytes may span two pages with different protection.
-    AutoVirtualProtect protectBefore(fn - 5, 5, PAGE_EXECUTE_READWRITE);
-    AutoVirtualProtect protectAfter(fn, 2, PAGE_EXECUTE_READWRITE);
-    if (!protectBefore.Protect() || !protectAfter.Protect()) {
+    if (!WriteHook(readOnlyTargetFn, aHookDest, aOrigFunc)) {
       return false;
     }
 
-    bool rv = WriteHook(fn, aHookDest, aOrigFunc);
-
-    if (rv) {
-      mPatchedFns[mPatchedFnsLen] = fn;
-      mPatchedFnsLen++;
-    }
-
-    return rv;
+    mPatchedFns.append(reinterpret_cast<void*>(readOnlyTargetFn.GetBaseAddress()));
+    return true;
   }
 
-  bool WriteHook(byteptr_t aFn, intptr_t aHookDest, void** aOrigFunc)
+  bool WriteHook(const ReadOnlyTargetFunction<MMPolicyT>& aFn,
+                 intptr_t aHookDest, void** aOrigFunc)
   {
-    // Check that the 5 bytes before aFn are NOP's or INT 3's,
-    // and that the 2 bytes after aFn are mov(edi, edi).
-    //
-    // It's safe to read aFn[-5] because we set it to PAGE_EXECUTE_READWRITE
-    // before calling WriteHook.
-
-    for (int i = -5; i <= -1; i++) {
-      if (aFn[i] != 0x90 && aFn[i] != 0xcc) { // nop or int 3
-        return false;
-      }
+    // Ensure we can read and write starting at fn - 5 (for the long jmp we're
+    // going to write) and ending at fn + 2 (for the short jmp up to the long
+    // jmp). These bytes may span two pages with different protection.
+    WritableTargetFunction<MMPolicyT> writableFn(aFn.Promote(7, -5));
+    if (!writableFn) {
+      return false;
     }
 
-    // mov edi, edi.  Yes, there are two ways to encode the same thing:
+    // Check that the 5 bytes before the function are NOP's or INT 3's,
+    const uint8_t nopOrBp[] = { 0x90, 0xCC };
+    if (!writableFn.VerifyValuesAreOneOf<uint8_t, 5>(nopOrBp)) {
+      return false;
+    }
+
+    // ... and that the first 2 bytes of the function are mov(edi, edi).
+    // There are two ways to encode the same thing:
     //
-    //   0x89ff == mov r/m, r
-    //   0x8bff == mov r, r/m
+    //   0x89 0xff == mov r/m, r
+    //   0x8b 0xff == mov r, r/m
     //
-    // where "r" is register and "r/m" is register or memory.  Windows seems to
-    // use 8bff; I include 89ff out of paranoia.
-    if ((aFn[0] != 0x8b && aFn[0] != 0x89) || aFn[1] != 0xff) {
+    // where "r" is register and "r/m" is register or memory.
+    // Windows seems to use 0x8B 0xFF. We include 0x89 0xFF out of paranoia.
+
+    // (These look backwards because little-endian)
+    const uint16_t possibleEncodings[] = { 0xFF8B, 0xFF89 };
+    if (!writableFn.VerifyValuesAreOneOf<uint16_t, 1>(possibleEncodings, 5)) {
       return false;
     }
 
     // Write a long jump into the space above the function.
-    aFn[-5] = 0xe9; // jmp
-    *((intptr_t*)(aFn - 4)) = aHookDest - (uintptr_t)(aFn); // target displacement
-
-    // Set aOrigFunc here, because after this point, aHookDest might be called,
-    // and aHookDest might use the aOrigFunc pointer.
-    *aOrigFunc = aFn + 2;
-
-    // Short jump up into our long jump.
-    *((uint16_t*)(aFn)) = 0xf9eb; // jmp $-5
-
-    // I think this routine is safe without this, but it can't hurt.
-    FlushInstructionCache(GetCurrentProcess(),
-                          /* ignored */ nullptr,
-                          /* ignored */ 0);
-
-    return true;
-  }
-
-private:
-  static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
-    // If function entry is jmp rel8 stub to the internal implementation, we
-    // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
-      if (offset <= 0) {
-        // Bail out for negative offset: probably already patched by some
-        // third-party code.
-        return aOriginalFunction;
-      }
-
-      for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
-          // Bail out on insufficient nop space.
-          return aOriginalFunction;
-        }
-      }
-
-      return aOriginalFunction + 2 + offset;
-    }
-
-    // If function entry is jmp [disp32] such as used by kernel32,
-    // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2)));
-    }
-
-    return aOriginalFunction;
-  }
-#else
-  void Init(const char* aModuleName)
-  {
-    // Not implemented except on x86-32.
-  }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Not implemented except on x86-32.
-    return false;
-  }
-#endif
-};
-
-class WindowsDllDetourPatcher
-{
-  typedef unsigned char* byteptr_t;
-public:
-  WindowsDllDetourPatcher()
-    : mModule(0), mHookPage(0), mMaxHooks(0), mCurHooks(0)
-  {
-  }
-
-  ~WindowsDllDetourPatcher()
-  {
-    int i;
-    byteptr_t p;
-    for (i = 0, p = mHookPage; i < mCurHooks; i++, p += kHookSize) {
-#if defined(_M_IX86)
-      size_t nBytes = 1 + sizeof(intptr_t);
-#elif defined(_M_X64)
-      size_t nBytes = 2 + sizeof(intptr_t);
-#else
-#error "Unknown processor type"
-#endif
-      byteptr_t origBytes = (byteptr_t)DecodePointer(*((byteptr_t*)p));
-
-      // ensure we can modify the original code
-      AutoVirtualProtect protect(origBytes, nBytes, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
-        continue;
-      }
-
-      // Remove the hook by making the original function jump directly
-      // in the trampoline.
-      intptr_t dest = (intptr_t)(p + sizeof(void*));
-#if defined(_M_IX86)
-      // Ensure the JMP from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0xE9)
-        continue;
-      *((intptr_t*)(origBytes + 1)) =
-        dest - (intptr_t)(origBytes + 5); // target displacement
-#elif defined(_M_X64)
-      // Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0x49 || origBytes[1] != 0xBB)
-        continue;
-      *((intptr_t*)(origBytes + 2)) = dest;
-#else
-#error "Unknown processor type"
-#endif
-    }
-  }
-
-  void Init(const char* aModuleName, int aNumHooks = 0)
-  {
-    if (mModule) {
-      return;
-    }
-
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
-      return;
-    }
-
-    int hooksPerPage = 4096 / kHookSize;
-    if (aNumHooks == 0) {
-      aNumHooks = hooksPerPage;
-    }
-
-    mMaxHooks = aNumHooks + (hooksPerPage % aNumHooks);
-
-    mHookPage = (byteptr_t)VirtualAllocEx(GetCurrentProcess(), nullptr,
-                                          mMaxHooks * kHookSize,
-                                          MEM_COMMIT | MEM_RESERVE,
-                                          PAGE_EXECUTE_READ);
-    if (!mHookPage) {
-      mModule = 0;
-      return;
-    }
-  }
-
-  bool Initialized() { return !!mModule; }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    if (!mModule) {
+    writableFn.WriteByte(0xe9); // jmp
+    if (!writableFn) {
       return false;
     }
 
-    void* pAddr = (void*)GetProcAddress(mModule, aName);
-    if (!pAddr) {
-      //printf ("GetProcAddress failed\n");
-      return false;
-    }
-
-    pAddr = ResolveRedirectedAddress((byteptr_t)pAddr);
-
-    CreateTrampoline(pAddr, aHookDest, aOrigFunc);
-    if (!*aOrigFunc) {
-      //printf ("CreateTrampoline failed\n");
+    writableFn.WriteDisp32(aHookDest); // target
+    if (!writableFn) {
       return false;
     }
 
-    return true;
-  }
-
-protected:
-  const static int kPageSize = 4096;
-  const static int kHookSize = 128;
-
-  HMODULE mModule;
-  byteptr_t mHookPage;
-  int mMaxHooks;
-  int mCurHooks;
-
-  // rex bits
-  static const BYTE kMaskHighNibble = 0xF0;
-  static const BYTE kRexOpcode = 0x40;
-  static const BYTE kMaskRexW = 0x08;
-  static const BYTE kMaskRexR = 0x04;
-  static const BYTE kMaskRexX = 0x02;
-  static const BYTE kMaskRexB = 0x01;
-
-  // mod r/m bits
-  static const BYTE kRegFieldShift = 3;
-  static const BYTE kMaskMod = 0xC0;
-  static const BYTE kMaskReg = 0x38;
-  static const BYTE kMaskRm = 0x07;
-  static const BYTE kRmNeedSib = 0x04;
-  static const BYTE kModReg = 0xC0;
-  static const BYTE kModDisp32 = 0x80;
-  static const BYTE kModDisp8 = 0x40;
-  static const BYTE kModNoRegDisp = 0x00;
-  static const BYTE kRmNoRegDispDisp32 = 0x05;
-
-  // sib bits
-  static const BYTE kMaskSibScale = 0xC0;
-  static const BYTE kMaskSibIndex = 0x38;
-  static const BYTE kMaskSibBase = 0x07;
-  static const BYTE kSibBaseEbp = 0x05;
-
-  // Register bit IDs.
-  static const BYTE kRegAx = 0x0;
-  static const BYTE kRegCx = 0x1;
-  static const BYTE kRegDx = 0x2;
-  static const BYTE kRegBx = 0x3;
-  static const BYTE kRegSp = 0x4;
-  static const BYTE kRegBp = 0x5;
-  static const BYTE kRegSi = 0x6;
-  static const BYTE kRegDi = 0x7;
-
-  // Special ModR/M codes.  These indicate operands that cannot be simply
-  // memcpy-ed.
-  // Operand is a 64-bit RIP-relative address.
-  static const int kModOperand64 = -2;
-  // Operand is not yet handled by our trampoline.
-  static const int kModUnknown = -1;
-
-  /**
-   * Returns the number of bytes taken by the ModR/M byte, SIB (if present)
-   * and the instruction's operand.  In special cases, the special MODRM codes
-   * above are returned.
-   * aModRm points to the ModR/M byte of the instruction.
-   * On return, aSubOpcode (if present) is filled with the subopcode/register
-   * code found in the ModR/M byte.
-   */
-  int CountModRmSib(const BYTE *aModRm, BYTE* aSubOpcode = nullptr)
-  {
-    if (!aModRm) {
-      MOZ_ASSERT(aModRm, "Missing ModRM byte");
-      return kModUnknown;
-    }
-    int numBytes = 1; // Start with 1 for mod r/m byte itself
-    switch (*aModRm & kMaskMod) {
-      case kModReg:
-        return numBytes;
-      case kModDisp8:
-        numBytes += 1;
-        break;
-      case kModDisp32:
-        numBytes += 4;
-        break;
-      case kModNoRegDisp:
-        if ((*aModRm & kMaskRm) == kRmNoRegDispDisp32) {
-#if defined(_M_X64)
-          if (aSubOpcode) {
-            *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
-          }
-          return kModOperand64;
-#else
-          // On IA-32, all ModR/M instruction modes address memory relative to 0
-          numBytes += 4;
-#endif
-        } else if (((*aModRm & kMaskRm) == kRmNeedSib &&
-             (*(aModRm + 1) & kMaskSibBase) == kSibBaseEbp)) {
-          numBytes += 4;
-        }
-        break;
-      default:
-        // This should not be reachable
-        MOZ_ASSERT_UNREACHABLE("Impossible value for modr/m byte mod bits");
-        return kModUnknown;
-    }
-    if ((*aModRm & kMaskRm) == kRmNeedSib) {
-      // SIB byte
-      numBytes += 1;
-    }
-    if (aSubOpcode) {
-      *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
-    }
-    return numBytes;
-  }
-
-#if defined(_M_X64)
-  // To patch for JMP and JE
-
-  enum JumpType {
-   Je,
-   Jne,
-   Jmp,
-   Call
-  };
-
-  struct JumpPatch {
-    JumpPatch()
-      : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp)
-    {
-    }
-
-    JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp)
-      : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType)
-    {
-    }
-
-    size_t GenerateJump(uint8_t* aCode)
-    {
-      size_t offset = mHookOffset;
-      if (mType == JumpType::Je) {
-        // JNE RIP+14
-        aCode[offset]     = 0x75;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      } else if (mType == JumpType::Jne) {
-        // JE RIP+14
-        aCode[offset]     = 0x74;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      }
-
-      // Near call/jmp, absolute indirect, address given in r/m32
-      if (mType == JumpType::Call) {
-        // CALL [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x15;
-        // The offset to jump destination -- ie it is placed 2 bytes after the offset.
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 2;
-        aCode[offset + 2 + 4] = 0xeb;    // JMP +8 (jump over mJumpAddress)
-        aCode[offset + 2 + 4 + 1] = 8;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4 + 2) = mJumpAddress;
-        return offset + 2 + 4 + 2 + 8;
-      } else {
-        // JMP [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x25;
-        // The offset to jump destination is 0
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress;
-        return offset + 2 + 4 + 8;
-      }
-    }
-
-    size_t mHookOffset;
-    intptr_t mJumpAddress;
-    JumpType mType;
-  };
-
-#endif
-
-  enum ePrefixGroupBits
-  {
-    eNoPrefixes = 0,
-    ePrefixGroup1 = (1 << 0),
-    ePrefixGroup2 = (1 << 1),
-    ePrefixGroup3 = (1 << 2),
-    ePrefixGroup4 = (1 << 3)
-  };
-
-  int CountPrefixBytes(byteptr_t aBytes, const int aBytesIndex,
-                       unsigned char* aOutGroupBits)
-  {
-    unsigned char& groupBits = *aOutGroupBits;
-    groupBits = eNoPrefixes;
-    int index = aBytesIndex;
-    while (true) {
-      switch (aBytes[index]) {
-        // Group 1
-        case 0xF0: // LOCK
-        case 0xF2: // REPNZ
-        case 0xF3: // REP / REPZ
-          if (groupBits & ePrefixGroup1) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup1;
-          ++index;
-          break;
-
-        // Group 2
-        case 0x2E: // CS override / branch not taken
-        case 0x36: // SS override
-        case 0x3E: // DS override / branch taken
-        case 0x64: // FS override
-        case 0x65: // GS override
-          if (groupBits & ePrefixGroup2) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup2;
-          ++index;
-          break;
-
-        // Group 3
-        case 0x66: // operand size override
-          if (groupBits & ePrefixGroup3) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup3;
-          ++index;
-          break;
-
-        // Group 4
-        case 0x67: // Address size override
-          if (groupBits & ePrefixGroup4) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup4;
-          ++index;
-          break;
-
-        default:
-          return index - aBytesIndex;
-      }
-    }
-  }
-
-  // Return a ModR/M byte made from the 2 Mod bits, the register used for the
-  // reg bits and the register used for the R/M bits.
-  BYTE BuildModRmByte(BYTE aModBits, BYTE aReg, BYTE aRm)
-  {
-    MOZ_ASSERT((aRm & kMaskRm) == aRm);
-    MOZ_ASSERT((aModBits & kMaskMod) == aModBits);
-    MOZ_ASSERT(((aReg << kRegFieldShift) & kMaskReg) == (aReg << kRegFieldShift));
-    return aModBits | (aReg << kRegFieldShift) | aRm;
-  }
-
-  void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp)
-  {
-    *aOutTramp = nullptr;
-
-    AutoVirtualProtect protectHookPage(mHookPage, mMaxHooks * kHookSize,
-                                       PAGE_EXECUTE_READWRITE);
-    if (!protectHookPage.Protect()) {
-      return;
-    }
-
-    byteptr_t tramp = FindTrampolineSpace();
-    if (!tramp) {
-      return;
-    }
-
-    // We keep the address of the original function in the first bytes of
-    // the trampoline buffer
-    *((void**)tramp) = EncodePointer(aOrigFunction);
-    tramp += sizeof(void*);
-
-    byteptr_t origBytes = (byteptr_t)aOrigFunction;
-
-    // # of bytes of the original function that we can overwrite.
-    int nOrigBytes = 0;
-
-#if defined(_M_IX86)
-    int pJmp32 = -1;
-    while (nOrigBytes < 5) {
-      // Understand some simple instructions that might be found in a
-      // prologue; we might need to extend this as necessary.
-      //
-      // Note!  If we ever need to understand jump instructions, we'll
-      // need to rewrite the displacement argument.
-      unsigned char prefixGroups;
-      int numPrefixBytes = CountPrefixBytes(origBytes, nOrigBytes, &prefixGroups);
-      if (numPrefixBytes < 0 || (prefixGroups & (ePrefixGroup3 | ePrefixGroup4))) {
-        // Either the prefix sequence was bad, or there are prefixes that
-        // we don't currently support (groups 3 and 4)
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-      nOrigBytes += numPrefixBytes;
-      if (origBytes[nOrigBytes] >= 0x88 &&
-          origBytes[nOrigBytes] <= 0x8B) {
-        // various MOVs
-        ++nOrigBytes;
-        int len = CountModRmSib(origBytes + nOrigBytes);
-        if (len < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-          return;
-        }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0x0f &&
-                 (origBytes[nOrigBytes + 1] == 0x10 ||
-                  origBytes[nOrigBytes + 1] == 0x11)) {
-        // SSE: movups xmm, xmm/m128
-        //      movups xmm/m128, xmm
-        nOrigBytes += 2;
-        int len = CountModRmSib(origBytes + nOrigBytes);
-        if (len < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-          return;
-        }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0xA1) {
-        // MOV eax, [seg:offset]
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xB8) {
-        // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x33 &&
-                 (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
-        // XOR r32, r32
-        nOrigBytes += 2;
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0x40) {
-        // INC r32
-        nOrigBytes += 1;
-      } else if (origBytes[nOrigBytes] == 0x83) {
-        // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r/m, imm8
-        unsigned char b = origBytes[nOrigBytes + 1];
-        if ((b & 0xc0) == 0xc0) {
-          // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r, imm8
-          nOrigBytes += 3;
-        } else {
-          // bail
-          MOZ_ASSERT_UNREACHABLE("Unrecognized bit opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x68) {
-        // PUSH with 4-byte operand
-        nOrigBytes += 5;
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-        // 1-byte PUSH/POP
-        nOrigBytes++;
-      } else if (origBytes[nOrigBytes] == 0x6A) {
-        // PUSH imm8
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xe9) {
-        pJmp32 = nOrigBytes;
-        // jmp 32bit offset
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xff &&
-                 origBytes[nOrigBytes + 1] == 0x25) {
-        // jmp [disp32]
-        nOrigBytes += 6;
-      } else if (origBytes[nOrigBytes] == 0xc2) {
-        // ret imm16.  We can't handle this but it happens.  We don't ASSERT but we do fail to hook.
-#if defined(MOZILLA_INTERNAL_API)
-        NS_WARNING("Cannot hook method -- RET opcode found");
-#endif
-        return;
-      } else {
-        //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nOrigBytes]);
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-    }
-
-    // The trampoline is a copy of the instructions that we just traced,
-    // followed by a jump that we add below.
-    memcpy(tramp, aOrigFunction, nOrigBytes);
-#elif defined(_M_X64)
-    // The number of bytes used by the trampoline.
-    int nTrampBytes = 0;
-    bool foundJmp = false;
-
-    while (nOrigBytes < 13) {
-      // If we found JMP 32bit offset, we require that the next bytes must
-      // be NOP or INT3.  There is no reason to copy them.
-      // TODO: This used to trigger for Je as well.  Now that I allow
-      // instructions after CALL and JE, I don't think I need that.
-      // The only real value of this condition is that if code follows a JMP
-      // then its _probably_ the target of a JMP somewhere else and we
-      // will be overwriting it, which would be tragic.  This seems
-      // highly unlikely.
-      if (foundJmp) {
-        if (origBytes[nOrigBytes] == 0x90 || origBytes[nOrigBytes] == 0xcc) {
-          nOrigBytes++;
-          continue;
-        }
-        MOZ_ASSERT_UNREACHABLE("Opcode sequence includes commands after JMP");
-        return;
-      }
-      if (origBytes[nOrigBytes] == 0x0f) {
-        COPY_CODES(1);
-        if (origBytes[nOrigBytes] == 0x1f) {
-          // nop (multibyte)
-          COPY_CODES(1);
-          if ((origBytes[nOrigBytes] & 0xc0) == 0x40 &&
-              (origBytes[nOrigBytes] & 0x7) == 0x04) {
-            COPY_CODES(3);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x05) {
-          // syscall
-          COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] == 0x10 ||
-                   origBytes[nOrigBytes] == 0x11) {
-          // SSE: movups xmm, xmm/m128
-          //      movups xmm/m128, xmm
-          COPY_CODES(1);
-          int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes]);
-          if (nModRmSibBytes < 0) {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          } else {
-            COPY_CODES(nModRmSibBytes);
-          }
-        } else if (origBytes[nOrigBytes] == 0x84) {
-          // je rel32
-          JumpPatch jump(nTrampBytes - 1,  // overwrite the 0x0f we copied above
-                          (intptr_t)(origBytes + nOrigBytes + 5 +
-                                     *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                          JumpType::Je);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 5;
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x40 ||
-                 origBytes[nOrigBytes] == 0x41) {
-        // Plain REX or REX.B
-        COPY_CODES(1);
-        if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-          // push/pop with Rx register
-          COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] >= 0xb8 && origBytes[nOrigBytes] <= 0xbf) {
-          // mov r32, imm32
-          COPY_CODES(5);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x44) {
-        // REX.R
-        COPY_CODES(1);
+    // Set aOrigFunc here, because after this point, aHookDest might be called,
+    // and aHookDest might use the aOrigFunc pointer.
+    *aOrigFunc = reinterpret_cast<void*>(writableFn.GetCurrentAddress() +
+                                         sizeof(uint16_t));
 
-        // TODO: Combine with the "0x89" case below in the REX.W section
-        if (origBytes[nOrigBytes] == 0x89) {
-          // mov r/m32, r32
-          COPY_CODES(1);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-       } else if (origBytes[nOrigBytes] == 0x45) {
-        // REX.R & REX.B
-        COPY_CODES(1);
-
-        if (origBytes[nOrigBytes] == 0x33) {
-          // xor r32, r32
-          COPY_CODES(2);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if ((origBytes[nOrigBytes] & 0xfa) == 0x48) {
-        // REX.W | REX.WR | REX.WRB | REX.WB
-        COPY_CODES(1);
-
-        if (origBytes[nOrigBytes] == 0x81 &&
-            (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
-          // sub r, dword
-          COPY_CODES(6);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
-          // sub r, byte
-          COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == kModReg) {
-          // add r, byte
-          COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
-          // and [r+d], imm8
-          COPY_CODES(5);
-        } else if (origBytes[nOrigBytes] == 0x2b &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
-          // sub r64, r64
-          COPY_CODES(2);
-        } else if (origBytes[nOrigBytes] == 0x85) {
-          // 85 /r => TEST r/m32, r32
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0xc0) {
-            COPY_CODES(2);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if ((origBytes[nOrigBytes] & 0xfd) == 0x89) {
-          // MOV r/m64, r64 | MOV r64, r/m64
-          BYTE reg;
-          int len = CountModRmSib(origBytes + nOrigBytes + 1, &reg);
-          if (len < 0) {
-            MOZ_ASSERT(len == kModOperand64);
-            if (len != kModOperand64) {
-              return;
-            }
-            nOrigBytes += 2;   // skip the MOV and MOD R/M bytes
-
-            // The instruction MOVs 64-bit data from a RIP-relative memory
-            // address (determined with a 32-bit offset from RIP) into a
-            // 64-bit register.
-            int64_t* absAddr =
-              reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 4 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes));
-            nOrigBytes += 4;
-
-            if (reg == kRegAx) {
-              // Destination is RAX.  Encode instruction as MOVABS with a
-              // 64-bit absolute address as its immediate operand.
-              tramp[nTrampBytes] = 0xa1;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
-            } else {
-              // The MOV must be done in two steps.  First, we MOVABS the
-              // absolute 64-bit address into our target register.
-              // Then, we MOV from that address into the register
-              // using register-indirect addressing.
-              tramp[nTrampBytes] = 0xb8 + reg;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
-              tramp[nTrampBytes] = 0x48;
-              tramp[nTrampBytes+1] = 0x8b;
-              tramp[nTrampBytes+2] = BuildModRmByte(kModNoRegDisp, reg, reg);
-              nTrampBytes += 3;
-            }
-          } else {
-            COPY_CODES(len+1);
-          }
-        } else if (origBytes[nOrigBytes] == 0xc7) {
-          // MOV r/m64, imm32
-          if (origBytes[nOrigBytes + 1] == 0x44) {
-            // MOV [r64+disp8], imm32
-            // ModR/W + SIB + disp8 + imm32
-            COPY_CODES(8);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0xff) {
-          // JMP /4
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & 0x07) == 0x5) {
-            // [rip+disp32]
-            // convert JMP 32bit offset to JMP 64bit direct
-            JumpPatch jump(nTrampBytes - 1,  // overwrite the REX.W/REX.WR we copied above
-                           *reinterpret_cast<intptr_t*>(origBytes + nOrigBytes + 6 +
-                                                        *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2)),
-                           JumpType::Jmp);
-            nTrampBytes = jump.GenerateJump(tramp);
-            nOrigBytes += 6;
-            foundJmp = true;
-          } else {
-            // not support yet!
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x8d) {
-          // LEA reg, addr
-          if ((origBytes[nOrigBytes + 1] & kMaskMod) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & kMaskRm) == 0x5) {
-            // [rip+disp32]
-            // convert 32bit offset to 64bit direct and convert instruction
-            // to a simple 64-bit mov
-            BYTE reg = (origBytes[nOrigBytes + 1] & kMaskReg) >> kRegFieldShift;
-            intptr_t absAddr =
-              reinterpret_cast<intptr_t>(origBytes + nOrigBytes + 6 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-            nOrigBytes += 6;
-            tramp[nTrampBytes] = 0xb8 + reg;    // mov
-            ++nTrampBytes;
-            intptr_t* trampOperandPtr = reinterpret_cast<intptr_t*>(tramp + nTrampBytes);
-            *trampOperandPtr = absAddr;
-            nTrampBytes += 8;
-          } else {
-            // Above we dealt with RIP-relative instructions.  Any other
-            // operand form can simply be copied.
-            int len = CountModRmSib(origBytes + nOrigBytes + 1);
-            // We handled the kModOperand64 -- ie RIP-relative -- case above
-            MOZ_ASSERT(len > 0);
-            COPY_CODES(len + 1);
-          }
-        } else if (origBytes[nOrigBytes] == 0x63 &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
-          // movsxd r64, r32 (move + sign extend)
-          COPY_CODES(2);
-        } else {
-          // not support yet!
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x66) {
-        // operand override prefix
-        COPY_CODES(1);
-        // This is the same as the x86 version
-        if (origBytes[nOrigBytes] >= 0x88 && origBytes[nOrigBytes] <= 0x8B) {
-          // various MOVs
-          unsigned char b = origBytes[nOrigBytes + 1];
-          if (((b & 0xc0) == 0xc0) ||
-              (((b & 0xc0) == 0x00) &&
-               ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05))) {
-            // REG=r, R/M=r or REG=r, R/M=[r]
-            COPY_CODES(2);
-          } else if ((b & 0xc0) == 0x40) {
-            if ((b & 0x07) == 0x04) {
-              // REG=r, R/M=[SIB + disp8]
-              COPY_CODES(4);
-            } else {
-              // REG=r, R/M=[r + disp8]
-              COPY_CODES(3);
-            }
-          } else {
-            // complex MOV, bail
-            MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x44 &&
-                   origBytes[nOrigBytes+1] == 0x89) {
-          // mov word ptr [reg+disp8], reg
-          COPY_CODES(2);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            // no way to support this yet.
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        }
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-        // 1-byte push/pop
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0x65) {
-        // GS prefix
-        //
-        // The entry of GetKeyState on Windows 10 has the following code.
-        // 65 48 8b 04 25 30 00 00 00    mov   rax,qword ptr gs:[30h]
-        // (GS prefix + REX + MOV (0x8b) ...)
-        if (origBytes[nOrigBytes + 1] == 0x48 &&
-            (origBytes[nOrigBytes + 2] >= 0x88 && origBytes[nOrigBytes + 2] <= 0x8b)) {
-          COPY_CODES(3);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            // no way to support this yet.
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x80 &&
-                 origBytes[nOrigBytes + 1] == 0x3d) {
-        // cmp byte ptr [rip-relative address], imm8
-        // We'll compute the absolute address and do the cmp in r11
-
-        // push r11 (to save the old value)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x53;
-        ++nTrampBytes;
-
-        byteptr_t absAddr =
-          reinterpret_cast<byteptr_t>(origBytes + nOrigBytes + 7 +
-                                      *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-        nOrigBytes += 6;
-
-        // mov r11, absolute address
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0xbb;
-        ++nTrampBytes;
-
-        *reinterpret_cast<byteptr_t*>(tramp + nTrampBytes) = absAddr;
-        nTrampBytes += 8;
-
-        // cmp byte ptr [r11],...
-        tramp[nTrampBytes] = 0x41;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x80;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x3b;
-        ++nTrampBytes;
-
-        // ...imm8
-        COPY_CODES(1);
-
-        // pop r11 (doesn't affect the flags from the cmp)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x5b;
-        ++nTrampBytes;
-      } else if (origBytes[nOrigBytes] == 0x90) {
-        // nop
-        COPY_CODES(1);
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0xb8) {
-        // MOV r32, imm32
-        COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0x33) {
-        // xor r32, r/m32
-        COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xf6) {
-        // test r/m8, imm8 (used by ntdll on Windows 10 x64)
-        // (no flags are affected by near jmp since there is no task switch,
-        // so it is ok for a jmp to be written immediately after a test)
-        BYTE subOpcode = 0;
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1], &subOpcode);
-        if (nModRmSibBytes < 0 || subOpcode != 0) {
-          // Unsupported
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(2 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0x85) {
-        // test r/m32, r32
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1]);
-        if (nModRmSibBytes < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(1 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0xd1 &&
-                  (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
-        // bit shifts/rotates : (SA|SH|RO|RC)(R|L) r32
-        // (e.g. 0xd1 0xe0 is SAL, 0xd1 0xc8 is ROR)
-        COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xc3) {
-        // ret
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xcc) {
-        // int 3
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xe8 ||
-                 origBytes[nOrigBytes] == 0xe9) {
-        // CALL (0xe8) or JMP (0xe9) 32bit offset
-        foundJmp = origBytes[nOrigBytes] == 0xe9;
-        JumpPatch jump(nTrampBytes,
-                       (intptr_t)(origBytes + nOrigBytes + 5 +
-                                  *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                       origBytes[nOrigBytes] == 0xe8 ? JumpType::Call : JumpType::Jmp);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x74 || // je rel8 (0x74)
-                 origBytes[nOrigBytes] == 0x75) { // jne rel8 (0x75)
-        char offset = origBytes[nOrigBytes + 1];
-        auto jumpType = JumpType::Je;
-        if (origBytes[nOrigBytes] == 0x75)
-          jumpType = JumpType::Jne;
-        JumpPatch jump(nTrampBytes,
-          (intptr_t)(origBytes + nOrigBytes + 2 + offset), jumpType);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xff) {
-        if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == 0xf0) {
-          // push r64
-          COPY_CODES(2);
-        } else if (origBytes[nOrigBytes + 1] == 0x25) {
-          // jmp absolute indirect m32
-          foundJmp = true;
-          int32_t offset = *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-          int64_t* ptrToJmpDest = reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 6 + offset);
-          intptr_t jmpDest = static_cast<intptr_t>(*ptrToJmpDest);
-          JumpPatch jump(nTrampBytes, jmpDest, JumpType::Jmp);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 6;
-        } else if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == BuildModRmByte(kModReg, 2, 0)) {
-          // CALL reg (ff nn)
-          COPY_CODES(2);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x83 &&
-                 (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
-        // and [r+d], imm8
-        COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0xc6) {
-        // mov [r+d], imm8
-        int len = CountModRmSib(&origBytes[nOrigBytes + 1]);
-        if (len < 0) {
-          // RIP-relative not yet supported
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(len + 1);
-      } else {
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-    }
-#else
-#error "Unknown processor type"
-#endif
-
-    if (nOrigBytes > 100) {
-      //printf ("Too big!");
-      return;
-    }
-
-    // target address of the final jmp instruction in the trampoline
-    byteptr_t trampDest = origBytes + nOrigBytes;
-
-#if defined(_M_IX86)
-    if (pJmp32 >= 0) {
-      // Jump directly to the original target of the jump instead of jumping to the
-      // original function.
-      // Adjust jump target displacement to jump location in the trampoline.
-      *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp;
-    } else {
-      tramp[nOrigBytes] = 0xE9; // jmp
-      *((intptr_t*)(tramp + nOrigBytes + 1)) =
-        (intptr_t)trampDest - (intptr_t)(tramp + nOrigBytes + 5); // target displacement
-    }
-#elif defined(_M_X64)
-    // If the we found a Jmp, we don't need to add another instruction. However,
-    // if we found a _conditional_ jump or a CALL (or no control operations
-    // at all) then we still need to run the rest of aOriginalFunction.
-    if (!foundJmp) {
-      JumpPatch patch(nTrampBytes, reinterpret_cast<intptr_t>(trampDest));
-      patch.GenerateJump(tramp);
-    }
-#endif
-
-    // The trampoline is now valid.
-    *aOutTramp = tramp;
-
-    // ensure we can modify the original code
-    AutoVirtualProtect protect(aOrigFunction, nOrigBytes, PAGE_EXECUTE_READWRITE);
-    if (!protect.Protect()) {
-      return;
-    }
-
-#if defined(_M_IX86)
-    // now modify the original bytes
-    origBytes[0] = 0xE9; // jmp
-    *((intptr_t*)(origBytes + 1)) =
-      aDest - (intptr_t)(origBytes + 5); // target displacement
-#elif defined(_M_X64)
-    // mov r11, address
-    origBytes[0] = 0x49;
-    origBytes[1] = 0xbb;
-
-    *((intptr_t*)(origBytes + 2)) = aDest;
-
-    // jmp r11
-    origBytes[10] = 0x41;
-    origBytes[11] = 0xff;
-    origBytes[12] = 0xe3;
-#endif
-  }
-
-  byteptr_t FindTrampolineSpace()
-  {
-    if (mCurHooks >= mMaxHooks) {
-      return 0;
-    }
-
-    byteptr_t p = mHookPage + mCurHooks * kHookSize;
-
-    mCurHooks++;
-
-    return p;
-  }
-
-  static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
-    // If function entry is jmp rel8 stub to the internal implementation, we
-    // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
-      if (offset <= 0) {
-        // Bail out for negative offset: probably already patched by some
-        // third-party code.
-        return aOriginalFunction;
-      }
-
-      for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
-          // Bail out on insufficient nop space.
-          return aOriginalFunction;
-        }
-      }
-
-      return aOriginalFunction + 2 + offset;
-    }
-
-#if defined(_M_IX86)
-    // If function entry is jmp [disp32] such as used by kernel32,
-    // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (void*)(**((uint32_t**) (aOriginalFunction + 2)));
-    }
-#elif defined(_M_X64)
-    if (aOriginalFunction[0] == 0xe9) {
-      // require for TestDllInterceptor with --disable-optimize
-      int32_t offset = *((int32_t*)(aOriginalFunction + 1));
-      return aOriginalFunction + 5 + offset;
-    }
-#endif
-
-    return aOriginalFunction;
+    // Short jump up into our long jump.
+    writableFn.WriteShort(0xF9EB); // jmp $-5
+    return writableFn.Commit();
   }
 };
 
-} // namespace internal
-
-class WindowsDllInterceptor
-{
-  internal::WindowsDllNopSpacePatcher mNopSpacePatcher;
-  internal::WindowsDllDetourPatcher mDetourPatcher;
-
-  const char* mModuleName;
-  int mNHooks;
-
-public:
-  explicit WindowsDllInterceptor(const char* aModuleName = nullptr,
-                                 int aNumHooks = 0)
-    : mModuleName(nullptr)
-    , mNHooks(0)
-  {
-    if (aModuleName) {
-      Init(aModuleName, aNumHooks);
-    }
-  }
-
-  void Init(const char* aModuleName, int aNumHooks = 0)
-  {
-    if (mModuleName) {
-      return;
-    }
-
-    mModuleName = aModuleName;
-    mNHooks = aNumHooks;
-    mNopSpacePatcher.Init(aModuleName);
-
-    // Lazily initialize mDetourPatcher, since it allocates memory and we might
-    // not need it.
-  }
-
-  /**
-   * Hook/detour the method aName from the DLL we set in Init so that it calls
-   * aHookDest instead.  Returns the original method pointer in aOrigFunc
-   * and returns true if successful.
-   *
-   * IMPORTANT: If you use this method, please add your case to the
-   * TestDllInterceptor in order to detect future failures.  Even if this
-   * succeeds now, updates to the hooked DLL could cause it to fail in
-   * the future.
-   */
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Use a nop space patch if possible, otherwise fall back to a detour.
-    // This should be the preferred method for adding hooks.
-
-    if (!mModuleName) {
-      return false;
-    }
-
-    if (mNopSpacePatcher.AddHook(aName, aHookDest, aOrigFunc)) {
-      return true;
-    }
-
-    return AddDetour(aName, aHookDest, aOrigFunc);
-  }
-
-  /**
-   * Detour the method aName from the DLL we set in Init so that it calls
-   * aHookDest instead.  Returns the original method pointer in aOrigFunc
-   * and returns true if successful.
-   *
-   * IMPORTANT: If you use this method, please add your case to the
-   * TestDllInterceptor in order to detect future failures.  Even if this
-   * succeeds now, updates to the detoured DLL could cause it to fail in
-   * the future.
-   */
-  bool AddDetour(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Generally, code should not call this method directly. Use AddHook unless
-    // there is a specific need to avoid nop space patches.
-
-    if (!mModuleName) {
-      return false;
-    }
-
-    if (!mDetourPatcher.Initialized()) {
-      mDetourPatcher.Init(mModuleName, mNHooks);
-    }
-
-    return mDetourPatcher.AddHook(aName, aHookDest, aOrigFunc);
-  }
-};
-
+} // namespace interceptor
 } // namespace mozilla
 
-#endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */
+#endif // defined(_M_IX86)
+
+#endif // mozilla_interceptor_PatcherNopSpace_h
new file mode 100644
--- /dev/null
+++ b/mozglue/misc/interceptor/TargetFunction.h
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_interceptor_TargetFunction_h
+#define mozilla_interceptor_TargetFunction_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Types.h"
+
+#include <memory>
+
+namespace mozilla {
+namespace interceptor {
+
+template <typename MMPolicy>
+class MOZ_STACK_CLASS WritableTargetFunction final
+{
+public:
+  /**
+   * Used to initialize an invalid WritableTargetFunction, thus signalling an error.
+   */
+  explicit WritableTargetFunction(const MMPolicy& aMMPolicy)
+    : mMMPolicy(aMMPolicy)
+    , mFunc(0)
+    , mNumBytes(0)
+    , mOffset(0)
+    , mStartWriteOffset(0)
+    , mPrevProt(0)
+    , mAccumulatedStatus(false)
+  {
+  }
+
+  WritableTargetFunction(const MMPolicy& aMMPolicy, uintptr_t aFunc,
+                         size_t aNumBytes)
+    : mMMPolicy(aMMPolicy)
+    , mFunc(aFunc)
+    , mNumBytes(aNumBytes)
+    , mOffset(0)
+    , mStartWriteOffset(0)
+    , mPrevProt(0)
+    , mAccumulatedStatus(true)
+  {
+    aMMPolicy.Protect(reinterpret_cast<void*>(aFunc), aNumBytes,
+                      PAGE_EXECUTE_READWRITE, &mPrevProt);
+  }
+
+  WritableTargetFunction(WritableTargetFunction&& aOther)
+    : mMMPolicy(aOther.mMMPolicy)
+    , mFunc(aOther.mFunc)
+    , mNumBytes(aOther.mNumBytes)
+    , mOffset(aOther.mOffset)
+    , mStartWriteOffset(aOther.mStartWriteOffset)
+    , mPrevProt(aOther.mPrevProt)
+    , mLocalBytes(Move(aOther.mLocalBytes))
+    , mAccumulatedStatus(aOther.mAccumulatedStatus)
+  {
+    aOther.mPrevProt = 0;
+    aOther.mAccumulatedStatus = false;
+  }
+
+  ~WritableTargetFunction()
+  {
+    if (!mPrevProt) {
+      return;
+    }
+
+    MOZ_ASSERT(mLocalBytes.empty(), "Did you forget to call Commit?");
+
+    DebugOnly<bool> ok = mMMPolicy.Protect(reinterpret_cast<void*>(mFunc),
+                                           mNumBytes, mPrevProt, &mPrevProt);
+    MOZ_ASSERT(ok);
+  }
+
+  WritableTargetFunction(const WritableTargetFunction&) = delete;
+  WritableTargetFunction& operator=(const WritableTargetFunction&) = delete;
+  WritableTargetFunction& operator=(WritableTargetFunction&&) = delete;
+
+  /**
+   * @return true if data was successfully committed.
+   */
+  bool Commit()
+  {
+    if (!(*this)) {
+      return false;
+    }
+
+    if (mLocalBytes.empty()) {
+      // Nothing to commit, treat like success
+      return true;
+    }
+
+    bool ok = mMMPolicy.Write(reinterpret_cast<void*>(mFunc + mStartWriteOffset),
+                              mLocalBytes.begin(), mLocalBytes.length());
+    if (!ok) {
+      return false;
+    }
+
+    mMMPolicy.FlushInstructionCache();
+    mLocalBytes.clear();
+    return true;
+  }
+
+  explicit operator bool() const
+  {
+    return mPrevProt && mAccumulatedStatus;
+  }
+
+  void WriteByte(const uint8_t& aValue)
+  {
+    if (!mLocalBytes.append(aValue)) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    mOffset += sizeof(uint8_t);
+  }
+
+  Maybe<uint8_t> ReadByte()
+  {
+    // Reading is only permitted prior to any writing
+    MOZ_ASSERT(mOffset == mStartWriteOffset);
+    if (mOffset > mStartWriteOffset) {
+      mAccumulatedStatus = false;
+      return Nothing();
+    }
+
+    uint8_t value;
+    if (!mMMPolicy.Read(&value, reinterpret_cast<const void*>(mFunc + mOffset),
+                        sizeof(uint8_t))) {
+      mAccumulatedStatus = false;
+      return Nothing();
+    }
+
+    mOffset += sizeof(uint8_t);
+    mStartWriteOffset += sizeof(uint8_t);
+    return Some(value);
+  }
+
+  void WriteShort(const uint16_t& aValue)
+  {
+    if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aValue),
+                            sizeof(uint16_t))) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    mOffset += sizeof(uint16_t);
+  }
+
+  void WriteDisp32(const uintptr_t aAbsTarget)
+  {
+    intptr_t diff = static_cast<intptr_t>(aAbsTarget) -
+                    static_cast<intptr_t>(mFunc + mOffset + sizeof(int32_t));
+
+    CheckedInt<int32_t> checkedDisp(diff);
+    MOZ_ASSERT(checkedDisp.isValid());
+    if (!checkedDisp.isValid()) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    int32_t disp = checkedDisp.value();
+    if (!mLocalBytes.append(reinterpret_cast<uint8_t*>(&disp), sizeof(int32_t))) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    mOffset += sizeof(int32_t);
+  }
+
+  void WritePointer(const uintptr_t aAbsTarget)
+  {
+    if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aAbsTarget),
+                            sizeof(uintptr_t))) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    mOffset += sizeof(uintptr_t);
+  }
+
+  /**
+   * @param aValues N-sized array of type T that specifies the set of values
+   *                that are permissible in the first M bytes of the target
+   *                function at aOffset.
+   * @return true if M values of type T in the function are members of the
+   *         set specified by aValues.
+   */
+  template <typename T, size_t M, size_t N>
+  bool VerifyValuesAreOneOf(const T (&aValues)[N], const uint8_t aOffset = 0)
+  {
+    T buf[M];
+    if (!mMMPolicy.Read(buf, reinterpret_cast<const void*>(mFunc + mOffset + aOffset),
+                        M * sizeof(T))) {
+      return false;
+    }
+
+    for (auto&& fnValue : buf) {
+      bool match = false;
+      for (auto&& testValue : aValues) {
+        match |= (fnValue == testValue);
+      }
+
+      if (!match) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  uintptr_t GetCurrentAddress() const
+  {
+    return mFunc + mOffset;
+  }
+
+private:
+  const MMPolicy& mMMPolicy;
+  const uintptr_t mFunc;
+  const size_t mNumBytes;
+  uint32_t mOffset;
+  uint32_t mStartWriteOffset;
+  uint32_t mPrevProt;
+#if defined(_M_IX86)
+  static const size_t kInlineStorage = 16;
+#elif defined(_M_X64)
+  static const size_t kInlineStorage = 32;
+#endif
+  Vector<uint8_t, kInlineStorage> mLocalBytes;
+  bool mAccumulatedStatus;
+};
+
+template <typename MMPolicy>
+class ReadOnlyTargetBytes;
+
+template <>
+class ReadOnlyTargetBytes<MMPolicyInProcess>
+{
+public:
+  ReadOnlyTargetBytes(const MMPolicyInProcess& aMMPolicy, const void* aBase)
+    : mMMPolicy(aMMPolicy)
+    , mBase(reinterpret_cast<const uint8_t*>(aBase))
+  {
+  }
+
+  ReadOnlyTargetBytes(ReadOnlyTargetBytes&& aOther)
+    : mMMPolicy(aOther.mMMPolicy)
+    , mBase(aOther.mBase)
+  {
+  }
+
+  ReadOnlyTargetBytes(const ReadOnlyTargetBytes& aOther,
+                      const uint32_t aOffsetFromOther = 0)
+    : mMMPolicy(aOther.mMMPolicy)
+    , mBase(aOther.mBase + aOffsetFromOther)
+  {
+  }
+
+  void EnsureLimit(uint32_t aDesiredLimit)
+  {
+    // In the out-proc case we use this function to read the target function's
+    // bytes in the other process into a local buffer. We don't need that for
+    // the in-process case because we already have direct access to our target
+    // function's bytes.
+  }
+
+  bool IsValidAtOffset(const int8_t aOffset) const
+  {
+    if (!aOffset) {
+      return true;
+    }
+
+    uintptr_t base = reinterpret_cast<uintptr_t>(mBase);
+    uintptr_t adjusted = base + aOffset;
+    uint32_t pageSize = mMMPolicy.GetPageSize();
+
+    // If |adjusted| is within the same page as |mBase|, we're still valid
+    if ((base / pageSize) == (adjusted / pageSize)) {
+      return true;
+    }
+
+    // Otherwise, let's query |adjusted|
+    return mMMPolicy.IsPageAccessible(reinterpret_cast<void*>(adjusted));
+  }
+
+  const uint8_t* Get() const
+  {
+    return mBase;
+  }
+
+  const MMPolicyInProcess& GetMMPolicy() const
+  {
+    return mMMPolicy;
+  }
+
+  ReadOnlyTargetBytes& operator=(const ReadOnlyTargetBytes&) = delete;
+  ReadOnlyTargetBytes& operator=(ReadOnlyTargetBytes&&) = delete;
+
+private:
+  const MMPolicyInProcess&  mMMPolicy;
+  uint8_t const * const     mBase;
+};
+
+template <typename MMPolicy>
+class MOZ_STACK_CLASS ReadOnlyTargetFunction final
+{
+  template <typename TargetMMPolicy>
+  class TargetBytesPtr
+  {
+  public:
+    typedef TargetBytesPtr<TargetMMPolicy> Type;
+
+    static Type Make(const TargetMMPolicy& aMMPolicy, const void* aFunc)
+    {
+      return Move(TargetBytesPtr(aMMPolicy, aFunc));
+    }
+
+    static Type CopyFromOffset(const TargetBytesPtr& aOther,
+                               const uint32_t aOffsetFromOther)
+    {
+      return Move(TargetBytesPtr(aOther, aOffsetFromOther));
+    }
+
+    ReadOnlyTargetBytes<TargetMMPolicy>* operator->()
+    {
+      return &mTargetBytes;
+    }
+
+    TargetBytesPtr(TargetBytesPtr&& aOther)
+      : mTargetBytes(Move(aOther.mTargetBytes))
+    {
+    }
+
+    TargetBytesPtr(const TargetBytesPtr& aOther)
+      : mTargetBytes(aOther.mTargetBytes)
+    {
+    }
+
+    TargetBytesPtr& operator=(const TargetBytesPtr&) = delete;
+    TargetBytesPtr& operator=(TargetBytesPtr&&) = delete;
+
+  private:
+    TargetBytesPtr(const TargetMMPolicy& aMMPolicy, const void* aFunc)
+      : mTargetBytes(aMMPolicy, aFunc)
+    {
+    }
+
+    TargetBytesPtr(const TargetBytesPtr& aOther,
+                   const uint32_t aOffsetFromOther)
+      : mTargetBytes(aOther.mTargetBytes, aOffsetFromOther)
+    {
+    }
+
+    ReadOnlyTargetBytes<TargetMMPolicy> mTargetBytes;
+  };
+
+public:
+  ReadOnlyTargetFunction(const MMPolicy& aMMPolicy, const void* aFunc)
+    : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy, aFunc))
+    , mOffset(0)
+  {
+  }
+
+  ReadOnlyTargetFunction(const MMPolicy& aMMPolicy, uintptr_t aFunc)
+    : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy,
+        reinterpret_cast<const void*>(aFunc)))
+    , mOffset(0)
+  {
+  }
+
+  ReadOnlyTargetFunction(ReadOnlyTargetFunction&& aOther)
+    : mTargetBytes(Move(aOther.mTargetBytes))
+    , mOffset(aOther.mOffset)
+  {
+  }
+
+  ReadOnlyTargetFunction& operator=(const ReadOnlyTargetFunction&) = delete;
+  ReadOnlyTargetFunction& operator=(ReadOnlyTargetFunction&&) = delete;
+
+  ~ReadOnlyTargetFunction() = default;
+
+  ReadOnlyTargetFunction operator+(const uint32_t aOffset) const
+  {
+    return ReadOnlyTargetFunction(*this, mOffset + aOffset);
+  }
+
+  uintptr_t GetBaseAddress() const
+  {
+    return reinterpret_cast<uintptr_t>(mTargetBytes->Get());
+  }
+
+  uintptr_t GetAddress() const
+  {
+    return reinterpret_cast<uintptr_t>(mTargetBytes->Get() + mOffset);
+  }
+
+  uintptr_t AsEncodedPtr() const
+  {
+    return EncodePtr(const_cast<uint8_t*>(mTargetBytes->Get() + mOffset));
+  }
+
+  static uintptr_t EncodePtr(void* aPtr)
+  {
+    return reinterpret_cast<uintptr_t>(::EncodePointer(aPtr));
+  }
+
+  static uintptr_t DecodePtr(uintptr_t aEncodedPtr)
+  {
+    return reinterpret_cast<uintptr_t>(
+      ::DecodePointer(reinterpret_cast<PVOID>(aEncodedPtr)));
+  }
+
+  uint8_t const & operator*() const
+  {
+    mTargetBytes->EnsureLimit(mOffset);
+    return *(mTargetBytes->Get() + mOffset);
+  }
+
+  uint8_t const & operator[](uint32_t aIndex) const
+  {
+    mTargetBytes->EnsureLimit(mOffset + aIndex);
+    return *(mTargetBytes->Get() + mOffset + aIndex);
+  }
+
+  ReadOnlyTargetFunction& operator++()
+  {
+    ++mOffset;
+    return *this;
+  }
+
+  ReadOnlyTargetFunction& operator+=(uint32_t aDelta)
+  {
+    mOffset += aDelta;
+    return *this;
+  }
+
+  uint32_t GetOffset() const
+  {
+    return mOffset;
+  }
+
+  uintptr_t ReadDisp32AsAbsolute()
+  {
+    mTargetBytes->EnsureLimit(mOffset + sizeof(int32_t));
+    int32_t disp = *reinterpret_cast<const int32_t*>(mTargetBytes->Get() + mOffset);
+    uintptr_t result = reinterpret_cast<uintptr_t>(
+        mTargetBytes->Get() + mOffset + sizeof(int32_t) + disp);
+    mOffset += sizeof(int32_t);
+    return result;
+  }
+
+  uintptr_t OffsetToAbsolute(const uint8_t aOffset) const
+  {
+    return reinterpret_cast<uintptr_t>(mTargetBytes->Get() + mOffset + aOffset);
+  }
+
+  /**
+   * This method promotes the code referenced by this object to be writable.
+   *
+   * @param aLen    The length of the function's code to make writable. If set
+   *                to zero, this object's current offset is used as the length.
+   * @param aOffset The result's base address will be offset from this
+   *                object's base address by |aOffset| bytes. This value may be
+   *                negative.
+   */
+  WritableTargetFunction<MMPolicy> Promote(const uint32_t aLen = 0,
+                                           const int8_t aOffset = 0) const
+  {
+    const uint32_t effectiveLength = aLen ? aLen : mOffset;
+    MOZ_RELEASE_ASSERT(effectiveLength, "Cannot Promote a zero-length function");
+
+    if (!mTargetBytes->IsValidAtOffset(aOffset)) {
+      return WritableTargetFunction<MMPolicy>(mTargetBytes->GetMMPolicy());
+    }
+
+    WritableTargetFunction<MMPolicy> result(
+      mTargetBytes->GetMMPolicy(),
+      reinterpret_cast<uintptr_t>(mTargetBytes->Get() + aOffset),
+      effectiveLength);
+
+    return Move(result);
+  }
+
+private:
+  template <typename T>
+  struct ChasePointerHelper
+  {
+    template <typename MMPolicy>
+    static T Result(const MMPolicy&, T aValue)
+    {
+      return aValue;
+    }
+  };
+
+  template <typename T>
+  struct ChasePointerHelper<T*>
+  {
+    template <typename MMPolicy>
+    static auto Result(const MMPolicy& aPolicy, T* aValue)
+    {
+      ReadOnlyTargetFunction<MMPolicy> ptr(aPolicy, aValue);
+      return ptr.ChasePointer<T>();
+    }
+  };
+
+public:
+  // Keep chasing pointers until T is not a pointer type anymore
+  template <typename T>
+  auto ChasePointer()
+  {
+    mTargetBytes->EnsureLimit(mOffset + sizeof(T));
+    const typename RemoveCV<T>::Type result = *reinterpret_cast<const RemoveCV<T>::Type*>(mTargetBytes->Get() + mOffset);
+    return ChasePointerHelper<typename RemoveCV<T>::Type>::Result(mTargetBytes->GetMMPolicy(), result);
+  }
+
+  uintptr_t ChasePointerFromDisp()
+  {
+    uintptr_t ptrFromDisp = ReadDispAsAbsolute();
+    ReadOnlyTargetFunction<MMPolicy> ptr(mTargetBytes->GetMMPolicy(),
+                                         reinterpret_cast<const void*>(ptrFromDisp));
+    return ptr.template ChasePointer<uintptr_t>();
+  }
+
+private:
+  ReadOnlyTargetFunction(const ReadOnlyTargetFunction& aOther)
+    : mTargetBytes(aOther.mTargetBytes)
+    , mOffset(aOther.mOffset)
+  {
+  }
+
+  ReadOnlyTargetFunction(const ReadOnlyTargetFunction& aOther,
+                         const uint32_t aOffsetFromOther)
+    : mTargetBytes(TargetBytesPtr<MMPolicy>::CopyFromOffset(aOther.mTargetBytes,
+                                                            aOffsetFromOther))
+    , mOffset(0)
+  {
+  }
+
+private:
+  mutable typename TargetBytesPtr<MMPolicy>::Type mTargetBytes;
+  uint32_t                                        mOffset;
+};
+
+} // namespace interceptor
+} // namespace mozilla
+
+#endif // mozilla_interceptor_TargetFunction_h
new file mode 100644
--- /dev/null
+++ b/mozglue/misc/interceptor/Trampoline.h
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_interceptor_Trampoline_h
+#define mozilla_interceptor_Trampoline_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Types.h"
+
+namespace mozilla {
+namespace interceptor {
+
+template <typename MMPolicy>
+class MOZ_STACK_CLASS Trampoline final
+{
+public:
+  Trampoline(const MMPolicy* aMMPolicy, uint8_t* const aLocalBase,
+             const uintptr_t aRemoteBase, const uint32_t aChunkSize)
+    : mMMPolicy(aMMPolicy)
+    , mPrevLocalProt(0)
+    , mLocalBase(aLocalBase)
+    , mRemoteBase(aRemoteBase)
+    , mOffset(0)
+    , mExeOffset(0)
+    , mMaxOffset(aChunkSize)
+    , mAccumulatedStatus(true)
+  {
+    ::VirtualProtect(aLocalBase, aChunkSize, PAGE_EXECUTE_READWRITE,
+                     &mPrevLocalProt);
+  }
+
+  Trampoline(Trampoline&& aOther)
+    : mMMPolicy(aOther.mMMPolicy)
+    , mPrevLocalProt(aOther.mPrevLocalProt)
+    , mLocalBase(aOther.mLocalBase)
+    , mRemoteBase(aOther.mRemoteBase)
+    , mOffset(aOther.mOffset)
+    , mExeOffset(aOther.mExeOffset)
+    , mMaxOffset(aOther.mMaxOffset)
+    , mAccumulatedStatus(aOther.mAccumulatedStatus)
+  {
+    aOther.mPrevLocalProt = 0;
+    aOther.mAccumulatedStatus = false;
+  }
+
+  MOZ_IMPLICIT Trampoline(decltype(nullptr))
+    : mMMPolicy(nullptr)
+    , mPrevLocalProt(0)
+    , mLocalBase(nullptr)
+    , mRemoteBase(0)
+    , mOffset(0)
+    , mExeOffset(0)
+    , mMaxOffset(0)
+    , mAccumulatedStatus(false)
+  {
+  }
+
+  Trampoline(const Trampoline&) = delete;
+  Trampoline& operator=(const Trampoline&) = delete;
+  Trampoline&& operator=(Trampoline&&) = delete;
+
+  ~Trampoline()
+  {
+    if (!mLocalBase || !mPrevLocalProt) {
+      return;
+    }
+
+    ::VirtualProtect(mLocalBase, mMaxOffset, mPrevLocalProt, &mPrevLocalProt);
+  }
+
+  explicit operator bool() const
+  {
+    return mLocalBase && mRemoteBase && mPrevLocalProt && mAccumulatedStatus;
+  }
+
+  void WriteByte(uint8_t aValue)
+  {
+    if (mOffset >= mMaxOffset) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    *(mLocalBase + mOffset) = aValue;
+    ++mOffset;
+  }
+
+  void WriteInteger(int32_t aValue)
+  {
+    if (mOffset + sizeof(int32_t) > mMaxOffset) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    *reinterpret_cast<int32_t*>(mLocalBase + mOffset) = aValue;
+    mOffset += sizeof(int32_t);
+  }
+
+  void WritePointer(uintptr_t aValue)
+  {
+    if (mOffset + sizeof(uintptr_t) > mMaxOffset) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    *reinterpret_cast<uintptr_t*>(mLocalBase + mOffset) = aValue;
+    mOffset += sizeof(uintptr_t);
+  }
+
+  void WriteEncodedPointer(void* aValue)
+  {
+    uintptr_t encoded = ReadOnlyTargetFunction<MMPolicy>::EncodePtr(aValue);
+    WritePointer(encoded);
+  }
+
+  Maybe<uintptr_t> ReadPointer()
+  {
+    if (mOffset + sizeof(uintptr_t) > mMaxOffset) {
+      mAccumulatedStatus = false;
+      return Nothing();
+    }
+
+    auto result = Some(*reinterpret_cast<uintptr_t*>(mLocalBase + mOffset));
+    mOffset += sizeof(uintptr_t);
+    return Move(result);
+  }
+
+  Maybe<uintptr_t> ReadEncodedPointer()
+  {
+    Maybe<uintptr_t> encoded(ReadPointer());
+    if (!encoded) {
+      return encoded;
+    }
+
+    return Some(ReadOnlyTargetFunction<MMPolicy>::DecodePtr(encoded.value()));
+  }
+
+  void WriteDisp32(uintptr_t aAbsTarget)
+  {
+    if (mOffset + sizeof(int32_t) > mMaxOffset) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    // This needs to be computed from the remote location
+    intptr_t remoteTrampPosition = static_cast<intptr_t>(mRemoteBase + mOffset);
+
+    intptr_t diff = static_cast<intptr_t>(aAbsTarget) -
+                      (remoteTrampPosition + sizeof(int32_t));
+
+    CheckedInt<int32_t> checkedDisp(diff);
+    MOZ_ASSERT(checkedDisp.isValid());
+    if (!checkedDisp.isValid()) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    int32_t disp = checkedDisp.value();
+    *reinterpret_cast<int32_t*>(mLocalBase + mOffset) = disp;
+    mOffset += sizeof(int32_t);
+  }
+
+#if defined(_M_IX86)
+  // 32-bit only
+  void AdjustDisp32AtOffset(uint32_t aOffset, uintptr_t aAbsTarget)
+  {
+    uint32_t effectiveOffset = mExeOffset + aOffset;
+
+    if (effectiveOffset + sizeof(int32_t) > mMaxOffset) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    intptr_t diff = static_cast<intptr_t>(aAbsTarget) -
+                    static_cast<intptr_t>(mRemoteBase + mExeOffset);
+    *reinterpret_cast<int32_t*>(mLocalBase + effectiveOffset) += diff;
+  }
+#endif // defined(_M_IX86)
+
+  void CopyFrom(uintptr_t aOrigBytes, uint32_t aNumBytes)
+  {
+    if (!mMMPolicy || mOffset + aNumBytes > mMaxOffset) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    if (!mMMPolicy->Read(mLocalBase + mOffset,
+                         reinterpret_cast<void*>(aOrigBytes), aNumBytes)) {
+      mAccumulatedStatus = false;
+      return;
+    }
+
+    mOffset += aNumBytes;
+  }
+
+  void Rewind()
+  {
+    mOffset = 0;
+  }
+
+  uintptr_t GetCurrentRemoteAddress() const
+  {
+    return mRemoteBase + mOffset;
+  }
+
+  void StartExecutableCode()
+  {
+    MOZ_ASSERT(!mExeOffset);
+    mExeOffset = mOffset;
+  }
+
+  void* EndExecutableCode() const
+  {
+    if (!mAccumulatedStatus) {
+      return nullptr;
+    }
+
+    // This must always return the start address the executable code
+    // *in the target process*
+    return reinterpret_cast<void*>(mRemoteBase + mExeOffset);
+  }
+
+  Trampoline<MMPolicy>& operator--()
+  {
+    MOZ_ASSERT(mOffset);
+    --mOffset;
+    return *this;
+  }
+
+private:
+  const MMPolicy* mMMPolicy;
+  DWORD           mPrevLocalProt;
+  uint8_t* const  mLocalBase;
+  const uintptr_t mRemoteBase;
+  uint32_t        mOffset;
+  uint32_t        mExeOffset;
+  const uint32_t  mMaxOffset;
+  bool            mAccumulatedStatus;
+};
+
+template <typename MMPolicy>
+class MOZ_STACK_CLASS TrampolineCollection final
+{
+public:
+  class MOZ_STACK_CLASS TrampolineIterator final
+  {
+  public:
+    Trampoline<MMPolicy> operator*()
+    {
+      uint32_t offset = mCurTramp * mCollection.mTrampSize;
+      return Trampoline<MMPolicy>(nullptr,
+                                  mCollection.mLocalBase + offset,
+                                  mCollection.mRemoteBase + offset,
+                                  mCollection.mTrampSize);
+    }
+
+    TrampolineIterator& operator++()
+    {
+      ++mCurTramp;
+      return *this;
+    }
+
+    bool operator!=(const TrampolineIterator& aOther) const
+    {
+      return mCurTramp != aOther.mCurTramp;
+    }
+
+  private:
+    explicit TrampolineIterator(
+        const TrampolineCollection<MMPolicy>& aCollection,
+        const uint32_t aCurTramp = 0)
+      : mCollection(aCollection)
+      , mCurTramp(aCurTramp)
+    {
+    }
+
+    const TrampolineCollection<MMPolicy>& mCollection;
+    uint32_t  mCurTramp;
+
+    friend class TrampolineCollection<MMPolicy>;
+  };
+
+  explicit TrampolineCollection(const MMPolicy& aMMPolicy)
+    : mMMPolicy(aMMPolicy)
+    , mLocalBase(0)
+    , mRemoteBase(0)
+    , mTrampSize(0)
+    , mNumTramps(0)
+    , mPrevProt(0)
+  {
+  }
+
+  TrampolineCollection(const MMPolicy& aMMPolicy, uint8_t* const aLocalBase,
+                       const uintptr_t aRemoteBase, const uint32_t aTrampSize,
+                       const uint32_t aNumTramps)
+    : mMMPolicy(aMMPolicy)
+    , mLocalBase(aLocalBase)
+    , mRemoteBase(aRemoteBase)
+    , mTrampSize(aTrampSize)
+    , mNumTramps(aNumTramps)
+    , mPrevProt(0)
+  {
+    if (!aNumTramps) {
+      return;
+    }
+
+    DebugOnly<BOOL> ok = mMMPolicy.Protect(aLocalBase, aNumTramps * aTrampSize,
+                                           PAGE_EXECUTE_READWRITE, &mPrevProt);
+    MOZ_ASSERT(ok);
+  }
+
+  ~TrampolineCollection()
+  {
+    if (!mPrevProt) {
+      return;
+    }
+
+    mMMPolicy.Protect(mLocalBase, mNumTramps * mTrampSize,
+                      mPrevProt, &mPrevProt);
+  }
+
+  TrampolineIterator begin() const
+  {
+    if (!mPrevProt) {
+      return end();
+    }
+
+    return TrampolineIterator(*this);
+  }
+
+  TrampolineIterator end() const
+  {
+    return TrampolineIterator(*this, mNumTramps);
+  }
+
+  TrampolineCollection(const TrampolineCollection&) = delete;
+  TrampolineCollection& operator=(const TrampolineCollection&) = delete;
+  TrampolineCollection& operator=(TrampolineCollection&&) = delete;
+
+  TrampolineCollection(TrampolineCollection&& aOther)
+    : mMMPolicy(aOther.mMMPolicy)
+    , mLocalBase(aOther.mLocalBase)
+    , mRemoteBase(aOther.mRemoteBase)
+    , mTrampSize(aOther.mTrampSize)
+    , mNumTramps(aOther.mNumTramps)
+    , mPrevProt(aOther.mPrevProt)
+  {
+    aOther.mPrevProt = 0;
+  }
+
+private:
+  const MMPolicy& mMMPolicy;
+  uint8_t* const  mLocalBase;
+  const uintptr_t mRemoteBase;
+  const uint32_t  mTrampSize;
+  const uint32_t  mNumTramps;
+  uint32_t        mPrevProt;
+
+  friend class TrampolineIterator;
+};
+
+} // namespace interceptor
+} // namespace mozilla
+
+#endif // mozilla_interceptor_Trampoline_h
new file mode 100644
--- /dev/null
+++ b/mozglue/misc/interceptor/VMSharingPolicies.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_interceptor_VMSharingPolicies_h
+#define mozilla_interceptor_VMSharingPolicies_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Types.h"
+
+namespace mozilla {
+namespace interceptor {
+
+template <typename MMPolicy, uint32_t kChunkSize>
+class VMSharingPolicyUnique : public MMPolicy
+{
+public:
+  template <typename... Args>
+  explicit VMSharingPolicyUnique(Args... aArgs)
+    : MMPolicy(mozilla::Forward<Args>(aArgs)...)
+    , mNextChunkIndex(0)
+  {
+  }
+
+  bool Reserve(uint32_t aCount)
+  {
+    MOZ_ASSERT(aCount);
+    uint32_t bytesReserved = MMPolicy::Reserve(aCount * kChunkSize);
+    return !!bytesReserved;
+  }
+
+  Trampoline<MMPolicy> GetNextTrampoline()
+  {
+    uint32_t offset = mNextChunkIndex * kChunkSize;
+    if (!MaybeCommitNextPage(offset, kChunkSize)) {
+      return nullptr;
+    }
+
+
+    Trampoline<MMPolicy> result(this, GetLocalView() + offset,
+                                GetRemoteView() + offset, kChunkSize);
+    if (!!result) {
+      ++mNextChunkIndex;
+    }
+
+    return Move(result);
+  }
+
+  TrampolineCollection<MMPolicy> Items() const
+  {
+    return TrampolineCollection<MMPolicy>(*this, GetLocalView(), GetRemoteView(),
+                                          kChunkSize, mNextChunkIndex);
+  }
+
+  void Clear()
+  {
+    mNextChunkIndex = 0;
+  }
+
+  ~VMSharingPolicyUnique() = default;
+
+  VMSharingPolicyUnique(const VMSharingPolicyUnique&) = delete;
+  VMSharingPolicyUnique& operator=(const VMSharingPolicyUnique&) = delete;
+
+  VMSharingPolicyUnique(VMSharingPolicyUnique&& aOther)
+    : MMPolicy(Move(aOther))
+    , mNextChunkIndex(aOther.mNextChunkIndex)
+  {
+    aOther.mNextChunkIndex = 0;
+  }
+
+  VMSharingPolicyUnique& operator=(VMSharingPolicyUnique&& aOther)
+  {
+    static_cast<MMPolicy&>(*this) = Move(aOther);
+    mNextChunkIndex = aOther.mNextChunkIndex;
+    aOther.mNextChunkIndex = 0;
+    return *this;
+  }
+
+private:
+  uint32_t  mNextChunkIndex;
+};
+
+} // namespace interceptor
+} // namespace mozilla
+
+#endif // mozilla_interceptor_VMSharingPolicies_h
+
--- a/mozglue/misc/moz.build
+++ b/mozglue/misc/moz.build
@@ -28,16 +28,28 @@ SOURCES += [
     'TimeStamp.cpp',
 ]
 
 OS_LIBS += CONFIG['REALTIME_LIBS']
 
 DEFINES['IMPL_MFBT'] = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
+    EXPORTS += [
+        'nsWindowsDllInterceptor.h',
+    ]
+    EXPORTS.mozilla.interceptor += [
+        'interceptor/MMPolicies.h',
+        'interceptor/PatcherBase.h',
+        'interceptor/PatcherDetour.h',
+        'interceptor/PatcherNopSpace.h',
+        'interceptor/TargetFunction.h',
+        'interceptor/Trampoline.h',
+        'interceptor/VMSharingPolicies.h',
+    ]
     SOURCES += [
         'TimeStamp_windows.cpp',
     ]
     OS_LIBS += ['dbghelp']
 elif CONFIG['HAVE_CLOCK_MONOTONIC']:
     SOURCES += [
         'TimeStamp_posix.cpp',
     ]
copy from xpcom/build/nsWindowsDllInterceptor.h
copy to mozglue/misc/nsWindowsDllInterceptor.h
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/mozglue/misc/nsWindowsDllInterceptor.h
@@ -4,23 +4,34 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NS_WINDOWS_DLL_INTERCEPTOR_H_
 #define NS_WINDOWS_DLL_INTERCEPTOR_H_
 
 #include "mozilla/Assertions.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Types.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
 #include "nsWindowsHelpers.h"
 
 #include <wchar.h>
 #include <windows.h>
 #include <winternl.h>
 
+#include "mozilla/interceptor/MMPolicies.h"
+#include "mozilla/interceptor/PatcherDetour.h"
+#include "mozilla/interceptor/PatcherNopSpace.h"
+#include "mozilla/interceptor/VMSharingPolicies.h"
+
 /*
  * Simple function interception.
  *
  * We have two separate mechanisms for intercepting a function: We can use the
  * built-in nop space, if it exists, or we can create a detour.
  *
  * Using the built-in nop space works as follows: On x86-32, DLL functions
  * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of
@@ -63,1409 +74,127 @@
  * patched again to jump directly to the trampoline instead of going through
  * the hook function. As such, re-intercepting the same function won't work, as
  * jump instructions are not supported.
  *
  * Note that this is not thread-safe.  Sad day.
  *
  */
 
-#include <stdint.h>
-
-#define COPY_CODES(NBYTES)  do {    \
-  memcpy(&tramp[nTrampBytes], &origBytes[nOrigBytes], NBYTES);    \
-  nOrigBytes += NBYTES;             \
-  nTrampBytes += NBYTES;            \
-} while (0)
-
 namespace mozilla {
-namespace internal {
-
-class AutoVirtualProtect
-{
-public:
-  AutoVirtualProtect(void* aFunc, size_t aSize, DWORD aProtect)
-    : mFunc(aFunc), mSize(aSize), mNewProtect(aProtect), mOldProtect(0),
-      mSuccess(false)
-  {}
+namespace interceptor {
 
-  ~AutoVirtualProtect()
-  {
-    if (mSuccess) {
-      VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, mOldProtect,
-                       &mOldProtect);
-    }
-  }
-
-  bool Protect()
-  {
-    mSuccess = !!VirtualProtectEx(GetCurrentProcess(), mFunc, mSize,
-                                  mNewProtect, &mOldProtect);
-    if (!mSuccess) {
-      // printf("VirtualProtectEx failed! %d\n", GetLastError());
-    }
-    return mSuccess;
-  }
-
-private:
-  void* const mFunc;
-  size_t const mSize;
-  DWORD const mNewProtect;
-  DWORD mOldProtect;
-  bool mSuccess;
+enum
+{
+  kDefaultTrampolineSize = 128
 };
 
-class WindowsDllNopSpacePatcher
+template <typename VMPolicy =
+            mozilla::interceptor::VMSharingPolicyUnique<
+              mozilla::interceptor::MMPolicyInProcess, kDefaultTrampolineSize>>
+class WindowsDllInterceptor final
 {
-  typedef uint8_t* byteptr_t;
-  HMODULE mModule;
+  interceptor::WindowsDllDetourPatcher<VMPolicy> mDetourPatcher;
+#if defined(_M_IX86)
+  interceptor::WindowsDllNopSpacePatcher<typename VMPolicy::MMPolicyT> mNopSpacePatcher;
+#endif // defined(_M_IX86)
 
-  // Dumb array for remembering the addresses of functions we've patched.
-  // (This should be nsTArray, but non-XPCOM code uses this class.)
-  static const size_t maxPatchedFns = 16;
-  byteptr_t mPatchedFns[maxPatchedFns];
-  size_t mPatchedFnsLen;
+  HMODULE mModule;
+  int mNHooks;
 
 public:
-  WindowsDllNopSpacePatcher()
-    : mModule(0)
-    , mPatchedFnsLen(0)
-  {}
-
+  template <typename... Args>
+  explicit WindowsDllInterceptor(Args... aArgs)
+    : mDetourPatcher(mozilla::Forward<Args>(aArgs)...)
 #if defined(_M_IX86)
-  ~WindowsDllNopSpacePatcher()
-  {
-    // Restore the mov edi, edi to the beginning of each function we patched.
-
-    for (size_t i = 0; i < mPatchedFnsLen; i++) {
-      byteptr_t fn = mPatchedFns[i];
-
-      // Ensure we can write to the code.
-      AutoVirtualProtect protect(fn, 2, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
-        continue;
-      }
-
-      // mov edi, edi
-      *((uint16_t*)fn) = 0xff8b;
-
-      // I don't think this is actually necessary, but it can't hurt.
-      FlushInstructionCache(GetCurrentProcess(),
-                            /* ignored */ nullptr,
-                            /* ignored */ 0);
-    }
-  }
-
-  void Init(const char* aModuleName)
-  {
-    if (!IsCompatible()) {
-#if defined(MOZILLA_INTERNAL_API)
-      NS_WARNING("NOP space patching is unavailable for compatibility reasons");
-#endif
-      return;
-    }
-
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
-      return;
-    }
-  }
-
-  /**
-   * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions
-   * in our address space. There is a bug in Detours 2.x that causes it to
-   * patch at the wrong address when attempting to detour code that is already
-   * NOP space patched. This function is an effort to detect the presence of
-   * this NVIDIA code in our address space and disable NOP space patching if it
-   * is. We also check AppInit_DLLs since this is the mechanism that the Optimus
-   * drivers use to inject into our process.
-   */
-  static bool IsCompatible()
-  {
-    // These DLLs are known to have bad interactions with this style of patching
-    const wchar_t* kIncompatibleDLLs[] = {
-      L"detoured.dll",
-      L"_etoured.dll",
-      L"nvd3d9wrap.dll",
-      L"nvdxgiwrap.dll"
-    };
-    // See if the infringing DLLs are already loaded
-    for (unsigned int i = 0; i < mozilla::ArrayLength(kIncompatibleDLLs); ++i) {
-      if (GetModuleHandleW(kIncompatibleDLLs[i])) {
-        return false;
-      }
-    }
-    if (GetModuleHandleW(L"user32.dll")) {
-      // user32 is loaded but the infringing DLLs are not, assume we're safe to
-      // proceed.
-      return true;
-    }
-    // If user32 has not loaded yet, check AppInit_DLLs to ensure that Optimus
-    // won't be loaded once user32 is initialized.
-    HKEY hkey = NULL;
-    if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
-          0, KEY_QUERY_VALUE, &hkey)) {
-      nsAutoRegKey key(hkey);
-      DWORD numBytes = 0;
-      const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
-      // Query for required buffer size
-      LONG status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
-                                     nullptr, nullptr, &numBytes);
-      mozilla::UniquePtr<wchar_t[]> data;
-      if (!status) {
-        // Allocate the buffer and query for the actual data
-        data = mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
-        status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
-                                  nullptr, (LPBYTE)data.get(), &numBytes);
-      }
-      if (!status) {
-        // For each token, split up the filename components and then check the
-        // name of the file.
-        const wchar_t kDelimiters[] = L", ";
-        wchar_t* tokenContext = nullptr;
-        wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
-        while (token) {
-          wchar_t fname[_MAX_FNAME] = {0};
-          if (!_wsplitpath_s(token, nullptr, 0, nullptr, 0,
-                             fname, mozilla::ArrayLength(fname),
-                             nullptr, 0)) {
-            // nvinit.dll is responsible for bootstrapping the DLL injection, so
-            // that is the library that we check for here
-            const wchar_t kNvInitName[] = L"nvinit";
-            if (!_wcsnicmp(fname, kNvInitName,
-                           mozilla::ArrayLength(kNvInitName))) {
-              return false;
-            }
-          }
-          token = wcstok_s(nullptr, kDelimiters, &tokenContext);
-        }
-      }
-    }
-    return true;
-  }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    if (!mModule) {
-      return false;
-    }
-
-    if (!IsCompatible()) {
-#if defined(MOZILLA_INTERNAL_API)
-      NS_WARNING("NOP space patching is unavailable for compatibility reasons");
-#endif
-      return false;
-    }
-
-    MOZ_RELEASE_ASSERT(mPatchedFnsLen < maxPatchedFns, "No room for the hook");
-
-    byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName));
-    if (!fn) {
-      //printf ("GetProcAddress failed\n");
-      return false;
-    }
-
-    fn = ResolveRedirectedAddress(fn);
-
-    // Ensure we can read and write starting at fn - 5 (for the long jmp we're
-    // going to write) and ending at fn + 2 (for the short jmp up to the long
-    // jmp). These bytes may span two pages with different protection.
-    AutoVirtualProtect protectBefore(fn - 5, 5, PAGE_EXECUTE_READWRITE);
-    AutoVirtualProtect protectAfter(fn, 2, PAGE_EXECUTE_READWRITE);
-    if (!protectBefore.Protect() || !protectAfter.Protect()) {
-      return false;
-    }
-
-    bool rv = WriteHook(fn, aHookDest, aOrigFunc);
-
-    if (rv) {
-      mPatchedFns[mPatchedFnsLen] = fn;
-      mPatchedFnsLen++;
-    }
-
-    return rv;
-  }
-
-  bool WriteHook(byteptr_t aFn, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Check that the 5 bytes before aFn are NOP's or INT 3's,
-    // and that the 2 bytes after aFn are mov(edi, edi).
-    //
-    // It's safe to read aFn[-5] because we set it to PAGE_EXECUTE_READWRITE
-    // before calling WriteHook.
-
-    for (int i = -5; i <= -1; i++) {
-      if (aFn[i] != 0x90 && aFn[i] != 0xcc) { // nop or int 3
-        return false;
-      }
-    }
-
-    // mov edi, edi.  Yes, there are two ways to encode the same thing:
-    //
-    //   0x89ff == mov r/m, r
-    //   0x8bff == mov r, r/m
-    //
-    // where "r" is register and "r/m" is register or memory.  Windows seems to
-    // use 8bff; I include 89ff out of paranoia.
-    if ((aFn[0] != 0x8b && aFn[0] != 0x89) || aFn[1] != 0xff) {
-      return false;
-    }
-
-    // Write a long jump into the space above the function.
-    aFn[-5] = 0xe9; // jmp
-    *((intptr_t*)(aFn - 4)) = aHookDest - (uintptr_t)(aFn); // target displacement
-
-    // Set aOrigFunc here, because after this point, aHookDest might be called,
-    // and aHookDest might use the aOrigFunc pointer.
-    *aOrigFunc = aFn + 2;
-
-    // Short jump up into our long jump.
-    *((uint16_t*)(aFn)) = 0xf9eb; // jmp $-5
-
-    // I think this routine is safe without this, but it can't hurt.
-    FlushInstructionCache(GetCurrentProcess(),
-                          /* ignored */ nullptr,
-                          /* ignored */ 0);
-
-    return true;
-  }
-
-private:
-  static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
-    // If function entry is jmp rel8 stub to the internal implementation, we
-    // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
-      if (offset <= 0) {
-        // Bail out for negative offset: probably already patched by some
-        // third-party code.
-        return aOriginalFunction;
-      }
-
-      for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
-          // Bail out on insufficient nop space.
-          return aOriginalFunction;
-        }
-      }
-
-      return aOriginalFunction + 2 + offset;
-    }
-
-    // If function entry is jmp [disp32] such as used by kernel32,
-    // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2)));
-    }
-
-    return aOriginalFunction;
-  }
-#else
-  void Init(const char* aModuleName)
-  {
-    // Not implemented except on x86-32.
-  }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    // Not implemented except on x86-32.
-    return false;
-  }
-#endif
-};
-
-class WindowsDllDetourPatcher
-{
-  typedef unsigned char* byteptr_t;
-public:
-  WindowsDllDetourPatcher()
-    : mModule(0), mHookPage(0), mMaxHooks(0), mCurHooks(0)
+    , mNopSpacePatcher(mozilla::Forward<Args>(aArgs)...)
+#endif // defined(_M_IX86)
+    , mModule(nullptr)
+    , mNHooks(0)
   {
   }
 
-  ~WindowsDllDetourPatcher()
-  {
-    int i;
-    byteptr_t p;
-    for (i = 0, p = mHookPage; i < mCurHooks; i++, p += kHookSize) {
-#if defined(_M_IX86)
-      size_t nBytes = 1 + sizeof(intptr_t);
-#elif defined(_M_X64)
-      size_t nBytes = 2 + sizeof(intptr_t);
-#else
-#error "Unknown processor type"
-#endif
-      byteptr_t origBytes = (byteptr_t)DecodePointer(*((byteptr_t*)p));
+  WindowsDllInterceptor(const WindowsDllInterceptor&) = delete;
+  WindowsDllInterceptor(WindowsDllInterceptor&&) = delete;
+  WindowsDllInterceptor& operator=(const WindowsDllInterceptor&) = delete;
+  WindowsDllInterceptor& operator=(WindowsDllInterceptor&&) = delete;
 
-      // ensure we can modify the original code
-      AutoVirtualProtect protect(origBytes, nBytes, PAGE_EXECUTE_READWRITE);
-      if (!protect.Protect()) {
-        continue;
-      }
-
-      // Remove the hook by making the original function jump directly
-      // in the trampoline.
-      intptr_t dest = (intptr_t)(p + sizeof(void*));
-#if defined(_M_IX86)
-      // Ensure the JMP from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0xE9)
-        continue;
-      *((intptr_t*)(origBytes + 1)) =
-        dest - (intptr_t)(origBytes + 5); // target displacement
-#elif defined(_M_X64)
-      // Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
-      if (origBytes[0] != 0x49 || origBytes[1] != 0xBB)
-        continue;
-      *((intptr_t*)(origBytes + 2)) = dest;
-#else
-#error "Unknown processor type"
-#endif
-    }
+  ~WindowsDllInterceptor()
+  {
+    Clear();
   }
 
-  void Init(const char* aModuleName, int aNumHooks = 0)
+  template <size_t N>
+  void Init(const char (&aModuleName)[N], int aNumHooks = 0)
+  {
+    wchar_t moduleName[N];
+
+    for (size_t i = 0; i < N; ++i) {
+      MOZ_ASSERT(!(aModuleName[i] & 0x80),
+                 "Use wide-character overload for non-ASCII module names");
+      moduleName[i] = aModuleName[i];
+    }
+
+    Init(moduleName, aNumHooks);
+  }
+
+  void Init(const wchar_t* aModuleName, int aNumHooks = 0)
   {
     if (mModule) {
       return;
     }
 
-    mModule = LoadLibraryExA(aModuleName, nullptr, 0);
-    if (!mModule) {
-      //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
-      return;
-    }
-
-    int hooksPerPage = 4096 / kHookSize;
-    if (aNumHooks == 0) {
-      aNumHooks = hooksPerPage;
-    }
-
-    mMaxHooks = aNumHooks + (hooksPerPage % aNumHooks);
-
-    mHookPage = (byteptr_t)VirtualAllocEx(GetCurrentProcess(), nullptr,
-                                          mMaxHooks * kHookSize,
-                                          MEM_COMMIT | MEM_RESERVE,
-                                          PAGE_EXECUTE_READ);
-    if (!mHookPage) {
-      mModule = 0;
-      return;
-    }
-  }
-
-  bool Initialized() { return !!mModule; }
-
-  bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
-  {
-    if (!mModule) {
-      return false;
-    }
-
-    void* pAddr = (void*)GetProcAddress(mModule, aName);
-    if (!pAddr) {
-      //printf ("GetProcAddress failed\n");
-      return false;
-    }
-
-    pAddr = ResolveRedirectedAddress((byteptr_t)pAddr);
-
-    CreateTrampoline(pAddr, aHookDest, aOrigFunc);
-    if (!*aOrigFunc) {
-      //printf ("CreateTrampoline failed\n");
-      return false;
-    }
-
-    return true;
-  }
-
-protected:
-  const static int kPageSize = 4096;
-  const static int kHookSize = 128;
-
-  HMODULE mModule;
-  byteptr_t mHookPage;
-  int mMaxHooks;
-  int mCurHooks;
-
-  // rex bits
-  static const BYTE kMaskHighNibble = 0xF0;
-  static const BYTE kRexOpcode = 0x40;
-  static const BYTE kMaskRexW = 0x08;
-  static const BYTE kMaskRexR = 0x04;
-  static const BYTE kMaskRexX = 0x02;
-  static const BYTE kMaskRexB = 0x01;
-
-  // mod r/m bits
-  static const BYTE kRegFieldShift = 3;
-  static const BYTE kMaskMod = 0xC0;
-  static const BYTE kMaskReg = 0x38;
-  static const BYTE kMaskRm = 0x07;
-  static const BYTE kRmNeedSib = 0x04;
-  static const BYTE kModReg = 0xC0;
-  static const BYTE kModDisp32 = 0x80;
-  static const BYTE kModDisp8 = 0x40;
-  static const BYTE kModNoRegDisp = 0x00;
-  static const BYTE kRmNoRegDispDisp32 = 0x05;
-
-  // sib bits
-  static const BYTE kMaskSibScale = 0xC0;
-  static const BYTE kMaskSibIndex = 0x38;
-  static const BYTE kMaskSibBase = 0x07;
-  static const BYTE kSibBaseEbp = 0x05;
-
-  // Register bit IDs.
-  static const BYTE kRegAx = 0x0;
-  static const BYTE kRegCx = 0x1;
-  static const BYTE kRegDx = 0x2;
-  static const BYTE kRegBx = 0x3;
-  static const BYTE kRegSp = 0x4;
-  static const BYTE kRegBp = 0x5;
-  static const BYTE kRegSi = 0x6;
-  static const BYTE kRegDi = 0x7;
-
-  // Special ModR/M codes.  These indicate operands that cannot be simply
-  // memcpy-ed.
-  // Operand is a 64-bit RIP-relative address.
-  static const int kModOperand64 = -2;
-  // Operand is not yet handled by our trampoline.
-  static const int kModUnknown = -1;
-
-  /**
-   * Returns the number of bytes taken by the ModR/M byte, SIB (if present)
-   * and the instruction's operand.  In special cases, the special MODRM codes
-   * above are returned.
-   * aModRm points to the ModR/M byte of the instruction.
-   * On return, aSubOpcode (if present) is filled with the subopcode/register
-   * code found in the ModR/M byte.
-   */
-  int CountModRmSib(const BYTE *aModRm, BYTE* aSubOpcode = nullptr)
-  {
-    if (!aModRm) {
-      MOZ_ASSERT(aModRm, "Missing ModRM byte");
-      return kModUnknown;
-    }
-    int numBytes = 1; // Start with 1 for mod r/m byte itself
-    switch (*aModRm & kMaskMod) {
-      case kModReg:
-        return numBytes;
-      case kModDisp8:
-        numBytes += 1;
-        break;
-      case kModDisp32:
-        numBytes += 4;
-        break;
-      case kModNoRegDisp:
-        if ((*aModRm & kMaskRm) == kRmNoRegDispDisp32) {
-#if defined(_M_X64)
-          if (aSubOpcode) {
-            *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
-          }
-          return kModOperand64;
-#else
-          // On IA-32, all ModR/M instruction modes address memory relative to 0
-          numBytes += 4;
-#endif
-        } else if (((*aModRm & kMaskRm) == kRmNeedSib &&
-             (*(aModRm + 1) & kMaskSibBase) == kSibBaseEbp)) {
-          numBytes += 4;
-        }
-        break;
-      default:
-        // This should not be reachable
-        MOZ_ASSERT_UNREACHABLE("Impossible value for modr/m byte mod bits");
-        return kModUnknown;
-    }
-    if ((*aModRm & kMaskRm) == kRmNeedSib) {
-      // SIB byte
-      numBytes += 1;
-    }
-    if (aSubOpcode) {
-      *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
-    }
-    return numBytes;
-  }
-
-#if defined(_M_X64)
-  // To patch for JMP and JE
-
-  enum JumpType {
-   Je,
-   Jne,
-   Jmp,
-   Call
-  };
-
-  struct JumpPatch {
-    JumpPatch()
-      : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp)
-    {
-    }
-
-    JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp)
-      : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType)
-    {
-    }
-
-    size_t GenerateJump(uint8_t* aCode)
-    {
-      size_t offset = mHookOffset;
-      if (mType == JumpType::Je) {
-        // JNE RIP+14
-        aCode[offset]     = 0x75;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      } else if (mType == JumpType::Jne) {
-        // JE RIP+14
-        aCode[offset]     = 0x74;
-        aCode[offset + 1] = 14;
-        offset += 2;
-      }
-
-      // Near call/jmp, absolute indirect, address given in r/m32
-      if (mType == JumpType::Call) {
-        // CALL [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x15;
-        // The offset to jump destination -- ie it is placed 2 bytes after the offset.
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 2;
-        aCode[offset + 2 + 4] = 0xeb;    // JMP +8 (jump over mJumpAddress)
-        aCode[offset + 2 + 4 + 1] = 8;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4 + 2) = mJumpAddress;
-        return offset + 2 + 4 + 2 + 8;
-      } else {
-        // JMP [RIP+0]
-        aCode[offset] = 0xff;
-        aCode[offset + 1] = 0x25;
-        // The offset to jump destination is 0
-        *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0;
-        *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress;
-        return offset + 2 + 4 + 8;
-      }
-    }
-
-    size_t mHookOffset;
-    intptr_t mJumpAddress;
-    JumpType mType;
-  };
-
-#endif
-
-  enum ePrefixGroupBits
-  {
-    eNoPrefixes = 0,
-    ePrefixGroup1 = (1 << 0),
-    ePrefixGroup2 = (1 << 1),
-    ePrefixGroup3 = (1 << 2),
-    ePrefixGroup4 = (1 << 3)
-  };
-
-  int CountPrefixBytes(byteptr_t aBytes, const int aBytesIndex,
-                       unsigned char* aOutGroupBits)
-  {
-    unsigned char& groupBits = *aOutGroupBits;
-    groupBits = eNoPrefixes;
-    int index = aBytesIndex;
-    while (true) {
-      switch (aBytes[index]) {
-        // Group 1
-        case 0xF0: // LOCK
-        case 0xF2: // REPNZ
-        case 0xF3: // REP / REPZ
-          if (groupBits & ePrefixGroup1) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup1;
-          ++index;
-          break;
-
-        // Group 2
-        case 0x2E: // CS override / branch not taken
-        case 0x36: // SS override
-        case 0x3E: // DS override / branch taken
-        case 0x64: // FS override
-        case 0x65: // GS override
-          if (groupBits & ePrefixGroup2) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup2;
-          ++index;
-          break;
-
-        // Group 3
-        case 0x66: // operand size override
-          if (groupBits & ePrefixGroup3) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup3;
-          ++index;
-          break;
-
-        // Group 4
-        case 0x67: // Address size override
-          if (groupBits & ePrefixGroup4) {
-            return -1;
-          }
-          groupBits |= ePrefixGroup4;
-          ++index;
-          break;
-
-        default:
-          return index - aBytesIndex;
-      }
-    }
-  }
-
-  // Return a ModR/M byte made from the 2 Mod bits, the register used for the
-  // reg bits and the register used for the R/M bits.
-  BYTE BuildModRmByte(BYTE aModBits, BYTE aReg, BYTE aRm)
-  {
-    MOZ_ASSERT((aRm & kMaskRm) == aRm);
-    MOZ_ASSERT((aModBits & kMaskMod) == aModBits);
-    MOZ_ASSERT(((aReg << kRegFieldShift) & kMaskReg) == (aReg << kRegFieldShift));
-    return aModBits | (aReg << kRegFieldShift) | aRm;
+    mModule = ::LoadLibraryW(aModuleName);
+    mNHooks = aNumHooks;
   }
 
-  void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp)
+  void Clear()
   {
-    *aOutTramp = nullptr;
-
-    AutoVirtualProtect protectHookPage(mHookPage, mMaxHooks * kHookSize,
-                                       PAGE_EXECUTE_READWRITE);
-    if (!protectHookPage.Protect()) {
-      return;
-    }
-
-    byteptr_t tramp = FindTrampolineSpace();
-    if (!tramp) {
-      return;
-    }
-
-    // We keep the address of the original function in the first bytes of
-    // the trampoline buffer
-    *((void**)tramp) = EncodePointer(aOrigFunction);
-    tramp += sizeof(void*);
-
-    byteptr_t origBytes = (byteptr_t)aOrigFunction;
-
-    // # of bytes of the original function that we can overwrite.
-    int nOrigBytes = 0;
-
-#if defined(_M_IX86)
-    int pJmp32 = -1;
-    while (nOrigBytes < 5) {
-      // Understand some simple instructions that might be found in a
-      // prologue; we might need to extend this as necessary.
-      //
-      // Note!  If we ever need to understand jump instructions, we'll
-      // need to rewrite the displacement argument.
-      unsigned char prefixGroups;
-      int numPrefixBytes = CountPrefixBytes(origBytes, nOrigBytes, &prefixGroups);
-      if (numPrefixBytes < 0 || (prefixGroups & (ePrefixGroup3 | ePrefixGroup4))) {
-        // Either the prefix sequence was bad, or there are prefixes that
-        // we don't currently support (groups 3 and 4)
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-      nOrigBytes += numPrefixBytes;
-      if (origBytes[nOrigBytes] >= 0x88 &&
-          origBytes[nOrigBytes] <= 0x8B) {
-        // various MOVs
-        ++nOrigBytes;
-        int len = CountModRmSib(origBytes + nOrigBytes);
-        if (len < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-          return;
-        }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0x0f &&
-                 (origBytes[nOrigBytes + 1] == 0x10 ||
-                  origBytes[nOrigBytes + 1] == 0x11)) {
-        // SSE: movups xmm, xmm/m128
-        //      movups xmm/m128, xmm
-        nOrigBytes += 2;
-        int len = CountModRmSib(origBytes + nOrigBytes);
-        if (len < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-          return;
-        }
-        nOrigBytes += len;
-      } else if (origBytes[nOrigBytes] == 0xA1) {
-        // MOV eax, [seg:offset]
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xB8) {
-        // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x33 &&
-                 (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
-        // XOR r32, r32
-        nOrigBytes += 2;
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0x40) {
-        // INC r32
-        nOrigBytes += 1;
-      } else if (origBytes[nOrigBytes] == 0x83) {
-        // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r/m, imm8
-        unsigned char b = origBytes[nOrigBytes + 1];
-        if ((b & 0xc0) == 0xc0) {
-          // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r, imm8
-          nOrigBytes += 3;
-        } else {
-          // bail
-          MOZ_ASSERT_UNREACHABLE("Unrecognized bit opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x68) {
-        // PUSH with 4-byte operand
-        nOrigBytes += 5;
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-        // 1-byte PUSH/POP
-        nOrigBytes++;
-      } else if (origBytes[nOrigBytes] == 0x6A) {
-        // PUSH imm8
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xe9) {
-        pJmp32 = nOrigBytes;
-        // jmp 32bit offset
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0xff &&
-                 origBytes[nOrigBytes + 1] == 0x25) {
-        // jmp [disp32]
-        nOrigBytes += 6;
-      } else if (origBytes[nOrigBytes] == 0xc2) {
-        // ret imm16.  We can't handle this but it happens.  We don't ASSERT but we do fail to hook.
-#if defined(MOZILLA_INTERNAL_API)
-        NS_WARNING("Cannot hook method -- RET opcode found");
-#endif
-        return;
-      } else {
-        //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nOrigBytes]);
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-    }
-
-    // The trampoline is a copy of the instructions that we just traced,
-    // followed by a jump that we add below.
-    memcpy(tramp, aOrigFunction, nOrigBytes);
-#elif defined(_M_X64)
-    // The number of bytes used by the trampoline.
-    int nTrampBytes = 0;
-    bool foundJmp = false;
-
-    while (nOrigBytes < 13) {
-      // If we found JMP 32bit offset, we require that the next bytes must
-      // be NOP or INT3.  There is no reason to copy them.
-      // TODO: This used to trigger for Je as well.  Now that I allow
-      // instructions after CALL and JE, I don't think I need that.
-      // The only real value of this condition is that if code follows a JMP
-      // then its _probably_ the target of a JMP somewhere else and we
-      // will be overwriting it, which would be tragic.  This seems
-      // highly unlikely.
-      if (foundJmp) {
-        if (origBytes[nOrigBytes] == 0x90 || origBytes[nOrigBytes] == 0xcc) {
-          nOrigBytes++;
-          continue;
-        }
-        MOZ_ASSERT_UNREACHABLE("Opcode sequence includes commands after JMP");
-        return;
-      }
-      if (origBytes[nOrigBytes] == 0x0f) {
-        COPY_CODES(1);
-        if (origBytes[nOrigBytes] == 0x1f) {
-          // nop (multibyte)
-          COPY_CODES(1);
-          if ((origBytes[nOrigBytes] & 0xc0) == 0x40 &&
-              (origBytes[nOrigBytes] & 0x7) == 0x04) {
-            COPY_CODES(3);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x05) {
-          // syscall
-          COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] == 0x10 ||
-                   origBytes[nOrigBytes] == 0x11) {
-          // SSE: movups xmm, xmm/m128
-          //      movups xmm/m128, xmm
-          COPY_CODES(1);
-          int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes]);
-          if (nModRmSibBytes < 0) {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          } else {
-            COPY_CODES(nModRmSibBytes);
-          }
-        } else if (origBytes[nOrigBytes] == 0x84) {
-          // je rel32
-          JumpPatch jump(nTrampBytes - 1,  // overwrite the 0x0f we copied above
-                          (intptr_t)(origBytes + nOrigBytes + 5 +
-                                     *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                          JumpType::Je);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 5;
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x40 ||
-                 origBytes[nOrigBytes] == 0x41) {
-        // Plain REX or REX.B
-        COPY_CODES(1);
-        if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-          // push/pop with Rx register
-          COPY_CODES(1);
-        } else if (origBytes[nOrigBytes] >= 0xb8 && origBytes[nOrigBytes] <= 0xbf) {
-          // mov r32, imm32
-          COPY_CODES(5);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x44) {
-        // REX.R
-        COPY_CODES(1);
-
-        // TODO: Combine with the "0x89" case below in the REX.W section
-        if (origBytes[nOrigBytes] == 0x89) {
-          // mov r/m32, r32
-          COPY_CODES(1);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-       } else if (origBytes[nOrigBytes] == 0x45) {
-        // REX.R & REX.B
-        COPY_CODES(1);
-
-        if (origBytes[nOrigBytes] == 0x33) {
-          // xor r32, r32
-          COPY_CODES(2);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if ((origBytes[nOrigBytes] & 0xfa) == 0x48) {
-        // REX.W | REX.WR | REX.WRB | REX.WB
-        COPY_CODES(1);
-
-        if (origBytes[nOrigBytes] == 0x81 &&
-            (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
-          // sub r, dword
-          COPY_CODES(6);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0xe8) {
-          // sub r, byte
-          COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == kModReg) {
-          // add r, byte
-          COPY_CODES(3);
-        } else if (origBytes[nOrigBytes] == 0x83 &&
-                   (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
-          // and [r+d], imm8
-          COPY_CODES(5);
-        } else if (origBytes[nOrigBytes] == 0x2b &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
-          // sub r64, r64
-          COPY_CODES(2);
-        } else if (origBytes[nOrigBytes] == 0x85) {
-          // 85 /r => TEST r/m32, r32
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0xc0) {
-            COPY_CODES(2);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if ((origBytes[nOrigBytes] & 0xfd) == 0x89) {
-          // MOV r/m64, r64 | MOV r64, r/m64
-          BYTE reg;
-          int len = CountModRmSib(origBytes + nOrigBytes + 1, &reg);
-          if (len < 0) {
-            MOZ_ASSERT(len == kModOperand64);
-            if (len != kModOperand64) {
-              return;
-            }
-            nOrigBytes += 2;   // skip the MOV and MOD R/M bytes
-
-            // The instruction MOVs 64-bit data from a RIP-relative memory
-            // address (determined with a 32-bit offset from RIP) into a
-            // 64-bit register.
-            int64_t* absAddr =
-              reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 4 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes));
-            nOrigBytes += 4;
-
-            if (reg == kRegAx) {
-              // Destination is RAX.  Encode instruction as MOVABS with a
-              // 64-bit absolute address as its immediate operand.
-              tramp[nTrampBytes] = 0xa1;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
-            } else {
-              // The MOV must be done in two steps.  First, we MOVABS the
-              // absolute 64-bit address into our target register.
-              // Then, we MOV from that address into the register
-              // using register-indirect addressing.
-              tramp[nTrampBytes] = 0xb8 + reg;
-              ++nTrampBytes;
-              int64_t** trampOperandPtr = reinterpret_cast<int64_t**>(tramp + nTrampBytes);
-              *trampOperandPtr = absAddr;
-              nTrampBytes += 8;
-              tramp[nTrampBytes] = 0x48;
-              tramp[nTrampBytes+1] = 0x8b;
-              tramp[nTrampBytes+2] = BuildModRmByte(kModNoRegDisp, reg, reg);
-              nTrampBytes += 3;
-            }
-          } else {
-            COPY_CODES(len+1);
-          }
-        } else if (origBytes[nOrigBytes] == 0xc7) {
-          // MOV r/m64, imm32
-          if (origBytes[nOrigBytes + 1] == 0x44) {
-            // MOV [r64+disp8], imm32
-            // ModR/W + SIB + disp8 + imm32
-            COPY_CODES(8);
-          } else {
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0xff) {
-          // JMP /4
-          if ((origBytes[nOrigBytes + 1] & 0xc0) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & 0x07) == 0x5) {
-            // [rip+disp32]
-            // convert JMP 32bit offset to JMP 64bit direct
-            JumpPatch jump(nTrampBytes - 1,  // overwrite the REX.W/REX.WR we copied above
-                           *reinterpret_cast<intptr_t*>(origBytes + nOrigBytes + 6 +
-                                                        *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2)),
-                           JumpType::Jmp);
-            nTrampBytes = jump.GenerateJump(tramp);
-            nOrigBytes += 6;
-            foundJmp = true;
-          } else {
-            // not support yet!
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x8d) {
-          // LEA reg, addr
-          if ((origBytes[nOrigBytes + 1] & kMaskMod) == 0x0 &&
-              (origBytes[nOrigBytes + 1] & kMaskRm) == 0x5) {
-            // [rip+disp32]
-            // convert 32bit offset to 64bit direct and convert instruction
-            // to a simple 64-bit mov
-            BYTE reg = (origBytes[nOrigBytes + 1] & kMaskReg) >> kRegFieldShift;
-            intptr_t absAddr =
-              reinterpret_cast<intptr_t>(origBytes + nOrigBytes + 6 +
-                                         *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-            nOrigBytes += 6;
-            tramp[nTrampBytes] = 0xb8 + reg;    // mov
-            ++nTrampBytes;
-            intptr_t* trampOperandPtr = reinterpret_cast<intptr_t*>(tramp + nTrampBytes);
-            *trampOperandPtr = absAddr;
-            nTrampBytes += 8;
-          } else {
-            // Above we dealt with RIP-relative instructions.  Any other
-            // operand form can simply be copied.
-            int len = CountModRmSib(origBytes + nOrigBytes + 1);
-            // We handled the kModOperand64 -- ie RIP-relative -- case above
-            MOZ_ASSERT(len > 0);
-            COPY_CODES(len + 1);
-          }
-        } else if (origBytes[nOrigBytes] == 0x63 &&
-                   (origBytes[nOrigBytes + 1] & kMaskMod) == kModReg) {
-          // movsxd r64, r32 (move + sign extend)
-          COPY_CODES(2);
-        } else {
-          // not support yet!
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x66) {
-        // operand override prefix
-        COPY_CODES(1);
-        // This is the same as the x86 version
-        if (origBytes[nOrigBytes] >= 0x88 && origBytes[nOrigBytes] <= 0x8B) {
-          // various MOVs
-          unsigned char b = origBytes[nOrigBytes + 1];
-          if (((b & 0xc0) == 0xc0) ||
-              (((b & 0xc0) == 0x00) &&
-               ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05))) {
-            // REG=r, R/M=r or REG=r, R/M=[r]
-            COPY_CODES(2);
-          } else if ((b & 0xc0) == 0x40) {
-            if ((b & 0x07) == 0x04) {
-              // REG=r, R/M=[SIB + disp8]
-              COPY_CODES(4);
-            } else {
-              // REG=r, R/M=[r + disp8]
-              COPY_CODES(3);
-            }
-          } else {
-            // complex MOV, bail
-            MOZ_ASSERT_UNREACHABLE("Unrecognized MOV opcode sequence");
-            return;
-          }
-        } else if (origBytes[nOrigBytes] == 0x44 &&
-                   origBytes[nOrigBytes+1] == 0x89) {
-          // mov word ptr [reg+disp8], reg
-          COPY_CODES(2);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            // no way to support this yet.
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        }
-      } else if ((origBytes[nOrigBytes] & 0xf0) == 0x50) {
-        // 1-byte push/pop
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0x65) {
-        // GS prefix
-        //
-        // The entry of GetKeyState on Windows 10 has the following code.
-        // 65 48 8b 04 25 30 00 00 00    mov   rax,qword ptr gs:[30h]
-        // (GS prefix + REX + MOV (0x8b) ...)
-        if (origBytes[nOrigBytes + 1] == 0x48 &&
-            (origBytes[nOrigBytes + 2] >= 0x88 && origBytes[nOrigBytes + 2] <= 0x8b)) {
-          COPY_CODES(3);
-          int len = CountModRmSib(origBytes + nOrigBytes);
-          if (len < 0) {
-            // no way to support this yet.
-            MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-            return;
-          }
-          COPY_CODES(len);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x80 &&
-                 origBytes[nOrigBytes + 1] == 0x3d) {
-        // cmp byte ptr [rip-relative address], imm8
-        // We'll compute the absolute address and do the cmp in r11
-
-        // push r11 (to save the old value)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x53;
-        ++nTrampBytes;
-
-        byteptr_t absAddr =
-          reinterpret_cast<byteptr_t>(origBytes + nOrigBytes + 7 +
-                                      *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-        nOrigBytes += 6;
-
-        // mov r11, absolute address
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0xbb;
-        ++nTrampBytes;
-
-        *reinterpret_cast<byteptr_t*>(tramp + nTrampBytes) = absAddr;
-        nTrampBytes += 8;
-
-        // cmp byte ptr [r11],...
-        tramp[nTrampBytes] = 0x41;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x80;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x3b;
-        ++nTrampBytes;
-
-        // ...imm8
-        COPY_CODES(1);
-
-        // pop r11 (doesn't affect the flags from the cmp)
-        tramp[nTrampBytes] = 0x49;
-        ++nTrampBytes;
-        tramp[nTrampBytes] = 0x5b;
-        ++nTrampBytes;
-      } else if (origBytes[nOrigBytes] == 0x90) {
-        // nop
-        COPY_CODES(1);
-      } else if ((origBytes[nOrigBytes] & 0xf8) == 0xb8) {
-        // MOV r32, imm32
-        COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0x33) {
-        // xor r32, r/m32
-        COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xf6) {
-        // test r/m8, imm8 (used by ntdll on Windows 10 x64)
-        // (no flags are affected by near jmp since there is no task switch,
-        // so it is ok for a jmp to be written immediately after a test)
-        BYTE subOpcode = 0;
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1], &subOpcode);
-        if (nModRmSibBytes < 0 || subOpcode != 0) {
-          // Unsupported
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(2 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0x85) {
-        // test r/m32, r32
-        int nModRmSibBytes = CountModRmSib(&origBytes[nOrigBytes + 1]);
-        if (nModRmSibBytes < 0) {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(1 + nModRmSibBytes);
-      } else if (origBytes[nOrigBytes] == 0xd1 &&
-                  (origBytes[nOrigBytes+1] & kMaskMod) == kModReg) {
-        // bit shifts/rotates : (SA|SH|RO|RC)(R|L) r32
-        // (e.g. 0xd1 0xe0 is SAL, 0xd1 0xc8 is ROR)
-        COPY_CODES(2);
-      } else if (origBytes[nOrigBytes] == 0xc3) {
-        // ret
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xcc) {
-        // int 3
-        COPY_CODES(1);
-      } else if (origBytes[nOrigBytes] == 0xe8 ||
-                 origBytes[nOrigBytes] == 0xe9) {
-        // CALL (0xe8) or JMP (0xe9) 32bit offset
-        foundJmp = origBytes[nOrigBytes] == 0xe9;
-        JumpPatch jump(nTrampBytes,
-                       (intptr_t)(origBytes + nOrigBytes + 5 +
-                                  *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 1))),
-                       origBytes[nOrigBytes] == 0xe8 ? JumpType::Call : JumpType::Jmp);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 5;
-      } else if (origBytes[nOrigBytes] == 0x74 || // je rel8 (0x74)
-                 origBytes[nOrigBytes] == 0x75) { // jne rel8 (0x75)
-        char offset = origBytes[nOrigBytes + 1];
-        auto jumpType = JumpType::Je;
-        if (origBytes[nOrigBytes] == 0x75)
-          jumpType = JumpType::Jne;
-        JumpPatch jump(nTrampBytes,
-          (intptr_t)(origBytes + nOrigBytes + 2 + offset), jumpType);
-        nTrampBytes = jump.GenerateJump(tramp);
-        nOrigBytes += 2;
-      } else if (origBytes[nOrigBytes] == 0xff) {
-        if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == 0xf0) {
-          // push r64
-          COPY_CODES(2);
-        } else if (origBytes[nOrigBytes + 1] == 0x25) {
-          // jmp absolute indirect m32
-          foundJmp = true;
-          int32_t offset = *(reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
-          int64_t* ptrToJmpDest = reinterpret_cast<int64_t*>(origBytes + nOrigBytes + 6 + offset);
-          intptr_t jmpDest = static_cast<intptr_t>(*ptrToJmpDest);
-          JumpPatch jump(nTrampBytes, jmpDest, JumpType::Jmp);
-          nTrampBytes = jump.GenerateJump(tramp);
-          nOrigBytes += 6;
-        } else if ((origBytes[nOrigBytes + 1] & (kMaskMod|kMaskReg)) == BuildModRmByte(kModReg, 2, 0)) {
-          // CALL reg (ff nn)
-          COPY_CODES(2);
-        } else {
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-      } else if (origBytes[nOrigBytes] == 0x83 &&
-                 (origBytes[nOrigBytes + 1] & 0xf8) == 0x60) {
-        // and [r+d], imm8
-        COPY_CODES(5);
-      } else if (origBytes[nOrigBytes] == 0xc6) {
-        // mov [r+d], imm8
-        int len = CountModRmSib(&origBytes[nOrigBytes + 1]);
-        if (len < 0) {
-          // RIP-relative not yet supported
-          MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-          return;
-        }
-        COPY_CODES(len + 1);
-      } else {
-        MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
-        return;
-      }
-    }
-#else
-#error "Unknown processor type"
-#endif
-
-    if (nOrigBytes > 100) {
-      //printf ("Too big!");
-      return;
-    }
-
-    // target address of the final jmp instruction in the trampoline
-    byteptr_t trampDest = origBytes + nOrigBytes;
-
-#if defined(_M_IX86)
-    if (pJmp32 >= 0) {
-      // Jump directly to the original target of the jump instead of jumping to the
-      // original function.
-      // Adjust jump target displacement to jump location in the trampoline.
-      *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp;
-    } else {
-      tramp[nOrigBytes] = 0xE9; // jmp
-      *((intptr_t*)(tramp + nOrigBytes + 1)) =
-        (intptr_t)trampDest - (intptr_t)(tramp + nOrigBytes + 5); // target displacement
-    }
-#elif defined(_M_X64)
-    // If the we found a Jmp, we don't need to add another instruction. However,
-    // if we found a _conditional_ jump or a CALL (or no control operations
-    // at all) then we still need to run the rest of aOriginalFunction.
-    if (!foundJmp) {
-      JumpPatch patch(nTrampBytes, reinterpret_cast<intptr_t>(trampDest));
-      patch.GenerateJump(tramp);
-    }
-#endif
-
-    // The trampoline is now valid.
-    *aOutTramp = tramp;
-
-    // ensure we can modify the original code
-    AutoVirtualProtect protect(aOrigFunction, nOrigBytes, PAGE_EXECUTE_READWRITE);
-    if (!protect.Protect()) {
+    if (!mModule) {
       return;
     }
 
 #if defined(_M_IX86)
-    // now modify the original bytes
-    origBytes[0] = 0xE9; // jmp
-    *((intptr_t*)(origBytes + 1)) =
-      aDest - (intptr_t)(origBytes + 5); // target displacement
-#elif defined(_M_X64)
-    // mov r11, address
-    origBytes[0] = 0x49;
-    origBytes[1] = 0xbb;
-
-    *((intptr_t*)(origBytes + 2)) = aDest;
-
-    // jmp r11
-    origBytes[10] = 0x41;
-    origBytes[11] = 0xff;
-    origBytes[12] = 0xe3;
-#endif
-  }
-
-  byteptr_t FindTrampolineSpace()
-  {
-    if (mCurHooks >= mMaxHooks) {
-      return 0;
-    }
-
-    byteptr_t p = mHookPage + mCurHooks * kHookSize;
-
-    mCurHooks++;
-
-    return p;
-  }
-
-  static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
-  {
-    // If function entry is jmp rel8 stub to the internal implementation, we
-    // resolve redirected address from the jump target.
-    if (aOriginalFunction[0] == 0xeb) {
-      int8_t offset = (int8_t)(aOriginalFunction[1]);
-      if (offset <= 0) {
-        // Bail out for negative offset: probably already patched by some
-        // third-party code.
-        return aOriginalFunction;
-      }
-
-      for (int8_t i = 0; i < offset; i++) {
-        if (aOriginalFunction[2 + i] != 0x90) {
-          // Bail out on insufficient nop space.
-          return aOriginalFunction;
-        }
-      }
+    mNopSpacePatcher.Clear();
+#endif // defined(_M_IX86)
+    mDetourPatcher.Clear();
 
-      return aOriginalFunction + 2 + offset;
-    }
-
-#if defined(_M_IX86)
-    // If function entry is jmp [disp32] such as used by kernel32,
-    // we resolve redirected address from import table.
-    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
-      return (void*)(**((uint32_t**) (aOriginalFunction + 2)));
-    }
-#elif defined(_M_X64)
-    if (aOriginalFunction[0] == 0xe9) {
-      // require for TestDllInterceptor with --disable-optimize
-      int32_t offset = *((int32_t*)(aOriginalFunction + 1));
-      return aOriginalFunction + 5 + offset;
-    }
-#endif
-
-    return aOriginalFunction;
-  }
-};
-
-} // namespace internal
-
-class WindowsDllInterceptor
-{
-  internal::WindowsDllNopSpacePatcher mNopSpacePatcher;
-  internal::WindowsDllDetourPatcher mDetourPatcher;
-
-  const char* mModuleName;
-  int mNHooks;
-
-public:
-  explicit WindowsDllInterceptor(const char* aModuleName = nullptr,
-                                 int aNumHooks = 0)
-    : mModuleName(nullptr)
-    , mNHooks(0)
-  {
-    if (aModuleName) {
-      Init(aModuleName, aNumHooks);
-    }
-  }
-
-  void Init(const char* aModuleName, int aNumHooks = 0)
-  {
-    if (mModuleName) {
-      return;
-    }
-
-    mModuleName = aModuleName;
-    mNHooks = aNumHooks;
-    mNopSpacePatcher.Init(aModuleName);
-
-    // Lazily initialize mDetourPatcher, since it allocates memory and we might
-    // not need it.
+    // NB: We intentionally leak mModule
   }
 
   /**
    * Hook/detour the method aName from the DLL we set in Init so that it calls
    * aHookDest instead.  Returns the original method pointer in aOrigFunc
    * and returns true if successful.
    *
    * IMPORTANT: If you use this method, please add your case to the
    * TestDllInterceptor in order to detect future failures.  Even if this
    * succeeds now, updates to the hooked DLL could cause it to fail in
    * the future.
    */
   bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
   {
     // Use a nop space patch if possible, otherwise fall back to a detour.
     // This should be the preferred method for adding hooks.
-
-    if (!mModuleName) {
+    if (!mModule) {
       return false;
     }
 
-    if (mNopSpacePatcher.AddHook(aName, aHookDest, aOrigFunc)) {
+    FARPROC proc = ::GetProcAddress(mModule, aName);
+    if (!proc) {
+      return false;
+    }
+
+#if defined(_M_IX86)
+    if (mNopSpacePatcher.AddHook(proc, aHookDest, aOrigFunc)) {
       return true;
     }
+#endif // defined(_M_IX86)
 
-    return AddDetour(aName, aHookDest, aOrigFunc);
+    return AddDetour(proc, aHookDest, aOrigFunc);
   }
 
   /**
    * Detour the method aName from the DLL we set in Init so that it calls
    * aHookDest instead.  Returns the original method pointer in aOrigFunc
    * and returns true if successful.
    *
    * IMPORTANT: If you use this method, please add your case to the
@@ -1473,23 +202,40 @@ public:
    * succeeds now, updates to the detoured DLL could cause it to fail in
    * the future.
    */
   bool AddDetour(const char* aName, intptr_t aHookDest, void** aOrigFunc)
   {
     // Generally, code should not call this method directly. Use AddHook unless
     // there is a specific need to avoid nop space patches.
 
-    if (!mModuleName) {
+    if (!mModule) {
+      return false;
+    }
+
+    FARPROC proc = ::GetProcAddress(mModule, aName);
+    if (!proc) {
       return false;
     }
 
+    return AddDetour(proc, aHookDest, aOrigFunc);
+  }
+
+private:
+  bool AddDetour(FARPROC aProc, intptr_t aHookDest, void** aOrigFunc)
+  {
+    MOZ_ASSERT(mModule && aProc);
+
     if (!mDetourPatcher.Initialized()) {
-      mDetourPatcher.Init(mModuleName, mNHooks);
+      mDetourPatcher.Init(mNHooks);
     }
 
-    return mDetourPatcher.AddHook(aName, aHookDest, aOrigFunc);
+    return mDetourPatcher.AddHook(aProc, aHookDest, aOrigFunc);
   }
 };
 
+} // namespace interceptor
+
+using WindowsDllInterceptor = interceptor::WindowsDllInterceptor<>;
+
 } // namespace mozilla
 
 #endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */
rename from toolkit/xre/test/win/TestDllInterceptor.cpp
rename to mozglue/tests/interceptor/TestDllInterceptor.cpp
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/mozglue/tests/interceptor/TestDllInterceptor.cpp
@@ -56,17 +56,18 @@ bool CheckHook(HookTestFunc aHookTestFun
            "Executed hooked function %s from %s\n", aFuncName, aDllName);
     return true;
   }
   printf("TEST-FAILED | WindowsDllInterceptor | "
          "Failed to execute hooked function %s from %s\n", aFuncName, aDllName);
   return false;
 }
 
-bool TestHook(HookTestFunc funcTester, const char *dll, const char *func)
+template <size_t N>
+bool TestHook(HookTestFunc funcTester, const char (&dll)[N], const char *func)
 {
   void *orig_func;
   bool successful = false;
   {
     WindowsDllInterceptor TestIntercept;
     TestIntercept.Init(dll);
     successful = TestIntercept.AddHook(func, 0, &orig_func);
   }
@@ -75,17 +76,18 @@ bool TestHook(HookTestFunc funcTester, c
     printf("TEST-PASS | WindowsDllInterceptor | Could hook %s from %s\n", func, dll);
     return CheckHook(funcTester, orig_func, dll, func);
   } else {
     printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to hook %s from %s\n", func, dll);
     return false;
   }
 }
 
-bool TestDetour(const char *dll, const char *func)
+template <size_t N>
+bool TestDetour(const char (&dll)[N], const char *func)
 {
   void *orig_func;
   bool successful = false;
   {
     WindowsDllInterceptor TestIntercept;
     TestIntercept.Init(dll);
     successful = TestIntercept.AddDetour(func, 0, &orig_func);
   }
@@ -94,17 +96,18 @@ bool TestDetour(const char *dll, const c
     printf("TEST-PASS | WindowsDllInterceptor | Could detour %s from %s\n", func, dll);
     return true;
   } else {
     printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to detour %s from %s\n", func, dll);
     return false;
   }
 }
 
-bool MaybeTestHook(const bool cond, HookTestFunc funcTester, const char* dll, const char* func)
+template <size_t N>
+bool MaybeTestHook(const bool cond, HookTestFunc funcTester, const char (&dll)[N], const char* func)
 {
   if (!cond) {
     printf("TEST-SKIPPED | WindowsDllInterceptor | Skipped hook test for %s from %s\n", func, dll);
     return true;
   }
 
   return TestHook(funcTester, dll, func);
 }
@@ -590,16 +593,19 @@ bool TestFreeCredentialsHandle(void* aFu
 {
   auto patchedFreeCredentialsHandle =
     reinterpret_cast<decltype(&FreeCredentialsHandle)>(aFunc);
   return patchedFreeCredentialsHandle(&sCredHandle) == S_OK;
 }
 
 int main()
 {
+  LARGE_INTEGER start;
+  QueryPerformanceCounter(&start);
+
   // We disable this part of the test because the code coverage instrumentation
   // injects code in rotatePayload in a way that WindowsDllInterceptor doesn't
   // understand.
 #ifndef MOZ_CODE_COVERAGE
   payload initial = { 0x12345678, 0xfc4e9d31, 0x87654321 };
   payload p0, p1;
   ZeroMemory(&p0, sizeof(p0));
   ZeroMemory(&p1, sizeof(p1));
@@ -728,13 +734,26 @@ int main()
 
       TestHook(TestAcquireCredentialsHandleA, "sspicli.dll", "AcquireCredentialsHandleA") &&
       TestHook(TestQueryCredentialsAttributesA, "sspicli.dll", "QueryCredentialsAttributesA") &&
       TestHook(TestFreeCredentialsHandle, "sspicli.dll", "FreeCredentialsHandle") &&
 
       TestDetour("kernel32.dll", "BaseThreadInitThunk") &&
       TestDetour("ntdll.dll", "LdrLoadDll")) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
+
+    LARGE_INTEGER end, freq;
+    QueryPerformanceCounter(&end);
+
+    QueryPerformanceFrequency(&freq);
+
+    LARGE_INTEGER result;
+    result.QuadPart = end.QuadPart - start.QuadPart;
+    result.QuadPart *= 1000000;
+    result.QuadPart /= freq.QuadPart;
+
+    printf("Elapsed time: %lld microseconds\n", result.QuadPart);
+
     return 0;
   }
 
   return 1;
 }
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/interceptor/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+CppUnitTests([
+    'TestDllInterceptor',
+])
+
+DEFINES['NS_NO_XPCOM'] = True
+
+DisableStlWrapping()
+
+OS_LIBS += [
+    'ole32',
+]
+
+USE_LIBS += [
+    'mfbt',
+]
--- a/mozglue/tests/moz.build
+++ b/mozglue/tests/moz.build
@@ -8,8 +8,13 @@ DisableStlWrapping()
 
 GeckoCppUnitTests([
     'ShowSSEConfig',
 ], linkage=None)
 
 CppUnitTests([
     'TestPrintf',
 ])
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    TEST_DIRS += [
+        'interceptor',
+    ]
--- a/toolkit/xre/test/win/moz.build
+++ b/toolkit/xre/test/win/moz.build
@@ -3,28 +3,23 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SimplePrograms([
     'TestXREMakeCommandLineWin',
 ])
 
-CppUnitTests([
-    'TestDllInterceptor',
-])
-
 DEFINES['NS_NO_XPCOM'] = True
 
 LOCAL_INCLUDES += [
     '/config',
     '/toolkit/xre',
 ]
 
 DisableStlWrapping()
 USE_STATIC_LIBS = True
 
 OS_LIBS += [
     'comctl32',
-    'ole32',
     'shell32',
     'ws2_32',
 ]
--- a/xpcom/build/PoisonIOInterposerWin.cpp
+++ b/xpcom/build/PoisonIOInterposerWin.cpp
@@ -480,13 +480,13 @@ InitPoisonIOInterposer()
 
 void
 ClearPoisonIOInterposer()
 {
   MOZ_ASSERT(false);
   if (sIOPoisoned) {
     // Destroy the DLL interceptor
     sIOPoisoned = false;
-    sNtDllInterceptor = WindowsDllInterceptor();
+    sNtDllInterceptor.Clear();
   }
 }
 
 } // namespace mozilla
--- a/xpcom/build/moz.build
+++ b/xpcom/build/moz.build
@@ -21,18 +21,19 @@ EXPORTS.mozilla += [
     'LateWriteChecks.h',
     'Omnijar.h',
     'PoisonIOInterposer.h',
     'XPCOM.h',
     'XREAppData.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
-    EXPORTS += ['nsWindowsDllInterceptor.h']
-    EXPORTS.mozilla += ['perfprobe.h']
+    EXPORTS.mozilla += [
+        'perfprobe.h',
+    ]
     SOURCES += ['perfprobe.cpp']
     if CONFIG['CC_TYPE'] != 'gcc':
         SOURCES += [
             'PoisonIOInterposerBase.cpp',
             'PoisonIOInterposerWin.cpp',
         ]
     else:
         SOURCES += ['PoisonIOInterposerStub.cpp']