diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt index 5e6eda6ee95f6..55b7b51aaaafd 100644 --- a/src/coreclr/debug/createdump/CMakeLists.txt +++ b/src/coreclr/debug/createdump/CMakeLists.txt @@ -63,12 +63,14 @@ if(CLR_CMAKE_HOST_OSX) add_executable_clr(createdump crashinfomac.cpp threadinfomac.cpp + dumpwritermacho.cpp ${CREATEDUMP_SOURCES} ) else() add_executable_clr(createdump crashinfounix.cpp threadinfounix.cpp + dumpwriterelf.cpp ${CREATEDUMP_SOURCES} ${PAL_REDEFINES_FILE} ) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 4592b0d253a55..49f7cd2c17231 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -122,10 +122,13 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) return false; } #endif - TRACE("Module addresses:\n"); - for (const MemoryRegion& region : m_moduleAddresses) + if (g_diagnosticsVerbose) { - region.Trace(); + TRACE_VERBOSE("Module addresses:\n"); + for (const MemoryRegion& region : m_moduleAddresses) + { + region.Trace(); + } } // If full memory dump, include everything regardless of permissions if (minidumpType & MiniDumpWithFullMemory) @@ -589,7 +592,7 @@ CrashInfo::CombineMemoryRegions() TRACE("CombineMemoryRegions: FINISHED\n"); - if (g_diagnostics) + if (g_diagnosticsVerbose) { TRACE("Memory Regions:\n"); for (const MemoryRegion& region : m_memoryRegions) @@ -619,7 +622,21 @@ CrashInfo::SearchMemoryRegions(const std::set& regions, const Memo void CrashInfo::Trace(const char* format, ...) { - if (g_diagnostics) { + if (g_diagnostics) + { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} + +void +CrashInfo::TraceVerbose(const char* format, ...) +{ + if (g_diagnosticsVerbose) + { va_list args; va_start(args, format); vfprintf(stdout, format, args); diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 46b0825c0a7e3..a4ca0e55ddbc6 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -5,7 +5,6 @@ #include "../dbgutil/machoreader.h" #else #include "../dbgutil/elfreader.h" -#endif // typedef for our parsing of the auxv variables in /proc/pid/auxv. #if TARGET_64BIT @@ -29,6 +28,8 @@ typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t; // All interesting auvx entry types are AT_SYSINFO_EHDR and below #define AT_MAX (AT_SYSINFO_EHDR + 1) +#endif + class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, #ifdef __APPLE__ public MachOReader @@ -53,8 +54,8 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, std::set m_allMemoryRegions; // all memory regions on MacOS #else std::array m_auxvValues; // auxv values -#endif std::vector m_auxvEntries; // full auxv entries +#endif std::vector m_threads; // threads found and suspended std::set m_moduleMappings; // module memory mappings std::set m_otherMappings; // other memory mappings @@ -87,8 +88,10 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, inline const std::set ModuleMappings() const { return m_moduleMappings; } inline const std::set OtherMappings() const { return m_otherMappings; } inline const std::set MemoryRegions() const { return m_memoryRegions; } +#ifndef __APPLE__ inline const std::vector AuxvEntries() const { return m_auxvEntries; } inline size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); } +#endif // IUnknown STDMETHOD(QueryInterface)(___in REFIID InterfaceId, ___out PVOID* Interface); @@ -122,4 +125,5 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, bool ValidRegion(const MemoryRegion& region); void CombineMemoryRegions(); void Trace(const char* format, ...); + void TraceVerbose(const char* format, ...); }; diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp index 6ca449cfdccfe..98de700528113 100644 --- a/src/coreclr/debug/createdump/crashinfomac.cpp +++ b/src/coreclr/debug/createdump/crashinfomac.cpp @@ -15,8 +15,6 @@ CrashInfo::Initialize() fprintf(stderr, "task_for_pid(%d) FAILED %x %s\n", m_pid, result, mach_error_string(result)); return false; } - m_auxvEntries.push_back(elf_aux_entry { AT_BASE, { 0 } }); - m_auxvEntries.push_back(elf_aux_entry { AT_NULL, { 0 } }); return true; } @@ -110,7 +108,7 @@ CrashInfo::EnumerateMemoryRegions() TRACE("mach_vm_region_recurse for address %016llx %08llx FAILED %x %s\n", address, size, result, mach_error_string(result)); break; } - TRACE("%016llx - %016llx (%06llx) %08llx %s %d %d %d %c%c%c %02x\n", + TRACE_VERBOSE("%016llx - %016llx (%06llx) %08llx %s %d %d %d %c%c%c %02x\n", address, address + size, size / PAGE_SIZE, @@ -291,9 +289,11 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm const auto& found = m_moduleMappings.find(moduleRegion); if (found == m_moduleMappings.end()) { - TRACE("VisitSegment: "); - moduleRegion.Trace(); - + if (g_diagnosticsVerbose) + { + TRACE_VERBOSE("VisitSegment: "); + moduleRegion.Trace(); + } // Add this module segment to the module mappings list m_moduleMappings.insert(moduleRegion); @@ -380,9 +380,3 @@ CrashInfo::ReadProcessMemory(void* address, void* buffer, size_t size, size_t* r *read = numberOfBytesRead; return size == 0 || numberOfBytesRead > 0; } - -// For src/inc/llvm/ELF.h -Elf64_Ehdr::Elf64_Ehdr() -{ -} - diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index 41cb93414374d..b63b147ea43c3 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -11,11 +11,13 @@ #endif extern void trace_printf(const char* format, ...); +extern void trace_verbose_printf(const char* format, ...); extern bool g_diagnostics; +extern bool g_diagnosticsVerbose; #ifdef HOST_UNIX #define TRACE(args...) trace_printf(args) -#define TRACE_VERBOSE(args...) +#define TRACE_VERBOSE(args...) trace_verbose_printf(args) #else #define TRACE(args, ...) #define TRACE_VERBOSE(args, ...) @@ -82,7 +84,8 @@ typedef int T_CONTEXT; #include #ifdef HOST_UNIX #ifdef __APPLE__ -#include "mac.h" +#include +#include #endif #include "datatarget.h" #include "threadinfo.h" diff --git a/src/coreclr/debug/createdump/dumpwriter.cpp b/src/coreclr/debug/createdump/dumpwriter.cpp index 3c61aeb9e56cd..d7816cd448415 100644 --- a/src/coreclr/debug/createdump/dumpwriter.cpp +++ b/src/coreclr/debug/createdump/dumpwriter.cpp @@ -32,427 +32,6 @@ DumpWriter::OpenDump(const char* dumpFileName) return true; } -// Write the core dump file: -// ELF header -// Single section header (Shdr) for 64 bit program header count -// Phdr for the PT_NOTE -// PT_LOAD -// PT_NOTEs -// process info (prpsinfo_t) -// NT_FILE entries -// threads -// alignment -// memory blocks -bool -DumpWriter::WriteDump() -{ - // Write the ELF header - Ehdr ehdr; - memset(&ehdr, 0, sizeof(Ehdr)); - ehdr.e_ident[0] = ELFMAG0; - ehdr.e_ident[1] = ELFMAG1; - ehdr.e_ident[2] = ELFMAG2; - ehdr.e_ident[3] = ELFMAG3; - 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; - ehdr.e_machine = ELF_ARCH; - ehdr.e_version = EV_CURRENT; - ehdr.e_shoff = sizeof(Ehdr); - ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr); - - ehdr.e_ehsize = sizeof(Ehdr); - ehdr.e_phentsize = sizeof(Phdr); - ehdr.e_shentsize = sizeof(Shdr); - - // The ELF header only allows UINT16 for the number of program - // headers. In a core dump this equates to PT_NODE and PT_LOAD. - // - // When more program headers than 65534 the first section entry - // is used to store the actual program header count. - - // PT_NOTE + number of memory regions - uint64_t phnum = 1; - for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions()) - { - if (memoryRegion.IsBackedByMemory()) - { - phnum++; - } - } - - if (phnum < PH_HDR_CANARY) { - ehdr.e_phnum = phnum; - } - else { - ehdr.e_phnum = PH_HDR_CANARY; - } - - if (!WriteData(&ehdr, sizeof(Ehdr))) { - return false; - } - - size_t offset = sizeof(Ehdr) + sizeof(Shdr) + (phnum * sizeof(Phdr)); - size_t filesz = GetProcessInfoSize() + GetAuxvInfoSize() + GetThreadInfoSize() + GetNTFileInfoSize(); - - // Add single section containing the actual count - // of the program headers to be written. - Shdr shdr; - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_info = phnum; - // When section header offset is present but ehdr section num = 0 - // then is is expected that the sh_size indicates the size of the - // section array or 1 in our case. - shdr.sh_size = 1; - if (!WriteData(&shdr, sizeof(shdr))) { - return false; - } - - // PT_NOTE header - Phdr phdr; - memset(&phdr, 0, sizeof(Phdr)); - phdr.p_type = PT_NOTE; - phdr.p_offset = offset; - phdr.p_filesz = filesz; - - if (!WriteData(&phdr, sizeof(phdr))) { - return false; - } - - // PT_NOTE sections must end on 4 byte boundary - // We output the NT_FILE, AUX and Thread entries - // AUX is aligned, NT_FILE is aligned and then we - // check to pad end of the thread list - phdr.p_type = PT_LOAD; - phdr.p_align = 4096; - - size_t finalNoteAlignment = phdr.p_align - ((offset + filesz) % phdr.p_align); - if (finalNoteAlignment == phdr.p_align) { - finalNoteAlignment = 0; - } - offset += finalNoteAlignment; - - TRACE("Writing memory region headers to core file\n"); - - // Write memory region note headers - for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions()) - { - if (memoryRegion.IsBackedByMemory()) - { - phdr.p_flags = memoryRegion.Permissions(); - phdr.p_vaddr = memoryRegion.StartAddress(); - phdr.p_memsz = memoryRegion.Size(); - - offset += filesz; - phdr.p_filesz = filesz = memoryRegion.Size(); - phdr.p_offset = offset; - - if (!WriteData(&phdr, sizeof(phdr))) { - return false; - } - } - } - - // Write process info data to core file - if (!WriteProcessInfo()) { - return false; - } - - // Write auxv data to core file - if (!WriteAuxv()) { - return false; - } - - // Write NT_FILE entries to the core file - if (!WriteNTFileInfo()) { - return false; - } - - TRACE("Writing %zd thread entries to core file\n", m_crashInfo.Threads().size()); - - // Write all the thread's state and registers - for (const ThreadInfo* thread : m_crashInfo.Threads()) - { - if (!WriteThread(*thread, SIGABRT)) { - return false; - } - } - - // Zero out the end of the PT_NOTE section to the boundary - // and then laydown the memory blocks - if (finalNoteAlignment > 0) { - if (finalNoteAlignment > sizeof(m_tempBuffer)) { - fprintf(stderr, "finalNoteAlignment %zu > sizeof(m_tempBuffer)\n", finalNoteAlignment); - return false; - } - memset(m_tempBuffer, 0, finalNoteAlignment); - if (!WriteData(m_tempBuffer, finalNoteAlignment)) { - return false; - } - } - - TRACE("Writing %" PRIu64 " memory regions to core file\n", phnum - 1); - - // Read from target process and write memory regions to core - uint64_t total = 0; - for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions()) - { - // Only write the regions that are backed by memory - if (memoryRegion.IsBackedByMemory()) - { - uint64_t address = memoryRegion.StartAddress(); - size_t size = memoryRegion.Size(); - total += size; - - while (size > 0) - { - size_t bytesToRead = std::min(size, sizeof(m_tempBuffer)); - size_t read = 0; - - if (!m_crashInfo.ReadProcessMemory((void*)address, m_tempBuffer, bytesToRead, &read)) { - fprintf(stderr, "ReadProcessMemory(%" PRIA PRIx64 ", %08zx) FAILED\n", address, bytesToRead); - return false; - } - - // This can happen if the target process dies before createdump is finished - if (read == 0) { - fprintf(stderr, "ReadProcessMemory(%" PRIA PRIx64 ", %08zx) returned 0 bytes read\n", address, bytesToRead); - return false; - } - - if (!WriteData(m_tempBuffer, read)) { - return false; - } - - address += read; - size -= read; - } - } - } - - printf("Written %" PRId64 " bytes (%" PRId64 " pages) to core file\n", total, total / PAGE_SIZE); - - return true; -} - -bool -DumpWriter::WriteProcessInfo() -{ - prpsinfo_t processInfo; - memset(&processInfo, 0, sizeof(processInfo)); - processInfo.pr_sname = 'R'; - processInfo.pr_pid = m_crashInfo.Pid(); - processInfo.pr_ppid = m_crashInfo.Ppid(); - processInfo.pr_pgrp = m_crashInfo.Tgid(); - m_crashInfo.Name().copy(processInfo.pr_fname, sizeof(processInfo.pr_fname)); - - Nhdr nhdr; - memset(&nhdr, 0, sizeof(nhdr)); - nhdr.n_namesz = 5; - nhdr.n_descsz = sizeof(prpsinfo_t); - nhdr.n_type = NT_PRPSINFO; - - TRACE("Writing process information to core file\n"); - - // Write process info data to core file - if (!WriteData(&nhdr, sizeof(nhdr)) || - !WriteData("CORE\0PRP", 8) || - !WriteData(&processInfo, sizeof(prpsinfo_t))) { - return false; - } - return true; -} - -bool -DumpWriter::WriteAuxv() -{ - Nhdr nhdr; - memset(&nhdr, 0, sizeof(nhdr)); - nhdr.n_namesz = 5; - nhdr.n_descsz = m_crashInfo.GetAuxvSize(); - nhdr.n_type = NT_AUXV; - - TRACE("Writing %zd auxv entries to core file\n", m_crashInfo.AuxvEntries().size()); - - if (!WriteData(&nhdr, sizeof(nhdr)) || - !WriteData("CORE\0AUX", 8)) { - return false; - } - for (const auto& auxvEntry : m_crashInfo.AuxvEntries()) - { - if (!WriteData(&auxvEntry, sizeof(auxvEntry))) { - return false; - } - } - return true; -} - -struct NTFileEntry -{ - unsigned long StartAddress; - unsigned long EndAddress; - unsigned long Offset; -}; - -// Calculate the NT_FILE entries total size -size_t -DumpWriter::GetNTFileInfoSize(size_t* alignmentBytes) -{ - size_t count = m_crashInfo.ModuleMappings().size(); - size_t size = 0; - - // Header, CORE, entry count, page size - size = sizeof(Nhdr) + 8 + sizeof(count) + sizeof(size); - - // start_address, end_address, offset - size += count * sizeof(NTFileEntry); - - // \0 terminator for each filename - size += count; - - // File name storage needed - for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) { - size += image.FileName().length(); - } - // Notes must end on 4 byte alignment - size_t alignmentBytesNeeded = 4 - (size % 4); - size += alignmentBytesNeeded; - - if (alignmentBytes != nullptr) { - *alignmentBytes = alignmentBytesNeeded; - } - return size; -} - -// Write NT_FILE entries to the PT_NODE section -// -// Nhdr (NT_FILE) -// Total entries -// Page size -// [0] start_address end_address offset -// [1] start_address end_address offset -// [file name]\0[file name]\0... -bool -DumpWriter::WriteNTFileInfo() -{ - Nhdr nhdr; - memset(&nhdr, 0, sizeof(nhdr)); - - // CORE + \0 and we align on 4 byte boundary - // so we can use CORE\0FIL for easier hex debugging - nhdr.n_namesz = 5; - nhdr.n_type = NT_FILE; // "FILE" - - // Size of payload for NT_FILE after CORE tag written - size_t alignmentBytesNeeded = 0; - nhdr.n_descsz = GetNTFileInfoSize(&alignmentBytesNeeded) - sizeof(nhdr) - 8; - - size_t count = m_crashInfo.ModuleMappings().size(); - size_t pageSize = PAGE_SIZE; - - TRACE("Writing %zd NT_FILE entries to core file\n", m_crashInfo.ModuleMappings().size()); - - if (!WriteData(&nhdr, sizeof(nhdr)) || - !WriteData("CORE\0FIL", 8) || - !WriteData(&count, sizeof(count)) || - !WriteData(&pageSize, sizeof(pageSize))) { - return false; - } - - for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) - { - struct NTFileEntry entry { (unsigned long)image.StartAddress(), (unsigned long)image.EndAddress(), (unsigned long)(image.Offset() / pageSize) }; - if (!WriteData(&entry, sizeof(entry))) { - return false; - } - } - - for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) - { - if (!WriteData(image.FileName().c_str(), image.FileName().length()) || - !WriteData("\0", 1)) { - return false; - } - } - - // Has to end on a 4 byte boundary. Debugger, readelf and such - // will automatically align on next 4 bytes and look for a PT_NOTE - // header. - if (alignmentBytesNeeded) { - if (!WriteData("\0\0\0\0", alignmentBytesNeeded)) { - return false; - } - } - - return true; -} - -bool -DumpWriter::WriteThread(const ThreadInfo& thread, int fatal_signal) -{ - prstatus_t pr; - memset(&pr, 0, sizeof(pr)); - - pr.pr_info.si_signo = fatal_signal; - pr.pr_cursig = fatal_signal; - pr.pr_pid = thread.Tid(); - pr.pr_ppid = thread.Ppid(); - pr.pr_pgrp = thread.Tgid(); - memcpy(&pr.pr_reg, thread.GPRegisters(), sizeof(user_regs_struct)); - - Nhdr nhdr; - memset(&nhdr, 0, sizeof(nhdr)); - - // Name size is CORE plus the NULL terminator - // The format requires 4 byte alignment so the - // value written in 8 bytes. Stuff the last 3 - // bytes with the type of NT_PRSTATUS so it is - // easier to debug in a hex editor. - nhdr.n_namesz = 5; - nhdr.n_descsz = sizeof(prstatus_t); - nhdr.n_type = NT_PRSTATUS; - if (!WriteData(&nhdr, sizeof(nhdr)) || - !WriteData("CORE\0THR", 8) || - !WriteData(&pr, sizeof(prstatus_t))) { - return false; - } - - nhdr.n_descsz = sizeof(user_fpregs_struct); - nhdr.n_type = NT_FPREGSET; - if (!WriteData(&nhdr, sizeof(nhdr)) || - !WriteData("CORE\0FLT", 8) || - !WriteData(thread.FPRegisters(), sizeof(user_fpregs_struct))) { - return false; - } - - nhdr.n_namesz = 6; - -#if defined(__i386__) - nhdr.n_descsz = sizeof(user_fpxregs_struct); - nhdr.n_type = NT_PRXFPREG; - if (!WriteData(&nhdr, sizeof(nhdr)) || - !WriteData("LINUX\0\0\0", 8) || - !WriteData(thread.FPXRegisters(), sizeof(user_fpxregs_struct))) { - return false; - } -#endif - -#if defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) - nhdr.n_descsz = sizeof(user_vfpregs_struct); - nhdr.n_type = NT_ARM_VFP; - if (!WriteData(&nhdr, sizeof(nhdr)) || - !WriteData("LINUX\0\0\0", 8) || - !WriteData(thread.VFPRegisters(), sizeof(user_vfpregs_struct))) { - return false; - } -#endif - - return true; -} - // Write all of the given buffer, handling short writes and EINTR. Return true iff successful. bool DumpWriter::WriteData(const void* buffer, size_t length) diff --git a/src/coreclr/debug/createdump/dumpwriter.h b/src/coreclr/debug/createdump/dumpwriter.h index 4bd885cefa83b..96e37b9bdf11c 100644 --- a/src/coreclr/debug/createdump/dumpwriter.h +++ b/src/coreclr/debug/createdump/dumpwriter.h @@ -1,67 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#ifdef HOST_64BIT -#define ELF_CLASS ELFCLASS64 +#ifdef __APPLE__ +#include "dumpwritermacho.h" #else -#define ELF_CLASS ELFCLASS32 +#include "dumpwriterelf.h" #endif -#define Ehdr ElfW(Ehdr) -#define Phdr ElfW(Phdr) -#define Shdr ElfW(Shdr) -#define Nhdr ElfW(Nhdr) -#define auxv_t ElfW(auxv_t) - -#if defined(__x86_64__) -#define ELF_ARCH EM_X86_64 -#elif defined(__i386__) -#define ELF_ARCH EM_386 -#elif defined(__aarch64__) -#define ELF_ARCH EM_AARCH64 -#elif defined(__arm__) -#define ELF_ARCH EM_ARM -#endif - -#define PH_HDR_CANARY 0xFFFF - -#ifndef NT_FILE -#define NT_FILE 0x46494c45 -#endif - -class DumpWriter -{ -private: - int m_fd; - CrashInfo& m_crashInfo; - BYTE m_tempBuffer[0x4000]; - -public: - DumpWriter(CrashInfo& crashInfo); - virtual ~DumpWriter(); - bool OpenDump(const char* dumpFileName); - bool WriteDump(); - -private: - bool WriteProcessInfo(); - bool WriteAuxv(); - size_t GetNTFileInfoSize(size_t* alignmentBytes = nullptr); - bool WriteNTFileInfo(); - bool WriteThread(const ThreadInfo& thread, int fatal_signal); - bool WriteData(const void* buffer, size_t length); - - size_t GetProcessInfoSize() const { return sizeof(Nhdr) + 8 + sizeof(prpsinfo_t); } - size_t GetAuxvInfoSize() const { return sizeof(Nhdr) + 8 + m_crashInfo.GetAuxvSize(); } - size_t GetThreadInfoSize() const - { - return m_crashInfo.Threads().size() * ((sizeof(Nhdr) + 8 + sizeof(prstatus_t)) - + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) -#if defined(__i386__) - + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct) -#endif -#if defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) - + sizeof(Nhdr) + 8 + sizeof(user_vfpregs_struct) -#endif - ); - } -}; diff --git a/src/coreclr/debug/createdump/dumpwriterelf.cpp b/src/coreclr/debug/createdump/dumpwriterelf.cpp new file mode 100644 index 0000000000000..57249d83f7e6c --- /dev/null +++ b/src/coreclr/debug/createdump/dumpwriterelf.cpp @@ -0,0 +1,425 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "createdump.h" + +// Write the core dump file: +// ELF header +// Single section header (Shdr) for 64 bit program header count +// Phdr for the PT_NOTE +// PT_LOAD +// PT_NOTEs +// process info (prpsinfo_t) +// NT_FILE entries +// threads +// alignment +// memory blocks +bool +DumpWriter::WriteDump() +{ + // Write the ELF header + Ehdr ehdr; + memset(&ehdr, 0, sizeof(Ehdr)); + ehdr.e_ident[0] = ELFMAG0; + ehdr.e_ident[1] = ELFMAG1; + ehdr.e_ident[2] = ELFMAG2; + ehdr.e_ident[3] = ELFMAG3; + 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; + ehdr.e_machine = ELF_ARCH; + ehdr.e_version = EV_CURRENT; + ehdr.e_shoff = sizeof(Ehdr); + ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr); + + ehdr.e_ehsize = sizeof(Ehdr); + ehdr.e_phentsize = sizeof(Phdr); + ehdr.e_shentsize = sizeof(Shdr); + + // The ELF header only allows UINT16 for the number of program + // headers. In a core dump this equates to PT_NODE and PT_LOAD. + // + // When more program headers than 65534 the first section entry + // is used to store the actual program header count. + + // PT_NOTE + number of memory regions + uint64_t phnum = 1; + for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions()) + { + if (memoryRegion.IsBackedByMemory()) + { + phnum++; + } + } + + if (phnum < PH_HDR_CANARY) { + ehdr.e_phnum = phnum; + } + else { + ehdr.e_phnum = PH_HDR_CANARY; + } + + if (!WriteData(&ehdr, sizeof(Ehdr))) { + return false; + } + + size_t offset = sizeof(Ehdr) + sizeof(Shdr) + (phnum * sizeof(Phdr)); + size_t filesz = GetProcessInfoSize() + GetAuxvInfoSize() + GetThreadInfoSize() + GetNTFileInfoSize(); + + // Add single section containing the actual count + // of the program headers to be written. + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_info = phnum; + // When section header offset is present but ehdr section num = 0 + // then is is expected that the sh_size indicates the size of the + // section array or 1 in our case. + shdr.sh_size = 1; + if (!WriteData(&shdr, sizeof(shdr))) { + return false; + } + + // PT_NOTE header + Phdr phdr; + memset(&phdr, 0, sizeof(Phdr)); + phdr.p_type = PT_NOTE; + phdr.p_offset = offset; + phdr.p_filesz = filesz; + + if (!WriteData(&phdr, sizeof(phdr))) { + return false; + } + + // PT_NOTE sections must end on 4 byte boundary + // We output the NT_FILE, AUX and Thread entries + // AUX is aligned, NT_FILE is aligned and then we + // check to pad end of the thread list + phdr.p_type = PT_LOAD; + phdr.p_align = 4096; + + size_t finalNoteAlignment = phdr.p_align - ((offset + filesz) % phdr.p_align); + if (finalNoteAlignment == phdr.p_align) { + finalNoteAlignment = 0; + } + offset += finalNoteAlignment; + + TRACE("Writing memory region headers to core file\n"); + + // Write memory region note headers + for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions()) + { + if (memoryRegion.IsBackedByMemory()) + { + phdr.p_flags = memoryRegion.Permissions(); + phdr.p_vaddr = memoryRegion.StartAddress(); + phdr.p_memsz = memoryRegion.Size(); + + offset += filesz; + phdr.p_filesz = filesz = memoryRegion.Size(); + phdr.p_offset = offset; + + if (!WriteData(&phdr, sizeof(phdr))) { + return false; + } + } + } + + // Write process info data to core file + if (!WriteProcessInfo()) { + return false; + } + + // Write auxv data to core file + if (!WriteAuxv()) { + return false; + } + + // Write NT_FILE entries to the core file + if (!WriteNTFileInfo()) { + return false; + } + + TRACE("Writing %zd thread entries to core file\n", m_crashInfo.Threads().size()); + + // Write all the thread's state and registers + for (const ThreadInfo* thread : m_crashInfo.Threads()) + { + if (!WriteThread(*thread, SIGABRT)) { + return false; + } + } + + // Zero out the end of the PT_NOTE section to the boundary + // and then laydown the memory blocks + if (finalNoteAlignment > 0) { + if (finalNoteAlignment > sizeof(m_tempBuffer)) { + fprintf(stderr, "finalNoteAlignment %zu > sizeof(m_tempBuffer)\n", finalNoteAlignment); + return false; + } + memset(m_tempBuffer, 0, finalNoteAlignment); + if (!WriteData(m_tempBuffer, finalNoteAlignment)) { + return false; + } + } + + TRACE("Writing %" PRIu64 " memory regions to core file\n", phnum - 1); + + // Read from target process and write memory regions to core + uint64_t total = 0; + for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions()) + { + // Only write the regions that are backed by memory + if (memoryRegion.IsBackedByMemory()) + { + uint64_t address = memoryRegion.StartAddress(); + size_t size = memoryRegion.Size(); + total += size; + + while (size > 0) + { + size_t bytesToRead = std::min(size, sizeof(m_tempBuffer)); + size_t read = 0; + + if (!m_crashInfo.ReadProcessMemory((void*)address, m_tempBuffer, bytesToRead, &read)) { + fprintf(stderr, "ReadProcessMemory(%" PRIA PRIx64 ", %08zx) FAILED\n", address, bytesToRead); + return false; + } + + // This can happen if the target process dies before createdump is finished + if (read == 0) { + fprintf(stderr, "ReadProcessMemory(%" PRIA PRIx64 ", %08zx) returned 0 bytes read\n", address, bytesToRead); + return false; + } + + if (!WriteData(m_tempBuffer, read)) { + return false; + } + + address += read; + size -= read; + } + } + } + + printf("Written %" PRId64 " bytes (%" PRId64 " pages) to core file\n", total, total / PAGE_SIZE); + + return true; +} + +bool +DumpWriter::WriteProcessInfo() +{ + prpsinfo_t processInfo; + memset(&processInfo, 0, sizeof(processInfo)); + processInfo.pr_sname = 'R'; + processInfo.pr_pid = m_crashInfo.Pid(); + processInfo.pr_ppid = m_crashInfo.Ppid(); + processInfo.pr_pgrp = m_crashInfo.Tgid(); + m_crashInfo.Name().copy(processInfo.pr_fname, sizeof(processInfo.pr_fname)); + + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + nhdr.n_namesz = 5; + nhdr.n_descsz = sizeof(prpsinfo_t); + nhdr.n_type = NT_PRPSINFO; + + TRACE("Writing process information to core file\n"); + + // Write process info data to core file + if (!WriteData(&nhdr, sizeof(nhdr)) || + !WriteData("CORE\0PRP", 8) || + !WriteData(&processInfo, sizeof(prpsinfo_t))) { + return false; + } + return true; +} + +bool +DumpWriter::WriteAuxv() +{ + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + nhdr.n_namesz = 5; + nhdr.n_descsz = m_crashInfo.GetAuxvSize(); + nhdr.n_type = NT_AUXV; + + TRACE("Writing %zd auxv entries to core file\n", m_crashInfo.AuxvEntries().size()); + + if (!WriteData(&nhdr, sizeof(nhdr)) || + !WriteData("CORE\0AUX", 8)) { + return false; + } + for (const auto& auxvEntry : m_crashInfo.AuxvEntries()) + { + if (!WriteData(&auxvEntry, sizeof(auxvEntry))) { + return false; + } + } + return true; +} + +struct NTFileEntry +{ + unsigned long StartAddress; + unsigned long EndAddress; + unsigned long Offset; +}; + +// Calculate the NT_FILE entries total size +size_t +DumpWriter::GetNTFileInfoSize(size_t* alignmentBytes) +{ + size_t count = m_crashInfo.ModuleMappings().size(); + size_t size = 0; + + // Header, CORE, entry count, page size + size = sizeof(Nhdr) + 8 + sizeof(count) + sizeof(size); + + // start_address, end_address, offset + size += count * sizeof(NTFileEntry); + + // \0 terminator for each filename + size += count; + + // File name storage needed + for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) { + size += image.FileName().length(); + } + // Notes must end on 4 byte alignment + size_t alignmentBytesNeeded = 4 - (size % 4); + size += alignmentBytesNeeded; + + if (alignmentBytes != nullptr) { + *alignmentBytes = alignmentBytesNeeded; + } + return size; +} + +// Write NT_FILE entries to the PT_NODE section +// +// Nhdr (NT_FILE) +// Total entries +// Page size +// [0] start_address end_address offset +// [1] start_address end_address offset +// [file name]\0[file name]\0... +bool +DumpWriter::WriteNTFileInfo() +{ + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + + // CORE + \0 and we align on 4 byte boundary + // so we can use CORE\0FIL for easier hex debugging + nhdr.n_namesz = 5; + nhdr.n_type = NT_FILE; // "FILE" + + // Size of payload for NT_FILE after CORE tag written + size_t alignmentBytesNeeded = 0; + nhdr.n_descsz = GetNTFileInfoSize(&alignmentBytesNeeded) - sizeof(nhdr) - 8; + + size_t count = m_crashInfo.ModuleMappings().size(); + size_t pageSize = PAGE_SIZE; + + TRACE("Writing %zd NT_FILE entries to core file\n", m_crashInfo.ModuleMappings().size()); + + if (!WriteData(&nhdr, sizeof(nhdr)) || + !WriteData("CORE\0FIL", 8) || + !WriteData(&count, sizeof(count)) || + !WriteData(&pageSize, sizeof(pageSize))) { + return false; + } + + for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) + { + struct NTFileEntry entry { (unsigned long)image.StartAddress(), (unsigned long)image.EndAddress(), (unsigned long)(image.Offset() / pageSize) }; + if (!WriteData(&entry, sizeof(entry))) { + return false; + } + } + + for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) + { + if (!WriteData(image.FileName().c_str(), image.FileName().length()) || + !WriteData("\0", 1)) { + return false; + } + } + + // Has to end on a 4 byte boundary. Debugger, readelf and such + // will automatically align on next 4 bytes and look for a PT_NOTE + // header. + if (alignmentBytesNeeded) { + if (!WriteData("\0\0\0\0", alignmentBytesNeeded)) { + return false; + } + } + + return true; +} + +bool +DumpWriter::WriteThread(const ThreadInfo& thread, int fatal_signal) +{ + prstatus_t pr; + memset(&pr, 0, sizeof(pr)); + + pr.pr_info.si_signo = fatal_signal; + pr.pr_cursig = fatal_signal; + pr.pr_pid = thread.Tid(); + pr.pr_ppid = thread.Ppid(); + pr.pr_pgrp = thread.Tgid(); + memcpy(&pr.pr_reg, thread.GPRegisters(), sizeof(user_regs_struct)); + + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + + // Name size is CORE plus the NULL terminator + // The format requires 4 byte alignment so the + // value written in 8 bytes. Stuff the last 3 + // bytes with the type of NT_PRSTATUS so it is + // easier to debug in a hex editor. + nhdr.n_namesz = 5; + nhdr.n_descsz = sizeof(prstatus_t); + nhdr.n_type = NT_PRSTATUS; + if (!WriteData(&nhdr, sizeof(nhdr)) || + !WriteData("CORE\0THR", 8) || + !WriteData(&pr, sizeof(prstatus_t))) { + return false; + } + + nhdr.n_descsz = sizeof(user_fpregs_struct); + nhdr.n_type = NT_FPREGSET; + if (!WriteData(&nhdr, sizeof(nhdr)) || + !WriteData("CORE\0FLT", 8) || + !WriteData(thread.FPRegisters(), sizeof(user_fpregs_struct))) { + return false; + } + + nhdr.n_namesz = 6; + +#if defined(__i386__) + nhdr.n_descsz = sizeof(user_fpxregs_struct); + nhdr.n_type = NT_PRXFPREG; + if (!WriteData(&nhdr, sizeof(nhdr)) || + !WriteData("LINUX\0\0\0", 8) || + !WriteData(thread.FPXRegisters(), sizeof(user_fpxregs_struct))) { + return false; + } +#endif + +#if defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) + nhdr.n_descsz = sizeof(user_vfpregs_struct); + nhdr.n_type = NT_ARM_VFP; + if (!WriteData(&nhdr, sizeof(nhdr)) || + !WriteData("LINUX\0\0\0", 8) || + !WriteData(thread.VFPRegisters(), sizeof(user_vfpregs_struct))) { + return false; + } +#endif + + return true; +} diff --git a/src/coreclr/debug/createdump/dumpwriterelf.h b/src/coreclr/debug/createdump/dumpwriterelf.h new file mode 100644 index 0000000000000..4bd885cefa83b --- /dev/null +++ b/src/coreclr/debug/createdump/dumpwriterelf.h @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifdef HOST_64BIT +#define ELF_CLASS ELFCLASS64 +#else +#define ELF_CLASS ELFCLASS32 +#endif + +#define Ehdr ElfW(Ehdr) +#define Phdr ElfW(Phdr) +#define Shdr ElfW(Shdr) +#define Nhdr ElfW(Nhdr) +#define auxv_t ElfW(auxv_t) + +#if defined(__x86_64__) +#define ELF_ARCH EM_X86_64 +#elif defined(__i386__) +#define ELF_ARCH EM_386 +#elif defined(__aarch64__) +#define ELF_ARCH EM_AARCH64 +#elif defined(__arm__) +#define ELF_ARCH EM_ARM +#endif + +#define PH_HDR_CANARY 0xFFFF + +#ifndef NT_FILE +#define NT_FILE 0x46494c45 +#endif + +class DumpWriter +{ +private: + int m_fd; + CrashInfo& m_crashInfo; + BYTE m_tempBuffer[0x4000]; + +public: + DumpWriter(CrashInfo& crashInfo); + virtual ~DumpWriter(); + bool OpenDump(const char* dumpFileName); + bool WriteDump(); + +private: + bool WriteProcessInfo(); + bool WriteAuxv(); + size_t GetNTFileInfoSize(size_t* alignmentBytes = nullptr); + bool WriteNTFileInfo(); + bool WriteThread(const ThreadInfo& thread, int fatal_signal); + bool WriteData(const void* buffer, size_t length); + + size_t GetProcessInfoSize() const { return sizeof(Nhdr) + 8 + sizeof(prpsinfo_t); } + size_t GetAuxvInfoSize() const { return sizeof(Nhdr) + 8 + m_crashInfo.GetAuxvSize(); } + size_t GetThreadInfoSize() const + { + return m_crashInfo.Threads().size() * ((sizeof(Nhdr) + 8 + sizeof(prstatus_t)) + + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) +#if defined(__i386__) + + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct) +#endif +#if defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) + + sizeof(Nhdr) + 8 + sizeof(user_vfpregs_struct) +#endif + ); + } +}; diff --git a/src/coreclr/debug/createdump/dumpwritermacho.cpp b/src/coreclr/debug/createdump/dumpwritermacho.cpp new file mode 100644 index 0000000000000..9c271507b1df6 --- /dev/null +++ b/src/coreclr/debug/createdump/dumpwritermacho.cpp @@ -0,0 +1,281 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "createdump.h" +#include "specialthreadinfo.h" + +// Write the core dump file +bool +DumpWriter::WriteDump() +{ + BuildSegmentLoadCommands(); + + BuildThreadLoadCommands(); + + uint64_t fileOffset = 0; + if (!WriteHeader(&fileOffset)) { + return false; + } + + TRACE("Writing %zd thread commands to core file\n", m_threadLoadCommands.size()); + + // Write thread commands + for (const ThreadCommand& thread : m_threadLoadCommands) + { + if (!WriteData(&thread, thread.command.cmdsize)) { + return false; + } + } + + TRACE("Writing %zd segment commands to core file\n", m_segmentLoadCommands.size()); + + // Align first segment's file offset to the next page (0x1000) byte boundary + uint64_t alignment = 0; + if (fileOffset & (PAGE_SIZE - 1)) + { + alignment = fileOffset; + fileOffset = (fileOffset + (PAGE_SIZE - 1)) & PAGE_MASK; + alignment = fileOffset - alignment; + } + + // Write segment commands + for (segment_command_64& segment : m_segmentLoadCommands) + { + segment.fileoff = fileOffset; + fileOffset += segment.vmsize; + assert(segment.vmsize == segment.filesize); + + if (!WriteData(&segment, segment.cmdsize)) { + return false; + } + } + + // Write any segment alignment required to the core file + if (alignment > 0) + { + if (alignment > sizeof(m_tempBuffer)) { + fprintf(stderr, "Segment alignment %llu > sizeof(m_tempBuffer)\n", alignment); + return false; + } + memset(m_tempBuffer, 0, alignment); + if (!WriteData(m_tempBuffer, alignment)) { + return false; + } + } + + // Write memory regions + return WriteSegments(); +} + +vm_prot_t +ConvertFlags(uint32_t flags) +{ + vm_prot_t prot = 0; + if (flags & PF_R) { + prot |= VM_PROT_READ; + } + if (flags & PF_W) { + prot |= VM_PROT_WRITE; + } + if (flags & PF_X) { + prot |= VM_PROT_EXECUTE; + } + return prot; +} + +void +DumpWriter::BuildSegmentLoadCommands() +{ + for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions()) + { + if (memoryRegion.IsBackedByMemory()) + { + uint64_t size = memoryRegion.Size(); + uint32_t prot = ConvertFlags(memoryRegion.Permissions()); + + segment_command_64 segment = { + LC_SEGMENT_64, // uint32_t cmd; + sizeof(segment_command_64), // uint32_t cmdsize; + {0}, // char segname[16]; + memoryRegion.StartAddress(), // uint64_t vmaddr; + size, // uint64_t vmsize; + 0, // uint64_t fileoff; + size, // uint64_t filesize; + prot, // uint32_t maxprot; + prot, // uint32_t initprot; + 0, // uint32_t nsects; + 0 // uint32_t flags; + }; + m_segmentLoadCommands.push_back(segment); + } + } + + // Add special memory region containing the process and thread info + uint64_t size = sizeof(SpecialThreadInfoHeader) + (m_crashInfo.Threads().size() * sizeof(SpecialThreadInfoEntry)); + segment_command_64 segment = { + LC_SEGMENT_64, // uint32_t cmd; + sizeof(segment_command_64), // uint32_t cmdsize; + {0}, // char segname[16]; + SpecialThreadInfoAddress, // uint64_t vmaddr; + size, // uint64_t vmsize; + 0, // uint64_t fileoff; + size, // uint64_t filesize; + VM_PROT_READ, // uint32_t maxprot; + VM_PROT_READ, // uint32_t initprot; + 0, // uint32_t nsects; + 0 // uint32_t flags; + }; + m_segmentLoadCommands.push_back(segment); +} + +void +DumpWriter::BuildThreadLoadCommands() +{ + for (const ThreadInfo* thread : m_crashInfo.Threads()) + { + ThreadCommand threadCommand = { + { LC_THREAD, sizeof(ThreadCommand) }, + }; +#if defined(__x86_64__) + threadCommand.gpflavor = x86_THREAD_STATE64; + threadCommand.gpcount = sizeof(x86_thread_state64_t) / sizeof(uint32_t); + threadCommand.fpflavor = x86_FLOAT_STATE64; + threadCommand.fpcount = sizeof(x86_float_state64_t) / sizeof(uint32_t); + memcpy(&threadCommand.gpregisters, thread->GPRegisters(), sizeof(x86_thread_state64_t)); + memcpy(&threadCommand.fpregisters, thread->FPRegisters(), sizeof(x86_float_state64_t)); +#elif defined(__aarch64__) + threadCommand.gpflavor = ARM_THREAD_STATE64; + threadCommand.gpcount = sizeof(arm_thread_state64_t) / sizeof(uint32_t); + threadCommand.fpflavor = ARM_NEON_STATE64; + threadCommand.fpcount = sizeof(arm_neon_state64_t) / sizeof(uint32_t); + memcpy(&threadCommand.gpregisters, thread->GPRegisters(), sizeof(arm_thread_state64_t)); + memcpy(&threadCommand.fpregisters, thread->FPRegisters(), sizeof(arm_neon_state64_t)); +#endif + m_threadLoadCommands.push_back(threadCommand); + } +} + +bool +DumpWriter::WriteHeader(uint64_t* pFileOffset) +{ + mach_header_64 header; + memset(&header, 0, sizeof(mach_header_64)); + + header.magic = MH_MAGIC_64; +#if defined(__x86_64__) + header.cputype = CPU_TYPE_X86_64; +#elif defined(__aarch64__) + header.cputype = CPU_TYPE_ARM64; +#else +#error Unexpected architecture +#endif + header.cpusubtype = CPU_SUBTYPE_I386_ALL | CPU_SUBTYPE_LITTLE_ENDIAN; + header.filetype = MH_CORE; + + for (const ThreadCommand& thread : m_threadLoadCommands) + { + header.ncmds++; + header.sizeofcmds += thread.command.cmdsize; + } + + for (const segment_command_64& segment : m_segmentLoadCommands) + { + header.ncmds++; + header.sizeofcmds += segment.cmdsize; + } + + *pFileOffset = sizeof(mach_header_64) + header.sizeofcmds; + + TRACE("Macho header: magic %08x cputype %08x cpusubtype %08x filetype %08x ncmds %08x sizeofcmds %08x flags %08x reserved %08x\n", + header.magic, + header.cputype, + header.cpusubtype, + header.filetype, + header.ncmds, + header.sizeofcmds, + header.flags, + header.reserved); + + // Write header + return WriteData(&header, sizeof(mach_header_64)); +} + +bool +DumpWriter::WriteSegments() +{ + TRACE("Writing %" PRIu64 " memory regions to core file\n", m_segmentLoadCommands.size()); + + // Read from target process and write memory regions to core + uint64_t total = 0; + for (const segment_command_64& segment : m_segmentLoadCommands) + { + uint64_t address = segment.vmaddr; + size_t size = segment.vmsize; + total += size; + + TRACE("%" PRIA PRIx64 " - %" PRIA PRIx64 " (%06" PRIx64 ") %" PRIA PRIx64 " %c%c%c %02x\n", + segment.vmaddr, + segment.vmaddr + segment.vmsize, + segment.vmsize / PAGE_SIZE, + segment.fileoff, + (segment.initprot & VM_PROT_READ) ? 'r' : '-', + (segment.initprot & VM_PROT_WRITE) ? 'w' : '-', + (segment.initprot & VM_PROT_EXECUTE) ? 'x' : '-', + segment.initprot); + + if (address == SpecialThreadInfoAddress) + { + // Write the header + SpecialThreadInfoHeader header = { + {SPECIAL_THREADINFO_SIGNATURE}, + m_crashInfo.Pid() + }; + + if (!WriteData(&header, sizeof(header))) { + return false; + } + + // Write the tid and stack pointer for each thread + for (const ThreadInfo* thread : m_crashInfo.Threads()) + { + SpecialThreadInfoEntry entry = { + thread->Tid(), + thread->GetStackPointer() + }; + + if (!WriteData(&entry, sizeof(entry))) { + return false; + } + } + } + else + { + while (size > 0) + { + size_t bytesToRead = std::min(size, sizeof(m_tempBuffer)); + size_t read = 0; + + if (!m_crashInfo.ReadProcessMemory((void*)address, m_tempBuffer, bytesToRead, &read)) { + fprintf(stderr, "ReadProcessMemory(%" PRIA PRIx64 ", %08zx) FAILED\n", address, bytesToRead); + return false; + } + + // This can happen if the target process dies before createdump is finished + if (read == 0) { + fprintf(stderr, "ReadProcessMemory(%" PRIA PRIx64 ", %08zx) returned 0 bytes read\n", address, bytesToRead); + return false; + } + + if (!WriteData(m_tempBuffer, read)) { + return false; + } + + address += read; + size -= read; + } + } + } + + printf("Written %" PRId64 " bytes (%" PRId64 " pages) to core file\n", total, total / PAGE_SIZE); + return true; +} diff --git a/src/coreclr/debug/createdump/dumpwritermacho.h b/src/coreclr/debug/createdump/dumpwritermacho.h new file mode 100644 index 0000000000000..7a934339ec53b --- /dev/null +++ b/src/coreclr/debug/createdump/dumpwritermacho.h @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +struct ThreadCommand +{ + thread_command command; + uint32_t gpflavor; + uint32_t gpcount; +#if defined(__x86_64__) + x86_thread_state64_t gpregisters; +#elif defined(__aarch64__) + arm_thread_state64_t gpregisters; +#endif + uint32_t fpflavor; + uint32_t fpcount; +#if defined(__x86_64__) + x86_float_state64_t fpregisters; +#elif defined(__aarch64__) + arm_neon_state64_t fpregisters; +#endif +}; + +class DumpWriter +{ +private: + int m_fd; + CrashInfo& m_crashInfo; + + std::vector m_segmentLoadCommands; + std::vector m_threadLoadCommands; + BYTE m_tempBuffer[0x4000]; + +public: + DumpWriter(CrashInfo& crashInfo); + virtual ~DumpWriter(); + bool OpenDump(const char* dumpFileName); + bool WriteDump(); + +private: + void BuildSegmentLoadCommands(); + void BuildThreadLoadCommands(); + bool WriteHeader(uint64_t* pFileOffset); + bool WriteSegments(); + bool WriteData(const void* buffer, size_t length); +}; diff --git a/src/coreclr/debug/createdump/mac.h b/src/coreclr/debug/createdump/mac.h deleted file mode 100644 index 87745903ab183..0000000000000 --- a/src/coreclr/debug/createdump/mac.h +++ /dev/null @@ -1,211 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include -#include - -#define AT_SYSINFO_EHDR 33 - -#if TARGET_64BIT -#define TARGET_WORDSIZE 64 -#else -#define TARGET_WORDSIZE 32 -#endif - -#ifndef ElfW -/* We use this macro to refer to ELF types independent of the native wordsize. - `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ -#define ElfW(type) _ElfW (Elf, TARGET_WORDSIZE, type) -#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) -#define _ElfW_1(e,w,t) e##w##t -#endif - -#define ELFMAG0 0x7f /* Magic number byte 0 */ -#define ELFMAG1 'E' /* Magic number byte 1 */ -#define ELFMAG2 'L' /* Magic number byte 2 */ -#define ELFMAG3 'F' /* Magic number byte 3 */ - -enum { - NT_PRSTATUS = 1, - NT_FPREGSET, - NT_PRPSINFO, - NT_TASKSTRUCT, - NT_PLATFORM, - NT_AUXV, - NT_FILE = 0x46494c45, - NT_SIGINFO = 0x53494749, - NT_PPC_VMX = 0x100, - NT_PPC_VSX = 0x102, - NT_PRXFPREG = 0x46e62b7f, -}; - -typedef struct -{ - uint64_t a_type; /* Entry type */ - union - { - uint64_t a_val; /* Integer value */ - /* We use to have pointer elements added here. We cannot do that, - though, since it does not work when using 32-bit definitions - on 64-bit platforms and vice versa. */ - } a_un; -} Elf64_auxv_t; - -#define AT_NULL 0 /* end of vector */ -#define AT_BASE 7 /* base address of interpreter */ - -/* Note header in a PT_NOTE section */ -typedef struct elf32_note { - Elf32_Word n_namesz; /* Name size */ - Elf32_Word n_descsz; /* Content size */ - Elf32_Word n_type; /* Content type */ -} Elf32_Nhdr; - -/* Note header in a PT_NOTE section */ -typedef struct elf64_note { - Elf64_Word n_namesz; /* Name size */ - Elf64_Word n_descsz; /* Content size */ - Elf64_Word n_type; /* Content type */ -} Elf64_Nhdr; - -#if defined(TARGET_AMD64) -struct user_fpregs_struct -{ - unsigned short int cwd; - unsigned short int swd; - unsigned short int ftw; - unsigned short int fop; - unsigned long long int rip; - unsigned long long int rdp; - unsigned int mxcsr; - unsigned int mxcr_mask; - unsigned int st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ - unsigned int xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */ - unsigned int padding[24]; -}; - -struct user_regs_struct -{ - unsigned long long int r15; - unsigned long long int r14; - unsigned long long int r13; - unsigned long long int r12; - unsigned long long int rbp; - unsigned long long int rbx; - unsigned long long int r11; - unsigned long long int r10; - unsigned long long int r9; - unsigned long long int r8; - unsigned long long int rax; - unsigned long long int rcx; - unsigned long long int rdx; - unsigned long long int rsi; - unsigned long long int rdi; - unsigned long long int orig_rax; - unsigned long long int rip; - unsigned long long int cs; - unsigned long long int eflags; - unsigned long long int rsp; - unsigned long long int ss; - unsigned long long int fs_base; - unsigned long long int gs_base; - unsigned long long int ds; - unsigned long long int es; - unsigned long long int fs; - unsigned long long int gs; -}; -#elif defined(TARGET_ARM64) -struct user_fpsimd_struct -{ - uint64_t vregs[2*32]; - uint32_t fpcr; - uint32_t fpsr; -}; - -struct user_regs_struct -{ - uint64_t regs[31]; - uint64_t sp; - uint64_t pc; - uint32_t pstate; -}; -#else -#error Unexpected architecture -#endif - - -typedef pid_t __pid_t; - -/* Type for a general-purpose register. */ -#ifdef __x86_64__ -typedef unsigned long long elf_greg_t; -#else -typedef unsigned long elf_greg_t; -#endif - -/* And the whole bunch of them. We could have used `struct - user_regs_struct' directly in the typedef, but tradition says that - the register set is an array, which does have some peculiar - semantics, so leave it that way. */ -#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t)) -typedef elf_greg_t elf_gregset_t[ELF_NGREG]; - -/* Signal info. */ -struct elf_siginfo - { - int si_signo; /* Signal number. */ - int si_code; /* Extra code. */ - int si_errno; /* Errno. */ - }; - -/* Definitions to generate Intel SVR4-like core files. These mostly - have the same names as the SVR4 types with "elf_" tacked on the - front to prevent clashes with Linux definitions, and the typedef - forms have been avoided. This is mostly like the SVR4 structure, - but more Linuxy, with things that Linux does not support and which - GDB doesn't really use excluded. */ - -struct elf_prstatus - { - struct elf_siginfo pr_info; /* Info associated with signal. */ - short int pr_cursig; /* Current signal. */ - unsigned long int pr_sigpend; /* Set of pending signals. */ - unsigned long int pr_sighold; /* Set of held signals. */ - __pid_t pr_pid; - __pid_t pr_ppid; - __pid_t pr_pgrp; - __pid_t pr_sid; - struct timeval pr_utime; /* User time. */ - struct timeval pr_stime; /* System time. */ - struct timeval pr_cutime; /* Cumulative user time. */ - struct timeval pr_cstime; /* Cumulative system time. */ - elf_gregset_t pr_reg; /* GP registers. */ - int pr_fpvalid; /* True if math copro being used. */ - }; - - -#define ELF_PRARGSZ (80) /* Number of chars for args. */ - -struct elf_prpsinfo - { - char pr_state; /* Numeric process state. */ - char pr_sname; /* Char for pr_state. */ - char pr_zomb; /* Zombie. */ - char pr_nice; /* Nice val. */ - unsigned long int pr_flag; /* Flags. */ -#if __WORDSIZE == 32 - unsigned short int pr_uid; - unsigned short int pr_gid; -#else - unsigned int pr_uid; - unsigned int pr_gid; -#endif - int pr_pid, pr_ppid, pr_pgrp, pr_sid; - /* Lots missing */ - char pr_fname[16]; /* Filename of executable. */ - char pr_psargs[ELF_PRARGSZ]; /* Initial part of arg list. */ - }; - -/* Process status and info. In the end we do provide typedefs for them. */ -typedef struct elf_prstatus prstatus_t; -typedef struct elf_prpsinfo prpsinfo_t; diff --git a/src/coreclr/debug/createdump/main.cpp b/src/coreclr/debug/createdump/main.cpp index 4fde335b5aa11..00530de0243c9 100644 --- a/src/coreclr/debug/createdump/main.cpp +++ b/src/coreclr/debug/createdump/main.cpp @@ -22,8 +22,10 @@ const char* g_help = "createdump [options] pid\n" "-t, --triage - create triage minidump.\n" "-u, --full - create full core dump.\n" "-d, --diag - enable diagnostic messages.\n"; +"-vd, --verbose - enable verbose diagnostic messages.\n"; bool g_diagnostics = false; +bool g_diagnosticsVerbose = false; // // Main entry point @@ -42,15 +44,6 @@ int __cdecl main(const int argc, const char* argv[]) int exitCode = 0; int pid = 0; -#ifdef __APPLE__ - char* enabled = getenv("COMPlus_DbgEnableElfDumpOnMacOS"); - if (enabled == nullptr || strcmp(enabled, "1") != 0) - { - fprintf(stderr, "MachO coredumps are not supported. To enable ELF coredumps on MacOS, set the COMPlus_DbgEnableElfDumpOnMacOS environment variable to 1.\n"); - return -1; - } -#endif - #ifdef HOST_UNIX exitCode = PAL_InitializeDLL(); if (exitCode != 0) @@ -113,6 +106,11 @@ int __cdecl main(const int argc, const char* argv[]) { g_diagnostics = true; } + else if ((strcmp(*argv, "-vd") == 0) || (strcmp(*argv, "--verbose") == 0)) + { + g_diagnostics = true; + g_diagnosticsVerbose = true; + } else { pid = atoi(*argv); } @@ -168,7 +166,20 @@ void trace_printf(const char* format, ...) { if (g_diagnostics) - { + { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} + +void +trace_verbose_printf(const char* format, ...) +{ + if (g_diagnosticsVerbose) + { va_list args; va_start(args, format); vfprintf(stdout, format, args); @@ -177,3 +188,4 @@ trace_printf(const char* format, ...) } } + diff --git a/src/coreclr/debug/createdump/specialthreadinfo.h b/src/coreclr/debug/createdump/specialthreadinfo.h new file mode 100644 index 0000000000000..b64ba57997e51 --- /dev/null +++ b/src/coreclr/debug/createdump/specialthreadinfo.h @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// ****************************************************************************** +// WARNING!!!: This code is also used by SOS in the diagnostics repo. Should be +// updated in a backwards and forwards compatible way. +// See: https://github.com/dotnet/diagnostics/blob/main/src/SOS/inc/specialthreadinfo.h +// ****************************************************************************** + +// This defines a workaround to the MacOS dump format not having the OS process +// and thread ids that SOS needs to map thread "indexes" to thread "ids". The MacOS +// createdump adds this special memory region at this specific address that is not +// in the user or kernel address spaces. lldb is find with it. + +#define SPECIAL_THREADINFO_SIGNATURE "THREADINFO" + +const uint64_t SpecialThreadInfoAddress = 0x7fffffff00000000; + +struct SpecialThreadInfoHeader +{ + char signature[16]; + uint32_t pid; +}; + +struct SpecialThreadInfoEntry +{ + uint32_t tid; + uint64_t sp; +}; diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp index 0a663707758fc..330af44c1f833 100644 --- a/src/coreclr/debug/createdump/threadinfo.cpp +++ b/src/coreclr/debug/createdump/threadinfo.cpp @@ -150,17 +150,8 @@ ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess) void ThreadInfo::GetThreadStack() { - uint64_t startAddress; - size_t size; - -#if defined(__aarch64__) - startAddress = MCREG_Sp(m_gpRegisters) & PAGE_MASK; -#elif defined(__arm__) - startAddress = m_gpRegisters.ARM_sp & PAGE_MASK; -#else - startAddress = m_gpRegisters.rsp & PAGE_MASK; -#endif - size = 4 * PAGE_SIZE; + uint64_t startAddress = GetStackPointer() & PAGE_MASK; + size_t size = 4 * PAGE_SIZE; if (startAddress != 0) { @@ -184,122 +175,3 @@ ThreadInfo::GetThreadStack() TRACE("Thread %04x null stack pointer\n", m_tid); } } - -void -ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const -{ - context->ContextFlags = flags; -#if defined(__x86_64__) - if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) - { - context->Rbp = m_gpRegisters.rbp; - context->Rip = m_gpRegisters.rip; - context->SegCs = m_gpRegisters.cs; - context->EFlags = m_gpRegisters.eflags; - context->SegSs = m_gpRegisters.ss; - context->Rsp = m_gpRegisters.rsp; - } - if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) - { - context->Rdi = m_gpRegisters.rdi; - context->Rsi = m_gpRegisters.rsi; - context->Rbx = m_gpRegisters.rbx; - context->Rdx = m_gpRegisters.rdx; - context->Rcx = m_gpRegisters.rcx; - context->Rax = m_gpRegisters.rax; - context->R8 = m_gpRegisters.r8; - context->R9 = m_gpRegisters.r9; - context->R10 = m_gpRegisters.r10; - context->R11 = m_gpRegisters.r11; - context->R12 = m_gpRegisters.r12; - context->R13 = m_gpRegisters.r13; - context->R14 = m_gpRegisters.r14; - context->R15 = m_gpRegisters.r15; - } - if ((flags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) - { - context->SegDs = m_gpRegisters.ds; - context->SegEs = m_gpRegisters.es; - context->SegFs = m_gpRegisters.fs; - context->SegGs = m_gpRegisters.gs; - } - if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) - { - context->FltSave.ControlWord = m_fpRegisters.cwd; - context->FltSave.StatusWord = m_fpRegisters.swd; - context->FltSave.TagWord = m_fpRegisters.ftw; - context->FltSave.ErrorOpcode = m_fpRegisters.fop; - - context->FltSave.ErrorOffset = FPREG_ErrorOffset(m_fpRegisters); - context->FltSave.ErrorSelector = FPREG_ErrorSelector(m_fpRegisters); - context->FltSave.DataOffset = FPREG_DataOffset(m_fpRegisters); - context->FltSave.DataSelector = FPREG_DataSelector(m_fpRegisters); - - context->FltSave.MxCsr = m_fpRegisters.mxcsr; - context->FltSave.MxCsr_Mask = m_fpRegisters.mxcr_mask; - - assert(sizeof(context->FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space)); - memcpy(context->FltSave.FloatRegisters, m_fpRegisters.st_space, sizeof(context->FltSave.FloatRegisters)); - - assert(sizeof(context->FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space)); - memcpy(context->FltSave.XmmRegisters, m_fpRegisters.xmm_space, sizeof(context->FltSave.XmmRegisters)); - } - // TODO: debug registers? -#elif defined(__aarch64__) - if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) - { - context->Fp = MCREG_Fp(m_gpRegisters); - context->Lr = MCREG_Lr(m_gpRegisters); - context->Sp = MCREG_Sp(m_gpRegisters); - context->Pc = MCREG_Pc(m_gpRegisters); - context->Cpsr = MCREG_Cpsr(m_gpRegisters); - } - if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) - { - assert(sizeof(m_gpRegisters.regs) == (sizeof(context->X) + sizeof(context->Fp) + sizeof(context->Lr))); - memcpy(context->X, m_gpRegisters.regs, sizeof(context->X)); - } - if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) - { - assert(sizeof(m_fpRegisters.vregs) == sizeof(context->V)); - memcpy(context->V, m_fpRegisters.vregs, sizeof(context->V)); - context->Fpcr = m_fpRegisters.fpcr; - context->Fpsr = m_fpRegisters.fpsr; - } -#elif defined(__arm__) - if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) - { - context->Sp = m_gpRegisters.ARM_sp; - context->Lr = m_gpRegisters.ARM_lr; - context->Pc = m_gpRegisters.ARM_pc; - context->Cpsr = m_gpRegisters.ARM_cpsr; - } - if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) - { - context->R0 = m_gpRegisters.ARM_r0; - context->R1 = m_gpRegisters.ARM_r1; - context->R2 = m_gpRegisters.ARM_r2; - context->R3 = m_gpRegisters.ARM_r3; - context->R4 = m_gpRegisters.ARM_r4; - context->R5 = m_gpRegisters.ARM_r5; - context->R6 = m_gpRegisters.ARM_r6; - context->R7 = m_gpRegisters.ARM_r7; - context->R8 = m_gpRegisters.ARM_r8; - context->R9 = m_gpRegisters.ARM_r9; - context->R10 = m_gpRegisters.ARM_r10; - context->R11 = m_gpRegisters.ARM_fp; - context->R12 = m_gpRegisters.ARM_ip; - } - if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) - { -#if defined(__VFP_FP__) && !defined(__SOFTFP__) - context->Fpscr = m_vfpRegisters.fpscr; - - assert(sizeof(context->D) == sizeof(m_vfpRegisters.fpregs)); - memcpy(context->D, m_vfpRegisters.fpregs, sizeof(context->D)); -#endif - } -#else -#error Platform not supported -#endif -} diff --git a/src/coreclr/debug/createdump/threadinfo.h b/src/coreclr/debug/createdump/threadinfo.h index 2931bcafd4fb9..ac42d056da590 100644 --- a/src/coreclr/debug/createdump/threadinfo.h +++ b/src/coreclr/debug/createdump/threadinfo.h @@ -40,9 +40,17 @@ class ThreadInfo pid_t m_tid; // thread id pid_t m_ppid; // parent process pid_t m_tgid; // thread group + #ifdef __APPLE__ mach_port_t m_port; // MacOS thread port +#if defined(__x86_64__) + x86_thread_state64_t m_gpRegisters; // MacOS general purpose registers + x86_float_state64_t m_fpRegisters; // MacOS floating point registers +#elif defined(__aarch64__) + arm_thread_state64_t m_gpRegisters; // MacOS general purpose arm64 registers + arm_neon_state64_t m_fpRegisters; // MacOS floating point arm64 registers #endif +#else // __APPLE__ struct user_regs_struct m_gpRegisters; // general purpose registers struct user_fpregs_struct m_fpRegisters; // floating point registers #if defined(__i386__) @@ -50,6 +58,7 @@ class ThreadInfo #elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) struct user_vfpregs_struct m_vfpRegisters; // ARM VFP/NEON registers #endif +#endif // __APPLE__ public: #ifdef __APPLE__ @@ -68,6 +77,17 @@ class ThreadInfo inline pid_t Ppid() const { return m_ppid; } inline pid_t Tgid() const { return m_tgid; } +#ifdef __APPLE__ +#if defined(__x86_64__) + inline const x86_thread_state64_t* GPRegisters() const { return &m_gpRegisters; } + inline const x86_float_state64_t* FPRegisters() const { return &m_fpRegisters; } + inline const uint64_t GetStackPointer() const { return m_gpRegisters.__rsp; } +#elif defined(__aarch64__) + inline const arm_thread_state64_t* GPRegisters() const { return &m_gpRegisters; } + inline const arm_neon_state64_t* FPRegisters() const { return &m_fpRegisters; } + inline const uint64_t GetStackPointer() const { return arm_thread_state64_get_sp(m_gpRegisters); } +#endif +#else // __APPLE__ inline const user_regs_struct* GPRegisters() const { return &m_gpRegisters; } inline const user_fpregs_struct* FPRegisters() const { return &m_fpRegisters; } #if defined(__i386__) @@ -75,6 +95,17 @@ class ThreadInfo #elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) inline const user_vfpregs_struct* VFPRegisters() const { return &m_vfpRegisters; } #endif + inline const uint64_t GetStackPointer() const + { +#if defined(__x86_64__) + return m_gpRegisters.rsp; +#elif defined(__aarch64__) + return MCREG_Sp(m_gpRegisters); +#elif defined(__arm__) + return m_gpRegisters.ARM_sp; +#endif + } +#endif // __APPLE__ private: void UnwindNativeFrames(CONTEXT* pContext); diff --git a/src/coreclr/debug/createdump/threadinfomac.cpp b/src/coreclr/debug/createdump/threadinfomac.cpp index c9c8db8bb5058..d5c0f9d7495a6 100644 --- a/src/coreclr/debug/createdump/threadinfomac.cpp +++ b/src/coreclr/debug/createdump/threadinfomac.cpp @@ -25,102 +25,128 @@ ThreadInfo::Initialize() m_ppid = 0; m_tgid = 0; -#if defined(TARGET_AMD64) - x86_thread_state64_t state; +#if defined(__x86_64__) mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT; - kern_return_t result = ::thread_get_state(Port(), x86_THREAD_STATE64, (thread_state_t)&state, &stateCount); + kern_return_t result = ::thread_get_state(Port(), x86_THREAD_STATE64, (thread_state_t)&m_gpRegisters, &stateCount); if (result != KERN_SUCCESS) { fprintf(stderr, "thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result)); return false; } - m_gpRegisters.rbp = state.__rbp; - m_gpRegisters.rip = state.__rip; - m_gpRegisters.cs = state.__cs; - m_gpRegisters.eflags = state.__rflags; - m_gpRegisters.ss = 0; - m_gpRegisters.rsp = state.__rsp; - m_gpRegisters.rdi = state.__rdi; - - m_gpRegisters.rsi = state.__rsi; - m_gpRegisters.rbx = state.__rbx; - m_gpRegisters.rdx = state.__rdx; - m_gpRegisters.rcx = state.__rcx; - m_gpRegisters.rax = state.__rax; - m_gpRegisters.orig_rax = state.__rax; - m_gpRegisters.r8 = state.__r8; - m_gpRegisters.r9 = state.__r9; - m_gpRegisters.r10 = state.__r10; - m_gpRegisters.r11 = state.__r11; - m_gpRegisters.r12 = state.__r12; - m_gpRegisters.r13 = state.__r13; - m_gpRegisters.r14 = state.__r14; - m_gpRegisters.r15 = state.__r15; - - m_gpRegisters.fs = state.__fs; - m_gpRegisters.gs = state.__gs; - m_gpRegisters.ds = 0; - m_gpRegisters.es = 0; - m_gpRegisters.gs_base = 0; - m_gpRegisters.fs_base = 0; - - x86_float_state64_t fpstate; stateCount = x86_FLOAT_STATE64_COUNT; - result = ::thread_get_state(Port(), x86_FLOAT_STATE64, (thread_state_t)&fpstate, &stateCount); + result = ::thread_get_state(Port(), x86_FLOAT_STATE64, (thread_state_t)&m_fpRegisters, &stateCount); if (result != KERN_SUCCESS) { fprintf(stderr, "thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result)); return false; } - - m_fpRegisters.cwd = *((unsigned short *)&fpstate.__fpu_fcw); - m_fpRegisters.swd = *((unsigned short *)&fpstate.__fpu_fsw); - m_fpRegisters.ftw = fpstate.__fpu_ftw; - m_fpRegisters.fop = fpstate.__fpu_fop; - - FPREG_ErrorOffset(m_fpRegisters) = fpstate.__fpu_ip; - FPREG_ErrorSelector(m_fpRegisters) = fpstate.__fpu_cs; - FPREG_DataOffset(m_fpRegisters) = fpstate.__fpu_dp; - FPREG_DataSelector(m_fpRegisters) = fpstate.__fpu_ds; - - m_fpRegisters.mxcsr = fpstate.__fpu_mxcsr; - m_fpRegisters.mxcr_mask = fpstate.__fpu_mxcsrmask; - - memcpy(m_fpRegisters.st_space, &fpstate.__fpu_stmm0, sizeof(m_fpRegisters.st_space)); - memcpy(m_fpRegisters.xmm_space, &fpstate.__fpu_xmm0, sizeof(m_fpRegisters.xmm_space)); -#elif defined(TARGET_ARM64) - arm_thread_state64_t state; +#elif defined(__aarch64__) mach_msg_type_number_t stateCount = ARM_THREAD_STATE64_COUNT; - kern_return_t result = ::thread_get_state(Port(), ARM_THREAD_STATE64, (thread_state_t)&state, &stateCount); + kern_return_t result = ::thread_get_state(Port(), ARM_THREAD_STATE64, (thread_state_t)&m_gpRegisters, &stateCount); if (result != KERN_SUCCESS) { fprintf(stderr, "thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result)); return false; } - memcpy(m_gpRegisters.regs, &state.__x, sizeof(state.__x)); - m_gpRegisters.regs[29] = arm_thread_state64_get_fp(state); - m_gpRegisters.regs[30] = (uint64_t)arm_thread_state64_get_lr_fptr(state); - - m_gpRegisters.sp = arm_thread_state64_get_sp(state); - m_gpRegisters.pc = (uint64_t)arm_thread_state64_get_pc_fptr(state); - - arm_neon_state64_t fpstate; stateCount = ARM_NEON_STATE64_COUNT; - result = ::thread_get_state(Port(), ARM_NEON_STATE64, (thread_state_t)&fpstate, &stateCount); + result = ::thread_get_state(Port(), ARM_NEON_STATE64, (thread_state_t)&m_fpRegisters, &stateCount); if (result != KERN_SUCCESS) { fprintf(stderr, "thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result)); return false; } - - memcpy(m_fpRegisters.vregs, &fpstate.__v, sizeof(m_fpRegisters.vregs)); - m_fpRegisters.fpsr = fpstate.__fpsr; - m_fpRegisters.fpcr = fpstate.__fpcr; #else #error Unexpected architecture #endif - return true; } + +void +ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const +{ + context->ContextFlags = flags; +#if defined(__x86_64__) + if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + context->Rbp = m_gpRegisters.__rbp; + context->Rip = m_gpRegisters.__rip; + context->SegCs = m_gpRegisters.__cs; + context->EFlags = m_gpRegisters.__rflags; + // TODO: get "full" register state for the segment regs + context->SegSs = 0; // m_gpRegisters.__ss; + context->Rsp = m_gpRegisters.__rsp; + } + if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + context->Rdi = m_gpRegisters.__rdi; + context->Rsi = m_gpRegisters.__rsi; + context->Rbx = m_gpRegisters.__rbx; + context->Rdx = m_gpRegisters.__rdx; + context->Rcx = m_gpRegisters.__rcx; + context->Rax = m_gpRegisters.__rax; + context->R8 = m_gpRegisters.__r8; + context->R9 = m_gpRegisters.__r9; + context->R10 = m_gpRegisters.__r10; + context->R11 = m_gpRegisters.__r11; + context->R12 = m_gpRegisters.__r12; + context->R13 = m_gpRegisters.__r13; + context->R14 = m_gpRegisters.__r14; + context->R15 = m_gpRegisters.__r15; + } + if ((flags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) + { + // TODO: get "full" register state for the segment regs + context->SegDs = 0; // m_gpRegisters.__ds; + context->SegEs = 0; // m_gpRegisters.__es; + context->SegFs = m_gpRegisters.__fs; + context->SegGs = m_gpRegisters.__gs; + } + if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { + context->FltSave.ControlWord = *((unsigned short *)&m_fpRegisters.__fpu_fcw); + context->FltSave.StatusWord = *((unsigned short *)&m_fpRegisters.__fpu_fsw); + context->FltSave.TagWord = m_fpRegisters.__fpu_ftw; + context->FltSave.ErrorOpcode = m_fpRegisters.__fpu_fop; + + context->FltSave.ErrorOffset = m_fpRegisters.__fpu_ip; + context->FltSave.ErrorSelector = m_fpRegisters.__fpu_cs; + context->FltSave.DataOffset = m_fpRegisters.__fpu_dp; + context->FltSave.DataSelector = m_fpRegisters.__fpu_ds; + + context->FltSave.MxCsr = m_fpRegisters.__fpu_mxcsr; + context->FltSave.MxCsr_Mask = m_fpRegisters.__fpu_mxcsrmask; + + assert(sizeof(context->FltSave.FloatRegisters) == sizeof(m_fpRegisters.__fpu_stmm0) * 8); + memcpy(context->FltSave.FloatRegisters, &m_fpRegisters.__fpu_stmm0, sizeof(context->FltSave.FloatRegisters)); + + assert(sizeof(context->FltSave.XmmRegisters) == sizeof(m_fpRegisters.__fpu_xmm0) * 16); + memcpy(context->FltSave.XmmRegisters, &m_fpRegisters.__fpu_xmm0, sizeof(context->FltSave.XmmRegisters)); + } + // TODO: debug registers? +#elif defined(__aarch64__) + if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + context->Fp = arm_thread_state64_get_fp(m_gpRegisters); + context->Lr = (uint64_t)arm_thread_state64_get_lr_fptr(m_gpRegisters); + context->Sp = arm_thread_state64_get_sp(m_gpRegisters); + context->Pc = (uint64_t)arm_thread_state64_get_pc_fptr(m_gpRegisters); + context->Cpsr = m_gpRegisters.__cpsr; + } + if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + assert(sizeof(m_gpRegisters.__x) == (sizeof(context->X))); + memcpy(context->X, m_gpRegisters.__x, sizeof(context->X)); + } + if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { + assert(sizeof(m_fpRegisters.__v) == sizeof(context->V)); + memcpy(context->V, m_fpRegisters.__v, sizeof(context->V)); + context->Fpcr = m_fpRegisters.__fpcr; + context->Fpsr = m_fpRegisters.__fpsr; + } +#else +#error Platform not supported +#endif +} diff --git a/src/coreclr/debug/createdump/threadinfounix.cpp b/src/coreclr/debug/createdump/threadinfounix.cpp index 2cb33adf16273..e0ef048380d16 100644 --- a/src/coreclr/debug/createdump/threadinfounix.cpp +++ b/src/coreclr/debug/createdump/threadinfounix.cpp @@ -98,3 +98,122 @@ ThreadInfo::GetRegistersWithPTrace() #endif return true; } + +void +ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const +{ + context->ContextFlags = flags; +#if defined(__x86_64__) + if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + context->Rbp = m_gpRegisters.rbp; + context->Rip = m_gpRegisters.rip; + context->SegCs = m_gpRegisters.cs; + context->EFlags = m_gpRegisters.eflags; + context->SegSs = m_gpRegisters.ss; + context->Rsp = m_gpRegisters.rsp; + } + if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + context->Rdi = m_gpRegisters.rdi; + context->Rsi = m_gpRegisters.rsi; + context->Rbx = m_gpRegisters.rbx; + context->Rdx = m_gpRegisters.rdx; + context->Rcx = m_gpRegisters.rcx; + context->Rax = m_gpRegisters.rax; + context->R8 = m_gpRegisters.r8; + context->R9 = m_gpRegisters.r9; + context->R10 = m_gpRegisters.r10; + context->R11 = m_gpRegisters.r11; + context->R12 = m_gpRegisters.r12; + context->R13 = m_gpRegisters.r13; + context->R14 = m_gpRegisters.r14; + context->R15 = m_gpRegisters.r15; + } + if ((flags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) + { + context->SegDs = m_gpRegisters.ds; + context->SegEs = m_gpRegisters.es; + context->SegFs = m_gpRegisters.fs; + context->SegGs = m_gpRegisters.gs; + } + if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { + context->FltSave.ControlWord = m_fpRegisters.cwd; + context->FltSave.StatusWord = m_fpRegisters.swd; + context->FltSave.TagWord = m_fpRegisters.ftw; + context->FltSave.ErrorOpcode = m_fpRegisters.fop; + + context->FltSave.ErrorOffset = FPREG_ErrorOffset(m_fpRegisters); + context->FltSave.ErrorSelector = FPREG_ErrorSelector(m_fpRegisters); + context->FltSave.DataOffset = FPREG_DataOffset(m_fpRegisters); + context->FltSave.DataSelector = FPREG_DataSelector(m_fpRegisters); + + context->FltSave.MxCsr = m_fpRegisters.mxcsr; + context->FltSave.MxCsr_Mask = m_fpRegisters.mxcr_mask; + + assert(sizeof(context->FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space)); + memcpy(context->FltSave.FloatRegisters, m_fpRegisters.st_space, sizeof(context->FltSave.FloatRegisters)); + + assert(sizeof(context->FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space)); + memcpy(context->FltSave.XmmRegisters, m_fpRegisters.xmm_space, sizeof(context->FltSave.XmmRegisters)); + } + // TODO: debug registers? +#elif defined(__aarch64__) + if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + context->Fp = MCREG_Fp(m_gpRegisters); + context->Lr = MCREG_Lr(m_gpRegisters); + context->Sp = MCREG_Sp(m_gpRegisters); + context->Pc = MCREG_Pc(m_gpRegisters); + context->Cpsr = MCREG_Cpsr(m_gpRegisters); + } + if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + assert(sizeof(m_gpRegisters.regs) == (sizeof(context->X) + sizeof(context->Fp) + sizeof(context->Lr))); + memcpy(context->X, m_gpRegisters.regs, sizeof(context->X)); + } + if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { + assert(sizeof(m_fpRegisters.vregs) == sizeof(context->V)); + memcpy(context->V, m_fpRegisters.vregs, sizeof(context->V)); + context->Fpcr = m_fpRegisters.fpcr; + context->Fpsr = m_fpRegisters.fpsr; + } +#elif defined(__arm__) + if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + context->Sp = m_gpRegisters.ARM_sp; + context->Lr = m_gpRegisters.ARM_lr; + context->Pc = m_gpRegisters.ARM_pc; + context->Cpsr = m_gpRegisters.ARM_cpsr; + } + if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + context->R0 = m_gpRegisters.ARM_r0; + context->R1 = m_gpRegisters.ARM_r1; + context->R2 = m_gpRegisters.ARM_r2; + context->R3 = m_gpRegisters.ARM_r3; + context->R4 = m_gpRegisters.ARM_r4; + context->R5 = m_gpRegisters.ARM_r5; + context->R6 = m_gpRegisters.ARM_r6; + context->R7 = m_gpRegisters.ARM_r7; + context->R8 = m_gpRegisters.ARM_r8; + context->R9 = m_gpRegisters.ARM_r9; + context->R10 = m_gpRegisters.ARM_r10; + context->R11 = m_gpRegisters.ARM_fp; + context->R12 = m_gpRegisters.ARM_ip; + } + if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { +#if defined(__VFP_FP__) && !defined(__SOFTFP__) + context->Fpscr = m_vfpRegisters.fpscr; + + assert(sizeof(context->D) == sizeof(m_vfpRegisters.fpregs)); + memcpy(context->D, m_vfpRegisters.fpregs, sizeof(context->D)); +#endif + } +#else +#error Platform not supported +#endif +} diff --git a/src/coreclr/debug/dbgutil/elfreader.h b/src/coreclr/debug/dbgutil/elfreader.h index ac7d8a4b0f307..67ddc0d864653 100644 --- a/src/coreclr/debug/dbgutil/elfreader.h +++ b/src/coreclr/debug/dbgutil/elfreader.h @@ -66,4 +66,5 @@ class ElfReader virtual void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr) { }; virtual bool ReadMemory(void* address, void* buffer, size_t size) = 0; virtual void Trace(const char* format, ...) { }; + virtual void TraceVerbose(const char* format, ...) { }; }; diff --git a/src/coreclr/debug/dbgutil/machoreader.cpp b/src/coreclr/debug/dbgutil/machoreader.cpp index fa94b49b8d4a4..fea2862681a86 100644 --- a/src/coreclr/debug/dbgutil/machoreader.cpp +++ b/src/coreclr/debug/dbgutil/machoreader.cpp @@ -189,7 +189,7 @@ MachOModule::ReadLoadCommands() for (int i = 0; i < m_header.ncmds; i++) { - m_reader.Trace("CMD: load command cmd %02x (%d) size %d\n", command->cmd, command->cmd, command->cmdsize); + m_reader.TraceVerbose("CMD: load command cmd %02x (%d) size %d\n", command->cmd, command->cmd, command->cmdsize); switch (command->cmd) { @@ -206,10 +206,10 @@ MachOModule::ReadLoadCommands() if (strcmp(segment->segname, SEG_TEXT) == 0) { m_loadBias = m_baseAddress - segment->vmaddr; - m_reader.Trace("CMD: load bias %016llx\n", m_loadBias); + m_reader.TraceVerbose("CMD: load bias %016llx\n", m_loadBias); } - m_reader.Trace("CMD: vmaddr %016llx vmsize %016llx fileoff %016llx filesize %016llx nsects %d max %c%c%c init %c%c%c %02x %s\n", + m_reader.TraceVerbose("CMD: vmaddr %016llx vmsize %016llx fileoff %016llx filesize %016llx nsects %d max %c%c%c init %c%c%c %02x %s\n", segment->vmaddr, segment->vmsize, segment->fileoff, @@ -227,7 +227,7 @@ MachOModule::ReadLoadCommands() section_64* section = (section_64*)((uint64_t)segment + sizeof(segment_command_64)); for (int s = 0; s < segment->nsects; s++, section++) { - m_reader.Trace(" addr %016llx size %016llx off %08x align %02x flags %02x %s\n", + m_reader.TraceVerbose(" addr %016llx size %016llx off %08x align %02x flags %02x %s\n", section->addr, section->size, section->offset, @@ -257,7 +257,7 @@ MachOModule::ReadSymbolTable() _ASSERTE(m_symtabCommand != nullptr); _ASSERTE(m_strtab == nullptr); - m_reader.Trace("SYM: symoff %08x nsyms %d stroff %08x strsize %d\n", + m_reader.TraceVerbose("SYM: symoff %08x nsyms %d stroff %08x strsize %d\n", m_symtabCommand->symoff, m_symtabCommand->nsyms, m_symtabCommand->stroff, diff --git a/src/coreclr/debug/dbgutil/machoreader.h b/src/coreclr/debug/dbgutil/machoreader.h index f18a7950b18b4..38f1bb8e6559f 100644 --- a/src/coreclr/debug/dbgutil/machoreader.h +++ b/src/coreclr/debug/dbgutil/machoreader.h @@ -60,4 +60,5 @@ class MachOReader virtual void VisitSection(MachOModule& module, const section_64& section) { }; virtual bool ReadMemory(void* address, void* buffer, size_t size) = 0; virtual void Trace(const char* format, ...) { }; + virtual void TraceVerbose(const char* format, ...) { }; };