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 <tlhelp32.h>
8 #include <dbghelp.h>
9 #include <sstream>
10
11 #include "shared-libraries.h"
12 #include "nsWindowsHelpers.h"
13
14 #define CV_SIGNATURE 0x53445352 // 'SDSR'
15
16 struct CodeViewRecord70
17 {
18 uint32_t signature;
19 GUID pdbSignature;
20 uint32_t pdbAge;
21 char pdbFileName[1];
22 };
23
GetPdbInfo(uintptr_t aStart,nsID & aSignature,uint32_t & aAge,char ** aPdbName)24 static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge, char** aPdbName)
25 {
26 if (!aStart) {
27 return false;
28 }
29
30 PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(aStart);
31 if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
32 return false;
33 }
34
35 PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
36 aStart + dosHeader->e_lfanew);
37 if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
38 return false;
39 }
40
41 uint32_t relativeVirtualAddress =
42 ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
43 if (!relativeVirtualAddress) {
44 return false;
45 }
46
47 PIMAGE_DEBUG_DIRECTORY debugDirectory =
48 reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(aStart + relativeVirtualAddress);
49 if (!debugDirectory || debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
50 return false;
51 }
52
53 CodeViewRecord70 *debugInfo = reinterpret_cast<CodeViewRecord70 *>(
54 aStart + debugDirectory->AddressOfRawData);
55 if (!debugInfo || debugInfo->signature != CV_SIGNATURE) {
56 return false;
57 }
58
59 aAge = debugInfo->pdbAge;
60 GUID& pdbSignature = debugInfo->pdbSignature;
61 aSignature.m0 = pdbSignature.Data1;
62 aSignature.m1 = pdbSignature.Data2;
63 aSignature.m2 = pdbSignature.Data3;
64 memcpy(aSignature.m3, pdbSignature.Data4, sizeof(pdbSignature.Data4));
65
66 // The PDB file name could be different from module filename, so report both
67 // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb
68 char * leafName = strrchr(debugInfo->pdbFileName, '\\');
69 if (leafName) {
70 // Only report the file portion of the path
71 *aPdbName = leafName + 1;
72 } else {
73 *aPdbName = debugInfo->pdbFileName;
74 }
75
76 return true;
77 }
78
IsDashOrBraces(char c)79 static bool IsDashOrBraces(char c)
80 {
81 return c == '-' || c == '{' || c == '}';
82 }
83
GetInfoForSelf()84 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
85 {
86 SharedLibraryInfo sharedLibraryInfo;
87
88 nsAutoHandle snap(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()));
89
90 MODULEENTRY32 module = {0};
91 module.dwSize = sizeof(MODULEENTRY32);
92 if (Module32First(snap, &module)) {
93 do {
94 nsID pdbSig;
95 uint32_t pdbAge;
96 char *pdbName = NULL;
97
98 // Load the module again to make sure that its handle will remain remain
99 // valid as we attempt to read the PDB information from it. We load the
100 // DLL as a datafile so that if the module actually gets unloaded between
101 // the call to Module32Next and the following LoadLibraryEx, we don't end
102 // up running the now newly loaded module's DllMain function. If the
103 // module is already loaded, LoadLibraryEx just increments its refcount.
104 //
105 // Note that because of the race condition above, merely loading the DLL
106 // again is not safe enough, therefore we also need to make sure that we
107 // can read the memory mapped at the base address before we can safely
108 // proceed to actually access those pages.
109 HMODULE handleLock = LoadLibraryEx(module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
110 MEMORY_BASIC_INFORMATION vmemInfo = {0};
111 if (handleLock &&
112 sizeof(vmemInfo) == VirtualQuery(module.modBaseAddr, &vmemInfo, sizeof(vmemInfo)) &&
113 vmemInfo.State == MEM_COMMIT &&
114 GetPdbInfo((uintptr_t)module.modBaseAddr, pdbSig, pdbAge, &pdbName)) {
115 std::ostringstream stream;
116 stream << pdbSig.ToString() << std::hex << pdbAge;
117 std::string breakpadId = stream.str();
118 std::string::iterator end =
119 std::remove_if(breakpadId.begin(), breakpadId.end(), IsDashOrBraces);
120 breakpadId.erase(end, breakpadId.end());
121 std::transform(breakpadId.begin(), breakpadId.end(),
122 breakpadId.begin(), toupper);
123
124 SharedLibrary shlib((uintptr_t)module.modBaseAddr,
125 (uintptr_t)module.modBaseAddr+module.modBaseSize,
126 0, // DLLs are always mapped at offset 0 on Windows
127 breakpadId,
128 pdbName);
129 sharedLibraryInfo.AddSharedLibrary(shlib);
130 }
131 FreeLibrary(handleLock); // ok to free null handles
132 } while (Module32Next(snap, &module));
133 }
134
135 return sharedLibraryInfo;
136 }
137
138