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