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