xref: /freebsd/contrib/llvm-project/libunwind/src/CompactUnwinder.hpp (revision ce6a89e27cd190313be39bb479880aeda4778436)
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 
26 namespace 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
31 template <typename A>
32 class CompactUnwinder_x86 {
33 public:
34 
35   static int stepWithCompactEncoding(compact_unwind_encoding_t info,
36                                      uint32_t functionStart, A &addressSpace,
37                                      Registers_x86 &registers);
38 
39 private:
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 
55 template <typename A>
56 int 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 
73 template <typename A>
74 int 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 
117 template <typename A>
118 int 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 
236 template <typename A>
237 void 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 
248 template <typename A>
249 void 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
263 template <typename A>
264 class CompactUnwinder_x86_64 {
265 public:
266 
267   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
268                                      uint64_t functionStart, A &addressSpace,
269                                      Registers_x86_64 &registers);
270 
271 private:
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 
286 template <typename A>
287 int 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 
304 template <typename A>
305 int 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 
348 template <typename A>
349 int 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 
467 template <typename A>
468 void 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 
479 template <typename A>
480 void 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
495 template <typename A>
496 class CompactUnwinder_arm64 {
497 public:
498 
499   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
500                                      uint64_t functionStart, A &addressSpace,
501                                      Registers_arm64 &registers);
502 
503 private:
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 
515 template <typename A>
516 int 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 
530 template <typename A>
531 int 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 
612 template <typename A>
613 int 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