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