summaryrefslogtreecommitdiffstats
path: root/mozglue/baseprofiler/core/shared-libraries-win32.cc
blob: ab9d527528d7db5e1bdea20c56714b4cf493ef0b (plain)
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
/* -*- 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 "BaseProfilerSharedLibraries.h"

#include "mozilla/glue/WindowsUnicode.h"
#include "mozilla/NativeNt.h"
#include "mozilla/WindowsEnumProcessModules.h"
#include "mozilla/WindowsVersion.h"

#include <cctype>
#include <string>

static constexpr char digits[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
                                    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

static void AppendHex(const unsigned char* aBegin, const unsigned char* aEnd,
                      std::string& aOut) {
  for (const unsigned char* p = aBegin; p < aEnd; ++p) {
    unsigned char c = *p;
    aOut += digits[c >> 4];
    aOut += digits[c & 0xFu];
  }
}

static constexpr bool WITH_PADDING = true;
static constexpr bool WITHOUT_PADDING = false;
template <typename T>
static void AppendHex(T aValue, std::string& aOut, bool aWithPadding) {
  for (int i = sizeof(T) * 2 - 1; i >= 0; --i) {
    unsigned nibble = (aValue >> (i * 4)) & 0xFu;
    // If no-padding requested, skip starting zeroes -- unless we're on the very
    // last nibble (so we don't output a blank).
    if (!aWithPadding && i != 0) {
      if (nibble == 0) {
        // Requested no padding, skip zeroes.
        continue;
      }
      // Requested no padding, got first non-zero, pretend we now want padding
      // so we don't skip zeroes anymore.
      aWithPadding = true;
    }
    aOut += digits[nibble];
  }
}

static bool IsModuleUnsafeToLoad(const std::string& aModuleName) {
  auto LowerCaseEqualsLiteral = [](char aModuleChar, char aDetouredChar) {
    return std::tolower(aModuleChar) == aDetouredChar;
  };

#if defined(_M_AMD64) || defined(_M_IX86)
  // Hackaround for Bug 1607574.  Nvidia's shim driver nvd3d9wrap[x].dll detours
  // LoadLibraryExW and it causes AV when the following conditions are met.
  //   1. LoadLibraryExW was called for "detoured.dll"
  //   2. nvinit[x].dll was unloaded
  //   3. OS version is older than 6.2
#  if defined(_M_AMD64)
  LPCWSTR kNvidiaShimDriver = L"nvd3d9wrapx.dll";
  LPCWSTR kNvidiaInitDriver = L"nvinitx.dll";
#  elif defined(_M_IX86)
  LPCWSTR kNvidiaShimDriver = L"nvd3d9wrap.dll";
  LPCWSTR kNvidiaInitDriver = L"nvinit.dll";
#  endif
  constexpr std::string_view detoured_dll = "detoured.dll";
  if (std::equal(aModuleName.cbegin(), aModuleName.cend(),
                 detoured_dll.cbegin(), detoured_dll.cend(),
                 LowerCaseEqualsLiteral) &&
      !mozilla::IsWin8OrLater() && ::GetModuleHandleW(kNvidiaShimDriver) &&
      !::GetModuleHandleW(kNvidiaInitDriver)) {
    return true;
  }
#endif  // defined(_M_AMD64) || defined(_M_IX86)

  // Hackaround for Bug 1723868.  There is no safe way to prevent the module
  // Microsoft's VP9 Video Decoder from being unloaded because mfplat.dll may
  // have posted more than one task to unload the module in the work queue
  // without calling LoadLibrary.
  constexpr std::string_view vp9_decoder_dll = "msvp9dec_store.dll";
  if (std::equal(aModuleName.cbegin(), aModuleName.cend(),
                 vp9_decoder_dll.cbegin(), vp9_decoder_dll.cend(),
                 LowerCaseEqualsLiteral)) {
    return true;
  }

  return false;
}

SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
  SharedLibraryInfo sharedLibraryInfo;

  auto addSharedLibraryFromModuleInfo =
      [&sharedLibraryInfo](const wchar_t* aModulePath, HMODULE aModule) {
        mozilla::UniquePtr<char[]> utf8ModulePath(
            mozilla::glue::WideToUTF8(aModulePath));
        if (!utf8ModulePath) {
          return;
        }

        std::string modulePathStr(utf8ModulePath.get());
        size_t pos = modulePathStr.find_last_of("\\/");
        std::string moduleNameStr = (pos != std::string::npos)
                                        ? modulePathStr.substr(pos + 1)
                                        : modulePathStr;

        // If the module is unsafe to call LoadLibraryEx for, we skip.
        if (IsModuleUnsafeToLoad(moduleNameStr)) {
          return;
        }

        // 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 we don't end up running the newly loaded
        // module's DllMain function.  If the original handle |aModule| is
        // valid, LoadLibraryEx just increments its refcount.
        // LOAD_LIBRARY_AS_IMAGE_RESOURCE is needed to read information from the
        // sections (not PE headers) which should be relocated by the loader,
        // otherwise GetPdbInfo() will cause a crash.
        nsModuleHandle handleLock(::LoadLibraryExW(
            aModulePath, NULL,
            LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE));
        if (!handleLock) {
          return;
        }

        mozilla::nt::PEHeaders headers(handleLock.get());
        if (!headers) {
          return;
        }

        mozilla::Maybe<mozilla::Range<const uint8_t>> bounds =
            headers.GetBounds();
        if (!bounds) {
          return;
        }

        // Put the original |aModule| into SharedLibrary, but we get debug info
        // from |handleLock| as |aModule| might be inaccessible.
        const uintptr_t modStart = reinterpret_cast<uintptr_t>(aModule);
        const uintptr_t modEnd = modStart + bounds->length();

        std::string breakpadId;
        std::string pdbPathStr;
        std::string pdbNameStr;
        if (const auto* debugInfo = headers.GetPdbInfo()) {
          MOZ_ASSERT(breakpadId.empty());
          const GUID& pdbSig = debugInfo->pdbSignature;
          AppendHex(pdbSig.Data1, breakpadId, WITH_PADDING);
          AppendHex(pdbSig.Data2, breakpadId, WITH_PADDING);
          AppendHex(pdbSig.Data3, breakpadId, WITH_PADDING);
          AppendHex(reinterpret_cast<const unsigned char*>(&pdbSig.Data4),
                    reinterpret_cast<const unsigned char*>(&pdbSig.Data4) +
                        sizeof(pdbSig.Data4),
                    breakpadId);
          AppendHex(debugInfo->pdbAge, breakpadId, WITHOUT_PADDING);

          // 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
          pdbPathStr = debugInfo->pdbFileName;
          size_t pos = pdbPathStr.find_last_of("\\/");
          pdbNameStr = (pos != std::string::npos) ? pdbPathStr.substr(pos + 1)
                                                  : pdbPathStr;
        }

        std::string versionStr;
        uint64_t version;
        if (headers.GetVersionInfo(version)) {
          versionStr += std::to_string((version >> 48) & 0xFFFF);
          versionStr += '.';
          versionStr += std::to_string((version >> 32) & 0xFFFF);
          versionStr += '.';
          versionStr += std::to_string((version >> 16) & 0xFFFF);
          versionStr += '.';
          versionStr += std::to_string(version & 0xFFFF);
        }

        SharedLibrary shlib(modStart, modEnd,
                            0,  // DLLs are always mapped at offset 0 on Windows
                            breakpadId, moduleNameStr, modulePathStr,
                            pdbNameStr, pdbPathStr, versionStr, "");
        sharedLibraryInfo.AddSharedLibrary(shlib);
      };

  mozilla::EnumerateProcessModules(addSharedLibraryFromModuleInfo);
  return sharedLibraryInfo;
}

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