diff --git a/eng/packaging.targets b/eng/packaging.targets index 61f2f40a7248b1..198bee41d33c62 100644 --- a/eng/packaging.targets +++ b/eng/packaging.targets @@ -34,6 +34,10 @@ '$(IsRIDSpecificProject)' != 'true' and '$(PreReleaseVersionLabel)' == 'servicing' and '$(GitHubRepositoryName)' != 'runtimelab'">false + + true $(XmlDocFileRoot)1033\$(AssemblyName).xml true @@ -279,7 +283,7 @@ diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp index ad9c247e37dfdc..3ada3bd767c964 100644 --- a/src/coreclr/debug/createdump/crashinfomac.cpp +++ b/src/coreclr/debug/createdump/crashinfomac.cpp @@ -277,6 +277,9 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm uint64_t start = segment.vmaddr + module.LoadBias(); uint64_t end = start + segment.vmsize; + // Add this module segment to the set used by the thread unwinding to lookup the module base address for an ip. + AddModuleAddressRange(start, end, module.BaseAddress()); + // Round to page boundary start = start & PAGE_MASK; _ASSERTE(start > 0); @@ -297,9 +300,6 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm } // Add this module segment to the module mappings list m_moduleMappings.insert(moduleRegion); - - // Add this module segment to the set used by the thread unwinding to lookup the module base address for an ip. - AddModuleAddressRange(start, end, module.BaseAddress()); } else { diff --git a/src/coreclr/debug/createdump/stackframe.h b/src/coreclr/debug/createdump/stackframe.h index 75e20d93120c0c..00c3a1cfb7fd8f 100644 --- a/src/coreclr/debug/createdump/stackframe.h +++ b/src/coreclr/debug/createdump/stackframe.h @@ -66,9 +66,16 @@ struct StackFrame } } +// See comment in threadinfo.cpp UnwindNativeFrames function +#if defined(__aarch64__) + #define STACK_POINTER_MASK ~0x7 +#else + #define STACK_POINTER_MASK ~0x0 +#endif + inline uint64_t ModuleAddress() const { return m_moduleAddress; } inline uint64_t InstructionPointer() const { return m_instructionPointer; } - inline uint64_t StackPointer() const { return m_stackPointer; } + inline uint64_t StackPointer() const { return m_stackPointer & STACK_POINTER_MASK; } inline uint32_t NativeOffset() const { return m_nativeOffset; } inline uint32_t Token() const { return m_token; } inline uint32_t ILOffset() const { return m_ilOffset; } diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp index 82509f52750653..e64eafc8d29a96 100644 --- a/src/coreclr/debug/createdump/threadinfo.cpp +++ b/src/coreclr/debug/createdump/threadinfo.cpp @@ -53,6 +53,16 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) uint64_t ip = 0, sp = 0; GetFrameLocation(pContext, &ip, &sp); +#if defined(__aarch64__) + // ARM64 can have frames with the same SP but different IPs. Increment sp so it gets added to the stack + // frames in the correct order and to prevent the below loop termination on non-increasing sp. Since stack + // pointers are always 8 byte align, this increase is masked off in StackFrame::StackPointer() to get the + // original stack pointer. + if (sp == previousSp && ip != previousIp) + { + sp++; + } +#endif if (ip == 0 || sp <= previousSp) { TRACE_VERBOSE("Unwind: sp not increasing or ip == 0 sp %p ip %p\n", (void*)sp, (void*)ip); break; diff --git a/src/coreclr/debug/dbgutil/machoreader.cpp b/src/coreclr/debug/dbgutil/machoreader.cpp index d2801547cb9ba9..7fef34e1afb16d 100644 --- a/src/coreclr/debug/dbgutil/machoreader.cpp +++ b/src/coreclr/debug/dbgutil/machoreader.cpp @@ -229,9 +229,8 @@ MachOModule::ReadLoadCommands() m_segments.push_back(segment); // Calculate the load bias for the module. This is the value to add to the vmaddr of a - // segment to get the actual address. For shared modules, this is 0 since those segments - // are absolute address. - if (segment->fileoff == 0 && segment->filesize > 0) + // segment to get the actual address. + if (strcmp(segment->segname, SEG_TEXT) == 0) { m_loadBias = m_baseAddress - segment->vmaddr; } diff --git a/src/coreclr/pal/src/exception/remote-unwind.cpp b/src/coreclr/pal/src/exception/remote-unwind.cpp index af0293ba5bc60c..b27fc9680bbedf 100644 --- a/src/coreclr/pal/src/exception/remote-unwind.cpp +++ b/src/coreclr/pal/src/exception/remote-unwind.cpp @@ -57,6 +57,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include "compact_unwind_encoding.h" +#define MACOS_ARM64_POINTER_AUTH_MASK 0x7fffffffffffull #endif // Sub-headers included from the libunwind.h contain an empty struct @@ -1422,25 +1423,56 @@ StepWithCompactNoEncoding(const libunwindInfo* info) #if defined(TARGET_ARM64) -inline static bool +#define ARM64_SYSCALL_OPCODE 0xD4001001 +#define ARM64_BL_OPCODE_MASK 0xFC000000 +#define ARM64_BL_OPCODE 0x94000000 +#define ARM64_BLR_OPCODE_MASK 0xFFFFFC00 +#define ARM64_BLR_OPCODE 0xD63F0000 +#define ARM64_BLRA_OPCODE_MASK 0xFEFFF800 +#define ARM64_BLRA_OPCODE 0xD63F0800 + +static bool +StepWithCompactNoEncoding(const libunwindInfo* info) +{ + // Check that the function is a syscall "wrapper" and assume there is no frame and pop the return address. + uint32_t opcode; + unw_word_t addr = info->Context->Pc - sizeof(opcode); + if (!ReadValue32(info, &addr, &opcode)) { + ERROR("StepWithCompactNoEncoding: can read opcode %p\n", (void*)addr); + return false; + } + // Is the IP pointing just after a "syscall" opcode? + if (opcode != ARM64_SYSCALL_OPCODE) { + ERROR("StepWithCompactNoEncoding: not in syscall wrapper function\n"); + return false; + } + // Pop the return address from the stack + info->Context->Pc = info->Context->Lr; + TRACE("StepWithCompactNoEncoding: SUCCESS new pc %p sp %p\n", (void*)info->Context->Pc, (void*)info->Context->Sp); + return true; +} + +static bool ReadCompactEncodingRegister(const libunwindInfo* info, unw_word_t* addr, DWORD64* reg) { - *addr -= sizeof(uint64_t); - if (!ReadValue64(info, addr, (uint64_t*)reg)) { + uint64_t value; + if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) { return false; } + *reg = VAL64(value); + *addr -= sizeof(uint64_t); return true; } -inline static bool -ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, DWORD64*second, DWORD64* first) +static bool +ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, DWORD64* first, DWORD64* second) { // Registers are effectively pushed in pairs // + // *first = **addr // *addr -= 8 - // **addr = *first + // *second= **addr // *addr -= 8 - // **addr = *second if (!ReadCompactEncodingRegister(info, addr, first)) { return false; } @@ -1450,8 +1482,8 @@ ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, DWO return true; } -inline static bool -ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, NEON128*second, NEON128* first) +static bool +ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, NEON128* first, NEON128* second) { if (!ReadCompactEncodingRegisterPair(info, addr, &first->Low, &second->Low)) { return false; @@ -1484,30 +1516,28 @@ static bool StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, bool hasFrame) { CONTEXT* context = info->Context; + unw_word_t addr; - unw_word_t callerSp; - - if (hasFrame) { - // caller Sp is callee Fp plus saved FP and LR - callerSp = context->Fp + 2 * sizeof(uint64_t); - } else { + if (hasFrame) + { + context->Sp = context->Fp + 16; + addr = context->Fp + 8; + if (!ReadCompactEncodingRegisterPair(info, &addr, &context->Lr, &context->Fp)) { + return false; + } + // Strip pointer authentication bits + context->Lr &= MACOS_ARM64_POINTER_AUTH_MASK; + } + else + { // Get the leat significant bit in UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK uint64_t stackSizeScale = UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK & ~(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK - 1); - uint64_t stackSize = (compactEncoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK) / stackSizeScale * 16; + uint64_t stackSize = ((compactEncoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK) / stackSizeScale) * 16; - callerSp = context->Sp + stackSize; + addr = context->Sp + stackSize; } - context->Sp = callerSp; - - unw_word_t addr = callerSp; - - if (hasFrame && - !ReadCompactEncodingRegisterPair(info, &addr, &context->Lr, &context->Fp)) { - return false; - } - - // unwound return address is stored in Lr + // Unwound return address is stored in Lr context->Pc = context->Lr; if (compactEncoding & UNWIND_ARM64_FRAME_X19_X20_PAIR && @@ -1546,7 +1576,10 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_ !ReadCompactEncodingRegisterPair(info, &addr, &context->V[14], &context->V[15])) { return false; } - + if (!hasFrame) + { + context->Sp = addr; + } TRACE("SUCCESS: compact step encoding %08x pc %p sp %p fp %p lr %p\n", compactEncoding, (void*)context->Pc, (void*)context->Sp, (void*)context->Fp, (void*)context->Lr); return true; @@ -1557,11 +1590,11 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_ static bool StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, unw_word_t functionStart) { -#if defined(TARGET_AMD64) if (compactEncoding == 0) { return StepWithCompactNoEncoding(info); } +#if defined(TARGET_AMD64) switch (compactEncoding & UNWIND_X86_64_MODE_MASK) { case UNWIND_X86_64_MODE_RBP_FRAME: @@ -1575,11 +1608,6 @@ StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t com return false; } #elif defined(TARGET_ARM64) - if (compactEncoding == 0) - { - TRACE("Compact unwind missing for %p\n", (void*)info->Context->Pc); - return false; - } switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { case UNWIND_ARM64_MODE_FRAME: @@ -1717,6 +1745,12 @@ static void UnwindContextToContext(unw_cursor_t *cursor, CONTEXT *winContext) unw_get_reg(cursor, UNW_AARCH64_X28, (unw_word_t *) &winContext->X28); unw_get_reg(cursor, UNW_AARCH64_X29, (unw_word_t *) &winContext->Fp); unw_get_reg(cursor, UNW_AARCH64_X30, (unw_word_t *) &winContext->Lr); +#ifdef __APPLE__ + // Strip pointer authentication bits which seem to be leaking out of libunwind + // Seems like ptrauth_strip() / __builtin_ptrauth_strip() should work, but currently + // errors with "this target does not support pointer authentication" + winContext->Pc = winContext->Pc & MACOS_ARM64_POINTER_AUTH_MASK; +#endif // __APPLE__ TRACE("sp %p pc %p lr %p fp %p\n", winContext->Sp, winContext->Pc, winContext->Lr, winContext->Fp); #elif defined(TARGET_S390X) unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->PSWAddr); @@ -2126,6 +2160,33 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont #elif defined(TARGET_ARM64) TRACE("Unwind: pc %p sp %p fp %p\n", (void*)context->Pc, (void*)context->Sp, (void*)context->Fp); result = GetProcInfo(context->Pc, &procInfo, &info, &step, false); + if (result && step) + { + // If the PC is at the start of the function, the previous instruction is BL and the unwind encoding is frameless + // with nothing on stack (0x02000000), back up PC by 1 to the previous function and get the unwind info for that + // function. + if ((context->Pc == procInfo.start_ip) && + (procInfo.format & (UNWIND_ARM64_MODE_MASK | UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)) == UNWIND_ARM64_MODE_FRAMELESS) + { + uint32_t opcode; + unw_word_t addr = context->Pc - sizeof(opcode); + if (ReadValue32(&info, &addr, &opcode)) + { + // Is the previous instruction a BL opcode? + if ((opcode & ARM64_BL_OPCODE_MASK) == ARM64_BL_OPCODE || + (opcode & ARM64_BLR_OPCODE_MASK) == ARM64_BLR_OPCODE || + (opcode & ARM64_BLRA_OPCODE_MASK) == ARM64_BLRA_OPCODE) + { + TRACE("Unwind: getting unwind info for PC - 1 opcode %08x\n", opcode); + result = GetProcInfo(context->Pc - 1, &procInfo, &info, &step, false); + } + else + { + TRACE("Unwind: not BL* opcode %08x\n", opcode); + } + } + } + } #else #error Unexpected architecture #endif diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/debugger-test-with-full-debug-type.csproj b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/debugger-test-with-full-debug-type.csproj new file mode 100644 index 00000000000000..5eb1be9d8ea299 --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/debugger-test-with-full-debug-type.csproj @@ -0,0 +1,5 @@ + + + full + + \ No newline at end of file diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/test.cs b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/test.cs new file mode 100644 index 00000000000000..64609af24a547f --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/test.cs @@ -0,0 +1,20 @@ +using System; + +namespace DebuggerTests +{ + public class ClassToInspectWithDebugTypeFull + { + int a; + int b; + int c; + public ClassToInspectWithDebugTypeFull() + { + a = 10; + b = 20; + c = 30; + Console.WriteLine(a); + Console.WriteLine(b); + Console.WriteLine(c); + } + } +} \ No newline at end of file diff --git a/src/native/corehost/bundle/file_entry.cpp b/src/native/corehost/bundle/file_entry.cpp index ace0ef514927e6..a6610d7de97b92 100644 --- a/src/native/corehost/bundle/file_entry.cpp +++ b/src/native/corehost/bundle/file_entry.cpp @@ -19,15 +19,18 @@ file_entry_t file_entry_t::read(reader_t &reader, uint32_t bundle_major_version, // First read the fixed-sized portion of file-entry file_entry_fixed_t fixed_data; - fixed_data.offset = *(int64_t*)reader.read_direct(sizeof(int64_t)); - fixed_data.size = *(int64_t*)reader.read_direct(sizeof(int64_t)); + // NB: the file data is potentially unaligned, thus we use "read" to fetch 64bit values + reader.read(&fixed_data.offset, sizeof(int64_t)); + reader.read(&fixed_data.size, sizeof(int64_t)); // compressedSize is present only in v6+ headers - fixed_data.compressedSize = bundle_major_version >= 6 ? - *(int64_t*)reader.read_direct(sizeof(int64_t)) : - 0; + fixed_data.compressedSize = 0; + if (bundle_major_version >= 6) + { + reader.read(&fixed_data.compressedSize, sizeof(int64_t)); + } - fixed_data.type = *(file_type_t*)reader.read_direct(sizeof(file_type_t)); + fixed_data.type = (file_type_t)reader.read_byte(); file_entry_t entry(&fixed_data, force_extraction); diff --git a/src/native/corehost/bundle/header.cpp b/src/native/corehost/bundle/header.cpp index 0eb42d94f72182..bc549f4c7af8c7 100644 --- a/src/native/corehost/bundle/header.cpp +++ b/src/native/corehost/bundle/header.cpp @@ -24,23 +24,23 @@ bool header_fixed_t::is_valid() const header_t header_t::read(reader_t& reader) { - const header_fixed_t* fixed_header = reinterpret_cast(reader.read_direct(sizeof(header_fixed_t))); + header_fixed_t fixed_header; + reader.read(&fixed_header, sizeof(header_fixed_t)); - if (!fixed_header->is_valid()) + if (!fixed_header.is_valid()) { trace::error(_X("Failure processing application bundle.")); - trace::error(_X("Bundle header version compatibility check failed. Header version: %d.%d"), fixed_header->major_version, fixed_header->minor_version); + trace::error(_X("Bundle header version compatibility check failed. Header version: %d.%d"), fixed_header.major_version, fixed_header.minor_version); throw StatusCode::BundleExtractionFailure; } - header_t header(fixed_header->major_version, fixed_header->minor_version, fixed_header->num_embedded_files); + header_t header(fixed_header.major_version, fixed_header.minor_version, fixed_header.num_embedded_files); // bundle_id is a component of the extraction path reader.read_path_string(header.m_bundle_id); - const header_fixed_v2_t *v2_header = reinterpret_cast(reader.read_direct(sizeof(header_fixed_v2_t))); - header.m_v2_header = *v2_header; + reader.read(&header.m_v2_header, sizeof(header_fixed_v2_t)); return header; } diff --git a/src/native/corehost/bundle/reader.cpp b/src/native/corehost/bundle/reader.cpp index a7b5e580f0b562..8461e5db920be2 100644 --- a/src/native/corehost/bundle/reader.cpp +++ b/src/native/corehost/bundle/reader.cpp @@ -54,7 +54,7 @@ size_t reader_t::read_path_length() { size_t length = 0; - int8_t first_byte = read(); + int8_t first_byte = read_byte(); // If the high bit is set, it means there are more bytes to read. if ((first_byte & 0x80) == 0) @@ -63,7 +63,7 @@ size_t reader_t::read_path_length() } else { - int8_t second_byte = read(); + int8_t second_byte = read_byte(); if (second_byte & 0x80) { diff --git a/src/native/corehost/bundle/reader.h b/src/native/corehost/bundle/reader.h index 576ecd04c8e0fe..a4c1e07be19ece 100644 --- a/src/native/corehost/bundle/reader.h +++ b/src/native/corehost/bundle/reader.h @@ -81,7 +81,7 @@ namespace bundle return m_ptr; } - int8_t read() + int8_t read_byte() { bounds_check(); return *m_ptr++; @@ -100,16 +100,6 @@ namespace bundle m_ptr += len; } - // Return a pointer to the requested bytes within the memory-mapped file. - // Skip over len bytes. - const char* read_direct(int64_t len) - { - bounds_check(len); - const char *ptr = m_ptr; - m_ptr += len; - return ptr; - } - size_t read_path_length(); size_t read_path_string(pal::string_t &str);