| //===-------------------------- CompactUnwinder.hpp -----------------------===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is dual licensed under the MIT and the University of Illinois Open | 
 | // Source Licenses. See LICENSE.TXT for details. | 
 | // | 
 | // | 
 | //  Does runtime stack unwinding using compact unwind encodings. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #ifndef __COMPACT_UNWINDER_HPP__ | 
 | #define __COMPACT_UNWINDER_HPP__ | 
 |  | 
 | #include <stdint.h> | 
 | #include <stdlib.h> | 
 |  | 
 | #include <libunwind.h> | 
 | #include <mach-o/compact_unwind_encoding.h> | 
 |  | 
 | #include "Registers.hpp" | 
 |  | 
 | #define EXTRACT_BITS(value, mask)                                              \ | 
 |   ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) | 
 |  | 
 | namespace libunwind { | 
 |  | 
 | #if defined(_LIBUNWIND_TARGET_I386) | 
 | /// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka | 
 | /// unwind) by modifying a Registers_x86 register set | 
 | template <typename A> | 
 | class CompactUnwinder_x86 { | 
 | public: | 
 |  | 
 |   static int stepWithCompactEncoding(compact_unwind_encoding_t info, | 
 |                                      uint32_t functionStart, A &addressSpace, | 
 |                                      Registers_x86 ®isters); | 
 |  | 
 | private: | 
 |   typename A::pint_t pint_t; | 
 |  | 
 |   static void frameUnwind(A &addressSpace, Registers_x86 ®isters); | 
 |   static void framelessUnwind(A &addressSpace, | 
 |                               typename A::pint_t returnAddressLocation, | 
 |                               Registers_x86 ®isters); | 
 |   static int | 
 |       stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, | 
 |                                       uint32_t functionStart, A &addressSpace, | 
 |                                       Registers_x86 ®isters); | 
 |   static int stepWithCompactEncodingFrameless( | 
 |       compact_unwind_encoding_t compactEncoding, uint32_t functionStart, | 
 |       A &addressSpace, Registers_x86 ®isters, bool indirectStackSize); | 
 | }; | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_x86<A>::stepWithCompactEncoding( | 
 |     compact_unwind_encoding_t compactEncoding, uint32_t functionStart, | 
 |     A &addressSpace, Registers_x86 ®isters) { | 
 |   switch (compactEncoding & UNWIND_X86_MODE_MASK) { | 
 |   case UNWIND_X86_MODE_EBP_FRAME: | 
 |     return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, | 
 |                                            addressSpace, registers); | 
 |   case UNWIND_X86_MODE_STACK_IMMD: | 
 |     return stepWithCompactEncodingFrameless(compactEncoding, functionStart, | 
 |                                             addressSpace, registers, false); | 
 |   case UNWIND_X86_MODE_STACK_IND: | 
 |     return stepWithCompactEncodingFrameless(compactEncoding, functionStart, | 
 |                                             addressSpace, registers, true); | 
 |   } | 
 |   _LIBUNWIND_ABORT("invalid compact unwind encoding"); | 
 | } | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame( | 
 |     compact_unwind_encoding_t compactEncoding, uint32_t functionStart, | 
 |     A &addressSpace, Registers_x86 ®isters) { | 
 |   uint32_t savedRegistersOffset = | 
 |       EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); | 
 |   uint32_t savedRegistersLocations = | 
 |       EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); | 
 |  | 
 |   uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset; | 
 |   for (int i = 0; i < 5; ++i) { | 
 |     switch (savedRegistersLocations & 0x7) { | 
 |     case UNWIND_X86_REG_NONE: | 
 |       // no register saved in this slot | 
 |       break; | 
 |     case UNWIND_X86_REG_EBX: | 
 |       registers.setEBX(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_ECX: | 
 |       registers.setECX(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_EDX: | 
 |       registers.setEDX(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_EDI: | 
 |       registers.setEDI(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_ESI: | 
 |       registers.setESI(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     default: | 
 |       (void)functionStart; | 
 |       _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for  " | 
 |                            "function starting at 0x%X", | 
 |                             compactEncoding, functionStart); | 
 |       _LIBUNWIND_ABORT("invalid compact unwind encoding"); | 
 |     } | 
 |     savedRegisters += 4; | 
 |     savedRegistersLocations = (savedRegistersLocations >> 3); | 
 |   } | 
 |   frameUnwind(addressSpace, registers); | 
 |   return UNW_STEP_SUCCESS; | 
 | } | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless( | 
 |     compact_unwind_encoding_t encoding, uint32_t functionStart, | 
 |     A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) { | 
 |   uint32_t stackSizeEncoded = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); | 
 |   uint32_t stackAdjust = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); | 
 |   uint32_t regCount = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); | 
 |   uint32_t permutation = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); | 
 |   uint32_t stackSize = stackSizeEncoded * 4; | 
 |   if (indirectStackSize) { | 
 |     // stack size is encoded in subl $xxx,%esp instruction | 
 |     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); | 
 |     stackSize = subl + 4 * stackAdjust; | 
 |   } | 
 |   // decompress permutation | 
 |   uint32_t permunreg[6]; | 
 |   switch (regCount) { | 
 |   case 6: | 
 |     permunreg[0] = permutation / 120; | 
 |     permutation -= (permunreg[0] * 120); | 
 |     permunreg[1] = permutation / 24; | 
 |     permutation -= (permunreg[1] * 24); | 
 |     permunreg[2] = permutation / 6; | 
 |     permutation -= (permunreg[2] * 6); | 
 |     permunreg[3] = permutation / 2; | 
 |     permutation -= (permunreg[3] * 2); | 
 |     permunreg[4] = permutation; | 
 |     permunreg[5] = 0; | 
 |     break; | 
 |   case 5: | 
 |     permunreg[0] = permutation / 120; | 
 |     permutation -= (permunreg[0] * 120); | 
 |     permunreg[1] = permutation / 24; | 
 |     permutation -= (permunreg[1] * 24); | 
 |     permunreg[2] = permutation / 6; | 
 |     permutation -= (permunreg[2] * 6); | 
 |     permunreg[3] = permutation / 2; | 
 |     permutation -= (permunreg[3] * 2); | 
 |     permunreg[4] = permutation; | 
 |     break; | 
 |   case 4: | 
 |     permunreg[0] = permutation / 60; | 
 |     permutation -= (permunreg[0] * 60); | 
 |     permunreg[1] = permutation / 12; | 
 |     permutation -= (permunreg[1] * 12); | 
 |     permunreg[2] = permutation / 3; | 
 |     permutation -= (permunreg[2] * 3); | 
 |     permunreg[3] = permutation; | 
 |     break; | 
 |   case 3: | 
 |     permunreg[0] = permutation / 20; | 
 |     permutation -= (permunreg[0] * 20); | 
 |     permunreg[1] = permutation / 4; | 
 |     permutation -= (permunreg[1] * 4); | 
 |     permunreg[2] = permutation; | 
 |     break; | 
 |   case 2: | 
 |     permunreg[0] = permutation / 5; | 
 |     permutation -= (permunreg[0] * 5); | 
 |     permunreg[1] = permutation; | 
 |     break; | 
 |   case 1: | 
 |     permunreg[0] = permutation; | 
 |     break; | 
 |   } | 
 |   // re-number registers back to standard numbers | 
 |   int registersSaved[6]; | 
 |   bool used[7] = { false, false, false, false, false, false, false }; | 
 |   for (uint32_t i = 0; i < regCount; ++i) { | 
 |     uint32_t renum = 0; | 
 |     for (int u = 1; u < 7; ++u) { | 
 |       if (!used[u]) { | 
 |         if (renum == permunreg[i]) { | 
 |           registersSaved[i] = u; | 
 |           used[u] = true; | 
 |           break; | 
 |         } | 
 |         ++renum; | 
 |       } | 
 |     } | 
 |   } | 
 |   uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount; | 
 |   for (uint32_t i = 0; i < regCount; ++i) { | 
 |     switch (registersSaved[i]) { | 
 |     case UNWIND_X86_REG_EBX: | 
 |       registers.setEBX(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_ECX: | 
 |       registers.setECX(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_EDX: | 
 |       registers.setEDX(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_EDI: | 
 |       registers.setEDI(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_ESI: | 
 |       registers.setESI(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_REG_EBP: | 
 |       registers.setEBP(addressSpace.get32(savedRegisters)); | 
 |       break; | 
 |     default: | 
 |       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " | 
 |                            "function starting at 0x%X", | 
 |                            encoding, functionStart); | 
 |       _LIBUNWIND_ABORT("invalid compact unwind encoding"); | 
 |     } | 
 |     savedRegisters += 4; | 
 |   } | 
 |   framelessUnwind(addressSpace, savedRegisters, registers); | 
 |   return UNW_STEP_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | template <typename A> | 
 | void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace, | 
 |                                          Registers_x86 ®isters) { | 
 |   typename A::pint_t bp = registers.getEBP(); | 
 |   // ebp points to old ebp | 
 |   registers.setEBP(addressSpace.get32(bp)); | 
 |   // old esp is ebp less saved ebp and return address | 
 |   registers.setSP((uint32_t)bp + 8); | 
 |   // pop return address into eip | 
 |   registers.setIP(addressSpace.get32(bp + 4)); | 
 | } | 
 |  | 
 | template <typename A> | 
 | void CompactUnwinder_x86<A>::framelessUnwind( | 
 |     A &addressSpace, typename A::pint_t returnAddressLocation, | 
 |     Registers_x86 ®isters) { | 
 |   // return address is on stack after last saved register | 
 |   registers.setIP(addressSpace.get32(returnAddressLocation)); | 
 |   // old esp is before return address | 
 |   registers.setSP((uint32_t)returnAddressLocation + 4); | 
 | } | 
 | #endif // _LIBUNWIND_TARGET_I386 | 
 |  | 
 |  | 
 | #if defined(_LIBUNWIND_TARGET_X86_64) | 
 | /// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka | 
 | /// unwind) by modifying a Registers_x86_64 register set | 
 | template <typename A> | 
 | class CompactUnwinder_x86_64 { | 
 | public: | 
 |  | 
 |   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, | 
 |                                      uint64_t functionStart, A &addressSpace, | 
 |                                      Registers_x86_64 ®isters); | 
 |  | 
 | private: | 
 |   typename A::pint_t pint_t; | 
 |  | 
 |   static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters); | 
 |   static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation, | 
 |                               Registers_x86_64 ®isters); | 
 |   static int | 
 |       stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, | 
 |                                       uint64_t functionStart, A &addressSpace, | 
 |                                       Registers_x86_64 ®isters); | 
 |   static int stepWithCompactEncodingFrameless( | 
 |       compact_unwind_encoding_t compactEncoding, uint64_t functionStart, | 
 |       A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize); | 
 | }; | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_x86_64<A>::stepWithCompactEncoding( | 
 |     compact_unwind_encoding_t compactEncoding, uint64_t functionStart, | 
 |     A &addressSpace, Registers_x86_64 ®isters) { | 
 |   switch (compactEncoding & UNWIND_X86_64_MODE_MASK) { | 
 |   case UNWIND_X86_64_MODE_RBP_FRAME: | 
 |     return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, | 
 |                                            addressSpace, registers); | 
 |   case UNWIND_X86_64_MODE_STACK_IMMD: | 
 |     return stepWithCompactEncodingFrameless(compactEncoding, functionStart, | 
 |                                             addressSpace, registers, false); | 
 |   case UNWIND_X86_64_MODE_STACK_IND: | 
 |     return stepWithCompactEncodingFrameless(compactEncoding, functionStart, | 
 |                                             addressSpace, registers, true); | 
 |   } | 
 |   _LIBUNWIND_ABORT("invalid compact unwind encoding"); | 
 | } | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame( | 
 |     compact_unwind_encoding_t compactEncoding, uint64_t functionStart, | 
 |     A &addressSpace, Registers_x86_64 ®isters) { | 
 |   uint32_t savedRegistersOffset = | 
 |       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET); | 
 |   uint32_t savedRegistersLocations = | 
 |       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); | 
 |  | 
 |   uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset; | 
 |   for (int i = 0; i < 5; ++i) { | 
 |     switch (savedRegistersLocations & 0x7) { | 
 |     case UNWIND_X86_64_REG_NONE: | 
 |       // no register saved in this slot | 
 |       break; | 
 |     case UNWIND_X86_64_REG_RBX: | 
 |       registers.setRBX(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R12: | 
 |       registers.setR12(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R13: | 
 |       registers.setR13(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R14: | 
 |       registers.setR14(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R15: | 
 |       registers.setR15(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     default: | 
 |       (void)functionStart; | 
 |       _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for " | 
 |                            "function starting at 0x%llX", | 
 |                             compactEncoding, functionStart); | 
 |       _LIBUNWIND_ABORT("invalid compact unwind encoding"); | 
 |     } | 
 |     savedRegisters += 8; | 
 |     savedRegistersLocations = (savedRegistersLocations >> 3); | 
 |   } | 
 |   frameUnwind(addressSpace, registers); | 
 |   return UNW_STEP_SUCCESS; | 
 | } | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless( | 
 |     compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace, | 
 |     Registers_x86_64 ®isters, bool indirectStackSize) { | 
 |   uint32_t stackSizeEncoded = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); | 
 |   uint32_t stackAdjust = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); | 
 |   uint32_t regCount = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); | 
 |   uint32_t permutation = | 
 |       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); | 
 |   uint32_t stackSize = stackSizeEncoded * 8; | 
 |   if (indirectStackSize) { | 
 |     // stack size is encoded in subl $xxx,%esp instruction | 
 |     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); | 
 |     stackSize = subl + 8 * stackAdjust; | 
 |   } | 
 |   // decompress permutation | 
 |   uint32_t permunreg[6]; | 
 |   switch (regCount) { | 
 |   case 6: | 
 |     permunreg[0] = permutation / 120; | 
 |     permutation -= (permunreg[0] * 120); | 
 |     permunreg[1] = permutation / 24; | 
 |     permutation -= (permunreg[1] * 24); | 
 |     permunreg[2] = permutation / 6; | 
 |     permutation -= (permunreg[2] * 6); | 
 |     permunreg[3] = permutation / 2; | 
 |     permutation -= (permunreg[3] * 2); | 
 |     permunreg[4] = permutation; | 
 |     permunreg[5] = 0; | 
 |     break; | 
 |   case 5: | 
 |     permunreg[0] = permutation / 120; | 
 |     permutation -= (permunreg[0] * 120); | 
 |     permunreg[1] = permutation / 24; | 
 |     permutation -= (permunreg[1] * 24); | 
 |     permunreg[2] = permutation / 6; | 
 |     permutation -= (permunreg[2] * 6); | 
 |     permunreg[3] = permutation / 2; | 
 |     permutation -= (permunreg[3] * 2); | 
 |     permunreg[4] = permutation; | 
 |     break; | 
 |   case 4: | 
 |     permunreg[0] = permutation / 60; | 
 |     permutation -= (permunreg[0] * 60); | 
 |     permunreg[1] = permutation / 12; | 
 |     permutation -= (permunreg[1] * 12); | 
 |     permunreg[2] = permutation / 3; | 
 |     permutation -= (permunreg[2] * 3); | 
 |     permunreg[3] = permutation; | 
 |     break; | 
 |   case 3: | 
 |     permunreg[0] = permutation / 20; | 
 |     permutation -= (permunreg[0] * 20); | 
 |     permunreg[1] = permutation / 4; | 
 |     permutation -= (permunreg[1] * 4); | 
 |     permunreg[2] = permutation; | 
 |     break; | 
 |   case 2: | 
 |     permunreg[0] = permutation / 5; | 
 |     permutation -= (permunreg[0] * 5); | 
 |     permunreg[1] = permutation; | 
 |     break; | 
 |   case 1: | 
 |     permunreg[0] = permutation; | 
 |     break; | 
 |   } | 
 |   // re-number registers back to standard numbers | 
 |   int registersSaved[6]; | 
 |   bool used[7] = { false, false, false, false, false, false, false }; | 
 |   for (uint32_t i = 0; i < regCount; ++i) { | 
 |     uint32_t renum = 0; | 
 |     for (int u = 1; u < 7; ++u) { | 
 |       if (!used[u]) { | 
 |         if (renum == permunreg[i]) { | 
 |           registersSaved[i] = u; | 
 |           used[u] = true; | 
 |           break; | 
 |         } | 
 |         ++renum; | 
 |       } | 
 |     } | 
 |   } | 
 |   uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount; | 
 |   for (uint32_t i = 0; i < regCount; ++i) { | 
 |     switch (registersSaved[i]) { | 
 |     case UNWIND_X86_64_REG_RBX: | 
 |       registers.setRBX(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R12: | 
 |       registers.setR12(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R13: | 
 |       registers.setR13(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R14: | 
 |       registers.setR14(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_R15: | 
 |       registers.setR15(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     case UNWIND_X86_64_REG_RBP: | 
 |       registers.setRBP(addressSpace.get64(savedRegisters)); | 
 |       break; | 
 |     default: | 
 |       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " | 
 |                            "function starting at 0x%llX", | 
 |                             encoding, functionStart); | 
 |       _LIBUNWIND_ABORT("invalid compact unwind encoding"); | 
 |     } | 
 |     savedRegisters += 8; | 
 |   } | 
 |   framelessUnwind(addressSpace, savedRegisters, registers); | 
 |   return UNW_STEP_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | template <typename A> | 
 | void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace, | 
 |                                             Registers_x86_64 ®isters) { | 
 |   uint64_t rbp = registers.getRBP(); | 
 |   // ebp points to old ebp | 
 |   registers.setRBP(addressSpace.get64(rbp)); | 
 |   // old esp is ebp less saved ebp and return address | 
 |   registers.setSP(rbp + 16); | 
 |   // pop return address into eip | 
 |   registers.setIP(addressSpace.get64(rbp + 8)); | 
 | } | 
 |  | 
 | template <typename A> | 
 | void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace, | 
 |                                                 uint64_t returnAddressLocation, | 
 |                                                 Registers_x86_64 ®isters) { | 
 |   // return address is on stack after last saved register | 
 |   registers.setIP(addressSpace.get64(returnAddressLocation)); | 
 |   // old esp is before return address | 
 |   registers.setSP(returnAddressLocation + 8); | 
 | } | 
 | #endif // _LIBUNWIND_TARGET_X86_64 | 
 |  | 
 |  | 
 |  | 
 | #if defined(_LIBUNWIND_TARGET_AARCH64) | 
 | /// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka | 
 | /// unwind) by modifying a Registers_arm64 register set | 
 | template <typename A> | 
 | class CompactUnwinder_arm64 { | 
 | public: | 
 |  | 
 |   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, | 
 |                                      uint64_t functionStart, A &addressSpace, | 
 |                                      Registers_arm64 ®isters); | 
 |  | 
 | private: | 
 |   typename A::pint_t pint_t; | 
 |  | 
 |   static int | 
 |       stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding, | 
 |                                    uint64_t functionStart, A &addressSpace, | 
 |                                    Registers_arm64 ®isters); | 
 |   static int stepWithCompactEncodingFrameless( | 
 |       compact_unwind_encoding_t compactEncoding, uint64_t functionStart, | 
 |       A &addressSpace, Registers_arm64 ®isters); | 
 | }; | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_arm64<A>::stepWithCompactEncoding( | 
 |     compact_unwind_encoding_t compactEncoding, uint64_t functionStart, | 
 |     A &addressSpace, Registers_arm64 ®isters) { | 
 |   switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { | 
 |   case UNWIND_ARM64_MODE_FRAME: | 
 |     return stepWithCompactEncodingFrame(compactEncoding, functionStart, | 
 |                                         addressSpace, registers); | 
 |   case UNWIND_ARM64_MODE_FRAMELESS: | 
 |     return stepWithCompactEncodingFrameless(compactEncoding, functionStart, | 
 |                                             addressSpace, registers); | 
 |   } | 
 |   _LIBUNWIND_ABORT("invalid compact unwind encoding"); | 
 | } | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless( | 
 |     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, | 
 |     Registers_arm64 ®isters) { | 
 |   uint32_t stackSize = | 
 |       16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); | 
 |  | 
 |   uint64_t savedRegisterLoc = registers.getSP() + stackSize; | 
 |  | 
 |   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |  | 
 |   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D8, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D9, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D10, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D11, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D12, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D13, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D14, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D15, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |  | 
 |   // subtract stack size off of sp | 
 |   registers.setSP(savedRegisterLoc); | 
 |  | 
 |   // set pc to be value in lr | 
 |   registers.setIP(registers.getRegister(UNW_ARM64_LR)); | 
 |  | 
 |   return UNW_STEP_SUCCESS; | 
 | } | 
 |  | 
 | template <typename A> | 
 | int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame( | 
 |     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, | 
 |     Registers_arm64 ®isters) { | 
 |   uint64_t savedRegisterLoc = registers.getFP() - 8; | 
 |  | 
 |   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { | 
 |     registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |  | 
 |   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D8, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D9, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D10, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D11, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D12, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D13, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { | 
 |     registers.setFloatRegister(UNW_ARM64_D14, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |     registers.setFloatRegister(UNW_ARM64_D15, | 
 |                                addressSpace.getDouble(savedRegisterLoc)); | 
 |     savedRegisterLoc -= 8; | 
 |   } | 
 |  | 
 |   uint64_t fp = registers.getFP(); | 
 |   // fp points to old fp | 
 |   registers.setFP(addressSpace.get64(fp)); | 
 |   // old sp is fp less saved fp and lr | 
 |   registers.setSP(fp + 16); | 
 |   // pop return address into pc | 
 |   registers.setIP(addressSpace.get64(fp + 8)); | 
 |  | 
 |   return UNW_STEP_SUCCESS; | 
 | } | 
 | #endif // _LIBUNWIND_TARGET_AARCH64 | 
 |  | 
 |  | 
 | } // namespace libunwind | 
 |  | 
 | #endif // __COMPACT_UNWINDER_HPP__ |