michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "shared-libraries.h" michael@0: #include "nsWindowsHelpers.h" michael@0: michael@0: #define CV_SIGNATURE 0x53445352 // 'SDSR' michael@0: michael@0: struct CodeViewRecord70 michael@0: { michael@0: uint32_t signature; michael@0: GUID pdbSignature; michael@0: uint32_t pdbAge; michael@0: char pdbFileName[1]; michael@0: }; michael@0: michael@0: static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge, char** aPdbName) michael@0: { michael@0: if (!aStart) { michael@0: return false; michael@0: } michael@0: michael@0: PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(aStart); michael@0: if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { michael@0: return false; michael@0: } michael@0: michael@0: PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast( michael@0: aStart + dosHeader->e_lfanew); michael@0: if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) { michael@0: return false; michael@0: } michael@0: michael@0: uint32_t relativeVirtualAddress = michael@0: ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; michael@0: if (!relativeVirtualAddress) { michael@0: return false; michael@0: } michael@0: michael@0: PIMAGE_DEBUG_DIRECTORY debugDirectory = michael@0: reinterpret_cast(aStart + relativeVirtualAddress); michael@0: if (!debugDirectory || debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) { michael@0: return false; michael@0: } michael@0: michael@0: CodeViewRecord70 *debugInfo = reinterpret_cast( michael@0: aStart + debugDirectory->AddressOfRawData); michael@0: if (!debugInfo || debugInfo->signature != CV_SIGNATURE) { michael@0: return false; michael@0: } michael@0: michael@0: aAge = debugInfo->pdbAge; michael@0: GUID& pdbSignature = debugInfo->pdbSignature; michael@0: aSignature.m0 = pdbSignature.Data1; michael@0: aSignature.m1 = pdbSignature.Data2; michael@0: aSignature.m2 = pdbSignature.Data3; michael@0: memcpy(aSignature.m3, pdbSignature.Data4, sizeof(pdbSignature.Data4)); michael@0: michael@0: // The PDB file name could be different from module filename, so report both michael@0: // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb michael@0: char * leafName = strrchr(debugInfo->pdbFileName, '\\'); michael@0: if (leafName) { michael@0: // Only report the file portion of the path michael@0: *aPdbName = leafName + 1; michael@0: } else { michael@0: *aPdbName = debugInfo->pdbFileName; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool IsDashOrBraces(char c) michael@0: { michael@0: return c == '-' || c == '{' || c == '}'; michael@0: } michael@0: michael@0: SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() michael@0: { michael@0: SharedLibraryInfo sharedLibraryInfo; michael@0: michael@0: nsAutoHandle snap(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId())); michael@0: michael@0: MODULEENTRY32 module = {0}; michael@0: module.dwSize = sizeof(MODULEENTRY32); michael@0: if (Module32First(snap, &module)) { michael@0: do { michael@0: nsID pdbSig; michael@0: uint32_t pdbAge; michael@0: char *pdbName = NULL; michael@0: michael@0: // Load the module again to make sure that its handle will remain remain michael@0: // valid as we attempt to read the PDB information from it. We load the michael@0: // DLL as a datafile so that if the module actually gets unloaded between michael@0: // the call to Module32Next and the following LoadLibraryEx, we don't end michael@0: // up running the now newly loaded module's DllMain function. If the michael@0: // module is already loaded, LoadLibraryEx just increments its refcount. michael@0: // michael@0: // Note that because of the race condition above, merely loading the DLL michael@0: // again is not safe enough, therefore we also need to make sure that we michael@0: // can read the memory mapped at the base address before we can safely michael@0: // proceed to actually access those pages. michael@0: HMODULE handleLock = LoadLibraryEx(module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE); michael@0: MEMORY_BASIC_INFORMATION vmemInfo = {0}; michael@0: if (handleLock && michael@0: sizeof(vmemInfo) == VirtualQuery(module.modBaseAddr, &vmemInfo, sizeof(vmemInfo)) && michael@0: vmemInfo.State == MEM_COMMIT && michael@0: GetPdbInfo((uintptr_t)module.modBaseAddr, pdbSig, pdbAge, &pdbName)) { michael@0: std::ostringstream stream; michael@0: stream << pdbSig.ToString() << std::hex << pdbAge; michael@0: std::string breakpadId = stream.str(); michael@0: std::string::iterator end = michael@0: std::remove_if(breakpadId.begin(), breakpadId.end(), IsDashOrBraces); michael@0: breakpadId.erase(end, breakpadId.end()); michael@0: std::transform(breakpadId.begin(), breakpadId.end(), michael@0: breakpadId.begin(), toupper); michael@0: michael@0: SharedLibrary shlib((uintptr_t)module.modBaseAddr, michael@0: (uintptr_t)module.modBaseAddr+module.modBaseSize, michael@0: 0, // DLLs are always mapped at offset 0 on Windows michael@0: breakpadId, michael@0: pdbName); michael@0: sharedLibraryInfo.AddSharedLibrary(shlib); michael@0: } michael@0: FreeLibrary(handleLock); // ok to free null handles michael@0: } while (Module32Next(snap, &module)); michael@0: } michael@0: michael@0: return sharedLibraryInfo; michael@0: } michael@0: