blob: 2994bd7bb41fb0f91561d92b46d8799e01e512ac [file] [log] [blame]
Saleem Abdulrasool17552662015-04-24 19:39:17 +00001//===--------------------------- DwarfParser.hpp --------------------------===//
2//
Chandler Carruth61860a52019-01-19 10:56:40 +00003// 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
Saleem Abdulrasool17552662015-04-24 19:39:17 +00006//
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
Saleem Abdulrasool17552662015-04-24 19:39:17 +000020#include "libunwind.h"
21#include "dwarf2.h"
Daniel Cederman9f2f07a2019-01-14 10:15:20 +000022#include "Registers.hpp"
Saleem Abdulrasool17552662015-04-24 19:39:17 +000023
Saleem Abdulrasool286a9162017-01-25 02:27:45 +000024#include "config.h"
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +000025
Saleem Abdulrasool17552662015-04-24 19:39:17 +000026namespace libunwind {
27
28/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
Ed Maste4c43c3d2016-07-19 17:15:50 +000029/// See DWARF Spec for details:
Saleem Abdulrasool17552662015-04-24 19:39:17 +000030/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
31///
32template <typename A>
33class CFI_Parser {
34public:
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;
Luke Cheesemancba83c32018-12-17 11:43:24 +000052#if defined(_LIBUNWIND_TARGET_AARCH64)
53 bool addressesSignedWithBKey;
54#endif
Saleem Abdulrasool17552662015-04-24 19:39:17 +000055 };
56
57 /// Information about an FDE (Frame Description Entry)
58 struct FDE_Info {
59 pint_t fdeStart;
60 pint_t fdeLength;
61 pint_t fdeInstructions;
62 pint_t pcStart;
63 pint_t pcEnd;
64 pint_t lsda;
65 };
66
67 enum {
Ed Maste567984c2016-07-20 15:19:09 +000068 kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER
Saleem Abdulrasool17552662015-04-24 19:39:17 +000069 };
70 enum RegisterSavedWhere {
71 kRegisterUnused,
72 kRegisterInCFA,
73 kRegisterOffsetFromCFA,
74 kRegisterInRegister,
75 kRegisterAtExpression,
76 kRegisterIsExpression
77 };
78 struct RegisterLocation {
79 RegisterSavedWhere location;
80 int64_t value;
81 };
82 /// Information about a frame layout and registers saved determined
Ed Maste4c43c3d2016-07-19 17:15:50 +000083 /// by "running" the DWARF FDE "instructions"
Saleem Abdulrasool17552662015-04-24 19:39:17 +000084 struct PrologInfo {
85 uint32_t cfaRegister;
86 int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
87 int64_t cfaExpression; // CFA = expression
88 uint32_t spExtraArgSize;
89 uint32_t codeOffsetAtStackDecrement;
90 bool registersInOtherRegisters;
91 bool sameValueUsed;
Martin Storsjof10f3c92017-10-27 07:59:01 +000092 RegisterLocation savedRegisters[kMaxRegisterNumber + 1];
Saleem Abdulrasool17552662015-04-24 19:39:17 +000093 };
94
95 struct PrologInfoStackEntry {
96 PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i)
97 : next(n), info(i) {}
98 PrologInfoStackEntry *next;
99 PrologInfo info;
100 };
101
102 static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
103 uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo,
104 CIE_Info *cieInfo);
105 static const char *decodeFDE(A &addressSpace, pint_t fdeStart,
106 FDE_Info *fdeInfo, CIE_Info *cieInfo);
107 static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo,
108 const CIE_Info &cieInfo, pint_t upToPC,
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000109 int arch, PrologInfo *results);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000110
111 static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo);
112
113private:
114 static bool parseInstructions(A &addressSpace, pint_t instructions,
115 pint_t instructionsEnd, const CIE_Info &cieInfo,
116 pint_t pcoffset,
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000117 PrologInfoStackEntry *&rememberStack, int arch,
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000118 PrologInfo *results);
119};
120
121/// Parse a FDE into a CIE_Info and an FDE_Info
122template <typename A>
123const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart,
124 FDE_Info *fdeInfo, CIE_Info *cieInfo) {
125 pint_t p = fdeStart;
126 pint_t cfiLength = (pint_t)addressSpace.get32(p);
127 p += 4;
128 if (cfiLength == 0xffffffff) {
129 // 0xffffffff means length is really next 8 bytes
130 cfiLength = (pint_t)addressSpace.get64(p);
131 p += 8;
132 }
133 if (cfiLength == 0)
134 return "FDE has zero length"; // end marker
135 uint32_t ciePointer = addressSpace.get32(p);
136 if (ciePointer == 0)
137 return "FDE is really a CIE"; // this is a CIE not an FDE
138 pint_t nextCFI = p + cfiLength;
139 pint_t cieStart = p - ciePointer;
140 const char *err = parseCIE(addressSpace, cieStart, cieInfo);
141 if (err != NULL)
142 return err;
143 p += 4;
Ed Maste1b651132016-07-19 17:28:38 +0000144 // Parse pc begin and range.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000145 pint_t pcStart =
146 addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
147 pint_t pcRange =
148 addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
Ed Maste1b651132016-07-19 17:28:38 +0000149 // Parse rest of info.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000150 fdeInfo->lsda = 0;
Ed Maste1b651132016-07-19 17:28:38 +0000151 // Check for augmentation length.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000152 if (cieInfo->fdesHaveAugmentationData) {
153 pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
154 pint_t endOfAug = p + augLen;
155 if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
Ed Maste1b651132016-07-19 17:28:38 +0000156 // Peek at value (without indirection). Zero means no LSDA.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000157 pint_t lsdaStart = p;
158 if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) !=
159 0) {
Ed Maste1b651132016-07-19 17:28:38 +0000160 // Reset pointer and re-parse LSDA address.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000161 p = lsdaStart;
162 fdeInfo->lsda =
163 addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
164 }
165 }
166 p = endOfAug;
167 }
168 fdeInfo->fdeStart = fdeStart;
169 fdeInfo->fdeLength = nextCFI - fdeStart;
170 fdeInfo->fdeInstructions = p;
171 fdeInfo->pcStart = pcStart;
172 fdeInfo->pcEnd = pcStart + pcRange;
173 return NULL; // success
174}
175
176/// Scan an eh_frame section to find an FDE for a pc
177template <typename A>
178bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
179 uint32_t sectionLength, pint_t fdeHint,
180 FDE_Info *fdeInfo, CIE_Info *cieInfo) {
181 //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
182 pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
183 const pint_t ehSectionEnd = p + sectionLength;
184 while (p < ehSectionEnd) {
185 pint_t currentCFI = p;
186 //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
187 pint_t cfiLength = addressSpace.get32(p);
188 p += 4;
189 if (cfiLength == 0xffffffff) {
190 // 0xffffffff means length is really next 8 bytes
191 cfiLength = (pint_t)addressSpace.get64(p);
192 p += 8;
193 }
194 if (cfiLength == 0)
195 return false; // end marker
196 uint32_t id = addressSpace.get32(p);
197 if (id == 0) {
Ed Maste1b651132016-07-19 17:28:38 +0000198 // Skip over CIEs.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000199 p += cfiLength;
200 } else {
Ed Maste1b651132016-07-19 17:28:38 +0000201 // Process FDE to see if it covers pc.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000202 pint_t nextCFI = p + cfiLength;
203 uint32_t ciePointer = addressSpace.get32(p);
204 pint_t cieStart = p - ciePointer;
Ed Maste1b651132016-07-19 17:28:38 +0000205 // Validate pointer to CIE is within section.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000206 if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) {
207 if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) {
208 p += 4;
Ed Maste1b651132016-07-19 17:28:38 +0000209 // Parse pc begin and range.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000210 pint_t pcStart =
211 addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
212 pint_t pcRange = addressSpace.getEncodedP(
213 p, nextCFI, cieInfo->pointerEncoding & 0x0F);
Ed Maste1b651132016-07-19 17:28:38 +0000214 // Test if pc is within the function this FDE covers.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000215 if ((pcStart < pc) && (pc <= pcStart + pcRange)) {
216 // parse rest of info
217 fdeInfo->lsda = 0;
218 // check for augmentation length
219 if (cieInfo->fdesHaveAugmentationData) {
220 pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
221 pint_t endOfAug = p + augLen;
222 if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
Ed Maste1b651132016-07-19 17:28:38 +0000223 // Peek at value (without indirection). Zero means no LSDA.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000224 pint_t lsdaStart = p;
225 if (addressSpace.getEncodedP(
226 p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) {
Ed Maste1b651132016-07-19 17:28:38 +0000227 // Reset pointer and re-parse LSDA address.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000228 p = lsdaStart;
229 fdeInfo->lsda = addressSpace
230 .getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
231 }
232 }
233 p = endOfAug;
234 }
235 fdeInfo->fdeStart = currentCFI;
236 fdeInfo->fdeLength = nextCFI - currentCFI;
237 fdeInfo->fdeInstructions = p;
238 fdeInfo->pcStart = pcStart;
239 fdeInfo->pcEnd = pcStart + pcRange;
240 return true;
241 } else {
242 // pc is not in begin/range, skip this FDE
243 }
244 } else {
Ed Maste1b651132016-07-19 17:28:38 +0000245 // Malformed CIE, now augmentation describing pc range encoding.
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000246 }
247 } else {
248 // malformed FDE. CIE is bad
249 }
250 p = nextCFI;
251 }
252 }
253 return false;
254}
255
256/// Extract info from a CIE
257template <typename A>
258const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
259 CIE_Info *cieInfo) {
260 cieInfo->pointerEncoding = 0;
261 cieInfo->lsdaEncoding = DW_EH_PE_omit;
262 cieInfo->personalityEncoding = 0;
263 cieInfo->personalityOffsetInCIE = 0;
264 cieInfo->personality = 0;
265 cieInfo->codeAlignFactor = 0;
266 cieInfo->dataAlignFactor = 0;
267 cieInfo->isSignalFrame = false;
268 cieInfo->fdesHaveAugmentationData = false;
Luke Cheesemancba83c32018-12-17 11:43:24 +0000269#if defined(_LIBUNWIND_TARGET_AARCH64)
270 cieInfo->addressesSignedWithBKey = false;
271#endif
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000272 cieInfo->cieStart = cie;
273 pint_t p = cie;
274 pint_t cieLength = (pint_t)addressSpace.get32(p);
275 p += 4;
276 pint_t cieContentEnd = p + cieLength;
277 if (cieLength == 0xffffffff) {
278 // 0xffffffff means length is really next 8 bytes
279 cieLength = (pint_t)addressSpace.get64(p);
280 p += 8;
281 cieContentEnd = p + cieLength;
282 }
283 if (cieLength == 0)
284 return NULL;
285 // CIE ID is always 0
286 if (addressSpace.get32(p) != 0)
287 return "CIE ID is not zero";
288 p += 4;
289 // Version is always 1 or 3
290 uint8_t version = addressSpace.get8(p);
291 if ((version != 1) && (version != 3))
292 return "CIE version is not 1 or 3";
293 ++p;
294 // save start of augmentation string and find end
295 pint_t strStart = p;
296 while (addressSpace.get8(p) != 0)
297 ++p;
298 ++p;
299 // parse code aligment factor
300 cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd);
301 // parse data alignment factor
302 cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd);
303 // parse return address register
304 uint64_t raReg = addressSpace.getULEB128(p, cieContentEnd);
305 assert(raReg < 255 && "return address register too large");
306 cieInfo->returnAddressRegister = (uint8_t)raReg;
307 // parse augmentation data based on augmentation string
308 const char *result = NULL;
309 if (addressSpace.get8(strStart) == 'z') {
310 // parse augmentation data length
311 addressSpace.getULEB128(p, cieContentEnd);
312 for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) {
313 switch (addressSpace.get8(s)) {
314 case 'z':
315 cieInfo->fdesHaveAugmentationData = true;
316 break;
317 case 'P':
318 cieInfo->personalityEncoding = addressSpace.get8(p);
319 ++p;
320 cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie);
321 cieInfo->personality = addressSpace
322 .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
323 break;
324 case 'L':
325 cieInfo->lsdaEncoding = addressSpace.get8(p);
326 ++p;
327 break;
328 case 'R':
329 cieInfo->pointerEncoding = addressSpace.get8(p);
330 ++p;
331 break;
332 case 'S':
333 cieInfo->isSignalFrame = true;
334 break;
Luke Cheesemancba83c32018-12-17 11:43:24 +0000335#if defined(_LIBUNWIND_TARGET_AARCH64)
336 case 'B':
337 cieInfo->addressesSignedWithBKey = true;
338 break;
339#endif
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000340 default:
341 // ignore unknown letters
342 break;
343 }
344 }
345 }
346 cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
347 cieInfo->cieInstructions = p;
348 return result;
349}
350
351
Ed Maste4c43c3d2016-07-19 17:15:50 +0000352/// "run" the DWARF instructions and create the abstact PrologInfo for an FDE
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000353template <typename A>
354bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
355 const FDE_Info &fdeInfo,
356 const CIE_Info &cieInfo, pint_t upToPC,
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000357 int arch, PrologInfo *results) {
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000358 // clear results
359 memset(results, '\0', sizeof(PrologInfo));
360 PrologInfoStackEntry *rememberStack = NULL;
361
362 // parse CIE then FDE instructions
Jorge Gorbe Moyafd8399e2020-02-18 11:48:02 -0800363 bool returnValue =
364 parseInstructions(addressSpace, cieInfo.cieInstructions,
365 cieInfo.cieStart + cieInfo.cieLength, cieInfo,
366 (pint_t)(-1), rememberStack, arch, results) &&
367 parseInstructions(addressSpace, fdeInfo.fdeInstructions,
368 fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo,
369 upToPC - fdeInfo.pcStart, rememberStack, arch, results);
370
371 // Clean up rememberStack. Even in the case where every DW_CFA_remember_state
372 // is paired with a DW_CFA_restore_state, parseInstructions can skip restore
373 // opcodes if it reaches the target PC and stops interpreting, so we have to
374 // make sure we don't leak memory.
375 while (rememberStack) {
376 PrologInfoStackEntry *next = rememberStack->next;
377 free(rememberStack);
378 rememberStack = next;
379 }
380
381 return returnValue;
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000382}
383
Ed Maste4c43c3d2016-07-19 17:15:50 +0000384/// "run" the DWARF instructions
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000385template <typename A>
386bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions,
387 pint_t instructionsEnd,
388 const CIE_Info &cieInfo, pint_t pcoffset,
389 PrologInfoStackEntry *&rememberStack,
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000390 int arch, PrologInfo *results) {
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000391 pint_t p = instructions;
392 pint_t codeOffset = 0;
393 PrologInfo initialState = *results;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000394
395 _LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n",
396 static_cast<uint64_t>(instructionsEnd));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000397
Ed Maste4c43c3d2016-07-19 17:15:50 +0000398 // see DWARF Spec, section 6.4.2 for details on unwind opcodes
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000399 while ((p < instructionsEnd) && (codeOffset < pcoffset)) {
400 uint64_t reg;
401 uint64_t reg2;
402 int64_t offset;
403 uint64_t length;
404 uint8_t opcode = addressSpace.get8(p);
405 uint8_t operand;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000406#if !defined(_LIBUNWIND_NO_HEAP)
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000407 PrologInfoStackEntry *entry;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000408#endif
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000409 ++p;
410 switch (opcode) {
411 case DW_CFA_nop:
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000412 _LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000413 break;
414 case DW_CFA_set_loc:
415 codeOffset =
416 addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000417 _LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000418 break;
419 case DW_CFA_advance_loc1:
420 codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
421 p += 1;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000422 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n",
423 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000424 break;
425 case DW_CFA_advance_loc2:
426 codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
427 p += 2;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000428 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n",
429 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000430 break;
431 case DW_CFA_advance_loc4:
432 codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
433 p += 4;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000434 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n",
435 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000436 break;
437 case DW_CFA_offset_extended:
438 reg = addressSpace.getULEB128(p, instructionsEnd);
439 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
440 * cieInfo.dataAlignFactor;
441 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000442 _LIBUNWIND_LOG0(
443 "malformed DW_CFA_offset_extended DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000444 return false;
445 }
446 results->savedRegisters[reg].location = kRegisterInCFA;
447 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000448 _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", "
449 "offset=%" PRId64 ")\n",
450 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000451 break;
452 case DW_CFA_restore_extended:
453 reg = addressSpace.getULEB128(p, instructionsEnd);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000454 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000455 _LIBUNWIND_LOG0(
456 "malformed DW_CFA_restore_extended DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000457 return false;
458 }
459 results->savedRegisters[reg] = initialState.savedRegisters[reg];
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000460 _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000461 break;
462 case DW_CFA_undefined:
463 reg = addressSpace.getULEB128(p, instructionsEnd);
464 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000465 _LIBUNWIND_LOG0(
466 "malformed DW_CFA_undefined DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000467 return false;
468 }
469 results->savedRegisters[reg].location = kRegisterUnused;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000470 _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000471 break;
472 case DW_CFA_same_value:
473 reg = addressSpace.getULEB128(p, instructionsEnd);
474 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000475 _LIBUNWIND_LOG0(
476 "malformed DW_CFA_same_value DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000477 return false;
478 }
479 // <rdar://problem/8456377> DW_CFA_same_value unsupported
480 // "same value" means register was stored in frame, but its current
481 // value has not changed, so no need to restore from frame.
482 // We model this as if the register was never saved.
483 results->savedRegisters[reg].location = kRegisterUnused;
484 // set flag to disable conversion to compact unwind
485 results->sameValueUsed = true;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000486 _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000487 break;
488 case DW_CFA_register:
489 reg = addressSpace.getULEB128(p, instructionsEnd);
490 reg2 = addressSpace.getULEB128(p, instructionsEnd);
491 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000492 _LIBUNWIND_LOG0(
493 "malformed DW_CFA_register DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000494 return false;
495 }
496 if (reg2 > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000497 _LIBUNWIND_LOG0(
498 "malformed DW_CFA_register DWARF unwind, reg2 too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000499 return false;
500 }
501 results->savedRegisters[reg].location = kRegisterInRegister;
502 results->savedRegisters[reg].value = (int64_t)reg2;
503 // set flag to disable conversion to compact unwind
504 results->registersInOtherRegisters = true;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000505 _LIBUNWIND_TRACE_DWARF(
506 "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000507 break;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000508#if !defined(_LIBUNWIND_NO_HEAP)
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000509 case DW_CFA_remember_state:
510 // avoid operator new, because that would be an upward dependency
511 entry = (PrologInfoStackEntry *)malloc(sizeof(PrologInfoStackEntry));
512 if (entry != NULL) {
513 entry->next = rememberStack;
514 entry->info = *results;
515 rememberStack = entry;
516 } else {
517 return false;
518 }
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000519 _LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000520 break;
521 case DW_CFA_restore_state:
522 if (rememberStack != NULL) {
523 PrologInfoStackEntry *top = rememberStack;
524 *results = top->info;
525 rememberStack = top->next;
526 free((char *)top);
527 } else {
528 return false;
529 }
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000530 _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000531 break;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000532#endif
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000533 case DW_CFA_def_cfa:
534 reg = addressSpace.getULEB128(p, instructionsEnd);
535 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd);
536 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000537 _LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000538 return false;
539 }
540 results->cfaRegister = (uint32_t)reg;
541 results->cfaRegisterOffset = (int32_t)offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000542 _LIBUNWIND_TRACE_DWARF(
543 "DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 ")\n", reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000544 break;
545 case DW_CFA_def_cfa_register:
546 reg = addressSpace.getULEB128(p, instructionsEnd);
547 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000548 _LIBUNWIND_LOG0(
549 "malformed DW_CFA_def_cfa_register DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000550 return false;
551 }
552 results->cfaRegister = (uint32_t)reg;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000553 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000554 break;
555 case DW_CFA_def_cfa_offset:
556 results->cfaRegisterOffset = (int32_t)
557 addressSpace.getULEB128(p, instructionsEnd);
558 results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000559 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n",
560 results->cfaRegisterOffset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000561 break;
562 case DW_CFA_def_cfa_expression:
563 results->cfaRegister = 0;
564 results->cfaExpression = (int64_t)p;
565 length = addressSpace.getULEB128(p, instructionsEnd);
whitequark443f65c2017-12-25 21:08:41 +0000566 assert(length < static_cast<pint_t>(~0) && "pointer overflow");
Saleem Abdulrasool92acbf12017-03-08 16:03:27 +0000567 p += static_cast<pint_t>(length);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000568 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64
569 ", length=%" PRIu64 ")\n",
570 results->cfaExpression, length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000571 break;
572 case DW_CFA_expression:
573 reg = addressSpace.getULEB128(p, instructionsEnd);
574 if (reg > kMaxRegisterNumber) {
whitequarka3b115c2017-12-25 13:42:41 +0000575 _LIBUNWIND_LOG0(
whitequarkb718c5f2017-12-25 13:27:56 +0000576 "malformed DW_CFA_expression DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000577 return false;
578 }
579 results->savedRegisters[reg].location = kRegisterAtExpression;
580 results->savedRegisters[reg].value = (int64_t)p;
581 length = addressSpace.getULEB128(p, instructionsEnd);
whitequark443f65c2017-12-25 21:08:41 +0000582 assert(length < static_cast<pint_t>(~0) && "pointer overflow");
Saleem Abdulrasool92acbf12017-03-08 16:03:27 +0000583 p += static_cast<pint_t>(length);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000584 _LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", "
585 "expression=0x%" PRIx64 ", "
586 "length=%" PRIu64 ")\n",
587 reg, results->savedRegisters[reg].value, length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000588 break;
589 case DW_CFA_offset_extended_sf:
590 reg = addressSpace.getULEB128(p, instructionsEnd);
591 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000592 _LIBUNWIND_LOG0(
593 "malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000594 return false;
595 }
596 offset =
597 addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
598 results->savedRegisters[reg].location = kRegisterInCFA;
599 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000600 _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", "
601 "offset=%" PRId64 ")\n",
602 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000603 break;
604 case DW_CFA_def_cfa_sf:
605 reg = addressSpace.getULEB128(p, instructionsEnd);
606 offset =
607 addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
608 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000609 _LIBUNWIND_LOG0(
610 "malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000611 return false;
612 }
613 results->cfaRegister = (uint32_t)reg;
614 results->cfaRegisterOffset = (int32_t)offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000615 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", "
616 "offset=%" PRId64 ")\n",
617 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000618 break;
619 case DW_CFA_def_cfa_offset_sf:
620 results->cfaRegisterOffset = (int32_t)
621 (addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor);
622 results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000623 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n",
624 results->cfaRegisterOffset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000625 break;
626 case DW_CFA_val_offset:
627 reg = addressSpace.getULEB128(p, instructionsEnd);
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000628 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000629 _LIBUNWIND_LOG(
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000630 "malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64
631 ") out of range\n",
632 reg);
633 return false;
634 }
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000635 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
636 * cieInfo.dataAlignFactor;
637 results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
638 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000639 _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", "
640 "offset=%" PRId64 "\n",
641 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000642 break;
643 case DW_CFA_val_offset_sf:
644 reg = addressSpace.getULEB128(p, instructionsEnd);
645 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000646 _LIBUNWIND_LOG0(
647 "malformed DW_CFA_val_offset_sf DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000648 return false;
649 }
650 offset =
651 addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
652 results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
653 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000654 _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", "
655 "offset=%" PRId64 "\n",
656 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000657 break;
658 case DW_CFA_val_expression:
659 reg = addressSpace.getULEB128(p, instructionsEnd);
660 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000661 _LIBUNWIND_LOG0(
662 "malformed DW_CFA_val_expression DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000663 return false;
664 }
665 results->savedRegisters[reg].location = kRegisterIsExpression;
666 results->savedRegisters[reg].value = (int64_t)p;
667 length = addressSpace.getULEB128(p, instructionsEnd);
whitequark443f65c2017-12-25 21:08:41 +0000668 assert(length < static_cast<pint_t>(~0) && "pointer overflow");
Saleem Abdulrasool92acbf12017-03-08 16:03:27 +0000669 p += static_cast<pint_t>(length);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000670 _LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", "
671 "expression=0x%" PRIx64 ", length=%" PRIu64 ")\n",
672 reg, results->savedRegisters[reg].value, length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000673 break;
674 case DW_CFA_GNU_args_size:
675 length = addressSpace.getULEB128(p, instructionsEnd);
676 results->spExtraArgSize = (uint32_t)length;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000677 _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000678 break;
679 case DW_CFA_GNU_negative_offset_extended:
680 reg = addressSpace.getULEB128(p, instructionsEnd);
681 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000682 _LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF "
683 "unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000684 return false;
685 }
686 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
687 * cieInfo.dataAlignFactor;
688 results->savedRegisters[reg].location = kRegisterInCFA;
689 results->savedRegisters[reg].value = -offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000690 _LIBUNWIND_TRACE_DWARF(
691 "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000692 break;
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000693
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000694#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC)
695 // The same constant is used to represent different instructions on
696 // AArch64 (negate_ra_state) and SPARC (window_save).
697 static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save,
698 "uses the same constant");
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000699 case DW_CFA_AARCH64_negate_ra_state:
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000700 switch (arch) {
701#if defined(_LIBUNWIND_TARGET_AARCH64)
702 case REGISTERS_ARM64:
703 results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1;
704 _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n");
705 break;
706#endif
707#if defined(_LIBUNWIND_TARGET_SPARC)
708 // case DW_CFA_GNU_window_save:
709 case REGISTERS_SPARC:
710 _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n");
711 for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) {
712 results->savedRegisters[reg].location = kRegisterInRegister;
713 results->savedRegisters[reg].value =
714 ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0;
715 }
716
717 for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
718 results->savedRegisters[reg].location = kRegisterInCFA;
719 results->savedRegisters[reg].value =
720 ((int64_t)reg - UNW_SPARC_L0) * 4;
721 }
722 break;
723#endif
724 }
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000725 break;
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000726#else
727 (void)arch;
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000728#endif
729
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000730 default:
731 operand = opcode & 0x3F;
732 switch (opcode & 0xC0) {
733 case DW_CFA_offset:
734 reg = operand;
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000735 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000736 _LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64
737 ") out of range",
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000738 reg);
739 return false;
740 }
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000741 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
742 * cieInfo.dataAlignFactor;
743 results->savedRegisters[reg].location = kRegisterInCFA;
744 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000745 _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n",
746 operand, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000747 break;
748 case DW_CFA_advance_loc:
749 codeOffset += operand * cieInfo.codeAlignFactor;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000750 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n",
751 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000752 break;
753 case DW_CFA_restore:
754 reg = operand;
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000755 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000756 _LIBUNWIND_LOG("malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64
757 ") out of range",
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000758 reg);
759 return false;
760 }
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000761 results->savedRegisters[reg] = initialState.savedRegisters[reg];
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000762 _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n",
Saleem Abdulrasoolc73a3c32017-01-21 21:27:29 +0000763 static_cast<uint64_t>(operand));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000764 break;
765 default:
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000766 _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000767 return false;
768 }
769 }
770 }
771
772 return true;
773}
774
775} // namespace libunwind
776
777#endif // __DWARF_PARSER_HPP__