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