diff options
Diffstat (limited to 'src/debug/createdump/crashinfo.cpp')
-rw-r--r-- | src/debug/createdump/crashinfo.cpp | 522 |
1 files changed, 429 insertions, 93 deletions
diff --git a/src/debug/createdump/crashinfo.cpp b/src/debug/createdump/crashinfo.cpp index b825f4d301..edc31616de 100644 --- a/src/debug/createdump/crashinfo.cpp +++ b/src/debug/createdump/crashinfo.cpp @@ -89,6 +89,10 @@ CrashInfo::EnumMemoryRegion( return S_OK; } +// +// Suspends all the threads and creating a list of them. Should be the first before +// gather any info about the process. +// bool CrashInfo::EnumerateAndSuspendThreads() { @@ -134,6 +138,9 @@ CrashInfo::EnumerateAndSuspendThreads() return true; } +// +// Gather all the necessary crash dump info. +// bool CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType) { @@ -168,42 +175,33 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType) // If full memory dump, include everything regardless of permissions if (minidumpType & MiniDumpWithFullMemory) { - for (const MemoryRegion& region : m_moduleMappings) - { - if (ValidRegion(region)) - { - InsertMemoryRegion(region); - } + for (const MemoryRegion& region : m_moduleMappings) + { + InsertMemoryBackedRegion(region); } for (const MemoryRegion& region : m_otherMappings) { - if (ValidRegion(region)) - { - InsertMemoryRegion(region); - } + InsertMemoryBackedRegion(region); } } - else + // Add all the heap (read/write) memory regions (m_otherMappings contains the heaps) + else if (minidumpType & MiniDumpWithPrivateReadWriteMemory) { - // Add all the heap (read/write) memory regions but not the modules' r/w data segments - if (minidumpType & MiniDumpWithPrivateReadWriteMemory) + for (const MemoryRegion& region : m_otherMappings) { - for (const MemoryRegion& region : m_otherMappings) + if (region.Permissions() == (PF_R | PF_W)) { - if (region.Permissions() == (PF_R | PF_W)) - { - if (ValidRegion(region)) - { - InsertMemoryRegion(region); - } - } + InsertMemoryBackedRegion(region); } } - // Gather all the useful memory regions from the DAC - if (!EnumerateMemoryRegionsWithDAC(programPath, minidumpType)) - { - return false; - } + } + // Gather all the useful memory regions from the DAC + if (!EnumerateMemoryRegionsWithDAC(programPath, minidumpType)) + { + return false; + } + if ((minidumpType & MiniDumpWithFullMemory) == 0) + { // Add the thread's stack and some code memory to core for (ThreadInfo* thread : m_threads) { @@ -217,6 +215,19 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType) thread->GetThreadCode(&start, &size); InsertMemoryRegion(start, size); } + // All the regions added so far has been backed by memory. Now add the rest of + // mappings so the debuggers like lldb see that an address is code (PF_X) even + // if it isn't actually in the core dump. + for (const MemoryRegion& region : m_moduleMappings) + { + assert(!region.IsBackedByMemory()); + InsertMemoryRegion(region); + } + for (const MemoryRegion& region : m_otherMappings) + { + assert(!region.IsBackedByMemory()); + InsertMemoryRegion(region); + } } // Join all adjacent memory regions CombineMemoryRegions(); @@ -235,6 +246,9 @@ CrashInfo::ResumeThreads() } } +// +// Get the auxv entries to use and add to the core dump +// bool CrashInfo::GetAuxvEntries() { @@ -269,6 +283,9 @@ CrashInfo::GetAuxvEntries() return result; } +// +// Get the module mappings for the core dump NT_FILE notes +// bool CrashInfo::EnumerateModuleMappings() { @@ -320,30 +337,38 @@ CrashInfo::EnumerateModuleMappings() int c = sscanf(line, "%lx-%lx %m[-rwxsp] %lx %*[:0-9a-f] %*d %ms\n", &start, &end, &permissions, &offset, &moduleName); if (c == 4 || c == 5) { - if (linuxGateAddress != nullptr && reinterpret_cast<void*>(start) == linuxGateAddress) - { - InsertMemoryRegion(start, end - start); - free(moduleName); + // r = read + // w = write + // x = execute + // s = shared + // p = private (copy on write) + uint32_t regionFlags = 0; + if (strchr(permissions, 'r')) { + regionFlags |= PF_R; } - else { - uint32_t permissionFlags = 0; - if (strchr(permissions, 'r')) { - permissionFlags |= PF_R; - } - if (strchr(permissions, 'w')) { - permissionFlags |= PF_W; - } - if (strchr(permissions, 'x')) { - permissionFlags |= PF_X; - } - MemoryRegion memoryRegion(permissionFlags, start, end, offset, moduleName); + if (strchr(permissions, 'w')) { + regionFlags |= PF_W; + } + if (strchr(permissions, 'x')) { + regionFlags |= PF_X; + } + if (strchr(permissions, 's')) { + regionFlags |= MEMORY_REGION_FLAG_SHARED; + } + if (strchr(permissions, 'p')) { + regionFlags |= MEMORY_REGION_FLAG_PRIVATE; + } + MemoryRegion memoryRegion(regionFlags, start, end, offset, moduleName); - if (moduleName != nullptr && *moduleName == '/') { - m_moduleMappings.insert(memoryRegion); - } - else { - m_otherMappings.insert(memoryRegion); - } + if (moduleName != nullptr && *moduleName == '/') { + m_moduleMappings.insert(memoryRegion); + } + else { + m_otherMappings.insert(memoryRegion); + } + if (linuxGateAddress != nullptr && reinterpret_cast<void*>(start) == linuxGateAddress) + { + InsertMemoryBackedRegion(memoryRegion); } free(permissions); } @@ -354,12 +379,12 @@ CrashInfo::EnumerateModuleMappings() TRACE("Module mappings:\n"); for (const MemoryRegion& region : m_moduleMappings) { - region.Print(); + region.Trace(); } TRACE("Other mappings:\n"); for (const MemoryRegion& region : m_otherMappings) { - region.Print(); + region.Trace(); } } @@ -369,12 +394,16 @@ CrashInfo::EnumerateModuleMappings() return true; } +// +// All the shared (native) module info to the core dump +// bool CrashInfo::GetDSOInfo() { Phdr* phdrAddr = reinterpret_cast<Phdr*>(m_auxvValues[AT_PHDR]); int phnum = m_auxvValues[AT_PHNUM]; assert(m_auxvValues[AT_PHENT] == sizeof(Phdr)); + assert(phnum != PN_XNUM); if (phnum <= 0 || phdrAddr == nullptr) { return false; @@ -387,6 +416,7 @@ CrashInfo::GetDSOInfo() { Phdr ph; if (!ReadMemory(phdrAddr, &ph, sizeof(ph))) { + fprintf(stderr, "ReadMemory(%p, %lx) phdr FAILED\n", phdrAddr, sizeof(ph)); return false; } TRACE("DSO: phdr %p type %d (%x) vaddr %016lx memsz %016lx offset %016lx\n", @@ -396,7 +426,7 @@ CrashInfo::GetDSOInfo() { dynamicAddr = reinterpret_cast<ElfW(Dyn)*>(ph.p_vaddr); } - else if (ph.p_type == PT_GNU_EH_FRAME) + else if (ph.p_type == PT_NOTE || ph.p_type == PT_GNU_EH_FRAME) { if (ph.p_vaddr != 0 && ph.p_memsz != 0) { @@ -414,6 +444,7 @@ CrashInfo::GetDSOInfo() for (;;) { ElfW(Dyn) dyn; if (!ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) { + fprintf(stderr, "ReadMemory(%p, %lx) dyn FAILED\n", dynamicAddr, sizeof(dyn)); return false; } TRACE("DSO: dyn %p tag %ld (%lx) d_ptr %016lx\n", dynamicAddr, dyn.d_tag, dyn.d_tag, dyn.d_un.d_ptr); @@ -430,6 +461,7 @@ CrashInfo::GetDSOInfo() TRACE("DSO: rdebugAddr %p\n", rdebugAddr); struct r_debug debugEntry; if (!ReadMemory(rdebugAddr, &debugEntry, sizeof(debugEntry))) { + fprintf(stderr, "ReadMemory(%p, %lx) r_debug FAILED\n", rdebugAddr, sizeof(debugEntry)); return false; } @@ -438,8 +470,10 @@ CrashInfo::GetDSOInfo() for (struct link_map* linkMapAddr = debugEntry.r_map; linkMapAddr != nullptr;) { struct link_map map; if (!ReadMemory(linkMapAddr, &map, sizeof(map))) { + fprintf(stderr, "ReadMemory(%p, %lx) link_map FAILED\n", linkMapAddr, sizeof(map)); return false; } + // Read the module's name and make sure the memory is added to the core dump int i = 0; if (map.l_name != nullptr) { for (; i < PATH_MAX; i++) @@ -454,18 +488,134 @@ CrashInfo::GetDSOInfo() } } moduleName[i] = '\0'; - TRACE("DSO: link_map entry %p l_ld %p l_addr %lx %s\n", linkMapAddr, map.l_ld, map.l_addr, (char*)moduleName); + TRACE("\nDSO: link_map entry %p l_ld %p l_addr (Ehdr) %lx %s\n", linkMapAddr, map.l_ld, map.l_addr, (char*)moduleName); + + // Read the ELF header and info adding it to the core dump + if (!GetELFInfo(map.l_addr)) { + return false; + } linkMapAddr = map.l_next; } return true; } +inline bool +NameCompare(const char* name, const char* sectionName) +{ + return strncmp(name, sectionName, strlen(sectionName) + 1) == 0; +} + +// +// Add all the necessary ELF headers to the core dump +// +bool +CrashInfo::GetELFInfo(uint64_t baseAddress) +{ + if (baseAddress == 0) { + return true; + } + Ehdr ehdr; + if (!ReadMemory((void*)baseAddress, &ehdr, sizeof(ehdr))) { + fprintf(stderr, "ReadMemory(%p, %lx) ehdr FAILED\n", (void*)baseAddress, sizeof(ehdr)); + return false; + } + int phnum = ehdr.e_phnum; + int shnum = ehdr.e_shnum; + assert(phnum != PN_XNUM); + assert(shnum != SHN_XINDEX); + assert(ehdr.e_shstrndx != SHN_XINDEX); + assert(ehdr.e_phentsize == sizeof(Phdr)); + assert(ehdr.e_shentsize == sizeof(Shdr)); + assert(ehdr.e_ident[EI_CLASS] == ELFCLASS64); + assert(ehdr.e_ident[EI_DATA] == ELFDATA2LSB); + + TRACE("ELF: type %d mach 0x%x ver %d flags 0x%x phnum %d phoff %016lx phentsize 0x%02x shnum %d shoff %016lx shentsize 0x%02x shstrndx %d\n", + ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_flags, phnum, ehdr.e_phoff, ehdr.e_phentsize, shnum, ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shstrndx); + + if (ehdr.e_phoff != 0 && phnum > 0) + { + Phdr* phdrAddr = reinterpret_cast<Phdr*>(baseAddress + ehdr.e_phoff); + + // Add the program headers and search for the module's note and unwind info segments + for (int i = 0; i < phnum; i++, phdrAddr++) + { + Phdr ph; + if (!ReadMemory(phdrAddr, &ph, sizeof(ph))) { + fprintf(stderr, "ReadMemory(%p, %lx) phdr FAILED\n", phdrAddr, sizeof(ph)); + return false; + } + TRACE("ELF: phdr %p type %d (%x) vaddr %016lx memsz %016lx paddr %016lx filesz %016lx offset %016lx align %016lx\n", + phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align); + + if (ph.p_type == PT_DYNAMIC || ph.p_type == PT_NOTE || ph.p_type == PT_GNU_EH_FRAME) + { + if (ph.p_vaddr != 0 && ph.p_memsz != 0) + { + InsertMemoryRegion(baseAddress + ph.p_vaddr, ph.p_memsz); + } + } + } + } + + // Skip the "interpreter" module i.e. /lib64/ld-linux-x86-64.so.2 or ld-2.19.so. The in-memory section headers are + // not valid. Ignore all failures too because on debug builds of coreclr, the section headers are not in valid memory. + if (baseAddress != m_auxvValues[AT_BASE] && ehdr.e_shoff != 0 && shnum > 0 && ehdr.e_shstrndx != SHN_UNDEF) + { + Shdr* shdrAddr = reinterpret_cast<Shdr*>(baseAddress + ehdr.e_shoff); + + // Get the string table section header + Shdr stringTableSectionHeader; + if (!ReadMemory(shdrAddr + ehdr.e_shstrndx, &stringTableSectionHeader, sizeof(stringTableSectionHeader))) { + TRACE("ELF: %2d shdr %p ReadMemory string table section header FAILED\n", ehdr.e_shstrndx, shdrAddr + ehdr.e_shstrndx); + return true; + } + // Get the string table + ArrayHolder<char> stringTable = new char[stringTableSectionHeader.sh_size]; + if (!ReadMemory((void*)(baseAddress + stringTableSectionHeader.sh_offset), stringTable.GetPtr(), stringTableSectionHeader.sh_size)) { + TRACE("ELF: %2d shdr %p ReadMemory string table FAILED\n", ehdr.e_shstrndx, (void*)(baseAddress + stringTableSectionHeader.sh_offset)); + return true; + } + // Add the section headers to the core dump + for (int sectionIndex = 0; sectionIndex < shnum; sectionIndex++, shdrAddr++) + { + Shdr sh; + if (!ReadMemory(shdrAddr, &sh, sizeof(sh))) { + TRACE("ELF: %2d shdr %p ReadMemory FAILED\n", sectionIndex, shdrAddr); + return true; + } + TRACE("ELF: %2d shdr %p type %2d (%x) addr %016lx offset %016lx size %016lx link %08x info %08x name %4d %s\n", + sectionIndex, shdrAddr, sh.sh_type, sh.sh_type, sh.sh_addr, sh.sh_offset, sh.sh_size, sh.sh_link, sh.sh_info, sh.sh_name, &stringTable[sh.sh_name]); + + if (sh.sh_name != SHN_UNDEF && sh.sh_offset > 0 && sh.sh_size > 0) { + char* name = &stringTable[sh.sh_name]; + + // Add the .eh_frame/.eh_frame_hdr unwind info to the core dump + if (NameCompare(name, ".eh_frame") || + NameCompare(name, ".eh_frame_hdr") || + NameCompare(name, ".note.gnu.build-id") || + NameCompare(name, ".note.gnu.ABI-tag") || + NameCompare(name, ".gnu_debuglink")) + { + TRACE("ELF: %s %p size %016lx\n", name, (void*)(baseAddress + sh.sh_offset), sh.sh_size); + InsertMemoryRegion(baseAddress + sh.sh_offset, sh.sh_size); + } + } + } + } + + return true; +} + +// +// Enumerate all the memory regions using the DAC memory region support given a minidump type +// bool CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType) { PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr; - ICLRDataEnumMemoryRegions *clrDataEnumRegions = nullptr; + ICLRDataEnumMemoryRegions* clrDataEnumRegions = nullptr; + IXCLRDataProcess* clrDataProcess = nullptr; HMODULE hdac = nullptr; HRESULT hr = S_OK; bool result = false; @@ -475,7 +625,7 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE dacPath.append(programPath); dacPath.append("/"); dacPath.append(MAKEDLLNAME_A("mscordaccore")); - + // Load and initialize the DAC hdac = LoadLibraryA(dacPath.c_str()); if (hdac == nullptr) @@ -489,17 +639,30 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError()); goto exit; } - hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&clrDataEnumRegions); + if ((minidumpType & MiniDumpWithFullMemory) == 0) + { + hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&clrDataEnumRegions); + if (FAILED(hr)) + { + fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr); + goto exit; + } + // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC + hr = clrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT); + if (FAILED(hr)) + { + fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr); + goto exit; + } + } + hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), m_dataTarget, (void**)&clrDataProcess); if (FAILED(hr)) { - fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr); + fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr); goto exit; } - // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC - hr = clrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT); - if (FAILED(hr)) + if (!EnumerateManagedModules(clrDataProcess)) { - fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr); goto exit; } result = true; @@ -508,6 +671,10 @@ exit: { clrDataEnumRegions->Release(); } + if (clrDataProcess != nullptr) + { + clrDataProcess->Release(); + } if (hdac != nullptr) { FreeLibrary(hdac); @@ -516,6 +683,104 @@ exit: } // +// Enumerate all the managed modules and replace the module +// mapping with the module name found. +// +bool +CrashInfo::EnumerateManagedModules(IXCLRDataProcess* clrDataProcess) +{ + IXCLRDataModule* clrDataModule = nullptr; + CLRDATA_ENUM enumModules = 0; + HRESULT hr = S_OK; + + if (FAILED(hr = clrDataProcess->StartEnumModules(&enumModules))) { + fprintf(stderr, "StartEnumModules FAILED %08x\n", hr); + return false; + } + while ((hr = clrDataProcess->EnumModule(&enumModules, &clrDataModule)) == S_OK) + { + DacpGetModuleData moduleData; + if (SUCCEEDED(hr = moduleData.Request(clrDataModule))) + { + TRACE("MODULE: %016lx dyn %d inmem %d file %d pe %016lx pdb %016lx", moduleData.LoadedPEAddress, moduleData.IsDynamic, + moduleData.IsInMemory, moduleData.IsFileLayout, moduleData.PEFile, moduleData.InMemoryPdbAddress); + + if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) + { + ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; + if (SUCCEEDED(hr = clrDataModule->GetFileName(MAX_LONGPATH, NULL, wszUnicodeName))) + { + char* pszName = (char*)malloc(MAX_LONGPATH + 1); + if (pszName == nullptr) { + fprintf(stderr, "Allocating module name FAILED\n"); + return false; + } + sprintf_s(pszName, MAX_LONGPATH, "%S", (WCHAR*)wszUnicodeName); + TRACE(" %s\n", pszName); + + // Change the module mapping name + ReplaceModuleMapping(moduleData.LoadedPEAddress, pszName); + } + else { + TRACE("\nModule.GetFileName FAILED %08x\n", hr); + } + } + else { + TRACE("\n"); + } + } + else { + TRACE("moduleData.Request FAILED %08x\n", hr); + } + if (clrDataModule != nullptr) { + clrDataModule->Release(); + } + } + if (enumModules != 0) { + clrDataProcess->EndEnumModules(enumModules); + } + return true; +} + +// +// Replace an existing module mapping with one with a different name. +// +void +CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, const char* pszName) +{ + // Add or change the module mapping for this PE image. The managed assembly images are + // already in the module mappings list but in .NET 2.0 they have the name "/dev/zero". + MemoryRegion region(PF_R | PF_W | PF_X, baseAddress, baseAddress + PAGE_SIZE, 0, pszName); + const auto& found = m_moduleMappings.find(region); + if (found == m_moduleMappings.end()) + { + m_moduleMappings.insert(region); + + if (g_diagnostics) { + TRACE("MODULE: ADD "); + region.Trace(); + } + } + else + { + // Create the new memory region with the managed assembly name. + MemoryRegion newRegion(*found, pszName); + + // Remove and cleanup the old one + m_moduleMappings.erase(found); + const_cast<MemoryRegion&>(*found).Cleanup(); + + // Add the new memory region + m_moduleMappings.insert(newRegion); + + if (g_diagnostics) { + TRACE("MODULE: REPLACE "); + newRegion.Trace(); + } + } +} + +// // ReadMemory from target and add to memory regions list // bool @@ -524,7 +789,6 @@ CrashInfo::ReadMemory(void* address, void* buffer, size_t size) uint32_t read = 0; if (FAILED(m_dataTarget->ReadVirtual(reinterpret_cast<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), size, &read))) { - fprintf(stderr, "ReadMemory(%p, %lx) FAILED\n", address, size); return false; } InsertMemoryRegion(reinterpret_cast<uint64_t>(address), size); @@ -538,6 +802,8 @@ CrashInfo::ReadMemory(void* address, void* buffer, size_t size) void CrashInfo::InsertMemoryRegion(uint64_t address, size_t size) { + assert(size < UINT_MAX); + // Round to page boundary uint64_t start = address & PAGE_MASK; assert(start > 0); @@ -546,8 +812,16 @@ CrashInfo::InsertMemoryRegion(uint64_t address, size_t size) uint64_t end = ((address + size) + (PAGE_SIZE - 1)) & PAGE_MASK; assert(end > 0); - MemoryRegion region(start, end); - InsertMemoryRegion(region); + InsertMemoryRegion(MemoryRegion(GetMemoryRegionFlags(start) | MEMORY_REGION_FLAG_MEMORY_BACKED, start, end)); +} + +// +// Adds a memory backed flagged copy of the memory region. The file name is not preserved. +// +void +CrashInfo::InsertMemoryBackedRegion(const MemoryRegion& region) +{ + InsertMemoryRegion(MemoryRegion(region, region.Flags() | MEMORY_REGION_FLAG_MEMORY_BACKED)); } // @@ -556,52 +830,88 @@ CrashInfo::InsertMemoryRegion(uint64_t address, size_t size) void CrashInfo::InsertMemoryRegion(const MemoryRegion& region) { - // First check if the full memory region can be added without conflicts + // First check if the full memory region can be added without conflicts and is fully valid. const auto& found = m_memoryRegions.find(region); if (found == m_memoryRegions.end()) { - // Add full memory region - m_memoryRegions.insert(region); + // If the region is valid, add the full memory region + if (ValidRegion(region)) { + m_memoryRegions.insert(region); + return; + } } else { - // The memory region is not wholely contained in region found - if (!found->Contains(region)) - { - uint64_t start = region.StartAddress(); + // If the memory region is wholly contained in region found and both have the + // same backed by memory state, we're done. + if (found->Contains(region) && (found->IsBackedByMemory() == region.IsBackedByMemory())) { + return; + } + } + // Either part of the region was invalid, part of it hasn't been added or the backed + // by memory state is different. + uint64_t start = region.StartAddress(); - // The region overlaps/conflicts with one already in the set so - // add one page at a time to avoid the overlapping pages. - uint64_t numberPages = region.Size() >> PAGE_SHIFT; + // The region overlaps/conflicts with one already in the set so add one page at a + // time to avoid the overlapping pages. + uint64_t numberPages = region.Size() >> PAGE_SHIFT; - for (int p = 0; p < numberPages; p++, start += PAGE_SIZE) - { - MemoryRegion memoryRegionPage(start, start + PAGE_SIZE); + for (int p = 0; p < numberPages; p++, start += PAGE_SIZE) + { + MemoryRegion memoryRegionPage(region.Flags(), start, start + PAGE_SIZE); - const auto& found = m_memoryRegions.find(memoryRegionPage); - if (found == m_memoryRegions.end()) - { - m_memoryRegions.insert(memoryRegionPage); - } + const auto& found = m_memoryRegions.find(memoryRegionPage); + if (found == m_memoryRegions.end()) + { + // All the single pages added here will be combined in CombineMemoryRegions() + if (ValidRegion(memoryRegionPage)) { + m_memoryRegions.insert(memoryRegionPage); } } + else { + assert(found->IsBackedByMemory() || !region.IsBackedByMemory()); + } } } +// +// Get the memory region flags for a start address +// +uint32_t +CrashInfo::GetMemoryRegionFlags(uint64_t start) +{ + const MemoryRegion* region = SearchMemoryRegions(m_moduleMappings, start); + if (region != nullptr) { + return region->Flags(); + } + region = SearchMemoryRegions(m_otherMappings, start); + if (region != nullptr) { + return region->Flags(); + } + TRACE("GetMemoryRegionFlags: FAILED\n"); + return PF_R | PF_W | PF_X; +} + +// +// Validates a memory region +// bool CrashInfo::ValidRegion(const MemoryRegion& region) { - uint64_t start = region.StartAddress(); - uint64_t numberPages = region.Size() >> PAGE_SHIFT; - - for (int p = 0; p < numberPages; p++, start += PAGE_SIZE) + if (region.IsBackedByMemory()) { - BYTE buffer[1]; - uint32_t read; + uint64_t start = region.StartAddress(); + uint64_t numberPages = region.Size() >> PAGE_SHIFT; - if (FAILED(m_dataTarget->ReadVirtual(start, buffer, 1, &read))) + for (int p = 0; p < numberPages; p++, start += PAGE_SIZE) { - return false; + BYTE buffer[1]; + uint32_t read; + + if (FAILED(m_dataTarget->ReadVirtual(start, buffer, 1, &read))) + { + return false; + } } } return true; @@ -617,28 +927,34 @@ CrashInfo::CombineMemoryRegions() std::set<MemoryRegion> memoryRegionsNew; + // MEMORY_REGION_FLAG_SHARED and MEMORY_REGION_FLAG_PRIVATE are internal flags that + // don't affect the core dump so ignore them when comparing the flags. + uint32_t flags = m_memoryRegions.begin()->Flags() & (MEMORY_REGION_FLAG_MEMORY_BACKED | MEMORY_REGION_FLAG_PERMISSIONS_MASK); uint64_t start = m_memoryRegions.begin()->StartAddress(); uint64_t end = start; for (const MemoryRegion& region : m_memoryRegions) { - if (end == region.StartAddress()) + // To combine a region it needs to be contiguous, same permissions and memory backed flag. + if ((end == region.StartAddress()) && + (flags == (region.Flags() & (MEMORY_REGION_FLAG_MEMORY_BACKED | MEMORY_REGION_FLAG_PERMISSIONS_MASK)))) { end = region.EndAddress(); } else { - MemoryRegion memoryRegion(start, end); + MemoryRegion memoryRegion(flags, start, end); assert(memoryRegionsNew.find(memoryRegion) == memoryRegionsNew.end()); memoryRegionsNew.insert(memoryRegion); + flags = region.Flags() & (MEMORY_REGION_FLAG_MEMORY_BACKED | MEMORY_REGION_FLAG_PERMISSIONS_MASK); start = region.StartAddress(); end = region.EndAddress(); } } assert(start != end); - MemoryRegion memoryRegion(start, end); + MemoryRegion memoryRegion(flags, start, end); assert(memoryRegionsNew.find(memoryRegion) == memoryRegionsNew.end()); memoryRegionsNew.insert(memoryRegion); @@ -649,11 +965,31 @@ CrashInfo::CombineMemoryRegions() TRACE("Memory Regions:\n"); for (const MemoryRegion& region : m_memoryRegions) { - region.Print(); + region.Trace(); + } + } +} + +// +// Searches for a memory region given an address. +// +const MemoryRegion* +CrashInfo::SearchMemoryRegions(const std::set<MemoryRegion>& regions, uint64_t start) +{ + std::set<MemoryRegion>::iterator found = regions.find(MemoryRegion(0, start, start + PAGE_SIZE)); + for (; found != regions.end(); found++) + { + if (start >= found->StartAddress() && start < found->EndAddress()) + { + return &*found; } } + return nullptr; } +// +// Get the process or thread status +// bool CrashInfo::GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name) { |