summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike McLaughlin <mikem@microsoft.com>2017-06-23 15:02:36 -0700
committerGitHub <noreply@github.com>2017-06-23 15:02:36 -0700
commit619334db7e5595888e6409570750ccffe4c4c126 (patch)
tree777e8b932afa4abfaf557461daa41cb85d40ae8a
parent3a24095610ecaba62495740bf8319ad467af4997 (diff)
downloadcoreclr-619334db7e5595888e6409570750ccffe4c4c126.tar.gz
coreclr-619334db7e5595888e6409570750ccffe4c4c126.tar.bz2
coreclr-619334db7e5595888e6409570750ccffe4c4c126.zip
Add ELF program and section headers to core dump. (#12425)
Add ELF program and section headers to core dump. Issue #12341 This allows the build id for the modules be extracted properly. Add .eh_frame, .eh_frame_hdr and some other important sections to core dump. Add managed module names to core dump so the symbol acquisition tool can get symbol store keys for them. Add the non memory/file backed memory regions to core dump so debuggers like lldb can check if an address is "code" or not. Unwinding doesn't work when there isn't a module/.so file present.
-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
-rw-r--r--src/pal/src/thread/process.cpp4
8 files changed, 548 insertions, 162 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)
{
diff --git a/src/debug/createdump/crashinfo.h b/src/debug/createdump/crashinfo.h
index 914a88e8dd..43e82691aa 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 38c3525f34..6f72f0e955 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 69f0ece8af..06c5b96d01 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 0b4f88cbd6..7da0d63ccf 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 1332ab1416..1f6c5f53e6 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 e2c10fc4b7..35a4f0d4cd 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
diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp
index 850f2519fe..3294870fca 100644
--- a/src/pal/src/thread/process.cpp
+++ b/src/pal/src/thread/process.cpp
@@ -2951,6 +2951,10 @@ PROCAbortInitialize()
{
*argv++ = "--triage";
}
+ else if (strcmp(envvar, "4") == 0)
+ {
+ *argv++ = "--full";
+ }
}
envvar = getenv("COMPlus_CreateDumpDiagnostics");