Home | History | Annotate | Download | only in src
      1 //===-------------------------- CompactUnwinder.hpp -----------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is dual licensed under the MIT and the University of Illinois Open
      6 // Source Licenses. See LICENSE.TXT for details.
      7 //
      8 //
      9 //  Does runtime stack unwinding using compact unwind encodings.
     10 //
     11 //===----------------------------------------------------------------------===//
     12 
     13 #ifndef __COMPACT_UNWINDER_HPP__
     14 #define __COMPACT_UNWINDER_HPP__
     15 
     16 #include <stdint.h>
     17 #include <stdlib.h>
     18 
     19 #include <libunwind.h>
     20 #include <mach-o/compact_unwind_encoding.h>
     21 
     22 #include "Registers.hpp"
     23 
     24 #define EXTRACT_BITS(value, mask)                                              \
     25   ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
     26 
     27 namespace libunwind {
     28 
     29 #if defined(_LIBUNWIND_TARGET_I386)
     30 /// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
     31 /// unwind) by modifying a Registers_x86 register set
     32 template <typename A>
     33 class CompactUnwinder_x86 {
     34 public:
     35 
     36   static int stepWithCompactEncoding(compact_unwind_encoding_t info,
     37                                      uint32_t functionStart, A &addressSpace,
     38                                      Registers_x86 &registers);
     39 
     40 private:
     41   typename A::pint_t pint_t;
     42 
     43   static void frameUnwind(A &addressSpace, Registers_x86 &registers);
     44   static void framelessUnwind(A &addressSpace,
     45                               typename A::pint_t returnAddressLocation,
     46                               Registers_x86 &registers);
     47   static int
     48       stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
     49                                       uint32_t functionStart, A &addressSpace,
     50                                       Registers_x86 &registers);
     51   static int stepWithCompactEncodingFrameless(
     52       compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
     53       A &addressSpace, Registers_x86 &registers, bool indirectStackSize);
     54 };
     55 
     56 template <typename A>
     57 int CompactUnwinder_x86<A>::stepWithCompactEncoding(
     58     compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
     59     A &addressSpace, Registers_x86 &registers) {
     60   switch (compactEncoding & UNWIND_X86_MODE_MASK) {
     61   case UNWIND_X86_MODE_EBP_FRAME:
     62     return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart,
     63                                            addressSpace, registers);
     64   case UNWIND_X86_MODE_STACK_IMMD:
     65     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
     66                                             addressSpace, registers, false);
     67   case UNWIND_X86_MODE_STACK_IND:
     68     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
     69                                             addressSpace, registers, true);
     70   }
     71   _LIBUNWIND_ABORT("invalid compact unwind encoding");
     72 }
     73 
     74 template <typename A>
     75 int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(
     76     compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
     77     A &addressSpace, Registers_x86 &registers) {
     78   uint32_t savedRegistersOffset =
     79       EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
     80   uint32_t savedRegistersLocations =
     81       EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
     82 
     83   uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset;
     84   for (int i = 0; i < 5; ++i) {
     85     switch (savedRegistersLocations & 0x7) {
     86     case UNWIND_X86_REG_NONE:
     87       // no register saved in this slot
     88       break;
     89     case UNWIND_X86_REG_EBX:
     90       registers.setEBX(addressSpace.get32(savedRegisters));
     91       break;
     92     case UNWIND_X86_REG_ECX:
     93       registers.setECX(addressSpace.get32(savedRegisters));
     94       break;
     95     case UNWIND_X86_REG_EDX:
     96       registers.setEDX(addressSpace.get32(savedRegisters));
     97       break;
     98     case UNWIND_X86_REG_EDI:
     99       registers.setEDI(addressSpace.get32(savedRegisters));
    100       break;
    101     case UNWIND_X86_REG_ESI:
    102       registers.setESI(addressSpace.get32(savedRegisters));
    103       break;
    104     default:
    105       (void)functionStart;
    106       _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for  "
    107                            "function starting at 0x%X",
    108                             compactEncoding, functionStart);
    109       _LIBUNWIND_ABORT("invalid compact unwind encoding");
    110     }
    111     savedRegisters += 4;
    112     savedRegistersLocations = (savedRegistersLocations >> 3);
    113   }
    114   frameUnwind(addressSpace, registers);
    115   return UNW_STEP_SUCCESS;
    116 }
    117 
    118 template <typename A>
    119 int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(
    120     compact_unwind_encoding_t encoding, uint32_t functionStart,
    121     A &addressSpace, Registers_x86 &registers, bool indirectStackSize) {
    122   uint32_t stackSizeEncoded =
    123       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
    124   uint32_t stackAdjust =
    125       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
    126   uint32_t regCount =
    127       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
    128   uint32_t permutation =
    129       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
    130   uint32_t stackSize = stackSizeEncoded * 4;
    131   if (indirectStackSize) {
    132     // stack size is encoded in subl $xxx,%esp instruction
    133     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
    134     stackSize = subl + 4 * stackAdjust;
    135   }
    136   // decompress permutation
    137   uint32_t permunreg[6];
    138   switch (regCount) {
    139   case 6:
    140     permunreg[0] = permutation / 120;
    141     permutation -= (permunreg[0] * 120);
    142     permunreg[1] = permutation / 24;
    143     permutation -= (permunreg[1] * 24);
    144     permunreg[2] = permutation / 6;
    145     permutation -= (permunreg[2] * 6);
    146     permunreg[3] = permutation / 2;
    147     permutation -= (permunreg[3] * 2);
    148     permunreg[4] = permutation;
    149     permunreg[5] = 0;
    150     break;
    151   case 5:
    152     permunreg[0] = permutation / 120;
    153     permutation -= (permunreg[0] * 120);
    154     permunreg[1] = permutation / 24;
    155     permutation -= (permunreg[1] * 24);
    156     permunreg[2] = permutation / 6;
    157     permutation -= (permunreg[2] * 6);
    158     permunreg[3] = permutation / 2;
    159     permutation -= (permunreg[3] * 2);
    160     permunreg[4] = permutation;
    161     break;
    162   case 4:
    163     permunreg[0] = permutation / 60;
    164     permutation -= (permunreg[0] * 60);
    165     permunreg[1] = permutation / 12;
    166     permutation -= (permunreg[1] * 12);
    167     permunreg[2] = permutation / 3;
    168     permutation -= (permunreg[2] * 3);
    169     permunreg[3] = permutation;
    170     break;
    171   case 3:
    172     permunreg[0] = permutation / 20;
    173     permutation -= (permunreg[0] * 20);
    174     permunreg[1] = permutation / 4;
    175     permutation -= (permunreg[1] * 4);
    176     permunreg[2] = permutation;
    177     break;
    178   case 2:
    179     permunreg[0] = permutation / 5;
    180     permutation -= (permunreg[0] * 5);
    181     permunreg[1] = permutation;
    182     break;
    183   case 1:
    184     permunreg[0] = permutation;
    185     break;
    186   }
    187   // re-number registers back to standard numbers
    188   int registersSaved[6];
    189   bool used[7] = { false, false, false, false, false, false, false };
    190   for (uint32_t i = 0; i < regCount; ++i) {
    191     uint32_t renum = 0;
    192     for (int u = 1; u < 7; ++u) {
    193       if (!used[u]) {
    194         if (renum == permunreg[i]) {
    195           registersSaved[i] = u;
    196           used[u] = true;
    197           break;
    198         }
    199         ++renum;
    200       }
    201     }
    202   }
    203   uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount;
    204   for (uint32_t i = 0; i < regCount; ++i) {
    205     switch (registersSaved[i]) {
    206     case UNWIND_X86_REG_EBX:
    207       registers.setEBX(addressSpace.get32(savedRegisters));
    208       break;
    209     case UNWIND_X86_REG_ECX:
    210       registers.setECX(addressSpace.get32(savedRegisters));
    211       break;
    212     case UNWIND_X86_REG_EDX:
    213       registers.setEDX(addressSpace.get32(savedRegisters));
    214       break;
    215     case UNWIND_X86_REG_EDI:
    216       registers.setEDI(addressSpace.get32(savedRegisters));
    217       break;
    218     case UNWIND_X86_REG_ESI:
    219       registers.setESI(addressSpace.get32(savedRegisters));
    220       break;
    221     case UNWIND_X86_REG_EBP:
    222       registers.setEBP(addressSpace.get32(savedRegisters));
    223       break;
    224     default:
    225       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
    226                            "function starting at 0x%X",
    227                            encoding, functionStart);
    228       _LIBUNWIND_ABORT("invalid compact unwind encoding");
    229     }
    230     savedRegisters += 4;
    231   }
    232   framelessUnwind(addressSpace, savedRegisters, registers);
    233   return UNW_STEP_SUCCESS;
    234 }
    235 
    236 
    237 template <typename A>
    238 void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace,
    239                                          Registers_x86 &registers) {
    240   typename A::pint_t bp = registers.getEBP();
    241   // ebp points to old ebp
    242   registers.setEBP(addressSpace.get32(bp));
    243   // old esp is ebp less saved ebp and return address
    244   registers.setSP((uint32_t)bp + 8);
    245   // pop return address into eip
    246   registers.setIP(addressSpace.get32(bp + 4));
    247 }
    248 
    249 template <typename A>
    250 void CompactUnwinder_x86<A>::framelessUnwind(
    251     A &addressSpace, typename A::pint_t returnAddressLocation,
    252     Registers_x86 &registers) {
    253   // return address is on stack after last saved register
    254   registers.setIP(addressSpace.get32(returnAddressLocation));
    255   // old esp is before return address
    256   registers.setSP((uint32_t)returnAddressLocation + 4);
    257 }
    258 #endif // _LIBUNWIND_TARGET_I386
    259 
    260 
    261 #if defined(_LIBUNWIND_TARGET_X86_64)
    262 /// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
    263 /// unwind) by modifying a Registers_x86_64 register set
    264 template <typename A>
    265 class CompactUnwinder_x86_64 {
    266 public:
    267 
    268   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
    269                                      uint64_t functionStart, A &addressSpace,
    270                                      Registers_x86_64 &registers);
    271 
    272 private:
    273   typename A::pint_t pint_t;
    274 
    275   static void frameUnwind(A &addressSpace, Registers_x86_64 &registers);
    276   static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
    277                               Registers_x86_64 &registers);
    278   static int
    279       stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
    280                                       uint64_t functionStart, A &addressSpace,
    281                                       Registers_x86_64 &registers);
    282   static int stepWithCompactEncodingFrameless(
    283       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    284       A &addressSpace, Registers_x86_64 &registers, bool indirectStackSize);
    285 };
    286 
    287 template <typename A>
    288 int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
    289     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    290     A &addressSpace, Registers_x86_64 &registers) {
    291   switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
    292   case UNWIND_X86_64_MODE_RBP_FRAME:
    293     return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
    294                                            addressSpace, registers);
    295   case UNWIND_X86_64_MODE_STACK_IMMD:
    296     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
    297                                             addressSpace, registers, false);
    298   case UNWIND_X86_64_MODE_STACK_IND:
    299     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
    300                                             addressSpace, registers, true);
    301   }
    302   _LIBUNWIND_ABORT("invalid compact unwind encoding");
    303 }
    304 
    305 template <typename A>
    306 int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
    307     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    308     A &addressSpace, Registers_x86_64 &registers) {
    309   uint32_t savedRegistersOffset =
    310       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
    311   uint32_t savedRegistersLocations =
    312       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
    313 
    314   uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
    315   for (int i = 0; i < 5; ++i) {
    316     switch (savedRegistersLocations & 0x7) {
    317     case UNWIND_X86_64_REG_NONE:
    318       // no register saved in this slot
    319       break;
    320     case UNWIND_X86_64_REG_RBX:
    321       registers.setRBX(addressSpace.get64(savedRegisters));
    322       break;
    323     case UNWIND_X86_64_REG_R12:
    324       registers.setR12(addressSpace.get64(savedRegisters));
    325       break;
    326     case UNWIND_X86_64_REG_R13:
    327       registers.setR13(addressSpace.get64(savedRegisters));
    328       break;
    329     case UNWIND_X86_64_REG_R14:
    330       registers.setR14(addressSpace.get64(savedRegisters));
    331       break;
    332     case UNWIND_X86_64_REG_R15:
    333       registers.setR15(addressSpace.get64(savedRegisters));
    334       break;
    335     default:
    336       (void)functionStart;
    337       _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
    338                            "function starting at 0x%llX",
    339                             compactEncoding, functionStart);
    340       _LIBUNWIND_ABORT("invalid compact unwind encoding");
    341     }
    342     savedRegisters += 8;
    343     savedRegistersLocations = (savedRegistersLocations >> 3);
    344   }
    345   frameUnwind(addressSpace, registers);
    346   return UNW_STEP_SUCCESS;
    347 }
    348 
    349 template <typename A>
    350 int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
    351     compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
    352     Registers_x86_64 &registers, bool indirectStackSize) {
    353   uint32_t stackSizeEncoded =
    354       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
    355   uint32_t stackAdjust =
    356       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
    357   uint32_t regCount =
    358       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
    359   uint32_t permutation =
    360       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
    361   uint32_t stackSize = stackSizeEncoded * 8;
    362   if (indirectStackSize) {
    363     // stack size is encoded in subl $xxx,%esp instruction
    364     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
    365     stackSize = subl + 8 * stackAdjust;
    366   }
    367   // decompress permutation
    368   uint32_t permunreg[6];
    369   switch (regCount) {
    370   case 6:
    371     permunreg[0] = permutation / 120;
    372     permutation -= (permunreg[0] * 120);
    373     permunreg[1] = permutation / 24;
    374     permutation -= (permunreg[1] * 24);
    375     permunreg[2] = permutation / 6;
    376     permutation -= (permunreg[2] * 6);
    377     permunreg[3] = permutation / 2;
    378     permutation -= (permunreg[3] * 2);
    379     permunreg[4] = permutation;
    380     permunreg[5] = 0;
    381     break;
    382   case 5:
    383     permunreg[0] = permutation / 120;
    384     permutation -= (permunreg[0] * 120);
    385     permunreg[1] = permutation / 24;
    386     permutation -= (permunreg[1] * 24);
    387     permunreg[2] = permutation / 6;
    388     permutation -= (permunreg[2] * 6);
    389     permunreg[3] = permutation / 2;
    390     permutation -= (permunreg[3] * 2);
    391     permunreg[4] = permutation;
    392     break;
    393   case 4:
    394     permunreg[0] = permutation / 60;
    395     permutation -= (permunreg[0] * 60);
    396     permunreg[1] = permutation / 12;
    397     permutation -= (permunreg[1] * 12);
    398     permunreg[2] = permutation / 3;
    399     permutation -= (permunreg[2] * 3);
    400     permunreg[3] = permutation;
    401     break;
    402   case 3:
    403     permunreg[0] = permutation / 20;
    404     permutation -= (permunreg[0] * 20);
    405     permunreg[1] = permutation / 4;
    406     permutation -= (permunreg[1] * 4);
    407     permunreg[2] = permutation;
    408     break;
    409   case 2:
    410     permunreg[0] = permutation / 5;
    411     permutation -= (permunreg[0] * 5);
    412     permunreg[1] = permutation;
    413     break;
    414   case 1:
    415     permunreg[0] = permutation;
    416     break;
    417   }
    418   // re-number registers back to standard numbers
    419   int registersSaved[6];
    420   bool used[7] = { false, false, false, false, false, false, false };
    421   for (uint32_t i = 0; i < regCount; ++i) {
    422     uint32_t renum = 0;
    423     for (int u = 1; u < 7; ++u) {
    424       if (!used[u]) {
    425         if (renum == permunreg[i]) {
    426           registersSaved[i] = u;
    427           used[u] = true;
    428           break;
    429         }
    430         ++renum;
    431       }
    432     }
    433   }
    434   uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
    435   for (uint32_t i = 0; i < regCount; ++i) {
    436     switch (registersSaved[i]) {
    437     case UNWIND_X86_64_REG_RBX:
    438       registers.setRBX(addressSpace.get64(savedRegisters));
    439       break;
    440     case UNWIND_X86_64_REG_R12:
    441       registers.setR12(addressSpace.get64(savedRegisters));
    442       break;
    443     case UNWIND_X86_64_REG_R13:
    444       registers.setR13(addressSpace.get64(savedRegisters));
    445       break;
    446     case UNWIND_X86_64_REG_R14:
    447       registers.setR14(addressSpace.get64(savedRegisters));
    448       break;
    449     case UNWIND_X86_64_REG_R15:
    450       registers.setR15(addressSpace.get64(savedRegisters));
    451       break;
    452     case UNWIND_X86_64_REG_RBP:
    453       registers.setRBP(addressSpace.get64(savedRegisters));
    454       break;
    455     default:
    456       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
    457                            "function starting at 0x%llX",
    458                             encoding, functionStart);
    459       _LIBUNWIND_ABORT("invalid compact unwind encoding");
    460     }
    461     savedRegisters += 8;
    462   }
    463   framelessUnwind(addressSpace, savedRegisters, registers);
    464   return UNW_STEP_SUCCESS;
    465 }
    466 
    467 
    468 template <typename A>
    469 void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
    470                                             Registers_x86_64 &registers) {
    471   uint64_t rbp = registers.getRBP();
    472   // ebp points to old ebp
    473   registers.setRBP(addressSpace.get64(rbp));
    474   // old esp is ebp less saved ebp and return address
    475   registers.setSP(rbp + 16);
    476   // pop return address into eip
    477   registers.setIP(addressSpace.get64(rbp + 8));
    478 }
    479 
    480 template <typename A>
    481 void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
    482                                                 uint64_t returnAddressLocation,
    483                                                 Registers_x86_64 &registers) {
    484   // return address is on stack after last saved register
    485   registers.setIP(addressSpace.get64(returnAddressLocation));
    486   // old esp is before return address
    487   registers.setSP(returnAddressLocation + 8);
    488 }
    489 #endif // _LIBUNWIND_TARGET_X86_64
    490 
    491 
    492 
    493 #if defined(_LIBUNWIND_TARGET_AARCH64)
    494 /// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
    495 /// unwind) by modifying a Registers_arm64 register set
    496 template <typename A>
    497 class CompactUnwinder_arm64 {
    498 public:
    499 
    500   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
    501                                      uint64_t functionStart, A &addressSpace,
    502                                      Registers_arm64 &registers);
    503 
    504 private:
    505   typename A::pint_t pint_t;
    506 
    507   static int
    508       stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
    509                                    uint64_t functionStart, A &addressSpace,
    510                                    Registers_arm64 &registers);
    511   static int stepWithCompactEncodingFrameless(
    512       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    513       A &addressSpace, Registers_arm64 &registers);
    514 };
    515 
    516 template <typename A>
    517 int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
    518     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    519     A &addressSpace, Registers_arm64 &registers) {
    520   switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
    521   case UNWIND_ARM64_MODE_FRAME:
    522     return stepWithCompactEncodingFrame(compactEncoding, functionStart,
    523                                         addressSpace, registers);
    524   case UNWIND_ARM64_MODE_FRAMELESS:
    525     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
    526                                             addressSpace, registers);
    527   }
    528   _LIBUNWIND_ABORT("invalid compact unwind encoding");
    529 }
    530 
    531 template <typename A>
    532 int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
    533     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
    534     Registers_arm64 &registers) {
    535   uint32_t stackSize =
    536       16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
    537 
    538   uint64_t savedRegisterLoc = registers.getSP() + stackSize;
    539 
    540   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
    541     registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
    542     savedRegisterLoc -= 8;
    543     registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
    544     savedRegisterLoc -= 8;
    545   }
    546   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
    547     registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
    548     savedRegisterLoc -= 8;
    549     registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
    550     savedRegisterLoc -= 8;
    551   }
    552   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
    553     registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
    554     savedRegisterLoc -= 8;
    555     registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
    556     savedRegisterLoc -= 8;
    557   }
    558   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
    559     registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
    560     savedRegisterLoc -= 8;
    561     registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
    562     savedRegisterLoc -= 8;
    563   }
    564   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
    565     registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
    566     savedRegisterLoc -= 8;
    567     registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
    568     savedRegisterLoc -= 8;
    569   }
    570 
    571   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
    572     registers.setFloatRegister(UNW_ARM64_D8,
    573                                addressSpace.getDouble(savedRegisterLoc));
    574     savedRegisterLoc -= 8;
    575     registers.setFloatRegister(UNW_ARM64_D9,
    576                                addressSpace.getDouble(savedRegisterLoc));
    577     savedRegisterLoc -= 8;
    578   }
    579   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
    580     registers.setFloatRegister(UNW_ARM64_D10,
    581                                addressSpace.getDouble(savedRegisterLoc));
    582     savedRegisterLoc -= 8;
    583     registers.setFloatRegister(UNW_ARM64_D11,
    584                                addressSpace.getDouble(savedRegisterLoc));
    585     savedRegisterLoc -= 8;
    586   }
    587   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
    588     registers.setFloatRegister(UNW_ARM64_D12,
    589                                addressSpace.getDouble(savedRegisterLoc));
    590     savedRegisterLoc -= 8;
    591     registers.setFloatRegister(UNW_ARM64_D13,
    592                                addressSpace.getDouble(savedRegisterLoc));
    593     savedRegisterLoc -= 8;
    594   }
    595   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
    596     registers.setFloatRegister(UNW_ARM64_D14,
    597                                addressSpace.getDouble(savedRegisterLoc));
    598     savedRegisterLoc -= 8;
    599     registers.setFloatRegister(UNW_ARM64_D15,
    600                                addressSpace.getDouble(savedRegisterLoc));
    601     savedRegisterLoc -= 8;
    602   }
    603 
    604   // subtract stack size off of sp
    605   registers.setSP(savedRegisterLoc);
    606 
    607   // set pc to be value in lr
    608   registers.setIP(registers.getRegister(UNW_ARM64_LR));
    609 
    610   return UNW_STEP_SUCCESS;
    611 }
    612 
    613 template <typename A>
    614 int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
    615     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
    616     Registers_arm64 &registers) {
    617   uint64_t savedRegisterLoc = registers.getFP() - 8;
    618 
    619   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
    620     registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
    621     savedRegisterLoc -= 8;
    622     registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
    623     savedRegisterLoc -= 8;
    624   }
    625   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
    626     registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
    627     savedRegisterLoc -= 8;
    628     registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
    629     savedRegisterLoc -= 8;
    630   }
    631   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
    632     registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
    633     savedRegisterLoc -= 8;
    634     registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
    635     savedRegisterLoc -= 8;
    636   }
    637   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
    638     registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
    639     savedRegisterLoc -= 8;
    640     registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
    641     savedRegisterLoc -= 8;
    642   }
    643   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
    644     registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
    645     savedRegisterLoc -= 8;
    646     registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
    647     savedRegisterLoc -= 8;
    648   }
    649 
    650   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
    651     registers.setFloatRegister(UNW_ARM64_D8,
    652                                addressSpace.getDouble(savedRegisterLoc));
    653     savedRegisterLoc -= 8;
    654     registers.setFloatRegister(UNW_ARM64_D9,
    655                                addressSpace.getDouble(savedRegisterLoc));
    656     savedRegisterLoc -= 8;
    657   }
    658   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
    659     registers.setFloatRegister(UNW_ARM64_D10,
    660                                addressSpace.getDouble(savedRegisterLoc));
    661     savedRegisterLoc -= 8;
    662     registers.setFloatRegister(UNW_ARM64_D11,
    663                                addressSpace.getDouble(savedRegisterLoc));
    664     savedRegisterLoc -= 8;
    665   }
    666   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
    667     registers.setFloatRegister(UNW_ARM64_D12,
    668                                addressSpace.getDouble(savedRegisterLoc));
    669     savedRegisterLoc -= 8;
    670     registers.setFloatRegister(UNW_ARM64_D13,
    671                                addressSpace.getDouble(savedRegisterLoc));
    672     savedRegisterLoc -= 8;
    673   }
    674   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
    675     registers.setFloatRegister(UNW_ARM64_D14,
    676                                addressSpace.getDouble(savedRegisterLoc));
    677     savedRegisterLoc -= 8;
    678     registers.setFloatRegister(UNW_ARM64_D15,
    679                                addressSpace.getDouble(savedRegisterLoc));
    680     savedRegisterLoc -= 8;
    681   }
    682 
    683   uint64_t fp = registers.getFP();
    684   // fp points to old fp
    685   registers.setFP(addressSpace.get64(fp));
    686   // old sp is fp less saved fp and lr
    687   registers.setSP(fp + 16);
    688   // pop return address into pc
    689   registers.setIP(addressSpace.get64(fp + 8));
    690 
    691   return UNW_STEP_SUCCESS;
    692 }
    693 #endif // _LIBUNWIND_TARGET_AARCH64
    694 
    695 
    696 } // namespace libunwind
    697 
    698 #endif // __COMPACT_UNWINDER_HPP__
    699