xref: /freebsd-src/contrib/llvm-project/libunwind/src/CompactUnwinder.hpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //
80b57cec5SDimitry Andric //  Does runtime stack unwinding using compact unwind encodings.
90b57cec5SDimitry Andric //
100b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
110b57cec5SDimitry Andric 
120b57cec5SDimitry Andric #ifndef __COMPACT_UNWINDER_HPP__
130b57cec5SDimitry Andric #define __COMPACT_UNWINDER_HPP__
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include <stdint.h>
160b57cec5SDimitry Andric #include <stdlib.h>
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric #include <libunwind.h>
190b57cec5SDimitry Andric #include <mach-o/compact_unwind_encoding.h>
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric #include "Registers.hpp"
22*bdd1243dSDimitry Andric #include "libunwind_ext.h"
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric #define EXTRACT_BITS(value, mask)                                              \
250b57cec5SDimitry Andric   ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric namespace libunwind {
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_I386)
300b57cec5SDimitry Andric /// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
310b57cec5SDimitry Andric /// unwind) by modifying a Registers_x86 register set
320b57cec5SDimitry Andric template <typename A>
330b57cec5SDimitry Andric class CompactUnwinder_x86 {
340b57cec5SDimitry Andric public:
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric   static int stepWithCompactEncoding(compact_unwind_encoding_t info,
370b57cec5SDimitry Andric                                      uint32_t functionStart, A &addressSpace,
380b57cec5SDimitry Andric                                      Registers_x86 &registers);
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric private:
410b57cec5SDimitry Andric   typename A::pint_t pint_t;
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   static void frameUnwind(A &addressSpace, Registers_x86 &registers);
440b57cec5SDimitry Andric   static void framelessUnwind(A &addressSpace,
450b57cec5SDimitry Andric                               typename A::pint_t returnAddressLocation,
460b57cec5SDimitry Andric                               Registers_x86 &registers);
470b57cec5SDimitry Andric   static int
480b57cec5SDimitry Andric       stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
490b57cec5SDimitry Andric                                       uint32_t functionStart, A &addressSpace,
500b57cec5SDimitry Andric                                       Registers_x86 &registers);
510b57cec5SDimitry Andric   static int stepWithCompactEncodingFrameless(
520b57cec5SDimitry Andric       compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
530b57cec5SDimitry Andric       A &addressSpace, Registers_x86 &registers, bool indirectStackSize);
540b57cec5SDimitry Andric };
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric template <typename A>
stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,uint32_t functionStart,A & addressSpace,Registers_x86 & registers)570b57cec5SDimitry Andric int CompactUnwinder_x86<A>::stepWithCompactEncoding(
580b57cec5SDimitry Andric     compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
590b57cec5SDimitry Andric     A &addressSpace, Registers_x86 &registers) {
600b57cec5SDimitry Andric   switch (compactEncoding & UNWIND_X86_MODE_MASK) {
610b57cec5SDimitry Andric   case UNWIND_X86_MODE_EBP_FRAME:
620b57cec5SDimitry Andric     return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart,
630b57cec5SDimitry Andric                                            addressSpace, registers);
640b57cec5SDimitry Andric   case UNWIND_X86_MODE_STACK_IMMD:
650b57cec5SDimitry Andric     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
660b57cec5SDimitry Andric                                             addressSpace, registers, false);
670b57cec5SDimitry Andric   case UNWIND_X86_MODE_STACK_IND:
680b57cec5SDimitry Andric     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
690b57cec5SDimitry Andric                                             addressSpace, registers, true);
700b57cec5SDimitry Andric   }
710b57cec5SDimitry Andric   _LIBUNWIND_ABORT("invalid compact unwind encoding");
720b57cec5SDimitry Andric }
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric template <typename A>
stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,uint32_t functionStart,A & addressSpace,Registers_x86 & registers)750b57cec5SDimitry Andric int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(
760b57cec5SDimitry Andric     compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
770b57cec5SDimitry Andric     A &addressSpace, Registers_x86 &registers) {
780b57cec5SDimitry Andric   uint32_t savedRegistersOffset =
790b57cec5SDimitry Andric       EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
800b57cec5SDimitry Andric   uint32_t savedRegistersLocations =
810b57cec5SDimitry Andric       EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric   uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset;
840b57cec5SDimitry Andric   for (int i = 0; i < 5; ++i) {
850b57cec5SDimitry Andric     switch (savedRegistersLocations & 0x7) {
860b57cec5SDimitry Andric     case UNWIND_X86_REG_NONE:
870b57cec5SDimitry Andric       // no register saved in this slot
880b57cec5SDimitry Andric       break;
890b57cec5SDimitry Andric     case UNWIND_X86_REG_EBX:
900b57cec5SDimitry Andric       registers.setEBX(addressSpace.get32(savedRegisters));
910b57cec5SDimitry Andric       break;
920b57cec5SDimitry Andric     case UNWIND_X86_REG_ECX:
930b57cec5SDimitry Andric       registers.setECX(addressSpace.get32(savedRegisters));
940b57cec5SDimitry Andric       break;
950b57cec5SDimitry Andric     case UNWIND_X86_REG_EDX:
960b57cec5SDimitry Andric       registers.setEDX(addressSpace.get32(savedRegisters));
970b57cec5SDimitry Andric       break;
980b57cec5SDimitry Andric     case UNWIND_X86_REG_EDI:
990b57cec5SDimitry Andric       registers.setEDI(addressSpace.get32(savedRegisters));
1000b57cec5SDimitry Andric       break;
1010b57cec5SDimitry Andric     case UNWIND_X86_REG_ESI:
1020b57cec5SDimitry Andric       registers.setESI(addressSpace.get32(savedRegisters));
1030b57cec5SDimitry Andric       break;
1040b57cec5SDimitry Andric     default:
1050b57cec5SDimitry Andric       (void)functionStart;
1060b57cec5SDimitry Andric       _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for  "
1070b57cec5SDimitry Andric                            "function starting at 0x%X",
1080b57cec5SDimitry Andric                             compactEncoding, functionStart);
1090b57cec5SDimitry Andric       _LIBUNWIND_ABORT("invalid compact unwind encoding");
1100b57cec5SDimitry Andric     }
1110b57cec5SDimitry Andric     savedRegisters += 4;
1120b57cec5SDimitry Andric     savedRegistersLocations = (savedRegistersLocations >> 3);
1130b57cec5SDimitry Andric   }
1140b57cec5SDimitry Andric   frameUnwind(addressSpace, registers);
1150b57cec5SDimitry Andric   return UNW_STEP_SUCCESS;
1160b57cec5SDimitry Andric }
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric template <typename A>
stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding,uint32_t functionStart,A & addressSpace,Registers_x86 & registers,bool indirectStackSize)1190b57cec5SDimitry Andric int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(
1200b57cec5SDimitry Andric     compact_unwind_encoding_t encoding, uint32_t functionStart,
1210b57cec5SDimitry Andric     A &addressSpace, Registers_x86 &registers, bool indirectStackSize) {
1220b57cec5SDimitry Andric   uint32_t stackSizeEncoded =
1230b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
1240b57cec5SDimitry Andric   uint32_t stackAdjust =
1250b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
1260b57cec5SDimitry Andric   uint32_t regCount =
1270b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
1280b57cec5SDimitry Andric   uint32_t permutation =
1290b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
1300b57cec5SDimitry Andric   uint32_t stackSize = stackSizeEncoded * 4;
1310b57cec5SDimitry Andric   if (indirectStackSize) {
1320b57cec5SDimitry Andric     // stack size is encoded in subl $xxx,%esp instruction
1330b57cec5SDimitry Andric     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
1340b57cec5SDimitry Andric     stackSize = subl + 4 * stackAdjust;
1350b57cec5SDimitry Andric   }
1360b57cec5SDimitry Andric   // decompress permutation
1370b57cec5SDimitry Andric   uint32_t permunreg[6];
1380b57cec5SDimitry Andric   switch (regCount) {
1390b57cec5SDimitry Andric   case 6:
1400b57cec5SDimitry Andric     permunreg[0] = permutation / 120;
1410b57cec5SDimitry Andric     permutation -= (permunreg[0] * 120);
1420b57cec5SDimitry Andric     permunreg[1] = permutation / 24;
1430b57cec5SDimitry Andric     permutation -= (permunreg[1] * 24);
1440b57cec5SDimitry Andric     permunreg[2] = permutation / 6;
1450b57cec5SDimitry Andric     permutation -= (permunreg[2] * 6);
1460b57cec5SDimitry Andric     permunreg[3] = permutation / 2;
1470b57cec5SDimitry Andric     permutation -= (permunreg[3] * 2);
1480b57cec5SDimitry Andric     permunreg[4] = permutation;
1490b57cec5SDimitry Andric     permunreg[5] = 0;
1500b57cec5SDimitry Andric     break;
1510b57cec5SDimitry Andric   case 5:
1520b57cec5SDimitry Andric     permunreg[0] = permutation / 120;
1530b57cec5SDimitry Andric     permutation -= (permunreg[0] * 120);
1540b57cec5SDimitry Andric     permunreg[1] = permutation / 24;
1550b57cec5SDimitry Andric     permutation -= (permunreg[1] * 24);
1560b57cec5SDimitry Andric     permunreg[2] = permutation / 6;
1570b57cec5SDimitry Andric     permutation -= (permunreg[2] * 6);
1580b57cec5SDimitry Andric     permunreg[3] = permutation / 2;
1590b57cec5SDimitry Andric     permutation -= (permunreg[3] * 2);
1600b57cec5SDimitry Andric     permunreg[4] = permutation;
1610b57cec5SDimitry Andric     break;
1620b57cec5SDimitry Andric   case 4:
1630b57cec5SDimitry Andric     permunreg[0] = permutation / 60;
1640b57cec5SDimitry Andric     permutation -= (permunreg[0] * 60);
1650b57cec5SDimitry Andric     permunreg[1] = permutation / 12;
1660b57cec5SDimitry Andric     permutation -= (permunreg[1] * 12);
1670b57cec5SDimitry Andric     permunreg[2] = permutation / 3;
1680b57cec5SDimitry Andric     permutation -= (permunreg[2] * 3);
1690b57cec5SDimitry Andric     permunreg[3] = permutation;
1700b57cec5SDimitry Andric     break;
1710b57cec5SDimitry Andric   case 3:
1720b57cec5SDimitry Andric     permunreg[0] = permutation / 20;
1730b57cec5SDimitry Andric     permutation -= (permunreg[0] * 20);
1740b57cec5SDimitry Andric     permunreg[1] = permutation / 4;
1750b57cec5SDimitry Andric     permutation -= (permunreg[1] * 4);
1760b57cec5SDimitry Andric     permunreg[2] = permutation;
1770b57cec5SDimitry Andric     break;
1780b57cec5SDimitry Andric   case 2:
1790b57cec5SDimitry Andric     permunreg[0] = permutation / 5;
1800b57cec5SDimitry Andric     permutation -= (permunreg[0] * 5);
1810b57cec5SDimitry Andric     permunreg[1] = permutation;
1820b57cec5SDimitry Andric     break;
1830b57cec5SDimitry Andric   case 1:
1840b57cec5SDimitry Andric     permunreg[0] = permutation;
1850b57cec5SDimitry Andric     break;
1860b57cec5SDimitry Andric   }
1870b57cec5SDimitry Andric   // re-number registers back to standard numbers
1880b57cec5SDimitry Andric   int registersSaved[6];
1890b57cec5SDimitry Andric   bool used[7] = { false, false, false, false, false, false, false };
1900b57cec5SDimitry Andric   for (uint32_t i = 0; i < regCount; ++i) {
1910b57cec5SDimitry Andric     uint32_t renum = 0;
1920b57cec5SDimitry Andric     for (int u = 1; u < 7; ++u) {
1930b57cec5SDimitry Andric       if (!used[u]) {
1940b57cec5SDimitry Andric         if (renum == permunreg[i]) {
1950b57cec5SDimitry Andric           registersSaved[i] = u;
1960b57cec5SDimitry Andric           used[u] = true;
1970b57cec5SDimitry Andric           break;
1980b57cec5SDimitry Andric         }
1990b57cec5SDimitry Andric         ++renum;
2000b57cec5SDimitry Andric       }
2010b57cec5SDimitry Andric     }
2020b57cec5SDimitry Andric   }
2030b57cec5SDimitry Andric   uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount;
2040b57cec5SDimitry Andric   for (uint32_t i = 0; i < regCount; ++i) {
2050b57cec5SDimitry Andric     switch (registersSaved[i]) {
2060b57cec5SDimitry Andric     case UNWIND_X86_REG_EBX:
2070b57cec5SDimitry Andric       registers.setEBX(addressSpace.get32(savedRegisters));
2080b57cec5SDimitry Andric       break;
2090b57cec5SDimitry Andric     case UNWIND_X86_REG_ECX:
2100b57cec5SDimitry Andric       registers.setECX(addressSpace.get32(savedRegisters));
2110b57cec5SDimitry Andric       break;
2120b57cec5SDimitry Andric     case UNWIND_X86_REG_EDX:
2130b57cec5SDimitry Andric       registers.setEDX(addressSpace.get32(savedRegisters));
2140b57cec5SDimitry Andric       break;
2150b57cec5SDimitry Andric     case UNWIND_X86_REG_EDI:
2160b57cec5SDimitry Andric       registers.setEDI(addressSpace.get32(savedRegisters));
2170b57cec5SDimitry Andric       break;
2180b57cec5SDimitry Andric     case UNWIND_X86_REG_ESI:
2190b57cec5SDimitry Andric       registers.setESI(addressSpace.get32(savedRegisters));
2200b57cec5SDimitry Andric       break;
2210b57cec5SDimitry Andric     case UNWIND_X86_REG_EBP:
2220b57cec5SDimitry Andric       registers.setEBP(addressSpace.get32(savedRegisters));
2230b57cec5SDimitry Andric       break;
2240b57cec5SDimitry Andric     default:
2250b57cec5SDimitry Andric       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
2260b57cec5SDimitry Andric                            "function starting at 0x%X",
2270b57cec5SDimitry Andric                            encoding, functionStart);
2280b57cec5SDimitry Andric       _LIBUNWIND_ABORT("invalid compact unwind encoding");
2290b57cec5SDimitry Andric     }
2300b57cec5SDimitry Andric     savedRegisters += 4;
2310b57cec5SDimitry Andric   }
2320b57cec5SDimitry Andric   framelessUnwind(addressSpace, savedRegisters, registers);
2330b57cec5SDimitry Andric   return UNW_STEP_SUCCESS;
2340b57cec5SDimitry Andric }
2350b57cec5SDimitry Andric 
2360b57cec5SDimitry Andric 
2370b57cec5SDimitry Andric template <typename A>
frameUnwind(A & addressSpace,Registers_x86 & registers)2380b57cec5SDimitry Andric void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace,
2390b57cec5SDimitry Andric                                          Registers_x86 &registers) {
2400b57cec5SDimitry Andric   typename A::pint_t bp = registers.getEBP();
2410b57cec5SDimitry Andric   // ebp points to old ebp
2420b57cec5SDimitry Andric   registers.setEBP(addressSpace.get32(bp));
2430b57cec5SDimitry Andric   // old esp is ebp less saved ebp and return address
2440b57cec5SDimitry Andric   registers.setSP((uint32_t)bp + 8);
2450b57cec5SDimitry Andric   // pop return address into eip
2460b57cec5SDimitry Andric   registers.setIP(addressSpace.get32(bp + 4));
2470b57cec5SDimitry Andric }
2480b57cec5SDimitry Andric 
2490b57cec5SDimitry Andric template <typename A>
framelessUnwind(A & addressSpace,typename A::pint_t returnAddressLocation,Registers_x86 & registers)2500b57cec5SDimitry Andric void CompactUnwinder_x86<A>::framelessUnwind(
2510b57cec5SDimitry Andric     A &addressSpace, typename A::pint_t returnAddressLocation,
2520b57cec5SDimitry Andric     Registers_x86 &registers) {
2530b57cec5SDimitry Andric   // return address is on stack after last saved register
2540b57cec5SDimitry Andric   registers.setIP(addressSpace.get32(returnAddressLocation));
2550b57cec5SDimitry Andric   // old esp is before return address
2560b57cec5SDimitry Andric   registers.setSP((uint32_t)returnAddressLocation + 4);
2570b57cec5SDimitry Andric }
2580b57cec5SDimitry Andric #endif // _LIBUNWIND_TARGET_I386
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_X86_64)
2620b57cec5SDimitry Andric /// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
2630b57cec5SDimitry Andric /// unwind) by modifying a Registers_x86_64 register set
2640b57cec5SDimitry Andric template <typename A>
2650b57cec5SDimitry Andric class CompactUnwinder_x86_64 {
2660b57cec5SDimitry Andric public:
2670b57cec5SDimitry Andric 
2680b57cec5SDimitry Andric   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
2690b57cec5SDimitry Andric                                      uint64_t functionStart, A &addressSpace,
2700b57cec5SDimitry Andric                                      Registers_x86_64 &registers);
2710b57cec5SDimitry Andric 
2720b57cec5SDimitry Andric private:
2730b57cec5SDimitry Andric   typename A::pint_t pint_t;
2740b57cec5SDimitry Andric 
2750b57cec5SDimitry Andric   static void frameUnwind(A &addressSpace, Registers_x86_64 &registers);
2760b57cec5SDimitry Andric   static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
2770b57cec5SDimitry Andric                               Registers_x86_64 &registers);
2780b57cec5SDimitry Andric   static int
2790b57cec5SDimitry Andric       stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
2800b57cec5SDimitry Andric                                       uint64_t functionStart, A &addressSpace,
2810b57cec5SDimitry Andric                                       Registers_x86_64 &registers);
2820b57cec5SDimitry Andric   static int stepWithCompactEncodingFrameless(
2830b57cec5SDimitry Andric       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
2840b57cec5SDimitry Andric       A &addressSpace, Registers_x86_64 &registers, bool indirectStackSize);
2850b57cec5SDimitry Andric };
2860b57cec5SDimitry Andric 
2870b57cec5SDimitry Andric template <typename A>
stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,uint64_t functionStart,A & addressSpace,Registers_x86_64 & registers)2880b57cec5SDimitry Andric int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
2890b57cec5SDimitry Andric     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
2900b57cec5SDimitry Andric     A &addressSpace, Registers_x86_64 &registers) {
2910b57cec5SDimitry Andric   switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
2920b57cec5SDimitry Andric   case UNWIND_X86_64_MODE_RBP_FRAME:
2930b57cec5SDimitry Andric     return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
2940b57cec5SDimitry Andric                                            addressSpace, registers);
2950b57cec5SDimitry Andric   case UNWIND_X86_64_MODE_STACK_IMMD:
2960b57cec5SDimitry Andric     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
2970b57cec5SDimitry Andric                                             addressSpace, registers, false);
2980b57cec5SDimitry Andric   case UNWIND_X86_64_MODE_STACK_IND:
2990b57cec5SDimitry Andric     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
3000b57cec5SDimitry Andric                                             addressSpace, registers, true);
3010b57cec5SDimitry Andric   }
3020b57cec5SDimitry Andric   _LIBUNWIND_ABORT("invalid compact unwind encoding");
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric template <typename A>
stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,uint64_t functionStart,A & addressSpace,Registers_x86_64 & registers)3060b57cec5SDimitry Andric int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
3070b57cec5SDimitry Andric     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
3080b57cec5SDimitry Andric     A &addressSpace, Registers_x86_64 &registers) {
3090b57cec5SDimitry Andric   uint32_t savedRegistersOffset =
3100b57cec5SDimitry Andric       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
3110b57cec5SDimitry Andric   uint32_t savedRegistersLocations =
3120b57cec5SDimitry Andric       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric   uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
3150b57cec5SDimitry Andric   for (int i = 0; i < 5; ++i) {
3160b57cec5SDimitry Andric     switch (savedRegistersLocations & 0x7) {
3170b57cec5SDimitry Andric     case UNWIND_X86_64_REG_NONE:
3180b57cec5SDimitry Andric       // no register saved in this slot
3190b57cec5SDimitry Andric       break;
3200b57cec5SDimitry Andric     case UNWIND_X86_64_REG_RBX:
3210b57cec5SDimitry Andric       registers.setRBX(addressSpace.get64(savedRegisters));
3220b57cec5SDimitry Andric       break;
3230b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R12:
3240b57cec5SDimitry Andric       registers.setR12(addressSpace.get64(savedRegisters));
3250b57cec5SDimitry Andric       break;
3260b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R13:
3270b57cec5SDimitry Andric       registers.setR13(addressSpace.get64(savedRegisters));
3280b57cec5SDimitry Andric       break;
3290b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R14:
3300b57cec5SDimitry Andric       registers.setR14(addressSpace.get64(savedRegisters));
3310b57cec5SDimitry Andric       break;
3320b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R15:
3330b57cec5SDimitry Andric       registers.setR15(addressSpace.get64(savedRegisters));
3340b57cec5SDimitry Andric       break;
3350b57cec5SDimitry Andric     default:
3360b57cec5SDimitry Andric       (void)functionStart;
3370b57cec5SDimitry Andric       _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
3380b57cec5SDimitry Andric                            "function starting at 0x%llX",
3390b57cec5SDimitry Andric                             compactEncoding, functionStart);
3400b57cec5SDimitry Andric       _LIBUNWIND_ABORT("invalid compact unwind encoding");
3410b57cec5SDimitry Andric     }
3420b57cec5SDimitry Andric     savedRegisters += 8;
3430b57cec5SDimitry Andric     savedRegistersLocations = (savedRegistersLocations >> 3);
3440b57cec5SDimitry Andric   }
3450b57cec5SDimitry Andric   frameUnwind(addressSpace, registers);
3460b57cec5SDimitry Andric   return UNW_STEP_SUCCESS;
3470b57cec5SDimitry Andric }
3480b57cec5SDimitry Andric 
3490b57cec5SDimitry Andric template <typename A>
stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding,uint64_t functionStart,A & addressSpace,Registers_x86_64 & registers,bool indirectStackSize)3500b57cec5SDimitry Andric int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
3510b57cec5SDimitry Andric     compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
3520b57cec5SDimitry Andric     Registers_x86_64 &registers, bool indirectStackSize) {
3530b57cec5SDimitry Andric   uint32_t stackSizeEncoded =
3540b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
3550b57cec5SDimitry Andric   uint32_t stackAdjust =
3560b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
3570b57cec5SDimitry Andric   uint32_t regCount =
3580b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
3590b57cec5SDimitry Andric   uint32_t permutation =
3600b57cec5SDimitry Andric       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
3610b57cec5SDimitry Andric   uint32_t stackSize = stackSizeEncoded * 8;
3620b57cec5SDimitry Andric   if (indirectStackSize) {
3630b57cec5SDimitry Andric     // stack size is encoded in subl $xxx,%esp instruction
3640b57cec5SDimitry Andric     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
3650b57cec5SDimitry Andric     stackSize = subl + 8 * stackAdjust;
3660b57cec5SDimitry Andric   }
3670b57cec5SDimitry Andric   // decompress permutation
3680b57cec5SDimitry Andric   uint32_t permunreg[6];
3690b57cec5SDimitry Andric   switch (regCount) {
3700b57cec5SDimitry Andric   case 6:
3710b57cec5SDimitry Andric     permunreg[0] = permutation / 120;
3720b57cec5SDimitry Andric     permutation -= (permunreg[0] * 120);
3730b57cec5SDimitry Andric     permunreg[1] = permutation / 24;
3740b57cec5SDimitry Andric     permutation -= (permunreg[1] * 24);
3750b57cec5SDimitry Andric     permunreg[2] = permutation / 6;
3760b57cec5SDimitry Andric     permutation -= (permunreg[2] * 6);
3770b57cec5SDimitry Andric     permunreg[3] = permutation / 2;
3780b57cec5SDimitry Andric     permutation -= (permunreg[3] * 2);
3790b57cec5SDimitry Andric     permunreg[4] = permutation;
3800b57cec5SDimitry Andric     permunreg[5] = 0;
3810b57cec5SDimitry Andric     break;
3820b57cec5SDimitry Andric   case 5:
3830b57cec5SDimitry Andric     permunreg[0] = permutation / 120;
3840b57cec5SDimitry Andric     permutation -= (permunreg[0] * 120);
3850b57cec5SDimitry Andric     permunreg[1] = permutation / 24;
3860b57cec5SDimitry Andric     permutation -= (permunreg[1] * 24);
3870b57cec5SDimitry Andric     permunreg[2] = permutation / 6;
3880b57cec5SDimitry Andric     permutation -= (permunreg[2] * 6);
3890b57cec5SDimitry Andric     permunreg[3] = permutation / 2;
3900b57cec5SDimitry Andric     permutation -= (permunreg[3] * 2);
3910b57cec5SDimitry Andric     permunreg[4] = permutation;
3920b57cec5SDimitry Andric     break;
3930b57cec5SDimitry Andric   case 4:
3940b57cec5SDimitry Andric     permunreg[0] = permutation / 60;
3950b57cec5SDimitry Andric     permutation -= (permunreg[0] * 60);
3960b57cec5SDimitry Andric     permunreg[1] = permutation / 12;
3970b57cec5SDimitry Andric     permutation -= (permunreg[1] * 12);
3980b57cec5SDimitry Andric     permunreg[2] = permutation / 3;
3990b57cec5SDimitry Andric     permutation -= (permunreg[2] * 3);
4000b57cec5SDimitry Andric     permunreg[3] = permutation;
4010b57cec5SDimitry Andric     break;
4020b57cec5SDimitry Andric   case 3:
4030b57cec5SDimitry Andric     permunreg[0] = permutation / 20;
4040b57cec5SDimitry Andric     permutation -= (permunreg[0] * 20);
4050b57cec5SDimitry Andric     permunreg[1] = permutation / 4;
4060b57cec5SDimitry Andric     permutation -= (permunreg[1] * 4);
4070b57cec5SDimitry Andric     permunreg[2] = permutation;
4080b57cec5SDimitry Andric     break;
4090b57cec5SDimitry Andric   case 2:
4100b57cec5SDimitry Andric     permunreg[0] = permutation / 5;
4110b57cec5SDimitry Andric     permutation -= (permunreg[0] * 5);
4120b57cec5SDimitry Andric     permunreg[1] = permutation;
4130b57cec5SDimitry Andric     break;
4140b57cec5SDimitry Andric   case 1:
4150b57cec5SDimitry Andric     permunreg[0] = permutation;
4160b57cec5SDimitry Andric     break;
4170b57cec5SDimitry Andric   }
4180b57cec5SDimitry Andric   // re-number registers back to standard numbers
4190b57cec5SDimitry Andric   int registersSaved[6];
4200b57cec5SDimitry Andric   bool used[7] = { false, false, false, false, false, false, false };
4210b57cec5SDimitry Andric   for (uint32_t i = 0; i < regCount; ++i) {
4220b57cec5SDimitry Andric     uint32_t renum = 0;
4230b57cec5SDimitry Andric     for (int u = 1; u < 7; ++u) {
4240b57cec5SDimitry Andric       if (!used[u]) {
4250b57cec5SDimitry Andric         if (renum == permunreg[i]) {
4260b57cec5SDimitry Andric           registersSaved[i] = u;
4270b57cec5SDimitry Andric           used[u] = true;
4280b57cec5SDimitry Andric           break;
4290b57cec5SDimitry Andric         }
4300b57cec5SDimitry Andric         ++renum;
4310b57cec5SDimitry Andric       }
4320b57cec5SDimitry Andric     }
4330b57cec5SDimitry Andric   }
4340b57cec5SDimitry Andric   uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
4350b57cec5SDimitry Andric   for (uint32_t i = 0; i < regCount; ++i) {
4360b57cec5SDimitry Andric     switch (registersSaved[i]) {
4370b57cec5SDimitry Andric     case UNWIND_X86_64_REG_RBX:
4380b57cec5SDimitry Andric       registers.setRBX(addressSpace.get64(savedRegisters));
4390b57cec5SDimitry Andric       break;
4400b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R12:
4410b57cec5SDimitry Andric       registers.setR12(addressSpace.get64(savedRegisters));
4420b57cec5SDimitry Andric       break;
4430b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R13:
4440b57cec5SDimitry Andric       registers.setR13(addressSpace.get64(savedRegisters));
4450b57cec5SDimitry Andric       break;
4460b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R14:
4470b57cec5SDimitry Andric       registers.setR14(addressSpace.get64(savedRegisters));
4480b57cec5SDimitry Andric       break;
4490b57cec5SDimitry Andric     case UNWIND_X86_64_REG_R15:
4500b57cec5SDimitry Andric       registers.setR15(addressSpace.get64(savedRegisters));
4510b57cec5SDimitry Andric       break;
4520b57cec5SDimitry Andric     case UNWIND_X86_64_REG_RBP:
4530b57cec5SDimitry Andric       registers.setRBP(addressSpace.get64(savedRegisters));
4540b57cec5SDimitry Andric       break;
4550b57cec5SDimitry Andric     default:
4560b57cec5SDimitry Andric       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
4570b57cec5SDimitry Andric                            "function starting at 0x%llX",
4580b57cec5SDimitry Andric                             encoding, functionStart);
4590b57cec5SDimitry Andric       _LIBUNWIND_ABORT("invalid compact unwind encoding");
4600b57cec5SDimitry Andric     }
4610b57cec5SDimitry Andric     savedRegisters += 8;
4620b57cec5SDimitry Andric   }
4630b57cec5SDimitry Andric   framelessUnwind(addressSpace, savedRegisters, registers);
4640b57cec5SDimitry Andric   return UNW_STEP_SUCCESS;
4650b57cec5SDimitry Andric }
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric 
4680b57cec5SDimitry Andric template <typename A>
frameUnwind(A & addressSpace,Registers_x86_64 & registers)4690b57cec5SDimitry Andric void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
4700b57cec5SDimitry Andric                                             Registers_x86_64 &registers) {
4710b57cec5SDimitry Andric   uint64_t rbp = registers.getRBP();
4720b57cec5SDimitry Andric   // ebp points to old ebp
4730b57cec5SDimitry Andric   registers.setRBP(addressSpace.get64(rbp));
4740b57cec5SDimitry Andric   // old esp is ebp less saved ebp and return address
4750b57cec5SDimitry Andric   registers.setSP(rbp + 16);
4760b57cec5SDimitry Andric   // pop return address into eip
4770b57cec5SDimitry Andric   registers.setIP(addressSpace.get64(rbp + 8));
4780b57cec5SDimitry Andric }
4790b57cec5SDimitry Andric 
4800b57cec5SDimitry Andric template <typename A>
framelessUnwind(A & addressSpace,uint64_t returnAddressLocation,Registers_x86_64 & registers)4810b57cec5SDimitry Andric void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
4820b57cec5SDimitry Andric                                                 uint64_t returnAddressLocation,
4830b57cec5SDimitry Andric                                                 Registers_x86_64 &registers) {
4840b57cec5SDimitry Andric   // return address is on stack after last saved register
4850b57cec5SDimitry Andric   registers.setIP(addressSpace.get64(returnAddressLocation));
4860b57cec5SDimitry Andric   // old esp is before return address
4870b57cec5SDimitry Andric   registers.setSP(returnAddressLocation + 8);
4880b57cec5SDimitry Andric }
4890b57cec5SDimitry Andric #endif // _LIBUNWIND_TARGET_X86_64
4900b57cec5SDimitry Andric 
4910b57cec5SDimitry Andric 
4920b57cec5SDimitry Andric 
4930b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_AARCH64)
4940b57cec5SDimitry Andric /// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
4950b57cec5SDimitry Andric /// unwind) by modifying a Registers_arm64 register set
4960b57cec5SDimitry Andric template <typename A>
4970b57cec5SDimitry Andric class CompactUnwinder_arm64 {
4980b57cec5SDimitry Andric public:
4990b57cec5SDimitry Andric 
5000b57cec5SDimitry Andric   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
5010b57cec5SDimitry Andric                                      uint64_t functionStart, A &addressSpace,
5020b57cec5SDimitry Andric                                      Registers_arm64 &registers);
5030b57cec5SDimitry Andric 
5040b57cec5SDimitry Andric private:
5050b57cec5SDimitry Andric   typename A::pint_t pint_t;
5060b57cec5SDimitry Andric 
5070b57cec5SDimitry Andric   static int
5080b57cec5SDimitry Andric       stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
5090b57cec5SDimitry Andric                                    uint64_t functionStart, A &addressSpace,
5100b57cec5SDimitry Andric                                    Registers_arm64 &registers);
5110b57cec5SDimitry Andric   static int stepWithCompactEncodingFrameless(
5120b57cec5SDimitry Andric       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
5130b57cec5SDimitry Andric       A &addressSpace, Registers_arm64 &registers);
5140b57cec5SDimitry Andric };
5150b57cec5SDimitry Andric 
5160b57cec5SDimitry Andric template <typename A>
stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,uint64_t functionStart,A & addressSpace,Registers_arm64 & registers)5170b57cec5SDimitry Andric int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
5180b57cec5SDimitry Andric     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
5190b57cec5SDimitry Andric     A &addressSpace, Registers_arm64 &registers) {
5200b57cec5SDimitry Andric   switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
5210b57cec5SDimitry Andric   case UNWIND_ARM64_MODE_FRAME:
5220b57cec5SDimitry Andric     return stepWithCompactEncodingFrame(compactEncoding, functionStart,
5230b57cec5SDimitry Andric                                         addressSpace, registers);
5240b57cec5SDimitry Andric   case UNWIND_ARM64_MODE_FRAMELESS:
5250b57cec5SDimitry Andric     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
5260b57cec5SDimitry Andric                                             addressSpace, registers);
5270b57cec5SDimitry Andric   }
5280b57cec5SDimitry Andric   _LIBUNWIND_ABORT("invalid compact unwind encoding");
5290b57cec5SDimitry Andric }
5300b57cec5SDimitry Andric 
5310b57cec5SDimitry Andric template <typename A>
stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding,uint64_t,A & addressSpace,Registers_arm64 & registers)5320b57cec5SDimitry Andric int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
5330b57cec5SDimitry Andric     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
5340b57cec5SDimitry Andric     Registers_arm64 &registers) {
5350b57cec5SDimitry Andric   uint32_t stackSize =
5360b57cec5SDimitry Andric       16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
5370b57cec5SDimitry Andric 
5380b57cec5SDimitry Andric   uint64_t savedRegisterLoc = registers.getSP() + stackSize;
5390b57cec5SDimitry Andric 
5400b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
541349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
5420b57cec5SDimitry Andric     savedRegisterLoc -= 8;
543349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
5440b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5450b57cec5SDimitry Andric   }
5460b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
547349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
5480b57cec5SDimitry Andric     savedRegisterLoc -= 8;
549349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
5500b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5510b57cec5SDimitry Andric   }
5520b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
553349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
5540b57cec5SDimitry Andric     savedRegisterLoc -= 8;
555349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
5560b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5570b57cec5SDimitry Andric   }
5580b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
559349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
5600b57cec5SDimitry Andric     savedRegisterLoc -= 8;
561349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
5620b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5630b57cec5SDimitry Andric   }
5640b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
565349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
5660b57cec5SDimitry Andric     savedRegisterLoc -= 8;
567349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
5680b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5690b57cec5SDimitry Andric   }
5700b57cec5SDimitry Andric 
5710b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
572349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V8,
5730b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
5740b57cec5SDimitry Andric     savedRegisterLoc -= 8;
575349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V9,
5760b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
5770b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5780b57cec5SDimitry Andric   }
5790b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
580349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V10,
5810b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
5820b57cec5SDimitry Andric     savedRegisterLoc -= 8;
583349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V11,
5840b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
5850b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5860b57cec5SDimitry Andric   }
5870b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
588349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V12,
5890b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
5900b57cec5SDimitry Andric     savedRegisterLoc -= 8;
591349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V13,
5920b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
5930b57cec5SDimitry Andric     savedRegisterLoc -= 8;
5940b57cec5SDimitry Andric   }
5950b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
596349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V14,
5970b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
5980b57cec5SDimitry Andric     savedRegisterLoc -= 8;
599349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V15,
6000b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6010b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6020b57cec5SDimitry Andric   }
6030b57cec5SDimitry Andric 
6040b57cec5SDimitry Andric   // subtract stack size off of sp
6050b57cec5SDimitry Andric   registers.setSP(savedRegisterLoc);
6060b57cec5SDimitry Andric 
6070b57cec5SDimitry Andric   // set pc to be value in lr
608349cc55cSDimitry Andric   registers.setIP(registers.getRegister(UNW_AARCH64_LR));
6090b57cec5SDimitry Andric 
6100b57cec5SDimitry Andric   return UNW_STEP_SUCCESS;
6110b57cec5SDimitry Andric }
6120b57cec5SDimitry Andric 
6130b57cec5SDimitry Andric template <typename A>
stepWithCompactEncodingFrame(compact_unwind_encoding_t encoding,uint64_t,A & addressSpace,Registers_arm64 & registers)6140b57cec5SDimitry Andric int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
6150b57cec5SDimitry Andric     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
6160b57cec5SDimitry Andric     Registers_arm64 &registers) {
6170b57cec5SDimitry Andric   uint64_t savedRegisterLoc = registers.getFP() - 8;
6180b57cec5SDimitry Andric 
6190b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
620349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
6210b57cec5SDimitry Andric     savedRegisterLoc -= 8;
622349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
6230b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6240b57cec5SDimitry Andric   }
6250b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
626349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
6270b57cec5SDimitry Andric     savedRegisterLoc -= 8;
628349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
6290b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6300b57cec5SDimitry Andric   }
6310b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
632349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
6330b57cec5SDimitry Andric     savedRegisterLoc -= 8;
634349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
6350b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6360b57cec5SDimitry Andric   }
6370b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
638349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
6390b57cec5SDimitry Andric     savedRegisterLoc -= 8;
640349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
6410b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6420b57cec5SDimitry Andric   }
6430b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
644349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
6450b57cec5SDimitry Andric     savedRegisterLoc -= 8;
646349cc55cSDimitry Andric     registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
6470b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6480b57cec5SDimitry Andric   }
6490b57cec5SDimitry Andric 
6500b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
651349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V8,
6520b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6530b57cec5SDimitry Andric     savedRegisterLoc -= 8;
654349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V9,
6550b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6560b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6570b57cec5SDimitry Andric   }
6580b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
659349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V10,
6600b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6610b57cec5SDimitry Andric     savedRegisterLoc -= 8;
662349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V11,
6630b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6640b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6650b57cec5SDimitry Andric   }
6660b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
667349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V12,
6680b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6690b57cec5SDimitry Andric     savedRegisterLoc -= 8;
670349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V13,
6710b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6720b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6730b57cec5SDimitry Andric   }
6740b57cec5SDimitry Andric   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
675349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V14,
6760b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6770b57cec5SDimitry Andric     savedRegisterLoc -= 8;
678349cc55cSDimitry Andric     registers.setFloatRegister(UNW_AARCH64_V15,
6790b57cec5SDimitry Andric                                addressSpace.getDouble(savedRegisterLoc));
6800b57cec5SDimitry Andric     savedRegisterLoc -= 8;
6810b57cec5SDimitry Andric   }
6820b57cec5SDimitry Andric 
6830b57cec5SDimitry Andric   uint64_t fp = registers.getFP();
6840b57cec5SDimitry Andric   // fp points to old fp
6850b57cec5SDimitry Andric   registers.setFP(addressSpace.get64(fp));
6860b57cec5SDimitry Andric   // old sp is fp less saved fp and lr
6870b57cec5SDimitry Andric   registers.setSP(fp + 16);
6880b57cec5SDimitry Andric   // pop return address into pc
6890b57cec5SDimitry Andric   registers.setIP(addressSpace.get64(fp + 8));
6900b57cec5SDimitry Andric 
6910b57cec5SDimitry Andric   return UNW_STEP_SUCCESS;
6920b57cec5SDimitry Andric }
6930b57cec5SDimitry Andric #endif // _LIBUNWIND_TARGET_AARCH64
6940b57cec5SDimitry Andric 
6950b57cec5SDimitry Andric 
6960b57cec5SDimitry Andric } // namespace libunwind
6970b57cec5SDimitry Andric 
6980b57cec5SDimitry Andric #endif // __COMPACT_UNWINDER_HPP__
699