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