1288149Semaste//===-------------------------- CompactUnwinder.hpp -----------------------===//
2288149Semaste//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6288149Semaste//
7288149Semaste//
8288149Semaste//  Does runtime stack unwinding using compact unwind encodings.
9288149Semaste//
10288149Semaste//===----------------------------------------------------------------------===//
11288149Semaste
12288149Semaste#ifndef __COMPACT_UNWINDER_HPP__
13288149Semaste#define __COMPACT_UNWINDER_HPP__
14288149Semaste
15288149Semaste#include <stdint.h>
16288149Semaste#include <stdlib.h>
17288149Semaste
18288149Semaste#include <libunwind.h>
19288149Semaste#include <mach-o/compact_unwind_encoding.h>
20288149Semaste
21288149Semaste#include "Registers.hpp"
22288149Semaste
23288149Semaste#define EXTRACT_BITS(value, mask)                                              \
24288149Semaste  ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
25288149Semaste
26288149Semastenamespace libunwind {
27288149Semaste
28302450Semaste#if defined(_LIBUNWIND_TARGET_I386)
29288149Semaste/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
30288149Semaste/// unwind) by modifying a Registers_x86 register set
31288149Semastetemplate <typename A>
32288149Semasteclass CompactUnwinder_x86 {
33288149Semastepublic:
34288149Semaste
35288149Semaste  static int stepWithCompactEncoding(compact_unwind_encoding_t info,
36288149Semaste                                     uint32_t functionStart, A &addressSpace,
37288149Semaste                                     Registers_x86 &registers);
38288149Semaste
39288149Semasteprivate:
40288149Semaste  typename A::pint_t pint_t;
41288149Semaste
42288149Semaste  static void frameUnwind(A &addressSpace, Registers_x86 &registers);
43288149Semaste  static void framelessUnwind(A &addressSpace,
44288149Semaste                              typename A::pint_t returnAddressLocation,
45288149Semaste                              Registers_x86 &registers);
46288149Semaste  static int
47288149Semaste      stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
48288149Semaste                                      uint32_t functionStart, A &addressSpace,
49288149Semaste                                      Registers_x86 &registers);
50288149Semaste  static int stepWithCompactEncodingFrameless(
51288149Semaste      compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
52288149Semaste      A &addressSpace, Registers_x86 &registers, bool indirectStackSize);
53288149Semaste};
54288149Semaste
55288149Semastetemplate <typename A>
56288149Semasteint CompactUnwinder_x86<A>::stepWithCompactEncoding(
57288149Semaste    compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
58288149Semaste    A &addressSpace, Registers_x86 &registers) {
59288149Semaste  switch (compactEncoding & UNWIND_X86_MODE_MASK) {
60288149Semaste  case UNWIND_X86_MODE_EBP_FRAME:
61288149Semaste    return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart,
62288149Semaste                                           addressSpace, registers);
63288149Semaste  case UNWIND_X86_MODE_STACK_IMMD:
64288149Semaste    return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
65288149Semaste                                            addressSpace, registers, false);
66288149Semaste  case UNWIND_X86_MODE_STACK_IND:
67288149Semaste    return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
68288149Semaste                                            addressSpace, registers, true);
69288149Semaste  }
70288149Semaste  _LIBUNWIND_ABORT("invalid compact unwind encoding");
71288149Semaste}
72288149Semaste
73288149Semastetemplate <typename A>
74288149Semasteint CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(
75288149Semaste    compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
76288149Semaste    A &addressSpace, Registers_x86 &registers) {
77288149Semaste  uint32_t savedRegistersOffset =
78288149Semaste      EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
79288149Semaste  uint32_t savedRegistersLocations =
80288149Semaste      EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
81288149Semaste
82288149Semaste  uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset;
83288149Semaste  for (int i = 0; i < 5; ++i) {
84288149Semaste    switch (savedRegistersLocations & 0x7) {
85288149Semaste    case UNWIND_X86_REG_NONE:
86288149Semaste      // no register saved in this slot
87288149Semaste      break;
88288149Semaste    case UNWIND_X86_REG_EBX:
89288149Semaste      registers.setEBX(addressSpace.get32(savedRegisters));
90288149Semaste      break;
91288149Semaste    case UNWIND_X86_REG_ECX:
92288149Semaste      registers.setECX(addressSpace.get32(savedRegisters));
93288149Semaste      break;
94288149Semaste    case UNWIND_X86_REG_EDX:
95288149Semaste      registers.setEDX(addressSpace.get32(savedRegisters));
96288149Semaste      break;
97288149Semaste    case UNWIND_X86_REG_EDI:
98288149Semaste      registers.setEDI(addressSpace.get32(savedRegisters));
99288149Semaste      break;
100288149Semaste    case UNWIND_X86_REG_ESI:
101288149Semaste      registers.setESI(addressSpace.get32(savedRegisters));
102288149Semaste      break;
103288149Semaste    default:
104288149Semaste      (void)functionStart;
105288149Semaste      _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for  "
106308006Semaste                           "function starting at 0x%X",
107288149Semaste                            compactEncoding, functionStart);
108288149Semaste      _LIBUNWIND_ABORT("invalid compact unwind encoding");
109288149Semaste    }
110288149Semaste    savedRegisters += 4;
111288149Semaste    savedRegistersLocations = (savedRegistersLocations >> 3);
112288149Semaste  }
113288149Semaste  frameUnwind(addressSpace, registers);
114288149Semaste  return UNW_STEP_SUCCESS;
115288149Semaste}
116288149Semaste
117288149Semastetemplate <typename A>
118288149Semasteint CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(
119288149Semaste    compact_unwind_encoding_t encoding, uint32_t functionStart,
120288149Semaste    A &addressSpace, Registers_x86 &registers, bool indirectStackSize) {
121288149Semaste  uint32_t stackSizeEncoded =
122288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
123288149Semaste  uint32_t stackAdjust =
124288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
125288149Semaste  uint32_t regCount =
126288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
127288149Semaste  uint32_t permutation =
128288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
129288149Semaste  uint32_t stackSize = stackSizeEncoded * 4;
130288149Semaste  if (indirectStackSize) {
131288149Semaste    // stack size is encoded in subl $xxx,%esp instruction
132288149Semaste    uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
133288149Semaste    stackSize = subl + 4 * stackAdjust;
134288149Semaste  }
135288149Semaste  // decompress permutation
136288149Semaste  uint32_t permunreg[6];
137288149Semaste  switch (regCount) {
138288149Semaste  case 6:
139288149Semaste    permunreg[0] = permutation / 120;
140288149Semaste    permutation -= (permunreg[0] * 120);
141288149Semaste    permunreg[1] = permutation / 24;
142288149Semaste    permutation -= (permunreg[1] * 24);
143288149Semaste    permunreg[2] = permutation / 6;
144288149Semaste    permutation -= (permunreg[2] * 6);
145288149Semaste    permunreg[3] = permutation / 2;
146288149Semaste    permutation -= (permunreg[3] * 2);
147288149Semaste    permunreg[4] = permutation;
148288149Semaste    permunreg[5] = 0;
149288149Semaste    break;
150288149Semaste  case 5:
151288149Semaste    permunreg[0] = permutation / 120;
152288149Semaste    permutation -= (permunreg[0] * 120);
153288149Semaste    permunreg[1] = permutation / 24;
154288149Semaste    permutation -= (permunreg[1] * 24);
155288149Semaste    permunreg[2] = permutation / 6;
156288149Semaste    permutation -= (permunreg[2] * 6);
157288149Semaste    permunreg[3] = permutation / 2;
158288149Semaste    permutation -= (permunreg[3] * 2);
159288149Semaste    permunreg[4] = permutation;
160288149Semaste    break;
161288149Semaste  case 4:
162288149Semaste    permunreg[0] = permutation / 60;
163288149Semaste    permutation -= (permunreg[0] * 60);
164288149Semaste    permunreg[1] = permutation / 12;
165288149Semaste    permutation -= (permunreg[1] * 12);
166288149Semaste    permunreg[2] = permutation / 3;
167288149Semaste    permutation -= (permunreg[2] * 3);
168288149Semaste    permunreg[3] = permutation;
169288149Semaste    break;
170288149Semaste  case 3:
171288149Semaste    permunreg[0] = permutation / 20;
172288149Semaste    permutation -= (permunreg[0] * 20);
173288149Semaste    permunreg[1] = permutation / 4;
174288149Semaste    permutation -= (permunreg[1] * 4);
175288149Semaste    permunreg[2] = permutation;
176288149Semaste    break;
177288149Semaste  case 2:
178288149Semaste    permunreg[0] = permutation / 5;
179288149Semaste    permutation -= (permunreg[0] * 5);
180288149Semaste    permunreg[1] = permutation;
181288149Semaste    break;
182288149Semaste  case 1:
183288149Semaste    permunreg[0] = permutation;
184288149Semaste    break;
185288149Semaste  }
186288149Semaste  // re-number registers back to standard numbers
187288149Semaste  int registersSaved[6];
188288149Semaste  bool used[7] = { false, false, false, false, false, false, false };
189288149Semaste  for (uint32_t i = 0; i < regCount; ++i) {
190288149Semaste    uint32_t renum = 0;
191288149Semaste    for (int u = 1; u < 7; ++u) {
192288149Semaste      if (!used[u]) {
193288149Semaste        if (renum == permunreg[i]) {
194288149Semaste          registersSaved[i] = u;
195288149Semaste          used[u] = true;
196288149Semaste          break;
197288149Semaste        }
198288149Semaste        ++renum;
199288149Semaste      }
200288149Semaste    }
201288149Semaste  }
202288149Semaste  uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount;
203288149Semaste  for (uint32_t i = 0; i < regCount; ++i) {
204288149Semaste    switch (registersSaved[i]) {
205288149Semaste    case UNWIND_X86_REG_EBX:
206288149Semaste      registers.setEBX(addressSpace.get32(savedRegisters));
207288149Semaste      break;
208288149Semaste    case UNWIND_X86_REG_ECX:
209288149Semaste      registers.setECX(addressSpace.get32(savedRegisters));
210288149Semaste      break;
211288149Semaste    case UNWIND_X86_REG_EDX:
212288149Semaste      registers.setEDX(addressSpace.get32(savedRegisters));
213288149Semaste      break;
214288149Semaste    case UNWIND_X86_REG_EDI:
215288149Semaste      registers.setEDI(addressSpace.get32(savedRegisters));
216288149Semaste      break;
217288149Semaste    case UNWIND_X86_REG_ESI:
218288149Semaste      registers.setESI(addressSpace.get32(savedRegisters));
219288149Semaste      break;
220288149Semaste    case UNWIND_X86_REG_EBP:
221288149Semaste      registers.setEBP(addressSpace.get32(savedRegisters));
222288149Semaste      break;
223288149Semaste    default:
224288149Semaste      _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
225308006Semaste                           "function starting at 0x%X",
226288149Semaste                           encoding, functionStart);
227288149Semaste      _LIBUNWIND_ABORT("invalid compact unwind encoding");
228288149Semaste    }
229288149Semaste    savedRegisters += 4;
230288149Semaste  }
231288149Semaste  framelessUnwind(addressSpace, savedRegisters, registers);
232288149Semaste  return UNW_STEP_SUCCESS;
233288149Semaste}
234288149Semaste
235288149Semaste
236288149Semastetemplate <typename A>
237288149Semastevoid CompactUnwinder_x86<A>::frameUnwind(A &addressSpace,
238288149Semaste                                         Registers_x86 &registers) {
239288149Semaste  typename A::pint_t bp = registers.getEBP();
240288149Semaste  // ebp points to old ebp
241288149Semaste  registers.setEBP(addressSpace.get32(bp));
242288149Semaste  // old esp is ebp less saved ebp and return address
243288149Semaste  registers.setSP((uint32_t)bp + 8);
244288149Semaste  // pop return address into eip
245288149Semaste  registers.setIP(addressSpace.get32(bp + 4));
246288149Semaste}
247288149Semaste
248288149Semastetemplate <typename A>
249288149Semastevoid CompactUnwinder_x86<A>::framelessUnwind(
250288149Semaste    A &addressSpace, typename A::pint_t returnAddressLocation,
251288149Semaste    Registers_x86 &registers) {
252288149Semaste  // return address is on stack after last saved register
253288149Semaste  registers.setIP(addressSpace.get32(returnAddressLocation));
254288149Semaste  // old esp is before return address
255288149Semaste  registers.setSP((uint32_t)returnAddressLocation + 4);
256288149Semaste}
257302450Semaste#endif // _LIBUNWIND_TARGET_I386
258288149Semaste
259288149Semaste
260302450Semaste#if defined(_LIBUNWIND_TARGET_X86_64)
261288149Semaste/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
262288149Semaste/// unwind) by modifying a Registers_x86_64 register set
263288149Semastetemplate <typename A>
264288149Semasteclass CompactUnwinder_x86_64 {
265288149Semastepublic:
266288149Semaste
267288149Semaste  static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
268288149Semaste                                     uint64_t functionStart, A &addressSpace,
269288149Semaste                                     Registers_x86_64 &registers);
270288149Semaste
271288149Semasteprivate:
272288149Semaste  typename A::pint_t pint_t;
273288149Semaste
274288149Semaste  static void frameUnwind(A &addressSpace, Registers_x86_64 &registers);
275288149Semaste  static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
276288149Semaste                              Registers_x86_64 &registers);
277288149Semaste  static int
278288149Semaste      stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
279288149Semaste                                      uint64_t functionStart, A &addressSpace,
280288149Semaste                                      Registers_x86_64 &registers);
281288149Semaste  static int stepWithCompactEncodingFrameless(
282288149Semaste      compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
283288149Semaste      A &addressSpace, Registers_x86_64 &registers, bool indirectStackSize);
284288149Semaste};
285288149Semaste
286288149Semastetemplate <typename A>
287288149Semasteint CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
288288149Semaste    compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
289288149Semaste    A &addressSpace, Registers_x86_64 &registers) {
290288149Semaste  switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
291288149Semaste  case UNWIND_X86_64_MODE_RBP_FRAME:
292288149Semaste    return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
293288149Semaste                                           addressSpace, registers);
294288149Semaste  case UNWIND_X86_64_MODE_STACK_IMMD:
295288149Semaste    return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
296288149Semaste                                            addressSpace, registers, false);
297288149Semaste  case UNWIND_X86_64_MODE_STACK_IND:
298288149Semaste    return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
299288149Semaste                                            addressSpace, registers, true);
300288149Semaste  }
301288149Semaste  _LIBUNWIND_ABORT("invalid compact unwind encoding");
302288149Semaste}
303288149Semaste
304288149Semastetemplate <typename A>
305288149Semasteint CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
306288149Semaste    compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
307288149Semaste    A &addressSpace, Registers_x86_64 &registers) {
308288149Semaste  uint32_t savedRegistersOffset =
309288149Semaste      EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
310288149Semaste  uint32_t savedRegistersLocations =
311288149Semaste      EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
312288149Semaste
313288149Semaste  uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
314288149Semaste  for (int i = 0; i < 5; ++i) {
315288149Semaste    switch (savedRegistersLocations & 0x7) {
316288149Semaste    case UNWIND_X86_64_REG_NONE:
317288149Semaste      // no register saved in this slot
318288149Semaste      break;
319288149Semaste    case UNWIND_X86_64_REG_RBX:
320288149Semaste      registers.setRBX(addressSpace.get64(savedRegisters));
321288149Semaste      break;
322288149Semaste    case UNWIND_X86_64_REG_R12:
323288149Semaste      registers.setR12(addressSpace.get64(savedRegisters));
324288149Semaste      break;
325288149Semaste    case UNWIND_X86_64_REG_R13:
326288149Semaste      registers.setR13(addressSpace.get64(savedRegisters));
327288149Semaste      break;
328288149Semaste    case UNWIND_X86_64_REG_R14:
329288149Semaste      registers.setR14(addressSpace.get64(savedRegisters));
330288149Semaste      break;
331288149Semaste    case UNWIND_X86_64_REG_R15:
332288149Semaste      registers.setR15(addressSpace.get64(savedRegisters));
333288149Semaste      break;
334288149Semaste    default:
335288149Semaste      (void)functionStart;
336288149Semaste      _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
337308006Semaste                           "function starting at 0x%llX",
338288149Semaste                            compactEncoding, functionStart);
339288149Semaste      _LIBUNWIND_ABORT("invalid compact unwind encoding");
340288149Semaste    }
341288149Semaste    savedRegisters += 8;
342288149Semaste    savedRegistersLocations = (savedRegistersLocations >> 3);
343288149Semaste  }
344288149Semaste  frameUnwind(addressSpace, registers);
345288149Semaste  return UNW_STEP_SUCCESS;
346288149Semaste}
347288149Semaste
348288149Semastetemplate <typename A>
349288149Semasteint CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
350288149Semaste    compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
351288149Semaste    Registers_x86_64 &registers, bool indirectStackSize) {
352288149Semaste  uint32_t stackSizeEncoded =
353288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
354288149Semaste  uint32_t stackAdjust =
355288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
356288149Semaste  uint32_t regCount =
357288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
358288149Semaste  uint32_t permutation =
359288149Semaste      EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
360288149Semaste  uint32_t stackSize = stackSizeEncoded * 8;
361288149Semaste  if (indirectStackSize) {
362288149Semaste    // stack size is encoded in subl $xxx,%esp instruction
363288149Semaste    uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
364288149Semaste    stackSize = subl + 8 * stackAdjust;
365288149Semaste  }
366288149Semaste  // decompress permutation
367288149Semaste  uint32_t permunreg[6];
368288149Semaste  switch (regCount) {
369288149Semaste  case 6:
370288149Semaste    permunreg[0] = permutation / 120;
371288149Semaste    permutation -= (permunreg[0] * 120);
372288149Semaste    permunreg[1] = permutation / 24;
373288149Semaste    permutation -= (permunreg[1] * 24);
374288149Semaste    permunreg[2] = permutation / 6;
375288149Semaste    permutation -= (permunreg[2] * 6);
376288149Semaste    permunreg[3] = permutation / 2;
377288149Semaste    permutation -= (permunreg[3] * 2);
378288149Semaste    permunreg[4] = permutation;
379288149Semaste    permunreg[5] = 0;
380288149Semaste    break;
381288149Semaste  case 5:
382288149Semaste    permunreg[0] = permutation / 120;
383288149Semaste    permutation -= (permunreg[0] * 120);
384288149Semaste    permunreg[1] = permutation / 24;
385288149Semaste    permutation -= (permunreg[1] * 24);
386288149Semaste    permunreg[2] = permutation / 6;
387288149Semaste    permutation -= (permunreg[2] * 6);
388288149Semaste    permunreg[3] = permutation / 2;
389288149Semaste    permutation -= (permunreg[3] * 2);
390288149Semaste    permunreg[4] = permutation;
391288149Semaste    break;
392288149Semaste  case 4:
393288149Semaste    permunreg[0] = permutation / 60;
394288149Semaste    permutation -= (permunreg[0] * 60);
395288149Semaste    permunreg[1] = permutation / 12;
396288149Semaste    permutation -= (permunreg[1] * 12);
397288149Semaste    permunreg[2] = permutation / 3;
398288149Semaste    permutation -= (permunreg[2] * 3);
399288149Semaste    permunreg[3] = permutation;
400288149Semaste    break;
401288149Semaste  case 3:
402288149Semaste    permunreg[0] = permutation / 20;
403288149Semaste    permutation -= (permunreg[0] * 20);
404288149Semaste    permunreg[1] = permutation / 4;
405288149Semaste    permutation -= (permunreg[1] * 4);
406288149Semaste    permunreg[2] = permutation;
407288149Semaste    break;
408288149Semaste  case 2:
409288149Semaste    permunreg[0] = permutation / 5;
410288149Semaste    permutation -= (permunreg[0] * 5);
411288149Semaste    permunreg[1] = permutation;
412288149Semaste    break;
413288149Semaste  case 1:
414288149Semaste    permunreg[0] = permutation;
415288149Semaste    break;
416288149Semaste  }
417288149Semaste  // re-number registers back to standard numbers
418288149Semaste  int registersSaved[6];
419288149Semaste  bool used[7] = { false, false, false, false, false, false, false };
420288149Semaste  for (uint32_t i = 0; i < regCount; ++i) {
421288149Semaste    uint32_t renum = 0;
422288149Semaste    for (int u = 1; u < 7; ++u) {
423288149Semaste      if (!used[u]) {
424288149Semaste        if (renum == permunreg[i]) {
425288149Semaste          registersSaved[i] = u;
426288149Semaste          used[u] = true;
427288149Semaste          break;
428288149Semaste        }
429288149Semaste        ++renum;
430288149Semaste      }
431288149Semaste    }
432288149Semaste  }
433288149Semaste  uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
434288149Semaste  for (uint32_t i = 0; i < regCount; ++i) {
435288149Semaste    switch (registersSaved[i]) {
436288149Semaste    case UNWIND_X86_64_REG_RBX:
437288149Semaste      registers.setRBX(addressSpace.get64(savedRegisters));
438288149Semaste      break;
439288149Semaste    case UNWIND_X86_64_REG_R12:
440288149Semaste      registers.setR12(addressSpace.get64(savedRegisters));
441288149Semaste      break;
442288149Semaste    case UNWIND_X86_64_REG_R13:
443288149Semaste      registers.setR13(addressSpace.get64(savedRegisters));
444288149Semaste      break;
445288149Semaste    case UNWIND_X86_64_REG_R14:
446288149Semaste      registers.setR14(addressSpace.get64(savedRegisters));
447288149Semaste      break;
448288149Semaste    case UNWIND_X86_64_REG_R15:
449288149Semaste      registers.setR15(addressSpace.get64(savedRegisters));
450288149Semaste      break;
451288149Semaste    case UNWIND_X86_64_REG_RBP:
452288149Semaste      registers.setRBP(addressSpace.get64(savedRegisters));
453288149Semaste      break;
454288149Semaste    default:
455288149Semaste      _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
456308006Semaste                           "function starting at 0x%llX",
457288149Semaste                            encoding, functionStart);
458288149Semaste      _LIBUNWIND_ABORT("invalid compact unwind encoding");
459288149Semaste    }
460288149Semaste    savedRegisters += 8;
461288149Semaste  }
462288149Semaste  framelessUnwind(addressSpace, savedRegisters, registers);
463288149Semaste  return UNW_STEP_SUCCESS;
464288149Semaste}
465288149Semaste
466288149Semaste
467288149Semastetemplate <typename A>
468288149Semastevoid CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
469288149Semaste                                            Registers_x86_64 &registers) {
470288149Semaste  uint64_t rbp = registers.getRBP();
471288149Semaste  // ebp points to old ebp
472288149Semaste  registers.setRBP(addressSpace.get64(rbp));
473288149Semaste  // old esp is ebp less saved ebp and return address
474288149Semaste  registers.setSP(rbp + 16);
475288149Semaste  // pop return address into eip
476288149Semaste  registers.setIP(addressSpace.get64(rbp + 8));
477288149Semaste}
478288149Semaste
479288149Semastetemplate <typename A>
480288149Semastevoid CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
481288149Semaste                                                uint64_t returnAddressLocation,
482288149Semaste                                                Registers_x86_64 &registers) {
483288149Semaste  // return address is on stack after last saved register
484288149Semaste  registers.setIP(addressSpace.get64(returnAddressLocation));
485288149Semaste  // old esp is before return address
486288149Semaste  registers.setSP(returnAddressLocation + 8);
487288149Semaste}
488302450Semaste#endif // _LIBUNWIND_TARGET_X86_64
489288149Semaste
490288149Semaste
491288149Semaste
492302450Semaste#if defined(_LIBUNWIND_TARGET_AARCH64)
493288149Semaste/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
494288149Semaste/// unwind) by modifying a Registers_arm64 register set
495288149Semastetemplate <typename A>
496288149Semasteclass CompactUnwinder_arm64 {
497288149Semastepublic:
498288149Semaste
499288149Semaste  static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
500288149Semaste                                     uint64_t functionStart, A &addressSpace,
501288149Semaste                                     Registers_arm64 &registers);
502288149Semaste
503288149Semasteprivate:
504288149Semaste  typename A::pint_t pint_t;
505288149Semaste
506288149Semaste  static int
507288149Semaste      stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
508288149Semaste                                   uint64_t functionStart, A &addressSpace,
509288149Semaste                                   Registers_arm64 &registers);
510288149Semaste  static int stepWithCompactEncodingFrameless(
511288149Semaste      compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
512288149Semaste      A &addressSpace, Registers_arm64 &registers);
513288149Semaste};
514288149Semaste
515288149Semastetemplate <typename A>
516288149Semasteint CompactUnwinder_arm64<A>::stepWithCompactEncoding(
517288149Semaste    compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
518288149Semaste    A &addressSpace, Registers_arm64 &registers) {
519288149Semaste  switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
520288149Semaste  case UNWIND_ARM64_MODE_FRAME:
521288149Semaste    return stepWithCompactEncodingFrame(compactEncoding, functionStart,
522288149Semaste                                        addressSpace, registers);
523288149Semaste  case UNWIND_ARM64_MODE_FRAMELESS:
524288149Semaste    return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
525288149Semaste                                            addressSpace, registers);
526288149Semaste  }
527288149Semaste  _LIBUNWIND_ABORT("invalid compact unwind encoding");
528288149Semaste}
529288149Semaste
530288149Semastetemplate <typename A>
531288149Semasteint CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
532288149Semaste    compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
533288149Semaste    Registers_arm64 &registers) {
534288149Semaste  uint32_t stackSize =
535288149Semaste      16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
536288149Semaste
537288149Semaste  uint64_t savedRegisterLoc = registers.getSP() + stackSize;
538288149Semaste
539288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
540288149Semaste    registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
541288149Semaste    savedRegisterLoc -= 8;
542288149Semaste    registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
543288149Semaste    savedRegisterLoc -= 8;
544288149Semaste  }
545288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
546288149Semaste    registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
547288149Semaste    savedRegisterLoc -= 8;
548288149Semaste    registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
549288149Semaste    savedRegisterLoc -= 8;
550288149Semaste  }
551288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
552288149Semaste    registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
553288149Semaste    savedRegisterLoc -= 8;
554288149Semaste    registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
555288149Semaste    savedRegisterLoc -= 8;
556288149Semaste  }
557288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
558288149Semaste    registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
559288149Semaste    savedRegisterLoc -= 8;
560288149Semaste    registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
561288149Semaste    savedRegisterLoc -= 8;
562288149Semaste  }
563288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
564288149Semaste    registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
565288149Semaste    savedRegisterLoc -= 8;
566288149Semaste    registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
567288149Semaste    savedRegisterLoc -= 8;
568288149Semaste  }
569288149Semaste
570288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
571288149Semaste    registers.setFloatRegister(UNW_ARM64_D8,
572288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
573288149Semaste    savedRegisterLoc -= 8;
574288149Semaste    registers.setFloatRegister(UNW_ARM64_D9,
575288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
576288149Semaste    savedRegisterLoc -= 8;
577288149Semaste  }
578288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
579288149Semaste    registers.setFloatRegister(UNW_ARM64_D10,
580288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
581288149Semaste    savedRegisterLoc -= 8;
582288149Semaste    registers.setFloatRegister(UNW_ARM64_D11,
583288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
584288149Semaste    savedRegisterLoc -= 8;
585288149Semaste  }
586288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
587288149Semaste    registers.setFloatRegister(UNW_ARM64_D12,
588288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
589288149Semaste    savedRegisterLoc -= 8;
590288149Semaste    registers.setFloatRegister(UNW_ARM64_D13,
591288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
592288149Semaste    savedRegisterLoc -= 8;
593288149Semaste  }
594288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
595288149Semaste    registers.setFloatRegister(UNW_ARM64_D14,
596288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
597288149Semaste    savedRegisterLoc -= 8;
598288149Semaste    registers.setFloatRegister(UNW_ARM64_D15,
599288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
600288149Semaste    savedRegisterLoc -= 8;
601288149Semaste  }
602288149Semaste
603288149Semaste  // subtract stack size off of sp
604288149Semaste  registers.setSP(savedRegisterLoc);
605288149Semaste
606288149Semaste  // set pc to be value in lr
607288149Semaste  registers.setIP(registers.getRegister(UNW_ARM64_LR));
608288149Semaste
609288149Semaste  return UNW_STEP_SUCCESS;
610288149Semaste}
611288149Semaste
612288149Semastetemplate <typename A>
613288149Semasteint CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
614288149Semaste    compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
615288149Semaste    Registers_arm64 &registers) {
616288149Semaste  uint64_t savedRegisterLoc = registers.getFP() - 8;
617288149Semaste
618288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
619288149Semaste    registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
620288149Semaste    savedRegisterLoc -= 8;
621288149Semaste    registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
622288149Semaste    savedRegisterLoc -= 8;
623288149Semaste  }
624288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
625288149Semaste    registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
626288149Semaste    savedRegisterLoc -= 8;
627288149Semaste    registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
628288149Semaste    savedRegisterLoc -= 8;
629288149Semaste  }
630288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
631288149Semaste    registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
632288149Semaste    savedRegisterLoc -= 8;
633288149Semaste    registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
634288149Semaste    savedRegisterLoc -= 8;
635288149Semaste  }
636288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
637288149Semaste    registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
638288149Semaste    savedRegisterLoc -= 8;
639288149Semaste    registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
640288149Semaste    savedRegisterLoc -= 8;
641288149Semaste  }
642288149Semaste  if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
643288149Semaste    registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
644288149Semaste    savedRegisterLoc -= 8;
645288149Semaste    registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
646288149Semaste    savedRegisterLoc -= 8;
647288149Semaste  }
648288149Semaste
649288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
650288149Semaste    registers.setFloatRegister(UNW_ARM64_D8,
651288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
652288149Semaste    savedRegisterLoc -= 8;
653288149Semaste    registers.setFloatRegister(UNW_ARM64_D9,
654288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
655288149Semaste    savedRegisterLoc -= 8;
656288149Semaste  }
657288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
658288149Semaste    registers.setFloatRegister(UNW_ARM64_D10,
659288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
660288149Semaste    savedRegisterLoc -= 8;
661288149Semaste    registers.setFloatRegister(UNW_ARM64_D11,
662288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
663288149Semaste    savedRegisterLoc -= 8;
664288149Semaste  }
665288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
666288149Semaste    registers.setFloatRegister(UNW_ARM64_D12,
667288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
668288149Semaste    savedRegisterLoc -= 8;
669288149Semaste    registers.setFloatRegister(UNW_ARM64_D13,
670288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
671288149Semaste    savedRegisterLoc -= 8;
672288149Semaste  }
673288149Semaste  if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
674288149Semaste    registers.setFloatRegister(UNW_ARM64_D14,
675288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
676288149Semaste    savedRegisterLoc -= 8;
677288149Semaste    registers.setFloatRegister(UNW_ARM64_D15,
678288149Semaste                               addressSpace.getDouble(savedRegisterLoc));
679288149Semaste    savedRegisterLoc -= 8;
680288149Semaste  }
681288149Semaste
682288149Semaste  uint64_t fp = registers.getFP();
683288149Semaste  // fp points to old fp
684288149Semaste  registers.setFP(addressSpace.get64(fp));
685288149Semaste  // old sp is fp less saved fp and lr
686288149Semaste  registers.setSP(fp + 16);
687288149Semaste  // pop return address into pc
688288149Semaste  registers.setIP(addressSpace.get64(fp + 8));
689288149Semaste
690288149Semaste  return UNW_STEP_SUCCESS;
691288149Semaste}
692302450Semaste#endif // _LIBUNWIND_TARGET_AARCH64
693288149Semaste
694288149Semaste
695288149Semaste} // namespace libunwind
696288149Semaste
697288149Semaste#endif // __COMPACT_UNWINDER_HPP__
698