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 case UNWIND_ARM64_MODE_FRAME_PAUTH_LR:
524 return stepWithCompactEncodingFrame(compactEncoding, functionStart,
525 addressSpace, registers);
526 case UNWIND_ARM64_MODE_FRAMELESS:
527 return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
528 addressSpace, registers);
529 }
530 _LIBUNWIND_ABORT("invalid compact unwind encoding");
531}
532
533template <typename A>
534int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
535 compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
536 Registers_arm64 &registers) {
537 uint32_t stackSize =
538 16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
539
540 uint64_t savedRegisterLoc = registers.getSP() + stackSize;
541
542 if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
543 registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
544 savedRegisterLoc -= 8;
545 registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
546 savedRegisterLoc -= 8;
547 }
548 if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
549 registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
550 savedRegisterLoc -= 8;
551 registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
552 savedRegisterLoc -= 8;
553 }
554 if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
555 registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
556 savedRegisterLoc -= 8;
557 registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
558 savedRegisterLoc -= 8;
559 }
560 if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
561 registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
562 savedRegisterLoc -= 8;
563 registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
564 savedRegisterLoc -= 8;
565 }
566 if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
567 registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
568 savedRegisterLoc -= 8;
569 registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
570 savedRegisterLoc -= 8;
571 }
572
573 if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
574 registers.setFloatRegister(UNW_AARCH64_V8,
575 addressSpace.getDouble(savedRegisterLoc));
576 savedRegisterLoc -= 8;
577 registers.setFloatRegister(UNW_AARCH64_V9,
578 addressSpace.getDouble(savedRegisterLoc));
579 savedRegisterLoc -= 8;
580 }
581 if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
582 registers.setFloatRegister(UNW_AARCH64_V10,
583 addressSpace.getDouble(savedRegisterLoc));
584 savedRegisterLoc -= 8;
585 registers.setFloatRegister(UNW_AARCH64_V11,
586 addressSpace.getDouble(savedRegisterLoc));
587 savedRegisterLoc -= 8;
588 }
589 if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
590 registers.setFloatRegister(UNW_AARCH64_V12,
591 addressSpace.getDouble(savedRegisterLoc));
592 savedRegisterLoc -= 8;
593 registers.setFloatRegister(UNW_AARCH64_V13,
594 addressSpace.getDouble(savedRegisterLoc));
595 savedRegisterLoc -= 8;
596 }
597 if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
598 registers.setFloatRegister(UNW_AARCH64_V14,
599 addressSpace.getDouble(savedRegisterLoc));
600 savedRegisterLoc -= 8;
601 registers.setFloatRegister(UNW_AARCH64_V15,
602 addressSpace.getDouble(savedRegisterLoc));
603 savedRegisterLoc -= 8;
604 }
605
606 // We load the link register prior to setting the new SP as the authentication
607 // schema for LR entangles the SP of the old frame into the diversifier.
608 Registers_arm64::reg_t linkRegister = registers.getRegister(UNW_AARCH64_LR);
609
610 // subtract stack size off of sp
611 registers.setSP(savedRegisterLoc);
612
613 // Set pc to be value in lr. This needs to be performed after the new SP has
614 // been set, as the PC authentication schema entangles the SP of the new
615 // frame.
616 registers.setIP(linkRegister);
617
618 return UNW_STEP_SUCCESS;
619}
620
621template <typename A>
622int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
623 compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
624 Registers_arm64 &registers) {
625 Registers_arm64::reg_t savedRegisterLoc = registers.getFP() - 8;
626
627 if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
628 registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
629 savedRegisterLoc -= 8;
630 registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
631 savedRegisterLoc -= 8;
632 }
633 if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
634 registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
635 savedRegisterLoc -= 8;
636 registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
637 savedRegisterLoc -= 8;
638 }
639 if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
640 registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
641 savedRegisterLoc -= 8;
642 registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
643 savedRegisterLoc -= 8;
644 }
645 if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
646 registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
647 savedRegisterLoc -= 8;
648 registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
649 savedRegisterLoc -= 8;
650 }
651 if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
652 registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
653 savedRegisterLoc -= 8;
654 registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
655 savedRegisterLoc -= 8;
656 }
657
658 if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
659 registers.setFloatRegister(UNW_AARCH64_V8,
660 addressSpace.getDouble(savedRegisterLoc));
661 savedRegisterLoc -= 8;
662 registers.setFloatRegister(UNW_AARCH64_V9,
663 addressSpace.getDouble(savedRegisterLoc));
664 savedRegisterLoc -= 8;
665 }
666 if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
667 registers.setFloatRegister(UNW_AARCH64_V10,
668 addressSpace.getDouble(savedRegisterLoc));
669 savedRegisterLoc -= 8;
670 registers.setFloatRegister(UNW_AARCH64_V11,
671 addressSpace.getDouble(savedRegisterLoc));
672 savedRegisterLoc -= 8;
673 }
674 if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
675 registers.setFloatRegister(UNW_AARCH64_V12,
676 addressSpace.getDouble(savedRegisterLoc));
677 savedRegisterLoc -= 8;
678 registers.setFloatRegister(UNW_AARCH64_V13,
679 addressSpace.getDouble(savedRegisterLoc));
680 savedRegisterLoc -= 8;
681 }
682 if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
683 registers.setFloatRegister(UNW_AARCH64_V14,
684 addressSpace.getDouble(savedRegisterLoc));
685 savedRegisterLoc -= 8;
686 registers.setFloatRegister(UNW_AARCH64_V15,
687 addressSpace.getDouble(savedRegisterLoc));
688 savedRegisterLoc -= 8;
689 }
690
691 Registers_arm64::reg_t fp = registers.getFP();
692
693 // fp points to old fp
694 registers.setFP(addressSpace.get64(fp));
695
696 // Old sp is fp less saved fp and lr. We need to set this prior to setting
697 // the lr as the pointer authentication schema for the lr incorporates the
698 // sp as part of the diversifier.
699 registers.setSP(fp + 16);
700
701 // pop return address into pc
702 Registers_arm64::reg_t linkRegister = addressSpace.get64(fp + 8);
703
704 // authenticate lr, if needed
705 if ((encoding & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_FRAME_PAUTH_LR) {
706 // Offset from the beginning of the range to the pacibsppc, in words.
707 uint32_t offset = (encoding & UNWIND_ARM64_PAUTH_LR_OFFSET_MASK) >>
708 UNWIND_ARM64_PAUTH_LR_OFFSET_SHIFT;
709
710 // Address of the pacibsppc
711 uint64_t pacibsppcAddr = functionStart + offset * 4;
712
713 registers.setIPPAuthLR(linkRegister, pacibsppcAddr);
714 } else {
715 registers.setIP(linkRegister);
716 }
717
718 return UNW_STEP_SUCCESS;
719}
720#endif // _LIBUNWIND_TARGET_AARCH64
721
722
723} // namespace libunwind
724
725#endif // __COMPACT_UNWINDER_HPP__
726