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