1 | //===--------------------------- DwarfParser.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 | // Parses DWARF CFIs (FDEs and CIEs). |
9 | // |
10 | //===----------------------------------------------------------------------===// |
11 | |
12 | #ifndef __DWARF_PARSER_HPP__ |
13 | #define __DWARF_PARSER_HPP__ |
14 | |
15 | #include <inttypes.h> |
16 | #include <stdint.h> |
17 | #include <stdio.h> |
18 | #include <stdlib.h> |
19 | |
20 | #include "libunwind.h" |
21 | #include "dwarf2.h" |
22 | #include "Registers.hpp" |
23 | |
24 | #include "config.h" |
25 | |
26 | namespace libunwind { |
27 | |
28 | enum { |
29 | kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER |
30 | }; |
31 | |
32 | enum RegisterSavedWhere { |
33 | kRegisterUnused, |
34 | kRegisterInCFA, |
35 | kRegisterOffsetFromCFA, |
36 | kRegisterInRegister, |
37 | kRegisterAtExpression, |
38 | kRegisterIsExpression |
39 | }; |
40 | |
41 | struct RegisterLocation { |
42 | RegisterSavedWhere location; |
43 | int64_t value; |
44 | }; |
45 | |
46 | /// Information about a frame layout and registers saved determined |
47 | /// by "running" the DWARF FDE "instructions" |
48 | struct PrologInfo { |
49 | uint32_t cfaRegister; |
50 | int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset |
51 | int64_t cfaExpression; // CFA = expression |
52 | uint32_t ; |
53 | uint32_t codeOffsetAtStackDecrement; |
54 | bool registersInOtherRegisters; |
55 | bool sameValueUsed; |
56 | RegisterLocation savedRegisters[kMaxRegisterNumber + 1]; |
57 | }; |
58 | |
59 | struct PrologInfoStackEntry { |
60 | PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i) |
61 | : next(n), info(i) {} |
62 | PrologInfoStackEntry *next; |
63 | PrologInfo info; |
64 | }; |
65 | |
66 | } |
67 | |
68 | namespace { |
69 | // DWARF instructions DW_CFA_remember_state and DW_CFA_restore_state require |
70 | // stack. We cannot use malloc as it is not signal safe. mmap is technically |
71 | // signal safe but allocates pages too slow. Small buffer is introduced instead |
72 | // along with upper bound on maximum stack depth |
73 | |
74 | class StackGuard { |
75 | public: |
76 | void *push() { |
77 | if (size + entrySize > capacity) { |
78 | abort(); |
79 | } |
80 | |
81 | void* result = static_cast<void *>(buffer + size); |
82 | size += entrySize; |
83 | return result; |
84 | } |
85 | |
86 | void pop() { |
87 | if (size < entrySize) { |
88 | abort(); |
89 | } |
90 | |
91 | size -= entrySize; |
92 | } |
93 | |
94 | private: |
95 | static constexpr size_t entrySize = sizeof(libunwind::PrologInfoStackEntry); |
96 | static constexpr size_t capacity = entrySize * LIBUNWIND_MAX_STACK_SIZE; |
97 | |
98 | size_t size = 0; |
99 | char buffer[capacity]; |
100 | }; |
101 | } |
102 | |
103 | namespace libunwind { |
104 | /// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. |
105 | /// See DWARF Spec for details: |
106 | /// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html |
107 | /// |
108 | template <typename A> |
109 | class CFI_Parser { |
110 | public: |
111 | typedef typename A::pint_t pint_t; |
112 | |
113 | /// Information encoded in a CIE (Common Information Entry) |
114 | struct CIE_Info { |
115 | pint_t cieStart; |
116 | pint_t cieLength; |
117 | pint_t cieInstructions; |
118 | uint8_t pointerEncoding; |
119 | uint8_t lsdaEncoding; |
120 | uint8_t personalityEncoding; |
121 | uint8_t personalityOffsetInCIE; |
122 | pint_t personality; |
123 | uint32_t codeAlignFactor; |
124 | int dataAlignFactor; |
125 | bool isSignalFrame; |
126 | bool fdesHaveAugmentationData; |
127 | uint8_t returnAddressRegister; |
128 | #if defined(_LIBUNWIND_TARGET_AARCH64) |
129 | bool addressesSignedWithBKey; |
130 | #endif |
131 | }; |
132 | |
133 | /// Information about an FDE (Frame Description Entry) |
134 | struct FDE_Info { |
135 | pint_t fdeStart; |
136 | pint_t fdeLength; |
137 | pint_t fdeInstructions; |
138 | pint_t pcStart; |
139 | pint_t pcEnd; |
140 | pint_t lsda; |
141 | }; |
142 | |
143 | static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, |
144 | uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo, |
145 | CIE_Info *cieInfo); |
146 | static const char *decodeFDE(A &addressSpace, pint_t fdeStart, |
147 | FDE_Info *fdeInfo, CIE_Info *cieInfo); |
148 | static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo, |
149 | const CIE_Info &cieInfo, pint_t upToPC, |
150 | int arch, PrologInfo *results); |
151 | |
152 | static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo); |
153 | |
154 | private: |
155 | static bool parseInstructions(A &addressSpace, pint_t instructions, |
156 | pint_t instructionsEnd, const CIE_Info &cieInfo, |
157 | pint_t pcoffset, |
158 | PrologInfoStackEntry *&rememberStack, int arch, |
159 | PrologInfo *results, |
160 | StackGuard &stack); |
161 | |
162 | }; |
163 | |
164 | |
165 | /// Parse a FDE into a CIE_Info and an FDE_Info |
166 | template <typename A> |
167 | const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart, |
168 | FDE_Info *fdeInfo, CIE_Info *cieInfo) { |
169 | pint_t p = fdeStart; |
170 | pint_t cfiLength = (pint_t)addressSpace.get32(p); |
171 | p += 4; |
172 | if (cfiLength == 0xffffffff) { |
173 | // 0xffffffff means length is really next 8 bytes |
174 | cfiLength = (pint_t)addressSpace.get64(p); |
175 | p += 8; |
176 | } |
177 | if (cfiLength == 0) |
178 | return "FDE has zero length" ; // end marker |
179 | uint32_t ciePointer = addressSpace.get32(p); |
180 | if (ciePointer == 0) |
181 | return "FDE is really a CIE" ; // this is a CIE not an FDE |
182 | pint_t nextCFI = p + cfiLength; |
183 | pint_t cieStart = p - ciePointer; |
184 | const char *err = parseCIE(addressSpace, cieStart, cieInfo); |
185 | if (err != NULL) |
186 | return err; |
187 | p += 4; |
188 | // Parse pc begin and range. |
189 | pint_t pcStart = |
190 | addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); |
191 | pint_t pcRange = |
192 | addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); |
193 | // Parse rest of info. |
194 | fdeInfo->lsda = 0; |
195 | // Check for augmentation length. |
196 | if (cieInfo->fdesHaveAugmentationData) { |
197 | pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); |
198 | pint_t endOfAug = p + augLen; |
199 | if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { |
200 | // Peek at value (without indirection). Zero means no LSDA. |
201 | pint_t lsdaStart = p; |
202 | if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != |
203 | 0) { |
204 | // Reset pointer and re-parse LSDA address. |
205 | p = lsdaStart; |
206 | fdeInfo->lsda = |
207 | addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); |
208 | } |
209 | } |
210 | p = endOfAug; |
211 | } |
212 | fdeInfo->fdeStart = fdeStart; |
213 | fdeInfo->fdeLength = nextCFI - fdeStart; |
214 | fdeInfo->fdeInstructions = p; |
215 | fdeInfo->pcStart = pcStart; |
216 | fdeInfo->pcEnd = pcStart + pcRange; |
217 | return NULL; // success |
218 | } |
219 | |
220 | /// Scan an eh_frame section to find an FDE for a pc |
221 | template <typename A> |
222 | bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, |
223 | uint32_t sectionLength, pint_t fdeHint, |
224 | FDE_Info *fdeInfo, CIE_Info *cieInfo) { |
225 | //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); |
226 | pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; |
227 | const pint_t ehSectionEnd = p + sectionLength; |
228 | while (p < ehSectionEnd) { |
229 | pint_t currentCFI = p; |
230 | //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); |
231 | pint_t cfiLength = addressSpace.get32(p); |
232 | p += 4; |
233 | if (cfiLength == 0xffffffff) { |
234 | // 0xffffffff means length is really next 8 bytes |
235 | cfiLength = (pint_t)addressSpace.get64(p); |
236 | p += 8; |
237 | } |
238 | if (cfiLength == 0) |
239 | return false; // end marker |
240 | uint32_t id = addressSpace.get32(p); |
241 | if (id == 0) { |
242 | // Skip over CIEs. |
243 | p += cfiLength; |
244 | } else { |
245 | // Process FDE to see if it covers pc. |
246 | pint_t nextCFI = p + cfiLength; |
247 | uint32_t ciePointer = addressSpace.get32(p); |
248 | pint_t cieStart = p - ciePointer; |
249 | // Validate pointer to CIE is within section. |
250 | if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) { |
251 | if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) { |
252 | p += 4; |
253 | // Parse pc begin and range. |
254 | pint_t pcStart = |
255 | addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); |
256 | pint_t pcRange = addressSpace.getEncodedP( |
257 | p, nextCFI, cieInfo->pointerEncoding & 0x0F); |
258 | // Test if pc is within the function this FDE covers. |
259 | if ((pcStart < pc) && (pc <= pcStart + pcRange)) { |
260 | // parse rest of info |
261 | fdeInfo->lsda = 0; |
262 | // check for augmentation length |
263 | if (cieInfo->fdesHaveAugmentationData) { |
264 | pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); |
265 | pint_t endOfAug = p + augLen; |
266 | if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { |
267 | // Peek at value (without indirection). Zero means no LSDA. |
268 | pint_t lsdaStart = p; |
269 | if (addressSpace.getEncodedP( |
270 | p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) { |
271 | // Reset pointer and re-parse LSDA address. |
272 | p = lsdaStart; |
273 | fdeInfo->lsda = addressSpace |
274 | .getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); |
275 | } |
276 | } |
277 | p = endOfAug; |
278 | } |
279 | fdeInfo->fdeStart = currentCFI; |
280 | fdeInfo->fdeLength = nextCFI - currentCFI; |
281 | fdeInfo->fdeInstructions = p; |
282 | fdeInfo->pcStart = pcStart; |
283 | fdeInfo->pcEnd = pcStart + pcRange; |
284 | return true; |
285 | } else { |
286 | // pc is not in begin/range, skip this FDE |
287 | } |
288 | } else { |
289 | // Malformed CIE, now augmentation describing pc range encoding. |
290 | } |
291 | } else { |
292 | // malformed FDE. CIE is bad |
293 | } |
294 | p = nextCFI; |
295 | } |
296 | } |
297 | return false; |
298 | } |
299 | |
300 | /// Extract info from a CIE |
301 | template <typename A> |
302 | const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie, |
303 | CIE_Info *cieInfo) { |
304 | cieInfo->pointerEncoding = 0; |
305 | cieInfo->lsdaEncoding = DW_EH_PE_omit; |
306 | cieInfo->personalityEncoding = 0; |
307 | cieInfo->personalityOffsetInCIE = 0; |
308 | cieInfo->personality = 0; |
309 | cieInfo->codeAlignFactor = 0; |
310 | cieInfo->dataAlignFactor = 0; |
311 | cieInfo->isSignalFrame = false; |
312 | cieInfo->fdesHaveAugmentationData = false; |
313 | #if defined(_LIBUNWIND_TARGET_AARCH64) |
314 | cieInfo->addressesSignedWithBKey = false; |
315 | #endif |
316 | cieInfo->cieStart = cie; |
317 | pint_t p = cie; |
318 | pint_t cieLength = (pint_t)addressSpace.get32(p); |
319 | p += 4; |
320 | pint_t cieContentEnd = p + cieLength; |
321 | if (cieLength == 0xffffffff) { |
322 | // 0xffffffff means length is really next 8 bytes |
323 | cieLength = (pint_t)addressSpace.get64(p); |
324 | p += 8; |
325 | cieContentEnd = p + cieLength; |
326 | } |
327 | if (cieLength == 0) |
328 | return NULL; |
329 | // CIE ID is always 0 |
330 | if (addressSpace.get32(p) != 0) |
331 | return "CIE ID is not zero" ; |
332 | p += 4; |
333 | // Version is always 1 or 3 |
334 | uint8_t version = addressSpace.get8(p); |
335 | if ((version != 1) && (version != 3)) |
336 | return "CIE version is not 1 or 3" ; |
337 | ++p; |
338 | // save start of augmentation string and find end |
339 | pint_t strStart = p; |
340 | while (addressSpace.get8(p) != 0) |
341 | ++p; |
342 | ++p; |
343 | // parse code aligment factor |
344 | cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd); |
345 | // parse data alignment factor |
346 | cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd); |
347 | // parse return address register |
348 | uint64_t raReg = addressSpace.getULEB128(p, cieContentEnd); |
349 | assert(raReg < 255 && "return address register too large" ); |
350 | cieInfo->returnAddressRegister = (uint8_t)raReg; |
351 | // parse augmentation data based on augmentation string |
352 | const char *result = NULL; |
353 | if (addressSpace.get8(strStart) == 'z') { |
354 | // parse augmentation data length |
355 | addressSpace.getULEB128(p, cieContentEnd); |
356 | for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) { |
357 | switch (addressSpace.get8(s)) { |
358 | case 'z': |
359 | cieInfo->fdesHaveAugmentationData = true; |
360 | break; |
361 | case 'P': |
362 | cieInfo->personalityEncoding = addressSpace.get8(p); |
363 | ++p; |
364 | cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie); |
365 | cieInfo->personality = addressSpace |
366 | .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); |
367 | break; |
368 | case 'L': |
369 | cieInfo->lsdaEncoding = addressSpace.get8(p); |
370 | ++p; |
371 | break; |
372 | case 'R': |
373 | cieInfo->pointerEncoding = addressSpace.get8(p); |
374 | ++p; |
375 | break; |
376 | case 'S': |
377 | cieInfo->isSignalFrame = true; |
378 | break; |
379 | #if defined(_LIBUNWIND_TARGET_AARCH64) |
380 | case 'B': |
381 | cieInfo->addressesSignedWithBKey = true; |
382 | break; |
383 | #endif |
384 | default: |
385 | // ignore unknown letters |
386 | break; |
387 | } |
388 | } |
389 | } |
390 | cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; |
391 | cieInfo->cieInstructions = p; |
392 | return result; |
393 | } |
394 | |
395 | |
396 | /// "run" the DWARF instructions and create the abstact PrologInfo for an FDE |
397 | template <typename A> |
398 | bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace, |
399 | const FDE_Info &fdeInfo, |
400 | const CIE_Info &cieInfo, pint_t upToPC, |
401 | int arch, PrologInfo *results) { |
402 | // clear results |
403 | memset(results, '\0', sizeof(PrologInfo)); |
404 | PrologInfoStackEntry *rememberStack = NULL; |
405 | |
406 | StackGuard stack; |
407 | |
408 | // parse CIE then FDE instructions |
409 | return parseInstructions(addressSpace, cieInfo.cieInstructions, |
410 | cieInfo.cieStart + cieInfo.cieLength, cieInfo, |
411 | (pint_t)(-1), rememberStack, arch, results, stack) && |
412 | parseInstructions(addressSpace, fdeInfo.fdeInstructions, |
413 | fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, |
414 | upToPC - fdeInfo.pcStart, rememberStack, arch, |
415 | results, stack); |
416 | } |
417 | |
418 | /// "run" the DWARF instructions |
419 | template <typename A> |
420 | bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, |
421 | pint_t instructionsEnd, |
422 | const CIE_Info &cieInfo, pint_t pcoffset, |
423 | PrologInfoStackEntry *&rememberStack, |
424 | int arch, PrologInfo *results, |
425 | StackGuard &stack) { |
426 | pint_t p = instructions; |
427 | pint_t codeOffset = 0; |
428 | PrologInfo initialState = *results; |
429 | |
430 | _LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n" , |
431 | static_cast<uint64_t>(instructionsEnd)); |
432 | |
433 | // see DWARF Spec, section 6.4.2 for details on unwind opcodes |
434 | while ((p < instructionsEnd) && (codeOffset < pcoffset)) { |
435 | uint64_t reg; |
436 | uint64_t reg2; |
437 | int64_t offset; |
438 | uint64_t length; |
439 | uint8_t opcode = addressSpace.get8(p); |
440 | uint8_t operand; |
441 | PrologInfoStackEntry *entry; |
442 | ++p; |
443 | switch (opcode) { |
444 | case DW_CFA_nop: |
445 | _LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n" ); |
446 | break; |
447 | case DW_CFA_set_loc: |
448 | codeOffset = |
449 | addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); |
450 | _LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n" ); |
451 | break; |
452 | case DW_CFA_advance_loc1: |
453 | codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); |
454 | p += 1; |
455 | _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n" , |
456 | static_cast<uint64_t>(codeOffset)); |
457 | break; |
458 | case DW_CFA_advance_loc2: |
459 | codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); |
460 | p += 2; |
461 | _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n" , |
462 | static_cast<uint64_t>(codeOffset)); |
463 | break; |
464 | case DW_CFA_advance_loc4: |
465 | codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); |
466 | p += 4; |
467 | _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n" , |
468 | static_cast<uint64_t>(codeOffset)); |
469 | break; |
470 | case DW_CFA_offset_extended: |
471 | reg = addressSpace.getULEB128(p, instructionsEnd); |
472 | offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) |
473 | * cieInfo.dataAlignFactor; |
474 | if (reg > kMaxRegisterNumber) { |
475 | _LIBUNWIND_LOG0( |
476 | "malformed DW_CFA_offset_extended DWARF unwind, reg too big" ); |
477 | return false; |
478 | } |
479 | results->savedRegisters[reg].location = kRegisterInCFA; |
480 | results->savedRegisters[reg].value = offset; |
481 | _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", " |
482 | "offset=%" PRId64 ")\n" , |
483 | reg, offset); |
484 | break; |
485 | case DW_CFA_restore_extended: |
486 | reg = addressSpace.getULEB128(p, instructionsEnd); |
487 | if (reg > kMaxRegisterNumber) { |
488 | _LIBUNWIND_LOG0( |
489 | "malformed DW_CFA_restore_extended DWARF unwind, reg too big" ); |
490 | return false; |
491 | } |
492 | results->savedRegisters[reg] = initialState.savedRegisters[reg]; |
493 | _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n" , reg); |
494 | break; |
495 | case DW_CFA_undefined: |
496 | reg = addressSpace.getULEB128(p, instructionsEnd); |
497 | if (reg > kMaxRegisterNumber) { |
498 | _LIBUNWIND_LOG0( |
499 | "malformed DW_CFA_undefined DWARF unwind, reg too big" ); |
500 | return false; |
501 | } |
502 | results->savedRegisters[reg].location = kRegisterUnused; |
503 | _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n" , reg); |
504 | break; |
505 | case DW_CFA_same_value: |
506 | reg = addressSpace.getULEB128(p, instructionsEnd); |
507 | if (reg > kMaxRegisterNumber) { |
508 | _LIBUNWIND_LOG0( |
509 | "malformed DW_CFA_same_value DWARF unwind, reg too big" ); |
510 | return false; |
511 | } |
512 | // <rdar://problem/8456377> DW_CFA_same_value unsupported |
513 | // "same value" means register was stored in frame, but its current |
514 | // value has not changed, so no need to restore from frame. |
515 | // We model this as if the register was never saved. |
516 | results->savedRegisters[reg].location = kRegisterUnused; |
517 | // set flag to disable conversion to compact unwind |
518 | results->sameValueUsed = true; |
519 | _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n" , reg); |
520 | break; |
521 | case DW_CFA_register: |
522 | reg = addressSpace.getULEB128(p, instructionsEnd); |
523 | reg2 = addressSpace.getULEB128(p, instructionsEnd); |
524 | if (reg > kMaxRegisterNumber) { |
525 | _LIBUNWIND_LOG0( |
526 | "malformed DW_CFA_register DWARF unwind, reg too big" ); |
527 | return false; |
528 | } |
529 | if (reg2 > kMaxRegisterNumber) { |
530 | _LIBUNWIND_LOG0( |
531 | "malformed DW_CFA_register DWARF unwind, reg2 too big" ); |
532 | return false; |
533 | } |
534 | results->savedRegisters[reg].location = kRegisterInRegister; |
535 | results->savedRegisters[reg].value = (int64_t)reg2; |
536 | // set flag to disable conversion to compact unwind |
537 | results->registersInOtherRegisters = true; |
538 | _LIBUNWIND_TRACE_DWARF( |
539 | "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n" , reg, reg2); |
540 | break; |
541 | case DW_CFA_remember_state: |
542 | // avoid operator new, because that would be an upward dependency |
543 | entry = (PrologInfoStackEntry *)stack.push(); |
544 | if (entry != NULL) { |
545 | entry->next = rememberStack; |
546 | entry->info = *results; |
547 | rememberStack = entry; |
548 | } else { |
549 | return false; |
550 | } |
551 | _LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n" ); |
552 | break; |
553 | case DW_CFA_restore_state: |
554 | if (rememberStack != NULL) { |
555 | PrologInfoStackEntry *top = rememberStack; |
556 | *results = top->info; |
557 | rememberStack = top->next; |
558 | stack.pop(); |
559 | } else { |
560 | return false; |
561 | } |
562 | _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n" ); |
563 | break; |
564 | case DW_CFA_def_cfa: |
565 | reg = addressSpace.getULEB128(p, instructionsEnd); |
566 | offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd); |
567 | if (reg > kMaxRegisterNumber) { |
568 | _LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big" ); |
569 | return false; |
570 | } |
571 | results->cfaRegister = (uint32_t)reg; |
572 | results->cfaRegisterOffset = (int32_t)offset; |
573 | _LIBUNWIND_TRACE_DWARF( |
574 | "DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 ")\n" , reg, offset); |
575 | break; |
576 | case DW_CFA_def_cfa_register: |
577 | reg = addressSpace.getULEB128(p, instructionsEnd); |
578 | if (reg > kMaxRegisterNumber) { |
579 | _LIBUNWIND_LOG0( |
580 | "malformed DW_CFA_def_cfa_register DWARF unwind, reg too big" ); |
581 | return false; |
582 | } |
583 | results->cfaRegister = (uint32_t)reg; |
584 | _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n" , reg); |
585 | break; |
586 | case DW_CFA_def_cfa_offset: |
587 | results->cfaRegisterOffset = (int32_t) |
588 | addressSpace.getULEB128(p, instructionsEnd); |
589 | results->codeOffsetAtStackDecrement = (uint32_t)codeOffset; |
590 | _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n" , |
591 | results->cfaRegisterOffset); |
592 | break; |
593 | case DW_CFA_def_cfa_expression: |
594 | results->cfaRegister = 0; |
595 | results->cfaExpression = (int64_t)p; |
596 | length = addressSpace.getULEB128(p, instructionsEnd); |
597 | assert(length < static_cast<pint_t>(~0) && "pointer overflow" ); |
598 | p += static_cast<pint_t>(length); |
599 | _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64 |
600 | ", length=%" PRIu64 ")\n" , |
601 | results->cfaExpression, length); |
602 | break; |
603 | case DW_CFA_expression: |
604 | reg = addressSpace.getULEB128(p, instructionsEnd); |
605 | if (reg > kMaxRegisterNumber) { |
606 | _LIBUNWIND_LOG0( |
607 | "malformed DW_CFA_expression DWARF unwind, reg too big" ); |
608 | return false; |
609 | } |
610 | results->savedRegisters[reg].location = kRegisterAtExpression; |
611 | results->savedRegisters[reg].value = (int64_t)p; |
612 | length = addressSpace.getULEB128(p, instructionsEnd); |
613 | assert(length < static_cast<pint_t>(~0) && "pointer overflow" ); |
614 | p += static_cast<pint_t>(length); |
615 | _LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", " |
616 | "expression=0x%" PRIx64 ", " |
617 | "length=%" PRIu64 ")\n" , |
618 | reg, results->savedRegisters[reg].value, length); |
619 | break; |
620 | case DW_CFA_offset_extended_sf: |
621 | reg = addressSpace.getULEB128(p, instructionsEnd); |
622 | if (reg > kMaxRegisterNumber) { |
623 | _LIBUNWIND_LOG0( |
624 | "malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big" ); |
625 | return false; |
626 | } |
627 | offset = |
628 | addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; |
629 | results->savedRegisters[reg].location = kRegisterInCFA; |
630 | results->savedRegisters[reg].value = offset; |
631 | _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", " |
632 | "offset=%" PRId64 ")\n" , |
633 | reg, offset); |
634 | break; |
635 | case DW_CFA_def_cfa_sf: |
636 | reg = addressSpace.getULEB128(p, instructionsEnd); |
637 | offset = |
638 | addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; |
639 | if (reg > kMaxRegisterNumber) { |
640 | _LIBUNWIND_LOG0( |
641 | "malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big" ); |
642 | return false; |
643 | } |
644 | results->cfaRegister = (uint32_t)reg; |
645 | results->cfaRegisterOffset = (int32_t)offset; |
646 | _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", " |
647 | "offset=%" PRId64 ")\n" , |
648 | reg, offset); |
649 | break; |
650 | case DW_CFA_def_cfa_offset_sf: |
651 | results->cfaRegisterOffset = (int32_t) |
652 | (addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor); |
653 | results->codeOffsetAtStackDecrement = (uint32_t)codeOffset; |
654 | _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n" , |
655 | results->cfaRegisterOffset); |
656 | break; |
657 | case DW_CFA_val_offset: |
658 | reg = addressSpace.getULEB128(p, instructionsEnd); |
659 | if (reg > kMaxRegisterNumber) { |
660 | _LIBUNWIND_LOG( |
661 | "malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64 |
662 | ") out of range\n" , |
663 | reg); |
664 | return false; |
665 | } |
666 | offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) |
667 | * cieInfo.dataAlignFactor; |
668 | results->savedRegisters[reg].location = kRegisterOffsetFromCFA; |
669 | results->savedRegisters[reg].value = offset; |
670 | _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", " |
671 | "offset=%" PRId64 "\n" , |
672 | reg, offset); |
673 | break; |
674 | case DW_CFA_val_offset_sf: |
675 | reg = addressSpace.getULEB128(p, instructionsEnd); |
676 | if (reg > kMaxRegisterNumber) { |
677 | _LIBUNWIND_LOG0( |
678 | "malformed DW_CFA_val_offset_sf DWARF unwind, reg too big" ); |
679 | return false; |
680 | } |
681 | offset = |
682 | addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; |
683 | results->savedRegisters[reg].location = kRegisterOffsetFromCFA; |
684 | results->savedRegisters[reg].value = offset; |
685 | _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", " |
686 | "offset=%" PRId64 "\n" , |
687 | reg, offset); |
688 | break; |
689 | case DW_CFA_val_expression: |
690 | reg = addressSpace.getULEB128(p, instructionsEnd); |
691 | if (reg > kMaxRegisterNumber) { |
692 | _LIBUNWIND_LOG0( |
693 | "malformed DW_CFA_val_expression DWARF unwind, reg too big" ); |
694 | return false; |
695 | } |
696 | results->savedRegisters[reg].location = kRegisterIsExpression; |
697 | results->savedRegisters[reg].value = (int64_t)p; |
698 | length = addressSpace.getULEB128(p, instructionsEnd); |
699 | assert(length < static_cast<pint_t>(~0) && "pointer overflow" ); |
700 | p += static_cast<pint_t>(length); |
701 | _LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", " |
702 | "expression=0x%" PRIx64 ", length=%" PRIu64 ")\n" , |
703 | reg, results->savedRegisters[reg].value, length); |
704 | break; |
705 | case DW_CFA_GNU_args_size: |
706 | length = addressSpace.getULEB128(p, instructionsEnd); |
707 | results->spExtraArgSize = (uint32_t)length; |
708 | _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n" , length); |
709 | break; |
710 | case DW_CFA_GNU_negative_offset_extended: |
711 | reg = addressSpace.getULEB128(p, instructionsEnd); |
712 | if (reg > kMaxRegisterNumber) { |
713 | _LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF " |
714 | "unwind, reg too big" ); |
715 | return false; |
716 | } |
717 | offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) |
718 | * cieInfo.dataAlignFactor; |
719 | results->savedRegisters[reg].location = kRegisterInCFA; |
720 | results->savedRegisters[reg].value = -offset; |
721 | _LIBUNWIND_TRACE_DWARF( |
722 | "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n" , offset); |
723 | break; |
724 | |
725 | #if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) |
726 | // The same constant is used to represent different instructions on |
727 | // AArch64 (negate_ra_state) and SPARC (window_save). |
728 | static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save, |
729 | "uses the same constant" ); |
730 | case DW_CFA_AARCH64_negate_ra_state: |
731 | switch (arch) { |
732 | #if defined(_LIBUNWIND_TARGET_AARCH64) |
733 | case REGISTERS_ARM64: |
734 | results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1; |
735 | _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n" ); |
736 | break; |
737 | #endif |
738 | #if defined(_LIBUNWIND_TARGET_SPARC) |
739 | // case DW_CFA_GNU_window_save: |
740 | case REGISTERS_SPARC: |
741 | _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n" ); |
742 | for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) { |
743 | results->savedRegisters[reg].location = kRegisterInRegister; |
744 | results->savedRegisters[reg].value = |
745 | ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0; |
746 | } |
747 | |
748 | for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) { |
749 | results->savedRegisters[reg].location = kRegisterInCFA; |
750 | results->savedRegisters[reg].value = |
751 | ((int64_t)reg - UNW_SPARC_L0) * 4; |
752 | } |
753 | break; |
754 | #endif |
755 | } |
756 | break; |
757 | #else |
758 | (void)arch; |
759 | #endif |
760 | |
761 | default: |
762 | operand = opcode & 0x3F; |
763 | switch (opcode & 0xC0) { |
764 | case DW_CFA_offset: |
765 | reg = operand; |
766 | if (reg > kMaxRegisterNumber) { |
767 | _LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64 |
768 | ") out of range" , |
769 | reg); |
770 | return false; |
771 | } |
772 | offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) |
773 | * cieInfo.dataAlignFactor; |
774 | results->savedRegisters[reg].location = kRegisterInCFA; |
775 | results->savedRegisters[reg].value = offset; |
776 | _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n" , |
777 | operand, offset); |
778 | break; |
779 | case DW_CFA_advance_loc: |
780 | codeOffset += operand * cieInfo.codeAlignFactor; |
781 | _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n" , |
782 | static_cast<uint64_t>(codeOffset)); |
783 | break; |
784 | case DW_CFA_restore: |
785 | reg = operand; |
786 | if (reg > kMaxRegisterNumber) { |
787 | _LIBUNWIND_LOG("malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64 |
788 | ") out of range" , |
789 | reg); |
790 | return false; |
791 | } |
792 | results->savedRegisters[reg] = initialState.savedRegisters[reg]; |
793 | _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n" , |
794 | static_cast<uint64_t>(operand)); |
795 | break; |
796 | default: |
797 | _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n" , opcode); |
798 | return false; |
799 | } |
800 | } |
801 | } |
802 | |
803 | return true; |
804 | } |
805 | |
806 | } // namespace libunwind |
807 | |
808 | #endif // __DWARF_PARSER_HPP__ |
809 | |