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 
28 namespace libunwind {
29 
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>
stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,uint32_t functionStart,A & addressSpace,Registers_x86 & registers)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>
stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,uint32_t functionStart,A & addressSpace,Registers_x86 & registers)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\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 
118 template <typename A>
stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding,uint32_t functionStart,A & addressSpace,Registers_x86 & registers,bool indirectStackSize)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\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 
237 template <typename A>
frameUnwind(A & addressSpace,Registers_x86 & registers)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>
framelessUnwind(A & addressSpace,typename A::pint_t returnAddressLocation,Registers_x86 & registers)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 
259 
260 /// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
261 /// unwind) by modifying a Registers_x86_64 register set
262 template <typename A>
263 class CompactUnwinder_x86_64 {
264 public:
265 
266   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
267                                      uint64_t functionStart, A &addressSpace,
268                                      Registers_x86_64 &registers);
269 
270 private:
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 
285 template <typename A>
stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,uint64_t functionStart,A & addressSpace,Registers_x86_64 & registers)286 int 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 
303 template <typename A>
stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,uint64_t functionStart,A & addressSpace,Registers_x86_64 & registers)304 int 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 
347 template <typename A>
stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding,uint64_t functionStart,A & addressSpace,Registers_x86_64 & registers,bool indirectStackSize)348 int 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 
466 template <typename A>
frameUnwind(A & addressSpace,Registers_x86_64 & registers)467 void 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 
478 template <typename A>
framelessUnwind(A & addressSpace,uint64_t returnAddressLocation,Registers_x86_64 & registers)479 void 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
492 template <typename A>
493 class CompactUnwinder_arm64 {
494 public:
495 
496   static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
497                                      uint64_t functionStart, A &addressSpace,
498                                      Registers_arm64 &registers);
499 
500 private:
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 
512 template <typename A>
stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,uint64_t functionStart,A & addressSpace,Registers_arm64 & registers)513 int 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 
527 template <typename A>
stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding,uint64_t,A & addressSpace,Registers_arm64 & registers)528 int 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 
609 template <typename A>
stepWithCompactEncodingFrame(compact_unwind_encoding_t encoding,uint64_t,A & addressSpace,Registers_arm64 & registers)610 int 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