summaryrefslogtreecommitdiffstats
path: root/tools/profiler/core/shared-libraries-win32.cc
blob: 1dc0d06836bdd67e8d11d5482fcb8850ec14fc63 (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
/* -*- 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 "shared-libraries.h"
#include "nsWindowsHelpers.h"
#include "mozilla/NativeNt.h"
#include "mozilla/WindowsEnumProcessModules.h"
#include "mozilla/WindowsProcessMitigations.h"
#include "nsPrintfCString.h"

static bool IsModuleUnsafeToLoad(const nsAString& aModuleName) {
  // 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.
  if (aModuleName.LowerCaseEqualsLiteral("msvp9dec_store.dll")) {
    return true;
  }

  return false;
}

void AddSharedLibraryFromModuleInfo(SharedLibraryInfo& sharedLibraryInfo,
                                    const wchar_t* aModulePath,
                                    mozilla::Maybe<HMODULE> aModule) {
  nsDependentSubstring moduleNameStr(
      mozilla::nt::GetLeafName(nsDependentString(aModulePath)));

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

  // If EAF+ is enabled, parsing ntdll's PE header causes a crash.
  if (mozilla::IsEafPlusEnabled() &&
      moduleNameStr.LowerCaseEqualsLiteral("ntdll.dll")) {
    return;
  }

  // Load the module again - to make sure that its handle will remain valid as
  // we attempt to read the PDB information from it - or for the first time if
  // we only have a path. We want to load the DLL without running the newly
  // loaded module's DllMain function, but not as a data file because we want
  // to be able to do RVA computations easily. Hence, we use the flag
  // LOAD_LIBRARY_AS_IMAGE_RESOURCE which ensures that the sections (not PE
  // headers) will be relocated by the loader. Otherwise GetPdbInfo() and/or
  // GetVersionInfo() can cause a crash. If the original handle |aModule| is
  // valid, LoadLibraryEx just increments its refcount.
  nsModuleHandle handleLock(
      ::LoadLibraryExW(aModulePath, NULL, 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 =
      aModule.isSome() ? reinterpret_cast<uintptr_t>(*aModule)
                       : reinterpret_cast<uintptr_t>(handleLock.get());
  const uintptr_t modEnd = modStart + bounds->length();

  nsAutoCString breakpadId;
  nsAutoString pdbPathStr;
  if (const auto* debugInfo = headers.GetPdbInfo()) {
    MOZ_ASSERT(breakpadId.IsEmpty());
    const GUID& pdbSig = debugInfo->pdbSignature;
    breakpadId.AppendPrintf(
        "%08lX"                             // m0
        "%04X%04X"                          // m1,m2
        "%02X%02X%02X%02X%02X%02X%02X%02X"  // m3
        "%X",                               // pdbAge
        pdbSig.Data1, pdbSig.Data2, pdbSig.Data3, pdbSig.Data4[0],
        pdbSig.Data4[1], pdbSig.Data4[2], pdbSig.Data4[3], pdbSig.Data4[4],
        pdbSig.Data4[5], pdbSig.Data4[6], pdbSig.Data4[7], debugInfo->pdbAge);

    // 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 = NS_ConvertUTF8toUTF16(debugInfo->pdbFileName);
  }

  nsAutoCString codeId;
  DWORD timestamp;
  DWORD imageSize;
  if (headers.GetTimeStamp(timestamp) && headers.GetImageSize(imageSize)) {
    codeId.AppendPrintf(
        "%08lX"  // Uppercase 8 digits of hex timestamp with leading zeroes.
        "%lx",   // Lowercase hex image size
        timestamp, imageSize);
  }

  nsAutoCString versionStr;
  uint64_t version;
  if (headers.GetVersionInfo(version)) {
    versionStr.AppendPrintf("%u.%u.%u.%u",
                            static_cast<uint32_t>((version >> 48) & 0xFFFFu),
                            static_cast<uint32_t>((version >> 32) & 0xFFFFu),
                            static_cast<uint32_t>((version >> 16) & 0xFFFFu),
                            static_cast<uint32_t>(version & 0xFFFFu));
  }

  const nsString& pdbNameStr =
      PromiseFlatString(mozilla::nt::GetLeafName(pdbPathStr));
  SharedLibrary shlib(modStart, modEnd,
                      0,  // DLLs are always mapped at offset 0 on Windows
                      breakpadId, codeId, PromiseFlatString(moduleNameStr),
                      nsDependentString(aModulePath), pdbNameStr, pdbPathStr,
                      versionStr, "");
  sharedLibraryInfo.AddSharedLibrary(shlib);
}

SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
  SharedLibraryInfo sharedLibraryInfo;

  auto addSharedLibraryFromModuleInfo =
      [&sharedLibraryInfo](const wchar_t* aModulePath, HMODULE aModule) {
        AddSharedLibraryFromModuleInfo(sharedLibraryInfo, aModulePath,
                                       mozilla::Some(aModule));
      };

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

SharedLibraryInfo SharedLibraryInfo::GetInfoFromPath(const wchar_t* aPath) {
  SharedLibraryInfo sharedLibraryInfo;
  AddSharedLibraryFromModuleInfo(sharedLibraryInfo, aPath, mozilla::Nothing());
  return sharedLibraryInfo;
}

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