1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include <windows.h>
7 #include <dbghelp.h>
8 #include <sstream>
9 #include <psapi.h>
10
11 #include "shared-libraries.h"
12 #include "nsWindowsHelpers.h"
13 #include "mozilla/UniquePtr.h"
14 #include "mozilla/Unused.h"
15 #include "nsNativeCharsetUtils.h"
16
17 #define CV_SIGNATURE 0x53445352 // 'SDSR'
18
19 struct CodeViewRecord70 {
20 uint32_t signature;
21 GUID pdbSignature;
22 uint32_t pdbAge;
23 // A UTF-8 string, according to
24 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785
25 char pdbFileName[1];
26 };
27
GetPdbInfo(uintptr_t aStart,nsID & aSignature,uint32_t & aAge,char ** aPdbName)28 static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge,
29 char** aPdbName) {
30 if (!aStart) {
31 return false;
32 }
33
34 PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(aStart);
35 if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
36 return false;
37 }
38
39 PIMAGE_NT_HEADERS ntHeaders =
40 reinterpret_cast<PIMAGE_NT_HEADERS>(aStart + dosHeader->e_lfanew);
41 if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
42 return false;
43 }
44
45 uint32_t relativeVirtualAddress =
46 ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]
47 .VirtualAddress;
48 if (!relativeVirtualAddress) {
49 return false;
50 }
51
52 PIMAGE_DEBUG_DIRECTORY debugDirectory =
53 reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(aStart + relativeVirtualAddress);
54 if (!debugDirectory || debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
55 return false;
56 }
57
58 CodeViewRecord70* debugInfo = reinterpret_cast<CodeViewRecord70*>(
59 aStart + debugDirectory->AddressOfRawData);
60 if (!debugInfo || debugInfo->signature != CV_SIGNATURE) {
61 return false;
62 }
63
64 aAge = debugInfo->pdbAge;
65 GUID& pdbSignature = debugInfo->pdbSignature;
66 aSignature.m0 = pdbSignature.Data1;
67 aSignature.m1 = pdbSignature.Data2;
68 aSignature.m2 = pdbSignature.Data3;
69 memcpy(aSignature.m3, pdbSignature.Data4, sizeof(pdbSignature.Data4));
70
71 // The PDB file name could be different from module filename, so report both
72 // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb
73 *aPdbName = debugInfo->pdbFileName;
74
75 return true;
76 }
77
IsDashOrBraces(char c)78 static bool IsDashOrBraces(char c) { return c == '-' || c == '{' || c == '}'; }
79
GetVersion(WCHAR * dllPath)80 std::string GetVersion(WCHAR* dllPath) {
81 DWORD infoSize = GetFileVersionInfoSizeW(dllPath, nullptr);
82 if (infoSize == 0) {
83 return "";
84 }
85
86 mozilla::UniquePtr<unsigned char[]> infoData =
87 mozilla::MakeUnique<unsigned char[]>(infoSize);
88 if (!GetFileVersionInfoW(dllPath, 0, infoSize, infoData.get())) {
89 return "";
90 }
91
92 VS_FIXEDFILEINFO* vInfo;
93 UINT vInfoLen;
94 if (!VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo, &vInfoLen)) {
95 return "";
96 }
97 if (!vInfo) {
98 return "";
99 }
100
101 std::ostringstream stream;
102 stream << (vInfo->dwFileVersionMS >> 16) << "."
103 << (vInfo->dwFileVersionMS & 0xFFFF) << "."
104 << (vInfo->dwFileVersionLS >> 16) << "."
105 << (vInfo->dwFileVersionLS & 0xFFFF);
106
107 return stream.str();
108 }
109
GetInfoForSelf()110 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
111 SharedLibraryInfo sharedLibraryInfo;
112
113 HANDLE hProcess = GetCurrentProcess();
114 mozilla::UniquePtr<HMODULE[]> hMods;
115 size_t modulesNum = 0;
116 if (hProcess != NULL) {
117 DWORD modulesSize;
118 if (!EnumProcessModules(hProcess, nullptr, 0, &modulesSize)) {
119 return sharedLibraryInfo;
120 }
121 modulesNum = modulesSize / sizeof(HMODULE);
122 hMods = mozilla::MakeUnique<HMODULE[]>(modulesNum);
123 if (!EnumProcessModules(hProcess, hMods.get(), modulesNum * sizeof(HMODULE),
124 &modulesSize)) {
125 return sharedLibraryInfo;
126 }
127 // The list may have shrunk between calls
128 if (modulesSize / sizeof(HMODULE) < modulesNum) {
129 modulesNum = modulesSize / sizeof(HMODULE);
130 }
131 }
132
133 for (unsigned int i = 0; i < modulesNum; i++) {
134 nsID pdbSig;
135 uint32_t pdbAge;
136 nsAutoString pdbPathStr;
137 nsAutoString pdbNameStr;
138 char* pdbName = NULL;
139 std::string breakpadId;
140 WCHAR modulePath[MAX_PATH + 1];
141
142 if (!GetModuleFileNameEx(hProcess, hMods[i], modulePath,
143 sizeof(modulePath) / sizeof(WCHAR))) {
144 continue;
145 }
146
147 MODULEINFO module = {0};
148 if (!GetModuleInformation(hProcess, hMods[i], &module,
149 sizeof(MODULEINFO))) {
150 continue;
151 }
152
153 // Load the module again to make sure that its handle will remain
154 // valid as we attempt to read the PDB information from it. We load the
155 // DLL as a datafile so that if the module actually gets unloaded between
156 // the call to EnumProcessModules and the following LoadLibraryEx, we don't
157 // end up running the now newly loaded module's DllMain function. If the
158 // module is already loaded, LoadLibraryEx just increments its refcount.
159 //
160 // Note that because of the race condition above, merely loading the DLL
161 // again is not safe enough, therefore we also need to make sure that we
162 // can read the memory mapped at the base address before we can safely
163 // proceed to actually access those pages.
164 HMODULE handleLock =
165 LoadLibraryEx(modulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
166 MEMORY_BASIC_INFORMATION vmemInfo = {0};
167 if (handleLock &&
168 sizeof(vmemInfo) ==
169 VirtualQuery(module.lpBaseOfDll, &vmemInfo, sizeof(vmemInfo)) &&
170 vmemInfo.State == MEM_COMMIT &&
171 GetPdbInfo((uintptr_t)module.lpBaseOfDll, pdbSig, pdbAge, &pdbName)) {
172 std::ostringstream stream;
173 stream << pdbSig.ToString() << std::hex << pdbAge;
174 breakpadId = stream.str();
175 std::string::iterator end =
176 std::remove_if(breakpadId.begin(), breakpadId.end(), IsDashOrBraces);
177 breakpadId.erase(end, breakpadId.end());
178 std::transform(breakpadId.begin(), breakpadId.end(), breakpadId.begin(),
179 toupper);
180
181 pdbPathStr = NS_ConvertUTF8toUTF16(pdbName);
182 pdbNameStr = pdbPathStr;
183 int32_t pos = pdbNameStr.RFindChar('\\');
184 if (pos != kNotFound) {
185 pdbNameStr.Cut(0, pos + 1);
186 }
187 }
188
189 nsAutoString modulePathStr(modulePath);
190 nsAutoString moduleNameStr = modulePathStr;
191 int32_t pos = moduleNameStr.RFindChar('\\');
192 if (pos != kNotFound) {
193 moduleNameStr.Cut(0, pos + 1);
194 }
195
196 SharedLibrary shlib((uintptr_t)module.lpBaseOfDll,
197 (uintptr_t)module.lpBaseOfDll + module.SizeOfImage,
198 0, // DLLs are always mapped at offset 0 on Windows
199 breakpadId, moduleNameStr, modulePathStr, pdbNameStr,
200 pdbPathStr, GetVersion(modulePath), "");
201 sharedLibraryInfo.AddSharedLibrary(shlib);
202
203 FreeLibrary(handleLock); // ok to free null handles
204 }
205
206 return sharedLibraryInfo;
207 }
208
Initialize()209 void SharedLibraryInfo::Initialize() { /* do nothing */
210 }
211