summaryrefslogtreecommitdiff
path: root/src/debug
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug')
-rw-r--r--src/debug/createdump/crashinfo.cpp522
-rw-r--r--src/debug/createdump/crashinfo.h16
-rw-r--r--src/debug/createdump/createdump.h4
-rw-r--r--src/debug/createdump/dumpwriter.cpp65
-rw-r--r--src/debug/createdump/dumpwriter.h6
-rw-r--r--src/debug/createdump/memoryregion.h71
-rw-r--r--src/debug/createdump/threadinfo.cpp22
7 files changed, 544 insertions, 162 deletions
diff --git a/src/debug/createdump/crashinfo.cpp b/src/debug/createdump/crashinfo.cpp
index b825f4d..edc3161 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)
{
diff --git a/src/debug/createdump/crashinfo.h b/src/debug/createdump/crashinfo.h
index 914a88e..43e8269 100644
--- a/src/debug/createdump/crashinfo.h
+++ b/src/debug/createdump/crashinfo.h
@@ -38,6 +38,7 @@ public:
bool GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType);
void ResumeThreads();
static bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name);
+ static const MemoryRegion* SearchMemoryRegions(const std::set<MemoryRegion>& regions, uint64_t start);
const pid_t Pid() const { return m_pid; }
const pid_t Ppid() const { return m_ppid; }
@@ -46,9 +47,9 @@ public:
ICLRDataTarget* DataTarget() const { return m_dataTarget; }
const std::vector<ThreadInfo*> Threads() const { return m_threads; }
- const std::set<MemoryRegion> ModuleMappings() const { return m_moduleMappings; }
- const std::set<MemoryRegion> OtherMappings() const { return m_otherMappings; }
- const std::set<MemoryRegion> MemoryRegions() const { return m_memoryRegions; }
+ const std::set<MemoryRegion> ModuleMappings() const { return m_moduleMappings; }
+ const std::set<MemoryRegion> OtherMappings() const { return m_otherMappings; }
+ const std::set<MemoryRegion> MemoryRegions() const { return m_memoryRegions; }
const std::vector<elf_aux_entry> AuxvEntries() const { return m_auxvEntries; }
const size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); }
@@ -58,18 +59,21 @@ public:
STDMETHOD_(ULONG, Release)();
// ICLRDataEnumMemoryRegionsCallback
- virtual HRESULT STDMETHODCALLTYPE EnumMemoryRegion(
- /* [in] */ CLRDATA_ADDRESS address,
- /* [in] */ ULONG32 size);
+ virtual HRESULT STDMETHODCALLTYPE EnumMemoryRegion(/* [in] */ CLRDATA_ADDRESS address, /* [in] */ ULONG32 size);
private:
bool GetAuxvEntries();
bool EnumerateModuleMappings();
bool GetDSOInfo();
+ bool GetELFInfo(uint64_t baseAddress);
bool EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType);
+ bool EnumerateManagedModules(IXCLRDataProcess* clrDataProcess);
+ void ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, const char* pszName);
bool ReadMemory(void* address, void* buffer, size_t size);
+ void InsertMemoryBackedRegion(const MemoryRegion& region);
void InsertMemoryRegion(uint64_t address, size_t size);
void InsertMemoryRegion(const MemoryRegion& region);
+ uint32_t GetMemoryRegionFlags(uint64_t start);
bool ValidRegion(const MemoryRegion& region);
void CombineMemoryRegions();
};
diff --git a/src/debug/createdump/createdump.h b/src/debug/createdump/createdump.h
index 38c3525..6f72f0e 100644
--- a/src/debug/createdump/createdump.h
+++ b/src/debug/createdump/createdump.h
@@ -33,6 +33,8 @@ extern bool g_diagnostics;
#include <xcordebug.h>
#include <mscoree.h>
#include <dumpcommon.h>
+typedef int T_CONTEXT;
+#include <dacprivate.h>
#include <arrayholder.h>
#include <releaseholder.h>
#include <unistd.h>
@@ -54,4 +56,4 @@ extern bool g_diagnostics;
#include "threadinfo.h"
#include "memoryregion.h"
#include "crashinfo.h"
-#include "dumpwriter.h" \ No newline at end of file
+#include "dumpwriter.h"
diff --git a/src/debug/createdump/dumpwriter.cpp b/src/debug/createdump/dumpwriter.cpp
index 69f0ece..06c5b96 100644
--- a/src/debug/createdump/dumpwriter.cpp
+++ b/src/debug/createdump/dumpwriter.cpp
@@ -91,14 +91,9 @@ DumpWriter::WriteDump()
ehdr.e_ident[1] = ELFMAG1;
ehdr.e_ident[2] = ELFMAG2;
ehdr.e_ident[3] = ELFMAG3;
- ehdr.e_ident[4] = ELF_CLASS;
-
- // Note: The sex is the current system running minidump-2-core
- // Big or Little endian. This means you have to create
- // the core (minidump-2-core) on the system that matches
- // your intent to debug properly.
- ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
- ehdr.e_ident[6] = EV_CURRENT;
+ ehdr.e_ident[EI_CLASS] = ELF_CLASS;
+ ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr.e_ident[EI_VERSION] = EV_CURRENT;
ehdr.e_ident[EI_OSABI] = ELFOSABI_LINUX;
ehdr.e_type = ET_CORE;
@@ -180,9 +175,17 @@ DumpWriter::WriteDump()
phdr.p_vaddr = memoryRegion.StartAddress();
phdr.p_memsz = memoryRegion.Size();
- offset += filesz;
- phdr.p_filesz = filesz = memoryRegion.Size();
- phdr.p_offset = offset;
+ if (memoryRegion.IsBackedByMemory())
+ {
+ offset += filesz;
+ phdr.p_filesz = filesz = memoryRegion.Size();
+ phdr.p_offset = offset;
+ }
+ else
+ {
+ phdr.p_filesz = 0;
+ phdr.p_offset = 0;
+ }
if (!WriteData(&phdr, sizeof(phdr))) {
return false;
@@ -230,26 +233,30 @@ DumpWriter::WriteDump()
uint64_t total = 0;
for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions())
{
- uint32_t size = memoryRegion.Size();
- uint64_t address = memoryRegion.StartAddress();
- total += size;
-
- while (size > 0)
+ // Only write the regions that are backed by memory
+ if (memoryRegion.IsBackedByMemory())
{
- uint32_t bytesToRead = std::min(size, (uint32_t)sizeof(m_tempBuffer));
- uint32_t read = 0;
-
- if (FAILED(m_crashInfo.DataTarget()->ReadVirtual(address, m_tempBuffer, bytesToRead, &read))) {
- fprintf(stderr, "ReadVirtual(%016lx, %08x) FAILED\n", address, bytesToRead);
- return false;
- }
-
- if (!WriteData(m_tempBuffer, read)) {
- return false;
+ uint32_t size = memoryRegion.Size();
+ uint64_t address = memoryRegion.StartAddress();
+ total += size;
+
+ while (size > 0)
+ {
+ uint32_t bytesToRead = std::min(size, (uint32_t)sizeof(m_tempBuffer));
+ uint32_t read = 0;
+
+ if (FAILED(m_crashInfo.DataTarget()->ReadVirtual(address, m_tempBuffer, bytesToRead, &read))) {
+ fprintf(stderr, "ReadVirtual(%016lx, %08x) FAILED\n", address, bytesToRead);
+ return false;
+ }
+
+ if (!WriteData(m_tempBuffer, read)) {
+ return false;
+ }
+
+ address += read;
+ size -= read;
}
-
- address += read;
- size -= read;
}
}
diff --git a/src/debug/createdump/dumpwriter.h b/src/debug/createdump/dumpwriter.h
index 0b4f88c..7da0d63 100644
--- a/src/debug/createdump/dumpwriter.h
+++ b/src/debug/createdump/dumpwriter.h
@@ -69,9 +69,3 @@ private:
);
}
};
-
-static inline int sex()
-{
- int probe = 1;
- return !*(char *)&probe;
-}
diff --git a/src/debug/createdump/memoryregion.h b/src/debug/createdump/memoryregion.h
index 1332ab1..1f6c5f5 100644
--- a/src/debug/createdump/memoryregion.h
+++ b/src/debug/createdump/memoryregion.h
@@ -2,20 +2,31 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+enum MEMORY_REGION_FLAGS : uint32_t
+{
+ // PF_X = 0x01, // Execute
+ // PF_W = 0x02, // Write
+ // PF_R = 0x04, // Read
+ MEMORY_REGION_FLAG_PERMISSIONS_MASK = 0x0f,
+ MEMORY_REGION_FLAG_SHARED = 0x10,
+ MEMORY_REGION_FLAG_PRIVATE = 0x20,
+ MEMORY_REGION_FLAG_MEMORY_BACKED = 0x40
+};
+
struct MemoryRegion
{
private:
- uint32_t m_permissions;
+ uint32_t m_flags;
uint64_t m_startAddress;
uint64_t m_endAddress;
uint64_t m_offset;
// The name used for NT_FILE output
- char* m_fileName;
+ const char* m_fileName;
public:
- MemoryRegion(uint64_t start, uint64_t end) :
- m_permissions(PF_R | PF_W | PF_X),
+ MemoryRegion(uint32_t flags, uint64_t start, uint64_t end) :
+ m_flags(flags),
m_startAddress(start),
m_endAddress(end),
m_offset(0),
@@ -25,8 +36,8 @@ public:
assert((end & ~PAGE_MASK) == 0);
}
- MemoryRegion(uint32_t permissions, uint64_t start, uint64_t end, uint64_t offset, char* filename) :
- m_permissions(permissions),
+ MemoryRegion(uint32_t flags, uint64_t start, uint64_t end, uint64_t offset, const char* filename) :
+ m_flags(flags),
m_startAddress(start),
m_endAddress(end),
m_offset(offset),
@@ -36,7 +47,39 @@ public:
assert((end & ~PAGE_MASK) == 0);
}
- const uint32_t Permissions() const { return m_permissions; }
+ // copy with new file name constructor
+ MemoryRegion(const MemoryRegion& region, const char* fileName) :
+ m_flags(region.m_flags),
+ m_startAddress(region.m_startAddress),
+ m_endAddress(region.m_endAddress),
+ m_offset(region.m_offset),
+ m_fileName(fileName)
+ {
+ }
+
+ // copy with new flags constructor. The file name is not copied.
+ MemoryRegion(const MemoryRegion& region, uint32_t flags) :
+ m_flags(flags),
+ m_startAddress(region.m_startAddress),
+ m_endAddress(region.m_endAddress),
+ m_offset(region.m_offset),
+ m_fileName(nullptr)
+ {
+ }
+
+ // copy constructor
+ MemoryRegion(const MemoryRegion& region) :
+ m_flags(region.m_flags),
+ m_startAddress(region.m_startAddress),
+ m_endAddress(region.m_endAddress),
+ m_offset(region.m_offset),
+ m_fileName(region.m_fileName)
+ {
+ }
+
+ const uint32_t Permissions() const { return m_flags & MEMORY_REGION_FLAG_PERMISSIONS_MASK; }
+ const uint32_t Flags() const { return m_flags; }
+ const bool IsBackedByMemory() const { return (m_flags & MEMORY_REGION_FLAG_MEMORY_BACKED) != 0; }
const uint64_t StartAddress() const { return m_startAddress; }
const uint64_t EndAddress() const { return m_endAddress; }
const uint64_t Size() const { return m_endAddress - m_startAddress; }
@@ -48,27 +91,25 @@ public:
return (m_startAddress < rhs.m_startAddress) && (m_endAddress <= rhs.m_startAddress);
}
+ // Returns true if "rhs" is wholly contained in this one
bool Contains(const MemoryRegion& rhs) const
{
return (m_startAddress <= rhs.m_startAddress) && (m_endAddress >= rhs.m_endAddress);
}
+ // Free the file name memory
void Cleanup()
{
if (m_fileName != nullptr)
{
- free(m_fileName);
+ free((void*)m_fileName);
m_fileName = nullptr;
}
}
- void Print() const
+ void Trace() const
{
- if (m_fileName != nullptr) {
- TRACE("%016lx - %016lx (%06ld) %016lx %x %s\n", m_startAddress, m_endAddress, (Size() >> PAGE_SHIFT), m_offset, m_permissions, m_fileName);
- }
- else {
- TRACE("%016lx - %016lx (%06ld) %x\n", m_startAddress, m_endAddress, (Size() >> PAGE_SHIFT), m_permissions);
- }
+ TRACE("%s%016lx - %016lx (%06ld) %016lx %02x %s\n", IsBackedByMemory() ? "*" : " ", m_startAddress, m_endAddress,
+ (Size() >> PAGE_SHIFT), m_offset, m_flags, m_fileName != nullptr ? m_fileName : "");
}
};
diff --git a/src/debug/createdump/threadinfo.cpp b/src/debug/createdump/threadinfo.cpp
index e2c10fc..35a4f0d 100644
--- a/src/debug/createdump/threadinfo.cpp
+++ b/src/debug/createdump/threadinfo.cpp
@@ -145,21 +145,19 @@ ThreadInfo::GetThreadStack(const CrashInfo& crashInfo, uint64_t* startAddress, s
*startAddress = m_gpRegisters.rsp & PAGE_MASK;
*size = 4 * PAGE_SIZE;
- for (const MemoryRegion& mapping : crashInfo.OtherMappings())
- {
- if (*startAddress >= mapping.StartAddress() && *startAddress < mapping.EndAddress())
+ const MemoryRegion* region = CrashInfo::SearchMemoryRegions(crashInfo.OtherMappings(), *startAddress);
+ if (region != nullptr) {
+
+ // Use the mapping found for the size of the thread's stack
+ *size = region->EndAddress() - *startAddress;
+
+ if (g_diagnostics)
{
- // Use the mapping found for the size of the thread's stack
- *size = mapping.EndAddress() - *startAddress;
-
- if (g_diagnostics)
- {
- TRACE("Thread %04x stack found in other mapping (size %08lx): ", m_tid, *size);
- mapping.Print();
- }
- break;
+ TRACE("Thread %04x stack found in other mapping (size %08lx): ", m_tid, *size);
+ region->Trace();
}
}
+
}
void