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