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 (5f2e62175414)

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 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
/* -*- Mode: C++; tab-width: 2; 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/. */

#include <windows.h>
#include <dbghelp.h>
#include <sstream>
#include <psapi.h>

#include "shared-libraries.h"
#include "nsWindowsHelpers.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/WindowsVersion.h"
#include "nsNativeCharsetUtils.h"
#include "nsPrintfCString.h"
#include "nsReadableUtils.h"

#define CV_SIGNATURE 0x53445352  // 'SDSR'

struct CodeViewRecord70 {
  uint32_t signature;
  GUID pdbSignature;
  uint32_t pdbAge;
  // A UTF-8 string, according to
  // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785
  char pdbFileName[1];
};

static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge,
                       char** aPdbName) {
  if (!aStart) {
    return false;
  }

  PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(aStart);
  if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
    return false;
  }

  PIMAGE_NT_HEADERS ntHeaders =
      reinterpret_cast<PIMAGE_NT_HEADERS>(aStart + dosHeader->e_lfanew);
  if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
    return false;
  }

  uint32_t relativeVirtualAddress =
      ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]
          .VirtualAddress;
  if (!relativeVirtualAddress) {
    return false;
  }

  PIMAGE_DEBUG_DIRECTORY debugDirectory =
      reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(aStart + relativeVirtualAddress);
  if (!debugDirectory || debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
    return false;
  }

  CodeViewRecord70* debugInfo = reinterpret_cast<CodeViewRecord70*>(
      aStart + debugDirectory->AddressOfRawData);
  if (!debugInfo || debugInfo->signature != CV_SIGNATURE) {
    return false;
  }

  aAge = debugInfo->pdbAge;
  GUID& pdbSignature = debugInfo->pdbSignature;
  aSignature.m0 = pdbSignature.Data1;
  aSignature.m1 = pdbSignature.Data2;
  aSignature.m2 = pdbSignature.Data3;
  memcpy(aSignature.m3, pdbSignature.Data4, sizeof(pdbSignature.Data4));

  // The PDB file name could be different from module filename, so report both
  // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb
  *aPdbName = debugInfo->pdbFileName;

  return true;
}

static nsCString GetVersion(WCHAR* dllPath) {
  DWORD infoSize = GetFileVersionInfoSizeW(dllPath, nullptr);
  if (infoSize == 0) {
    return EmptyCString();
  }

  mozilla::UniquePtr<unsigned char[]> infoData =
      mozilla::MakeUnique<unsigned char[]>(infoSize);
  if (!GetFileVersionInfoW(dllPath, 0, infoSize, infoData.get())) {
    return EmptyCString();
  }

  VS_FIXEDFILEINFO* vInfo;
  UINT vInfoLen;
  if (!VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo, &vInfoLen)) {
    return EmptyCString();
  }
  if (!vInfo) {
    return EmptyCString();
  }

  nsPrintfCString version("%d.%d.%d.%d", vInfo->dwFileVersionMS >> 16,
                          vInfo->dwFileVersionMS & 0xFFFF,
                          vInfo->dwFileVersionLS >> 16,
                          vInfo->dwFileVersionLS & 0xFFFF);
  return std::move(version);
}

SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
  SharedLibraryInfo sharedLibraryInfo;

  HANDLE hProcess = GetCurrentProcess();
  mozilla::UniquePtr<HMODULE[]> hMods;
  size_t modulesNum = 0;
  if (hProcess != NULL) {
    DWORD modulesSize;
    if (!EnumProcessModules(hProcess, nullptr, 0, &modulesSize)) {
      return sharedLibraryInfo;
    }
    modulesNum = modulesSize / sizeof(HMODULE);
    hMods = mozilla::MakeUnique<HMODULE[]>(modulesNum);
    if (!EnumProcessModules(hProcess, hMods.get(), modulesNum * sizeof(HMODULE),
                            &modulesSize)) {
      return sharedLibraryInfo;
    }
    // The list may have shrunk between calls
    if (modulesSize / sizeof(HMODULE) < modulesNum) {
      modulesNum = modulesSize / sizeof(HMODULE);
    }
  }

  for (unsigned int i = 0; i < modulesNum; i++) {
    WCHAR modulePath[MAX_PATH + 1];
    if (!GetModuleFileNameEx(hProcess, hMods[i], modulePath,
                             sizeof(modulePath) / sizeof(WCHAR))) {
      continue;
    }

    MODULEINFO module = {0};
    if (!GetModuleInformation(hProcess, hMods[i], &module,
                              sizeof(MODULEINFO))) {
      continue;
    }

    nsAutoString modulePathStr(modulePath);
    nsAutoString moduleNameStr = modulePathStr;
    int32_t pos = moduleNameStr.RFindChar('\\');
    if (pos != kNotFound) {
      moduleNameStr.Cut(0, pos + 1);
    }

    // Hackaround for Bug 1607574.  Nvidia's shim driver nvd3d9wrapx.dll detours
    // LoadLibraryExW when it's loaded and the detour function causes AV when
    // the code tries to access data pointing to an address within unloaded
    // nvinitx.dll.
    // The crashing code is executed when a given parameter is "detoured.dll"
    // and OS version is older than 6.2.  We hit that crash at the following
    // call to LoadLibraryEx even if we specify LOAD_LIBRARY_AS_DATAFILE.
    // We work around it by skipping LoadLibraryEx, and add a library info with
    // a dummy breakpad id instead.
    if (moduleNameStr.LowerCaseEqualsLiteral("detoured.dll") &&
        !mozilla::IsWin8OrLater() && ::GetModuleHandle(L"nvd3d9wrapx.dll") &&
        !::GetModuleHandle(L"nvinitx.dll")) {
      NS_NAMED_LITERAL_STRING(pdbNameStr, "detoured.pdb");
      SharedLibrary shlib(
          (uintptr_t)module.lpBaseOfDll,
          (uintptr_t)module.lpBaseOfDll + module.SizeOfImage,
          0,  // DLLs are always mapped at offset 0 on Windows
          NS_LITERAL_CSTRING("000000000000000000000000000000000"),
          moduleNameStr, modulePathStr, pdbNameStr, pdbNameStr,
          GetVersion(modulePath), "");
      sharedLibraryInfo.AddSharedLibrary(shlib);
      continue;
    }

    nsCString breakpadId;
    // Load the module again to make sure that its handle will remain
    // valid as we attempt to read the PDB information from it.  We load the
    // DLL as a datafile so that if the module actually gets unloaded between
    // the call to EnumProcessModules and the following LoadLibraryEx, we don't
    // end up running the now newly loaded module's DllMain function.  If the
    // module is already loaded, LoadLibraryEx just increments its refcount.
    //
    // Note that because of the race condition above, merely loading the DLL
    // again is not safe enough, therefore we also need to make sure that we
    // can read the memory mapped at the base address before we can safely
    // proceed to actually access those pages.
    HMODULE handleLock =
        LoadLibraryEx(modulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
    MEMORY_BASIC_INFORMATION vmemInfo = {0};
    nsID pdbSig;
    uint32_t pdbAge;
    nsAutoString pdbPathStr;
    nsAutoString pdbNameStr;
    char* pdbName = nullptr;
    if (handleLock &&
        sizeof(vmemInfo) ==
            VirtualQuery(module.lpBaseOfDll, &vmemInfo, sizeof(vmemInfo)) &&
        vmemInfo.State == MEM_COMMIT &&
        GetPdbInfo((uintptr_t)module.lpBaseOfDll, pdbSig, pdbAge, &pdbName)) {
      MOZ_ASSERT(breakpadId.IsEmpty());
      breakpadId.AppendPrintf(
          "%08X"                              // m0
          "%04X%04X"                          // m1,m2
          "%02X%02X%02X%02X%02X%02X%02X%02X"  // m3
          "%X",                               // pdbAge
          pdbSig.m0, pdbSig.m1, pdbSig.m2, pdbSig.m3[0], pdbSig.m3[1],
          pdbSig.m3[2], pdbSig.m3[3], pdbSig.m3[4], pdbSig.m3[5], pdbSig.m3[6],
          pdbSig.m3[7], pdbAge);

      pdbPathStr = NS_ConvertUTF8toUTF16(pdbName);
      pdbNameStr = pdbPathStr;
      int32_t pos = pdbNameStr.RFindChar('\\');
      if (pos != kNotFound) {
        pdbNameStr.Cut(0, pos + 1);
      }
    }

    SharedLibrary shlib((uintptr_t)module.lpBaseOfDll,
                        (uintptr_t)module.lpBaseOfDll + module.SizeOfImage,
                        0,  // DLLs are always mapped at offset 0 on Windows
                        breakpadId, moduleNameStr, modulePathStr, pdbNameStr,
                        pdbPathStr, GetVersion(modulePath), "");
    sharedLibraryInfo.AddSharedLibrary(shlib);

    FreeLibrary(handleLock);  // ok to free null handles
  }

  return sharedLibraryInfo;
}

void SharedLibraryInfo::Initialize() { /* do nothing */
}