DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (4a108e94d3e2)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

/*
 * This file works around an incompatibility between Visual Studio 2013's
 * C Runtime DLL and Windows XP Service Pack 2.
 *
 * On XP SP2, msvcr120.dll fails to load, because it has a load-time dependency
 * on a kernel32 export named GetLogicalProcessorInformation, which is only
 * available in XP SP3 and newer. Microsoft has declared this to be by design.
 * See: https://connect.microsoft.com/VisualStudio/feedback/details/811379/
 *
 * The CRT calls GetLogicalProcessorInformation only from the concurrency
 * runtime, which our code does not use. A potential workaround is to 
 * static-link the CRT into all of our binaries and let the linker drop the
 * unused API calls. We don't want to take that approach, due to concerns
 * about binary bloat and jemalloc integration.
 *
 * Instead we hook the Windows loader and patch out the missing import.
 * We intercept ntdll!RtlImageNtHeader, which is a helper API called during
 * the DLL loading process. We walk the PE image and redirect the
 * GetLogicalProcessorInformation import to something benign like DebugBreak,
 * before the loader populates msvcr120.dll's import table.
 *
 * This is a fragile hack that only works if we can set up the hook before
 * Windows tries to load msvcr120.dll. This means that all .exe files:
 *  1) must static-link the CRT
 *  2) must delay-load anything with ties to msvcr120.dll (e.g. mozglue.dll)
 *  3) must not call malloc, because the linker would substitute our mozglue
 *     replacements, which leads to the CRT loading mozglue before main.
 * The remainder of our binaries can continue to dynamic-link the CRT.
 * Assertions enforce that our hooks are installed before msvcr120.dll.
 */

#ifndef WindowsCrtPatch_h
#define WindowsCrtPatch_h

#include "nsWindowsDllInterceptor.h"
#include "mozilla/WindowsVersion.h"

namespace WindowsCrtPatch {

mozilla::WindowsDllInterceptor NtdllIntercept;

typedef PIMAGE_NT_HEADERS (NTAPI *RtlImageNtHeader_func)(HMODULE module);
static RtlImageNtHeader_func stub_RtlImageNtHeader = 0;

// A helper to simplify the use of Relative Virtual Addresses.
template <typename T>
class RVAPtr
{
public:
  RVAPtr(HMODULE module, size_t rva)
    : _ptr(reinterpret_cast<T*>(reinterpret_cast<char*>(module) + rva)) {}
  operator T*() { return _ptr; }
  T* operator ->() { return _ptr; }
  T* operator ++() { return ++_ptr; }

private:
  T* _ptr;
};

void
PatchModuleImports(HMODULE module, PIMAGE_NT_HEADERS headers)
{
  static const WORD MAGIC_DOS = 0x5a4d; // "MZ"
  static const DWORD MAGIC_PE = 0x4550; // "PE\0\0"
  RVAPtr<IMAGE_DOS_HEADER> dosStub(module, 0);

  if (!module ||
      !headers ||
      dosStub->e_magic != MAGIC_DOS ||
      headers != RVAPtr<IMAGE_NT_HEADERS>(module, dosStub->e_lfanew) ||
      headers->Signature != MAGIC_PE ||
      headers->FileHeader.SizeOfOptionalHeader < sizeof(IMAGE_OPTIONAL_HEADER)) {
    return;
  }

  // The format of the import directory is described in:
  // "An In-Depth Look into the Win32 Portable Executable File Format, Part 2"
  // http://msdn.microsoft.com/en-us/magazine/cc301808.aspx

  IMAGE_DATA_DIRECTORY* importDirectory =
    &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
  RVAPtr<IMAGE_IMPORT_DESCRIPTOR> descriptor(module, importDirectory->VirtualAddress);

  for (; descriptor->OriginalFirstThunk; ++descriptor) {
    RVAPtr<char> importedModule(module, descriptor->Name);
    if (!stricmp(importedModule, "kernel32.dll")) {
      RVAPtr<IMAGE_THUNK_DATA> thunk(module, descriptor->OriginalFirstThunk);
      for (; thunk->u1.AddressOfData; ++thunk) {
        RVAPtr<IMAGE_IMPORT_BY_NAME> import(module, thunk->u1.AddressOfData);
        if (!strcmp((char*)import->Name, "GetLogicalProcessorInformation")) {
          memcpy(import->Name, "DebugBreak", sizeof("DebugBreak"));
        }
      }
    }
  }
}

PIMAGE_NT_HEADERS NTAPI
patched_RtlImageNtHeader(HMODULE module)
{
  PIMAGE_NT_HEADERS headers = stub_RtlImageNtHeader(module);

  if (module == GetModuleHandleW(L"msvcr120.dll")) {
    PatchModuleImports(module, headers);
  }

  return headers;
}

// Non-inline to make the asserts stand out
MOZ_NEVER_INLINE void
Init()
{
  // If the C Runtime DLL is already loaded, our hooks will be ineffective,
  // and we will fail to load on XP SP2 when built with Visual Studio 2013.
  // We assert the absence of these modules on all Windows builds in order to
  // catch breakage faster.
  //
  // If these assertions fail, see the comment at the top of this file for
  // possible causes. Any changes to the lines below MUST be tested on XP SP2!
  MOZ_ASSERT(!GetModuleHandleA("mozglue.dll"));
  MOZ_ASSERT(!GetModuleHandleA("msvcr120.dll"));
  MOZ_ASSERT(!GetModuleHandleA("msvcr120d.dll"));

#if defined(_M_IX86) && defined(_MSC_VER)
  if (!mozilla::IsWin2003OrLater()) {
    // Test for the export because we can't trust the SP version (bug 1137609)
    HMODULE kernel = GetModuleHandleA("kernel32.dll");
    if (!kernel || !GetProcAddress(kernel, "GetLogicalProcessorInformation")) {
      NtdllIntercept.Init("ntdll.dll");
      NtdllIntercept.AddHook("RtlImageNtHeader",
                             reinterpret_cast<intptr_t>(patched_RtlImageNtHeader),
                             reinterpret_cast<void**>(&stub_RtlImageNtHeader));
    }
  }
#endif
}

} // namespace WindowsCrtPatch

#endif // WindowsCrtPatch_h