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 "AddressSpace.hpp"
     23 #include "Registers.hpp"
     24 
     25 #define EXTRACT_BITS(value, mask)                                              \
     26   ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
     27 
     28 namespace libunwind {
     29 
     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\n",
    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\n",
    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 
    259 
    260 /// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
    261 /// unwind) by modifying a Registers_x86_64 register set
    262 template <typename A>
    263 class CompactUnwinder_x86_64 {
    264 public:
    265 
    266   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
    267                                      uint64_t functionStart, A &addressSpace,
    268                                      Registers_x86_64 &registers);
    269 
    270 private:
    271   typename A::pint_t pint_t;
    272 
    273   static void frameUnwind(A &addressSpace, Registers_x86_64 &registers);
    274   static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
    275                               Registers_x86_64 &registers);
    276   static int
    277       stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
    278                                       uint64_t functionStart, A &addressSpace,
    279                                       Registers_x86_64 &registers);
    280   static int stepWithCompactEncodingFrameless(
    281       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    282       A &addressSpace, Registers_x86_64 &registers, bool indirectStackSize);
    283 };
    284 
    285 template <typename A>
    286 int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
    287     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    288     A &addressSpace, Registers_x86_64 &registers) {
    289   switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
    290   case UNWIND_X86_64_MODE_RBP_FRAME:
    291     return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
    292                                            addressSpace, registers);
    293   case UNWIND_X86_64_MODE_STACK_IMMD:
    294     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
    295                                             addressSpace, registers, false);
    296   case UNWIND_X86_64_MODE_STACK_IND:
    297     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
    298                                             addressSpace, registers, true);
    299   }
    300   _LIBUNWIND_ABORT("invalid compact unwind encoding");
    301 }
    302 
    303 template <typename A>
    304 int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
    305     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    306     A &addressSpace, Registers_x86_64 &registers) {
    307   uint32_t savedRegistersOffset =
    308       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
    309   uint32_t savedRegistersLocations =
    310       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
    311 
    312   uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
    313   for (int i = 0; i < 5; ++i) {
    314     switch (savedRegistersLocations & 0x7) {
    315     case UNWIND_X86_64_REG_NONE:
    316       // no register saved in this slot
    317       break;
    318     case UNWIND_X86_64_REG_RBX:
    319       registers.setRBX(addressSpace.get64(savedRegisters));
    320       break;
    321     case UNWIND_X86_64_REG_R12:
    322       registers.setR12(addressSpace.get64(savedRegisters));
    323       break;
    324     case UNWIND_X86_64_REG_R13:
    325       registers.setR13(addressSpace.get64(savedRegisters));
    326       break;
    327     case UNWIND_X86_64_REG_R14:
    328       registers.setR14(addressSpace.get64(savedRegisters));
    329       break;
    330     case UNWIND_X86_64_REG_R15:
    331       registers.setR15(addressSpace.get64(savedRegisters));
    332       break;
    333     default:
    334       (void)functionStart;
    335       _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
    336                            "function starting at 0x%llX\n",
    337                             compactEncoding, functionStart);
    338       _LIBUNWIND_ABORT("invalid compact unwind encoding");
    339     }
    340     savedRegisters += 8;
    341     savedRegistersLocations = (savedRegistersLocations >> 3);
    342   }
    343   frameUnwind(addressSpace, registers);
    344   return UNW_STEP_SUCCESS;
    345 }
    346 
    347 template <typename A>
    348 int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
    349     compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
    350     Registers_x86_64 &registers, bool indirectStackSize) {
    351   uint32_t stackSizeEncoded =
    352       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
    353   uint32_t stackAdjust =
    354       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
    355   uint32_t regCount =
    356       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
    357   uint32_t permutation =
    358       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
    359   uint32_t stackSize = stackSizeEncoded * 8;
    360   if (indirectStackSize) {
    361     // stack size is encoded in subl $xxx,%esp instruction
    362     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
    363     stackSize = subl + 8 * stackAdjust;
    364   }
    365   // decompress permutation
    366   uint32_t permunreg[6];
    367   switch (regCount) {
    368   case 6:
    369     permunreg[0] = permutation / 120;
    370     permutation -= (permunreg[0] * 120);
    371     permunreg[1] = permutation / 24;
    372     permutation -= (permunreg[1] * 24);
    373     permunreg[2] = permutation / 6;
    374     permutation -= (permunreg[2] * 6);
    375     permunreg[3] = permutation / 2;
    376     permutation -= (permunreg[3] * 2);
    377     permunreg[4] = permutation;
    378     permunreg[5] = 0;
    379     break;
    380   case 5:
    381     permunreg[0] = permutation / 120;
    382     permutation -= (permunreg[0] * 120);
    383     permunreg[1] = permutation / 24;
    384     permutation -= (permunreg[1] * 24);
    385     permunreg[2] = permutation / 6;
    386     permutation -= (permunreg[2] * 6);
    387     permunreg[3] = permutation / 2;
    388     permutation -= (permunreg[3] * 2);
    389     permunreg[4] = permutation;
    390     break;
    391   case 4:
    392     permunreg[0] = permutation / 60;
    393     permutation -= (permunreg[0] * 60);
    394     permunreg[1] = permutation / 12;
    395     permutation -= (permunreg[1] * 12);
    396     permunreg[2] = permutation / 3;
    397     permutation -= (permunreg[2] * 3);
    398     permunreg[3] = permutation;
    399     break;
    400   case 3:
    401     permunreg[0] = permutation / 20;
    402     permutation -= (permunreg[0] * 20);
    403     permunreg[1] = permutation / 4;
    404     permutation -= (permunreg[1] * 4);
    405     permunreg[2] = permutation;
    406     break;
    407   case 2:
    408     permunreg[0] = permutation / 5;
    409     permutation -= (permunreg[0] * 5);
    410     permunreg[1] = permutation;
    411     break;
    412   case 1:
    413     permunreg[0] = permutation;
    414     break;
    415   }
    416   // re-number registers back to standard numbers
    417   int registersSaved[6];
    418   bool used[7] = { false, false, false, false, false, false, false };
    419   for (uint32_t i = 0; i < regCount; ++i) {
    420     uint32_t renum = 0;
    421     for (int u = 1; u < 7; ++u) {
    422       if (!used[u]) {
    423         if (renum == permunreg[i]) {
    424           registersSaved[i] = u;
    425           used[u] = true;
    426           break;
    427         }
    428         ++renum;
    429       }
    430     }
    431   }
    432   uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
    433   for (uint32_t i = 0; i < regCount; ++i) {
    434     switch (registersSaved[i]) {
    435     case UNWIND_X86_64_REG_RBX:
    436       registers.setRBX(addressSpace.get64(savedRegisters));
    437       break;
    438     case UNWIND_X86_64_REG_R12:
    439       registers.setR12(addressSpace.get64(savedRegisters));
    440       break;
    441     case UNWIND_X86_64_REG_R13:
    442       registers.setR13(addressSpace.get64(savedRegisters));
    443       break;
    444     case UNWIND_X86_64_REG_R14:
    445       registers.setR14(addressSpace.get64(savedRegisters));
    446       break;
    447     case UNWIND_X86_64_REG_R15:
    448       registers.setR15(addressSpace.get64(savedRegisters));
    449       break;
    450     case UNWIND_X86_64_REG_RBP:
    451       registers.setRBP(addressSpace.get64(savedRegisters));
    452       break;
    453     default:
    454       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
    455                            "function starting at 0x%llX\n",
    456                             encoding, functionStart);
    457       _LIBUNWIND_ABORT("invalid compact unwind encoding");
    458     }
    459     savedRegisters += 8;
    460   }
    461   framelessUnwind(addressSpace, savedRegisters, registers);
    462   return UNW_STEP_SUCCESS;
    463 }
    464 
    465 
    466 template <typename A>
    467 void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
    468                                             Registers_x86_64 &registers) {
    469   uint64_t rbp = registers.getRBP();
    470   // ebp points to old ebp
    471   registers.setRBP(addressSpace.get64(rbp));
    472   // old esp is ebp less saved ebp and return address
    473   registers.setSP(rbp + 16);
    474   // pop return address into eip
    475   registers.setIP(addressSpace.get64(rbp + 8));
    476 }
    477 
    478 template <typename A>
    479 void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
    480                                                 uint64_t returnAddressLocation,
    481                                                 Registers_x86_64 &registers) {
    482   // return address is on stack after last saved register
    483   registers.setIP(addressSpace.get64(returnAddressLocation));
    484   // old esp is before return address
    485   registers.setSP(returnAddressLocation + 8);
    486 }
    487 
    488 
    489 
    490 /// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
    491 /// unwind) by modifying a Registers_arm64 register set
    492 template <typename A>
    493 class CompactUnwinder_arm64 {
    494 public:
    495 
    496   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
    497                                      uint64_t functionStart, A &addressSpace,
    498                                      Registers_arm64 &registers);
    499 
    500 private:
    501   typename A::pint_t pint_t;
    502 
    503   static int
    504       stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
    505                                    uint64_t functionStart, A &addressSpace,
    506                                    Registers_arm64 &registers);
    507   static int stepWithCompactEncodingFrameless(
    508       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    509       A &addressSpace, Registers_arm64 &registers);
    510 };
    511 
    512 template <typename A>
    513 int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
    514     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
    515     A &addressSpace, Registers_arm64 &registers) {
    516   switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
    517   case UNWIND_ARM64_MODE_FRAME:
    518     return stepWithCompactEncodingFrame(compactEncoding, functionStart,
    519                                         addressSpace, registers);
    520   case UNWIND_ARM64_MODE_FRAMELESS:
    521     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
    522                                             addressSpace, registers);
    523   }
    524   _LIBUNWIND_ABORT("invalid compact unwind encoding");
    525 }
    526 
    527 template <typename A>
    528 int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
    529     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
    530     Registers_arm64 &registers) {
    531   uint32_t stackSize =
    532       16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
    533 
    534   uint64_t savedRegisterLoc = registers.getSP() + stackSize;
    535 
    536   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
    537     registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
    538     savedRegisterLoc -= 8;
    539     registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
    540     savedRegisterLoc -= 8;
    541   }
    542   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
    543     registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
    544     savedRegisterLoc -= 8;
    545     registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
    546     savedRegisterLoc -= 8;
    547   }
    548   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
    549     registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
    550     savedRegisterLoc -= 8;
    551     registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
    552     savedRegisterLoc -= 8;
    553   }
    554   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
    555     registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
    556     savedRegisterLoc -= 8;
    557     registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
    558     savedRegisterLoc -= 8;
    559   }
    560   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
    561     registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
    562     savedRegisterLoc -= 8;
    563     registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
    564     savedRegisterLoc -= 8;
    565   }
    566 
    567   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
    568     registers.setFloatRegister(UNW_ARM64_D8,
    569                                addressSpace.getDouble(savedRegisterLoc));
    570     savedRegisterLoc -= 8;
    571     registers.setFloatRegister(UNW_ARM64_D9,
    572                                addressSpace.getDouble(savedRegisterLoc));
    573     savedRegisterLoc -= 8;
    574   }
    575   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
    576     registers.setFloatRegister(UNW_ARM64_D10,
    577                                addressSpace.getDouble(savedRegisterLoc));
    578     savedRegisterLoc -= 8;
    579     registers.setFloatRegister(UNW_ARM64_D11,
    580                                addressSpace.getDouble(savedRegisterLoc));
    581     savedRegisterLoc -= 8;
    582   }
    583   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
    584     registers.setFloatRegister(UNW_ARM64_D12,
    585                                addressSpace.getDouble(savedRegisterLoc));
    586     savedRegisterLoc -= 8;
    587     registers.setFloatRegister(UNW_ARM64_D13,
    588                                addressSpace.getDouble(savedRegisterLoc));
    589     savedRegisterLoc -= 8;
    590   }
    591   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
    592     registers.setFloatRegister(UNW_ARM64_D14,
    593                                addressSpace.getDouble(savedRegisterLoc));
    594     savedRegisterLoc -= 8;
    595     registers.setFloatRegister(UNW_ARM64_D15,
    596                                addressSpace.getDouble(savedRegisterLoc));
    597     savedRegisterLoc -= 8;
    598   }
    599 
    600   // subtract stack size off of sp
    601   registers.setSP(savedRegisterLoc);
    602 
    603   // set pc to be value in lr
    604   registers.setIP(registers.getRegister(UNW_ARM64_LR));
    605 
    606   return UNW_STEP_SUCCESS;
    607 }
    608 
    609 template <typename A>
    610 int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
    611     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
    612     Registers_arm64 &registers) {
    613   uint64_t savedRegisterLoc = registers.getFP() - 8;
    614 
    615   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
    616     registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
    617     savedRegisterLoc -= 8;
    618     registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
    619     savedRegisterLoc -= 8;
    620   }
    621   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
    622     registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
    623     savedRegisterLoc -= 8;
    624     registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
    625     savedRegisterLoc -= 8;
    626   }
    627   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
    628     registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
    629     savedRegisterLoc -= 8;
    630     registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
    631     savedRegisterLoc -= 8;
    632   }
    633   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
    634     registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
    635     savedRegisterLoc -= 8;
    636     registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
    637     savedRegisterLoc -= 8;
    638   }
    639   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
    640     registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
    641     savedRegisterLoc -= 8;
    642     registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
    643     savedRegisterLoc -= 8;
    644   }
    645 
    646   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
    647     registers.setFloatRegister(UNW_ARM64_D8,
    648                                addressSpace.getDouble(savedRegisterLoc));
    649     savedRegisterLoc -= 8;
    650     registers.setFloatRegister(UNW_ARM64_D9,
    651                                addressSpace.getDouble(savedRegisterLoc));
    652     savedRegisterLoc -= 8;
    653   }
    654   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
    655     registers.setFloatRegister(UNW_ARM64_D10,
    656                                addressSpace.getDouble(savedRegisterLoc));
    657     savedRegisterLoc -= 8;
    658     registers.setFloatRegister(UNW_ARM64_D11,
    659                                addressSpace.getDouble(savedRegisterLoc));
    660     savedRegisterLoc -= 8;
    661   }
    662   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
    663     registers.setFloatRegister(UNW_ARM64_D12,
    664                                addressSpace.getDouble(savedRegisterLoc));
    665     savedRegisterLoc -= 8;
    666     registers.setFloatRegister(UNW_ARM64_D13,
    667                                addressSpace.getDouble(savedRegisterLoc));
    668     savedRegisterLoc -= 8;
    669   }
    670   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
    671     registers.setFloatRegister(UNW_ARM64_D14,
    672                                addressSpace.getDouble(savedRegisterLoc));
    673     savedRegisterLoc -= 8;
    674     registers.setFloatRegister(UNW_ARM64_D15,
    675                                addressSpace.getDouble(savedRegisterLoc));
    676     savedRegisterLoc -= 8;
    677   }
    678 
    679   uint64_t fp = registers.getFP();
    680   // fp points to old fp
    681   registers.setFP(addressSpace.get64(fp));
    682   // old sp is fp less saved fp and lr
    683   registers.setSP(fp + 16);
    684   // pop return address into pc
    685   registers.setIP(addressSpace.get64(fp + 8));
    686 
    687   return UNW_STEP_SUCCESS;
    688 }
    689 
    690 
    691 } // namespace libunwind
    692 
    693 #endif // __COMPACT_UNWINDER_HPP__
    694