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