gecko/toolkit/xre/WindowsCrtPatch.h

143 lines
5.3 KiB
C++

/* -*- 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 == GetModuleHandleA("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::IsXPSP3OrLater()) {
NtdllIntercept.Init("ntdll.dll");
NtdllIntercept.AddHook("RtlImageNtHeader",
reinterpret_cast<intptr_t>(patched_RtlImageNtHeader),
reinterpret_cast<void**>(&stub_RtlImageNtHeader));
}
#endif
}
} // namespace WindowsCrtPatch
#endif // WindowsCrtPatch_h