diff --git a/src/coreclr/debug/createdump/dumpwritermacho.cpp b/src/coreclr/debug/createdump/dumpwritermacho.cpp index 61fa49259c76b1..c459c9423cce61 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.cpp +++ b/src/coreclr/debug/createdump/dumpwritermacho.cpp @@ -16,11 +16,16 @@ DumpWriter::WriteDump() BuildThreadLoadCommands(); + BuildProcessMetadataNote(); + uint64_t fileOffset = 0; if (!WriteHeader(&fileOffset)) { return false; } + m_processMetadataNote.offset = fileOffset; + fileOffset += m_processMetadataNote.size; + TRACE("Writing %zd thread commands to core file\n", m_threadLoadCommands.size()); // Write thread commands @@ -54,6 +59,14 @@ DumpWriter::WriteDump() } } + // Write the process metadata note as the final load command + if (!WriteData(&m_processMetadataNote, m_processMetadataNote.cmdsize)) { + return false; + } + if (!WriteData(m_processMetadata.data(), m_processMetadata.size())) { + return false; + } + // Write any segment alignment required to the core file if (alignment > 0) { @@ -87,6 +100,33 @@ ConvertFlags(uint32_t flags) return prot; } +void +DumpWriter::BuildProcessMetadataNote() +{ + // LLDB specifies the "process metadata" LC_NOTE as compact JSON whose + // thread entries correspond by position to the LC_THREAD commands. + m_processMetadata = "{\"threads\":["; + bool first = true; + for (const ThreadInfo* thread : m_crashInfo.Threads()) + { + if (!first) + { + m_processMetadata.push_back(','); + } + first = false; + m_processMetadata.append("{\"thread_id\":"); + m_processMetadata.append(std::to_string(thread->Tid())); + m_processMetadata.push_back('}'); + } + m_processMetadata.append("]}"); + + m_processMetadataNote.cmd = LC_NOTE; + m_processMetadataNote.cmdsize = sizeof(note_command); + static_assert(sizeof(m_processMetadataNote.data_owner) == sizeof("process metadata") - 1); + memcpy(m_processMetadataNote.data_owner, "process metadata", sizeof(m_processMetadataNote.data_owner)); + m_processMetadataNote.size = m_processMetadata.size(); +} + void DumpWriter::BuildSegmentLoadCommands() { @@ -190,6 +230,9 @@ DumpWriter::WriteHeader(uint64_t* pFileOffset) header.sizeofcmds += segment.cmdsize; } + header.ncmds++; + header.sizeofcmds += m_processMetadataNote.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", diff --git a/src/coreclr/debug/createdump/dumpwritermacho.h b/src/coreclr/debug/createdump/dumpwritermacho.h index c1788a3b4bded1..866ef7361d5fdb 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.h +++ b/src/coreclr/debug/createdump/dumpwritermacho.h @@ -28,6 +28,8 @@ class DumpWriter std::vector m_segmentLoadCommands; std::vector m_threadLoadCommands; + note_command m_processMetadataNote{}; + std::string m_processMetadata; BYTE m_tempBuffer[0x4000]; // no public copy constructor @@ -43,6 +45,7 @@ class DumpWriter private: bool WriteDiagInfo(size_t size); + void BuildProcessMetadataNote(); void BuildSegmentLoadCommands(); void BuildThreadLoadCommands(); bool WriteHeader(uint64_t* pFileOffset); diff --git a/src/coreclr/debug/createdump/specialthreadinfo.h b/src/coreclr/debug/createdump/specialthreadinfo.h index 661473eee565e6..deb4d704be8084 100644 --- a/src/coreclr/debug/createdump/specialthreadinfo.h +++ b/src/coreclr/debug/createdump/specialthreadinfo.h @@ -14,7 +14,13 @@ #define SPECIAL_THREADINFO_SIGNATURE "THREADINFO" +#if defined(HOST_ARM64) || defined(__arm64__) || defined(__aarch64__) +// Apple Silicon (arm64) macOS user-space VM is 47 bits. lldb's core reader +// rejects segments above 0x7FFF_FFFF_FFFF, so use a 47-bit-valid address. +const uint64_t SpecialThreadInfoAddress = 0x00007ffffff00000; +#else const uint64_t SpecialThreadInfoAddress = 0x7fffffff00000000; +#endif struct SpecialThreadInfoHeader {