@@ -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
14261456ReadCompactEncodingRegister (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
14841516StepWithCompactEncodingArm64 (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_
15571590static bool
15581591StepWithCompactEncoding (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