@@ -57,6 +57,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57
57
#include < mach-o/nlist.h>
58
58
#include < mach-o/dyld_images.h>
59
59
#include " compact_unwind_encoding.h"
60
+ #define MACOS_ARM64_POINTER_AUTH_MASK 0x7fffffffffffull
60
61
#endif
61
62
62
63
// Sub-headers included from the libunwind.h contain an empty struct
@@ -1422,25 +1423,56 @@ StepWithCompactNoEncoding(const libunwindInfo* info)
1422
1423
1423
1424
#if defined(TARGET_ARM64)
1424
1425
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
1426
1456
ReadCompactEncodingRegister (const libunwindInfo* info, unw_word_t * addr, DWORD64* reg)
1427
1457
{
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) )) {
1430
1460
return false ;
1431
1461
}
1462
+ *reg = VAL64 (value);
1463
+ *addr -= sizeof (uint64_t );
1432
1464
return true ;
1433
1465
}
1434
1466
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 )
1437
1469
{
1438
1470
// Registers are effectively pushed in pairs
1439
1471
//
1472
+ // *first = **addr
1440
1473
// *addr -= 8
1441
- // **addr = *first
1474
+ // *second = **addr
1442
1475
// *addr -= 8
1443
- // **addr = *second
1444
1476
if (!ReadCompactEncodingRegister (info, addr, first)) {
1445
1477
return false ;
1446
1478
}
@@ -1450,8 +1482,8 @@ ReadCompactEncodingRegisterPair(const libunwindInfo* info, unw_word_t* addr, DWO
1450
1482
return true ;
1451
1483
}
1452
1484
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 )
1455
1487
{
1456
1488
if (!ReadCompactEncodingRegisterPair (info, addr, &first->Low , &second->Low )) {
1457
1489
return false ;
@@ -1484,30 +1516,28 @@ static bool
1484
1516
StepWithCompactEncodingArm64 (const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, bool hasFrame)
1485
1517
{
1486
1518
CONTEXT* context = info->Context ;
1519
+ unw_word_t addr;
1487
1520
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
+ {
1494
1533
// Get the leat significant bit in UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK
1495
1534
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 ;
1497
1536
1498
- callerSp = context->Sp + stackSize;
1537
+ addr = context->Sp + stackSize;
1499
1538
}
1500
1539
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
1511
1541
context->Pc = context->Lr ;
1512
1542
1513
1543
if (compactEncoding & UNWIND_ARM64_FRAME_X19_X20_PAIR &&
@@ -1546,7 +1576,10 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_
1546
1576
!ReadCompactEncodingRegisterPair (info, &addr, &context->V [14 ], &context->V [15 ])) {
1547
1577
return false ;
1548
1578
}
1549
-
1579
+ if (!hasFrame)
1580
+ {
1581
+ context->Sp = addr;
1582
+ }
1550
1583
TRACE (" SUCCESS: compact step encoding %08x pc %p sp %p fp %p lr %p\n " ,
1551
1584
compactEncoding, (void *)context->Pc , (void *)context->Sp , (void *)context->Fp , (void *)context->Lr );
1552
1585
return true ;
@@ -1557,11 +1590,11 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_
1557
1590
static bool
1558
1591
StepWithCompactEncoding (const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, unw_word_t functionStart)
1559
1592
{
1560
- #if defined(TARGET_AMD64)
1561
1593
if (compactEncoding == 0 )
1562
1594
{
1563
1595
return StepWithCompactNoEncoding (info);
1564
1596
}
1597
+ #if defined(TARGET_AMD64)
1565
1598
switch (compactEncoding & UNWIND_X86_64_MODE_MASK)
1566
1599
{
1567
1600
case UNWIND_X86_64_MODE_RBP_FRAME:
@@ -1575,11 +1608,6 @@ StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t com
1575
1608
return false ;
1576
1609
}
1577
1610
#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
- }
1583
1611
switch (compactEncoding & UNWIND_ARM64_MODE_MASK)
1584
1612
{
1585
1613
case UNWIND_ARM64_MODE_FRAME:
@@ -1717,6 +1745,12 @@ static void UnwindContextToContext(unw_cursor_t *cursor, CONTEXT *winContext)
1717
1745
unw_get_reg (cursor, UNW_AARCH64_X28, (unw_word_t *) &winContext->X28 );
1718
1746
unw_get_reg (cursor, UNW_AARCH64_X29, (unw_word_t *) &winContext->Fp );
1719
1747
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__
1720
1754
TRACE (" sp %p pc %p lr %p fp %p\n " , winContext->Sp , winContext->Pc , winContext->Lr , winContext->Fp );
1721
1755
#elif defined(TARGET_S390X)
1722
1756
unw_get_reg (cursor, UNW_REG_IP, (unw_word_t *) &winContext->PSWAddr );
@@ -2126,6 +2160,33 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont
2126
2160
#elif defined(TARGET_ARM64)
2127
2161
TRACE (" Unwind: pc %p sp %p fp %p\n " , (void *)context->Pc , (void *)context->Sp , (void *)context->Fp );
2128
2162
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
+ }
2129
2190
#else
2130
2191
#error Unexpected architecture
2131
2192
#endif
0 commit comments