Skip to content

Commit 3c2d2a1

Browse files
author
Mike McLaughlin
authored
Fix the MacOS remote unwinder for VS4Mac (dotnet#63405)
* Fix the MacOS remote unwinder for VS4Mac The wrong module was being passed to the remote unwinder because the load bias for shared modules was being calculated incorrectly. Issue: dotnet#63309 * Fix native frame unwind in syscall on arm64 for VS4Mac crash report From PR in main: dotnet#63598 Add arm64 version of StepWithCompactNoEncoding for syscall leaf node wrappers that have compact encoding of 0. Fix ReadCompactEncodingRegister so it actually decrements the addr. Change StepWithCompactEncodingArm64 to match what MacOS libunwind does for framed and frameless stepping. arm64 can have frames with the same SP (but different IPs). Increment SP for this condition so createdump's unwind loop doesn't break out on the "SP not increasing" check and the frames are added to the thread frame list in the correct order. Add getting the unwind info for tail called functions like this: __ZL14PROCEndProcessPvji: 36630: f6 57 bd a9 stp x22, x21, [sp, #-48]! 36634: f4 4f 01 a9 stp x20, x19, [sp, dotnet#16] 36638: fd 7b 02 a9 stp x29, x30, [sp, dotnet#32] 3663c: fd 83 00 91 add x29, sp, dotnet#32 ... 367ac: e9 01 80 52 mov w9, dotnet#15 367b0: 7f 3e 02 71 cmp w19, dotnet#143 367b4: 20 01 88 1a csel w0, w9, w8, eq 367b8: 2e 00 00 94 bl _PROCAbort _TerminateProcess: -> 367bc: 22 00 80 52 mov w2, #1 367c0: 9c ff ff 17 b __ZL14PROCEndProcessPvji The IP (367bc) returns the (incorrect) frameless encoding with nothing on the stack (uses an incorrect LR to unwind). To fix this get the unwind info for PC -1 which points to PROCEndProcess with the correct unwind info. This matches how lldb unwinds this frame. Always address module segment to IP lookup list instead of checking the module regions. Strip pointer authentication bits on PC/LR.
1 parent fe7335c commit 3c2d2a1

File tree

5 files changed

+118
-41
lines changed

5 files changed

+118
-41
lines changed

src/coreclr/debug/createdump/crashinfomac.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm
277277
uint64_t start = segment.vmaddr + module.LoadBias();
278278
uint64_t end = start + segment.vmsize;
279279

280+
// Add this module segment to the set used by the thread unwinding to lookup the module base address for an ip.
281+
AddModuleAddressRange(start, end, module.BaseAddress());
282+
280283
// Round to page boundary
281284
start = start & PAGE_MASK;
282285
_ASSERTE(start > 0);
@@ -297,9 +300,6 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm
297300
}
298301
// Add this module segment to the module mappings list
299302
m_moduleMappings.insert(moduleRegion);
300-
301-
// Add this module segment to the set used by the thread unwinding to lookup the module base address for an ip.
302-
AddModuleAddressRange(start, end, module.BaseAddress());
303303
}
304304
else
305305
{

src/coreclr/debug/createdump/stackframe.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,16 @@ struct StackFrame
6666
}
6767
}
6868

69+
// See comment in threadinfo.cpp UnwindNativeFrames function
70+
#if defined(__aarch64__)
71+
#define STACK_POINTER_MASK ~0x7
72+
#else
73+
#define STACK_POINTER_MASK ~0x0
74+
#endif
75+
6976
inline uint64_t ModuleAddress() const { return m_moduleAddress; }
7077
inline uint64_t InstructionPointer() const { return m_instructionPointer; }
71-
inline uint64_t StackPointer() const { return m_stackPointer; }
78+
inline uint64_t StackPointer() const { return m_stackPointer & STACK_POINTER_MASK; }
7279
inline uint32_t NativeOffset() const { return m_nativeOffset; }
7380
inline uint32_t Token() const { return m_token; }
7481
inline uint32_t ILOffset() const { return m_ilOffset; }

src/coreclr/debug/createdump/threadinfo.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext)
5353
uint64_t ip = 0, sp = 0;
5454
GetFrameLocation(pContext, &ip, &sp);
5555

56+
#if defined(__aarch64__)
57+
// ARM64 can have frames with the same SP but different IPs. Increment sp so it gets added to the stack
58+
// frames in the correct order and to prevent the below loop termination on non-increasing sp. Since stack
59+
// pointers are always 8 byte align, this increase is masked off in StackFrame::StackPointer() to get the
60+
// original stack pointer.
61+
if (sp == previousSp && ip != previousIp)
62+
{
63+
sp++;
64+
}
65+
#endif
5666
if (ip == 0 || sp <= previousSp) {
5767
TRACE_VERBOSE("Unwind: sp not increasing or ip == 0 sp %p ip %p\n", (void*)sp, (void*)ip);
5868
break;

src/coreclr/debug/dbgutil/machoreader.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,8 @@ MachOModule::ReadLoadCommands()
229229
m_segments.push_back(segment);
230230

231231
// Calculate the load bias for the module. This is the value to add to the vmaddr of a
232-
// segment to get the actual address. For shared modules, this is 0 since those segments
233-
// are absolute address.
234-
if (segment->fileoff == 0 && segment->filesize > 0)
232+
// segment to get the actual address.
233+
if (strcmp(segment->segname, SEG_TEXT) == 0)
235234
{
236235
m_loadBias = m_baseAddress - segment->vmaddr;
237236
}

src/coreclr/pal/src/exception/remote-unwind.cpp

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5757
#include <mach-o/nlist.h>
5858
#include <mach-o/dyld_images.h>
5959
#include "compact_unwind_encoding.h"
60+
#define MACOS_ARM64_POINTER_AUTH_MASK 0x7fffffffffffull
6061
#endif
6162

6263
// Sub-headers included from the libunwind.h contain an empty struct
@@ -1422,25 +1423,56 @@ StepWithCompactNoEncoding(const libunwindInfo* info)
14221423

14231424
#if defined(TARGET_ARM64)
14241425

1425-
inline static bool
1426+
#define ARM64_SYSCALL_OPCODE 0xD4001001
1427+
#define ARM64_BL_OPCODE_MASK 0xFC000000
1428+
#define ARM64_BL_OPCODE 0x94000000
1429+
#define ARM64_BLR_OPCODE_MASK 0xFFFFFC00
1430+
#define ARM64_BLR_OPCODE 0xD63F0000
1431+
#define ARM64_BLRA_OPCODE_MASK 0xFEFFF800
1432+
#define ARM64_BLRA_OPCODE 0xD63F0800
1433+
1434+
static bool
1435+
StepWithCompactNoEncoding(const libunwindInfo* info)
1436+
{
1437+
// Check that the function is a syscall "wrapper" and assume there is no frame and pop the return address.
1438+
uint32_t opcode;
1439+
unw_word_t addr = info->Context->Pc - sizeof(opcode);
1440+
if (!ReadValue32(info, &addr, &opcode)) {
1441+
ERROR("StepWithCompactNoEncoding: can read opcode %p\n", (void*)addr);
1442+
return false;
1443+
}
1444+
// Is the IP pointing just after a "syscall" opcode?
1445+
if (opcode != ARM64_SYSCALL_OPCODE) {
1446+
ERROR("StepWithCompactNoEncoding: not in syscall wrapper function\n");
1447+
return false;
1448+
}
1449+
// Pop the return address from the stack
1450+
info->Context->Pc = info->Context->Lr;
1451+
TRACE("StepWithCompactNoEncoding: SUCCESS new pc %p sp %p\n", (void*)info->Context->Pc, (void*)info->Context->Sp);
1452+
return true;
1453+
}
1454+
1455+
static bool
14261456
ReadCompactEncodingRegister(const libunwindInfo* info, unw_word_t* addr, DWORD64* reg)
14271457
{
1428-
*addr -= sizeof(uint64_t);
1429-
if (!ReadValue64(info, addr, (uint64_t*)reg)) {
1458+
uint64_t value;
1459+
if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) {
14301460
return false;
14311461
}
1462+
*reg = VAL64(value);
1463+
*addr -= sizeof(uint64_t);
14321464
return true;
14331465
}
14341466

1435-
inline static bool
1436-
ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, DWORD64*second, DWORD64* first)
1467+
static bool
1468+
ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, DWORD64* first, DWORD64* second)
14371469
{
14381470
// Registers are effectively pushed in pairs
14391471
//
1472+
// *first = **addr
14401473
// *addr -= 8
1441-
// **addr = *first
1474+
// *second= **addr
14421475
// *addr -= 8
1443-
// **addr = *second
14441476
if (!ReadCompactEncodingRegister(info, addr, first)) {
14451477
return false;
14461478
}
@@ -1450,8 +1482,8 @@ ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, DWO
14501482
return true;
14511483
}
14521484

1453-
inline static bool
1454-
ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, NEON128*second, NEON128* first)
1485+
static bool
1486+
ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, NEON128* first, NEON128* second)
14551487
{
14561488
if (!ReadCompactEncodingRegisterPair(info, addr, &first->Low, &second->Low)) {
14571489
return false;
@@ -1484,30 +1516,28 @@ static bool
14841516
StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, bool hasFrame)
14851517
{
14861518
CONTEXT* context = info->Context;
1519+
unw_word_t addr;
14871520

1488-
unw_word_t callerSp;
1489-
1490-
if (hasFrame) {
1491-
// caller Sp is callee Fp plus saved FP and LR
1492-
callerSp = context->Fp + 2 * sizeof(uint64_t);
1493-
} else {
1521+
if (hasFrame)
1522+
{
1523+
context->Sp = context->Fp + 16;
1524+
addr = context->Fp + 8;
1525+
if (!ReadCompactEncodingRegisterPair(info, &addr, &context->Lr, &context->Fp)) {
1526+
return false;
1527+
}
1528+
// Strip pointer authentication bits
1529+
context->Lr &= MACOS_ARM64_POINTER_AUTH_MASK;
1530+
}
1531+
else
1532+
{
14941533
// Get the leat significant bit in UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
14951534
uint64_t stackSizeScale = UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK & ~(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK - 1);
1496-
uint64_t stackSize = (compactEncoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK) / stackSizeScale * 16;
1535+
uint64_t stackSize = ((compactEncoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK) / stackSizeScale) * 16;
14971536

1498-
callerSp = context->Sp + stackSize;
1537+
addr = context->Sp + stackSize;
14991538
}
15001539

1501-
context->Sp = callerSp;
1502-
1503-
unw_word_t addr = callerSp;
1504-
1505-
if (hasFrame &&
1506-
!ReadCompactEncodingRegisterPair(info, &addr, &context->Lr, &context->Fp)) {
1507-
return false;
1508-
}
1509-
1510-
// unwound return address is stored in Lr
1540+
// Unwound return address is stored in Lr
15111541
context->Pc = context->Lr;
15121542

15131543
if (compactEncoding & UNWIND_ARM64_FRAME_X19_X20_PAIR &&
@@ -1546,7 +1576,10 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_
15461576
!ReadCompactEncodingRegisterPair(info, &addr, &context->V[14], &context->V[15])) {
15471577
return false;
15481578
}
1549-
1579+
if (!hasFrame)
1580+
{
1581+
context->Sp = addr;
1582+
}
15501583
TRACE("SUCCESS: compact step encoding %08x pc %p sp %p fp %p lr %p\n",
15511584
compactEncoding, (void*)context->Pc, (void*)context->Sp, (void*)context->Fp, (void*)context->Lr);
15521585
return true;
@@ -1557,11 +1590,11 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_
15571590
static bool
15581591
StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, unw_word_t functionStart)
15591592
{
1560-
#if defined(TARGET_AMD64)
15611593
if (compactEncoding == 0)
15621594
{
15631595
return StepWithCompactNoEncoding(info);
15641596
}
1597+
#if defined(TARGET_AMD64)
15651598
switch (compactEncoding & UNWIND_X86_64_MODE_MASK)
15661599
{
15671600
case UNWIND_X86_64_MODE_RBP_FRAME:
@@ -1575,11 +1608,6 @@ StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t com
15751608
return false;
15761609
}
15771610
#elif defined(TARGET_ARM64)
1578-
if (compactEncoding == 0)
1579-
{
1580-
TRACE("Compact unwind missing for %p\n", (void*)info->Context->Pc);
1581-
return false;
1582-
}
15831611
switch (compactEncoding & UNWIND_ARM64_MODE_MASK)
15841612
{
15851613
case UNWIND_ARM64_MODE_FRAME:
@@ -1717,6 +1745,12 @@ static void UnwindContextToContext(unw_cursor_t *cursor, CONTEXT *winContext)
17171745
unw_get_reg(cursor, UNW_AARCH64_X28, (unw_word_t *) &winContext->X28);
17181746
unw_get_reg(cursor, UNW_AARCH64_X29, (unw_word_t *) &winContext->Fp);
17191747
unw_get_reg(cursor, UNW_AARCH64_X30, (unw_word_t *) &winContext->Lr);
1748+
#ifdef __APPLE__
1749+
// Strip pointer authentication bits which seem to be leaking out of libunwind
1750+
// Seems like ptrauth_strip() / __builtin_ptrauth_strip() should work, but currently
1751+
// errors with "this target does not support pointer authentication"
1752+
winContext->Pc = winContext->Pc & MACOS_ARM64_POINTER_AUTH_MASK;
1753+
#endif // __APPLE__
17201754
TRACE("sp %p pc %p lr %p fp %p\n", winContext->Sp, winContext->Pc, winContext->Lr, winContext->Fp);
17211755
#elif defined(TARGET_S390X)
17221756
unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->PSWAddr);
@@ -2126,6 +2160,33 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont
21262160
#elif defined(TARGET_ARM64)
21272161
TRACE("Unwind: pc %p sp %p fp %p\n", (void*)context->Pc, (void*)context->Sp, (void*)context->Fp);
21282162
result = GetProcInfo(context->Pc, &procInfo, &info, &step, false);
2163+
if (result && step)
2164+
{
2165+
// If the PC is at the start of the function, the previous instruction is BL and the unwind encoding is frameless
2166+
// with nothing on stack (0x02000000), back up PC by 1 to the previous function and get the unwind info for that
2167+
// function.
2168+
if ((context->Pc == procInfo.start_ip) &&
2169+
(procInfo.format & (UNWIND_ARM64_MODE_MASK | UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)) == UNWIND_ARM64_MODE_FRAMELESS)
2170+
{
2171+
uint32_t opcode;
2172+
unw_word_t addr = context->Pc - sizeof(opcode);
2173+
if (ReadValue32(&info, &addr, &opcode))
2174+
{
2175+
// Is the previous instruction a BL opcode?
2176+
if ((opcode & ARM64_BL_OPCODE_MASK) == ARM64_BL_OPCODE ||
2177+
(opcode & ARM64_BLR_OPCODE_MASK) == ARM64_BLR_OPCODE ||
2178+
(opcode & ARM64_BLRA_OPCODE_MASK) == ARM64_BLRA_OPCODE)
2179+
{
2180+
TRACE("Unwind: getting unwind info for PC - 1 opcode %08x\n", opcode);
2181+
result = GetProcInfo(context->Pc - 1, &procInfo, &info, &step, false);
2182+
}
2183+
else
2184+
{
2185+
TRACE("Unwind: not BL* opcode %08x\n", opcode);
2186+
}
2187+
}
2188+
}
2189+
}
21292190
#else
21302191
#error Unexpected architecture
21312192
#endif

0 commit comments

Comments
 (0)