xref: /freebsd/contrib/llvm-project/libunwind/src/CompactUnwinder.hpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===----------------------------------------------------------------------===//
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 #include "libunwind_ext.h"
23 
24 #define EXTRACT_BITS(value, mask)                                              \
25   ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
26 
27 namespace libunwind {
28 
29 #if defined(_LIBUNWIND_TARGET_I386)
30 /// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
31 /// unwind) by modifying a Registers_x86 register set
32 template <typename A>
33 class CompactUnwinder_x86 {
34 public:
35 
36   static int stepWithCompactEncoding(compact_unwind_encoding_t info,
37                                      uint32_t functionStart, A &addressSpace,
38                                      Registers_x86 &registers);
39 
40 private:
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 
56 template <typename A>
57 int 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 
74 template <typename A>
75 int 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",
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 
118 template <typename A>
119 int 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",
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 
237 template <typename A>
238 void 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 
249 template <typename A>
250 void 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 #endif // _LIBUNWIND_TARGET_I386
259 
260 
261 #if defined(_LIBUNWIND_TARGET_X86_64)
262 /// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
263 /// unwind) by modifying a Registers_x86_64 register set
264 template <typename A>
265 class CompactUnwinder_x86_64 {
266 public:
267 
268   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
269                                      uint64_t functionStart, A &addressSpace,
270                                      Registers_x86_64 &registers);
271 
272 private:
273   typename A::pint_t pint_t;
274 
275   static void frameUnwind(A &addressSpace, Registers_x86_64 &registers);
276   static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
277                               Registers_x86_64 &registers);
278   static int
279       stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
280                                       uint64_t functionStart, A &addressSpace,
281                                       Registers_x86_64 &registers);
282   static int stepWithCompactEncodingFrameless(
283       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
284       A &addressSpace, Registers_x86_64 &registers, bool indirectStackSize);
285 };
286 
287 template <typename A>
288 int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
289     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
290     A &addressSpace, Registers_x86_64 &registers) {
291   switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
292   case UNWIND_X86_64_MODE_RBP_FRAME:
293     return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
294                                            addressSpace, registers);
295   case UNWIND_X86_64_MODE_STACK_IMMD:
296     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
297                                             addressSpace, registers, false);
298   case UNWIND_X86_64_MODE_STACK_IND:
299     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
300                                             addressSpace, registers, true);
301   }
302   _LIBUNWIND_ABORT("invalid compact unwind encoding");
303 }
304 
305 template <typename A>
306 int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
307     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
308     A &addressSpace, Registers_x86_64 &registers) {
309   uint32_t savedRegistersOffset =
310       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
311   uint32_t savedRegistersLocations =
312       EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
313 
314   uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
315   for (int i = 0; i < 5; ++i) {
316     switch (savedRegistersLocations & 0x7) {
317     case UNWIND_X86_64_REG_NONE:
318       // no register saved in this slot
319       break;
320     case UNWIND_X86_64_REG_RBX:
321       registers.setRBX(addressSpace.get64(savedRegisters));
322       break;
323     case UNWIND_X86_64_REG_R12:
324       registers.setR12(addressSpace.get64(savedRegisters));
325       break;
326     case UNWIND_X86_64_REG_R13:
327       registers.setR13(addressSpace.get64(savedRegisters));
328       break;
329     case UNWIND_X86_64_REG_R14:
330       registers.setR14(addressSpace.get64(savedRegisters));
331       break;
332     case UNWIND_X86_64_REG_R15:
333       registers.setR15(addressSpace.get64(savedRegisters));
334       break;
335     default:
336       (void)functionStart;
337       _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
338                            "function starting at 0x%llX",
339                             compactEncoding, functionStart);
340       _LIBUNWIND_ABORT("invalid compact unwind encoding");
341     }
342     savedRegisters += 8;
343     savedRegistersLocations = (savedRegistersLocations >> 3);
344   }
345   frameUnwind(addressSpace, registers);
346   return UNW_STEP_SUCCESS;
347 }
348 
349 template <typename A>
350 int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
351     compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
352     Registers_x86_64 &registers, bool indirectStackSize) {
353   uint32_t stackSizeEncoded =
354       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
355   uint32_t stackAdjust =
356       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
357   uint32_t regCount =
358       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
359   uint32_t permutation =
360       EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
361   uint32_t stackSize = stackSizeEncoded * 8;
362   if (indirectStackSize) {
363     // stack size is encoded in subl $xxx,%esp instruction
364     uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
365     stackSize = subl + 8 * stackAdjust;
366   }
367   // decompress permutation
368   uint32_t permunreg[6];
369   switch (regCount) {
370   case 6:
371     permunreg[0] = permutation / 120;
372     permutation -= (permunreg[0] * 120);
373     permunreg[1] = permutation / 24;
374     permutation -= (permunreg[1] * 24);
375     permunreg[2] = permutation / 6;
376     permutation -= (permunreg[2] * 6);
377     permunreg[3] = permutation / 2;
378     permutation -= (permunreg[3] * 2);
379     permunreg[4] = permutation;
380     permunreg[5] = 0;
381     break;
382   case 5:
383     permunreg[0] = permutation / 120;
384     permutation -= (permunreg[0] * 120);
385     permunreg[1] = permutation / 24;
386     permutation -= (permunreg[1] * 24);
387     permunreg[2] = permutation / 6;
388     permutation -= (permunreg[2] * 6);
389     permunreg[3] = permutation / 2;
390     permutation -= (permunreg[3] * 2);
391     permunreg[4] = permutation;
392     break;
393   case 4:
394     permunreg[0] = permutation / 60;
395     permutation -= (permunreg[0] * 60);
396     permunreg[1] = permutation / 12;
397     permutation -= (permunreg[1] * 12);
398     permunreg[2] = permutation / 3;
399     permutation -= (permunreg[2] * 3);
400     permunreg[3] = permutation;
401     break;
402   case 3:
403     permunreg[0] = permutation / 20;
404     permutation -= (permunreg[0] * 20);
405     permunreg[1] = permutation / 4;
406     permutation -= (permunreg[1] * 4);
407     permunreg[2] = permutation;
408     break;
409   case 2:
410     permunreg[0] = permutation / 5;
411     permutation -= (permunreg[0] * 5);
412     permunreg[1] = permutation;
413     break;
414   case 1:
415     permunreg[0] = permutation;
416     break;
417   }
418   // re-number registers back to standard numbers
419   int registersSaved[6];
420   bool used[7] = { false, false, false, false, false, false, false };
421   for (uint32_t i = 0; i < regCount; ++i) {
422     uint32_t renum = 0;
423     for (int u = 1; u < 7; ++u) {
424       if (!used[u]) {
425         if (renum == permunreg[i]) {
426           registersSaved[i] = u;
427           used[u] = true;
428           break;
429         }
430         ++renum;
431       }
432     }
433   }
434   uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
435   for (uint32_t i = 0; i < regCount; ++i) {
436     switch (registersSaved[i]) {
437     case UNWIND_X86_64_REG_RBX:
438       registers.setRBX(addressSpace.get64(savedRegisters));
439       break;
440     case UNWIND_X86_64_REG_R12:
441       registers.setR12(addressSpace.get64(savedRegisters));
442       break;
443     case UNWIND_X86_64_REG_R13:
444       registers.setR13(addressSpace.get64(savedRegisters));
445       break;
446     case UNWIND_X86_64_REG_R14:
447       registers.setR14(addressSpace.get64(savedRegisters));
448       break;
449     case UNWIND_X86_64_REG_R15:
450       registers.setR15(addressSpace.get64(savedRegisters));
451       break;
452     case UNWIND_X86_64_REG_RBP:
453       registers.setRBP(addressSpace.get64(savedRegisters));
454       break;
455     default:
456       _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
457                            "function starting at 0x%llX",
458                             encoding, functionStart);
459       _LIBUNWIND_ABORT("invalid compact unwind encoding");
460     }
461     savedRegisters += 8;
462   }
463   framelessUnwind(addressSpace, savedRegisters, registers);
464   return UNW_STEP_SUCCESS;
465 }
466 
467 
468 template <typename A>
469 void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
470                                             Registers_x86_64 &registers) {
471   uint64_t rbp = registers.getRBP();
472   // ebp points to old ebp
473   registers.setRBP(addressSpace.get64(rbp));
474   // old esp is ebp less saved ebp and return address
475   registers.setSP(rbp + 16);
476   // pop return address into eip
477   registers.setIP(addressSpace.get64(rbp + 8));
478 }
479 
480 template <typename A>
481 void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
482                                                 uint64_t returnAddressLocation,
483                                                 Registers_x86_64 &registers) {
484   // return address is on stack after last saved register
485   registers.setIP(addressSpace.get64(returnAddressLocation));
486   // old esp is before return address
487   registers.setSP(returnAddressLocation + 8);
488 }
489 #endif // _LIBUNWIND_TARGET_X86_64
490 
491 
492 
493 #if defined(_LIBUNWIND_TARGET_AARCH64)
494 /// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
495 /// unwind) by modifying a Registers_arm64 register set
496 template <typename A>
497 class CompactUnwinder_arm64 {
498 public:
499 
500   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
501                                      uint64_t functionStart, A &addressSpace,
502                                      Registers_arm64 &registers);
503 
504 private:
505   typename A::pint_t pint_t;
506 
507   static int
508       stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
509                                    uint64_t functionStart, A &addressSpace,
510                                    Registers_arm64 &registers);
511   static int stepWithCompactEncodingFrameless(
512       compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
513       A &addressSpace, Registers_arm64 &registers);
514 };
515 
516 template <typename A>
517 int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
518     compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
519     A &addressSpace, Registers_arm64 &registers) {
520   switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
521   case UNWIND_ARM64_MODE_FRAME:
522     return stepWithCompactEncodingFrame(compactEncoding, functionStart,
523                                         addressSpace, registers);
524   case UNWIND_ARM64_MODE_FRAMELESS:
525     return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
526                                             addressSpace, registers);
527   }
528   _LIBUNWIND_ABORT("invalid compact unwind encoding");
529 }
530 
531 template <typename A>
532 int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
533     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
534     Registers_arm64 &registers) {
535   uint32_t stackSize =
536       16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
537 
538   uint64_t savedRegisterLoc = registers.getSP() + stackSize;
539 
540   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
541     registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
542     savedRegisterLoc -= 8;
543     registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
544     savedRegisterLoc -= 8;
545   }
546   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
547     registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
548     savedRegisterLoc -= 8;
549     registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
550     savedRegisterLoc -= 8;
551   }
552   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
553     registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
554     savedRegisterLoc -= 8;
555     registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
556     savedRegisterLoc -= 8;
557   }
558   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
559     registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
560     savedRegisterLoc -= 8;
561     registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
562     savedRegisterLoc -= 8;
563   }
564   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
565     registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
566     savedRegisterLoc -= 8;
567     registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
568     savedRegisterLoc -= 8;
569   }
570 
571   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
572     registers.setFloatRegister(UNW_AARCH64_V8,
573                                addressSpace.getDouble(savedRegisterLoc));
574     savedRegisterLoc -= 8;
575     registers.setFloatRegister(UNW_AARCH64_V9,
576                                addressSpace.getDouble(savedRegisterLoc));
577     savedRegisterLoc -= 8;
578   }
579   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
580     registers.setFloatRegister(UNW_AARCH64_V10,
581                                addressSpace.getDouble(savedRegisterLoc));
582     savedRegisterLoc -= 8;
583     registers.setFloatRegister(UNW_AARCH64_V11,
584                                addressSpace.getDouble(savedRegisterLoc));
585     savedRegisterLoc -= 8;
586   }
587   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
588     registers.setFloatRegister(UNW_AARCH64_V12,
589                                addressSpace.getDouble(savedRegisterLoc));
590     savedRegisterLoc -= 8;
591     registers.setFloatRegister(UNW_AARCH64_V13,
592                                addressSpace.getDouble(savedRegisterLoc));
593     savedRegisterLoc -= 8;
594   }
595   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
596     registers.setFloatRegister(UNW_AARCH64_V14,
597                                addressSpace.getDouble(savedRegisterLoc));
598     savedRegisterLoc -= 8;
599     registers.setFloatRegister(UNW_AARCH64_V15,
600                                addressSpace.getDouble(savedRegisterLoc));
601     savedRegisterLoc -= 8;
602   }
603 
604   // subtract stack size off of sp
605   registers.setSP(savedRegisterLoc);
606 
607   // set pc to be value in lr
608   registers.setIP(registers.getRegister(UNW_AARCH64_LR));
609 
610   return UNW_STEP_SUCCESS;
611 }
612 
613 template <typename A>
614 int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
615     compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
616     Registers_arm64 &registers) {
617   uint64_t savedRegisterLoc = registers.getFP() - 8;
618 
619   if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
620     registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
621     savedRegisterLoc -= 8;
622     registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
623     savedRegisterLoc -= 8;
624   }
625   if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
626     registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
627     savedRegisterLoc -= 8;
628     registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
629     savedRegisterLoc -= 8;
630   }
631   if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
632     registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
633     savedRegisterLoc -= 8;
634     registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
635     savedRegisterLoc -= 8;
636   }
637   if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
638     registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
639     savedRegisterLoc -= 8;
640     registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
641     savedRegisterLoc -= 8;
642   }
643   if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
644     registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
645     savedRegisterLoc -= 8;
646     registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
647     savedRegisterLoc -= 8;
648   }
649 
650   if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
651     registers.setFloatRegister(UNW_AARCH64_V8,
652                                addressSpace.getDouble(savedRegisterLoc));
653     savedRegisterLoc -= 8;
654     registers.setFloatRegister(UNW_AARCH64_V9,
655                                addressSpace.getDouble(savedRegisterLoc));
656     savedRegisterLoc -= 8;
657   }
658   if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
659     registers.setFloatRegister(UNW_AARCH64_V10,
660                                addressSpace.getDouble(savedRegisterLoc));
661     savedRegisterLoc -= 8;
662     registers.setFloatRegister(UNW_AARCH64_V11,
663                                addressSpace.getDouble(savedRegisterLoc));
664     savedRegisterLoc -= 8;
665   }
666   if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
667     registers.setFloatRegister(UNW_AARCH64_V12,
668                                addressSpace.getDouble(savedRegisterLoc));
669     savedRegisterLoc -= 8;
670     registers.setFloatRegister(UNW_AARCH64_V13,
671                                addressSpace.getDouble(savedRegisterLoc));
672     savedRegisterLoc -= 8;
673   }
674   if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
675     registers.setFloatRegister(UNW_AARCH64_V14,
676                                addressSpace.getDouble(savedRegisterLoc));
677     savedRegisterLoc -= 8;
678     registers.setFloatRegister(UNW_AARCH64_V15,
679                                addressSpace.getDouble(savedRegisterLoc));
680     savedRegisterLoc -= 8;
681   }
682 
683   uint64_t fp = registers.getFP();
684   // fp points to old fp
685   registers.setFP(addressSpace.get64(fp));
686   // old sp is fp less saved fp and lr
687   registers.setSP(fp + 16);
688   // pop return address into pc
689   registers.setIP(addressSpace.get64(fp + 8));
690 
691   return UNW_STEP_SUCCESS;
692 }
693 #endif // _LIBUNWIND_TARGET_AARCH64
694 
695 
696 } // namespace libunwind
697 
698 #endif // __COMPACT_UNWINDER_HPP__
699