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