blob: df69c2a4bd237ead6c51f9eab2a3dc8fe13542fc [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
363 return parseInstructions(addressSpace, cieInfo.cieInstructions,
364 cieInfo.cieStart + cieInfo.cieLength, cieInfo,
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000365 (pint_t)(-1), rememberStack, arch, results) &&
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000366 parseInstructions(addressSpace, fdeInfo.fdeInstructions,
367 fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo,
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000368 upToPC - fdeInfo.pcStart, rememberStack, arch,
369 results);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000370}
371
Ed Maste4c43c3d2016-07-19 17:15:50 +0000372/// "run" the DWARF instructions
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000373template <typename A>
374bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions,
375 pint_t instructionsEnd,
376 const CIE_Info &cieInfo, pint_t pcoffset,
377 PrologInfoStackEntry *&rememberStack,
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000378 int arch, PrologInfo *results) {
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000379 pint_t p = instructions;
380 pint_t codeOffset = 0;
381 PrologInfo initialState = *results;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000382
383 _LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n",
384 static_cast<uint64_t>(instructionsEnd));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000385
Ed Maste4c43c3d2016-07-19 17:15:50 +0000386 // see DWARF Spec, section 6.4.2 for details on unwind opcodes
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000387 while ((p < instructionsEnd) && (codeOffset < pcoffset)) {
388 uint64_t reg;
389 uint64_t reg2;
390 int64_t offset;
391 uint64_t length;
392 uint8_t opcode = addressSpace.get8(p);
393 uint8_t operand;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000394#if !defined(_LIBUNWIND_NO_HEAP)
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000395 PrologInfoStackEntry *entry;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000396#endif
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000397 ++p;
398 switch (opcode) {
399 case DW_CFA_nop:
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000400 _LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000401 break;
402 case DW_CFA_set_loc:
403 codeOffset =
404 addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000405 _LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000406 break;
407 case DW_CFA_advance_loc1:
408 codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
409 p += 1;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000410 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n",
411 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000412 break;
413 case DW_CFA_advance_loc2:
414 codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
415 p += 2;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000416 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n",
417 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000418 break;
419 case DW_CFA_advance_loc4:
420 codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
421 p += 4;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000422 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n",
423 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000424 break;
425 case DW_CFA_offset_extended:
426 reg = addressSpace.getULEB128(p, instructionsEnd);
427 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
428 * cieInfo.dataAlignFactor;
429 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000430 _LIBUNWIND_LOG0(
431 "malformed DW_CFA_offset_extended DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000432 return false;
433 }
434 results->savedRegisters[reg].location = kRegisterInCFA;
435 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000436 _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", "
437 "offset=%" PRId64 ")\n",
438 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000439 break;
440 case DW_CFA_restore_extended:
441 reg = addressSpace.getULEB128(p, instructionsEnd);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000442 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000443 _LIBUNWIND_LOG0(
444 "malformed DW_CFA_restore_extended DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000445 return false;
446 }
447 results->savedRegisters[reg] = initialState.savedRegisters[reg];
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000448 _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000449 break;
450 case DW_CFA_undefined:
451 reg = addressSpace.getULEB128(p, instructionsEnd);
452 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000453 _LIBUNWIND_LOG0(
454 "malformed DW_CFA_undefined DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000455 return false;
456 }
457 results->savedRegisters[reg].location = kRegisterUnused;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000458 _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000459 break;
460 case DW_CFA_same_value:
461 reg = addressSpace.getULEB128(p, instructionsEnd);
462 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000463 _LIBUNWIND_LOG0(
464 "malformed DW_CFA_same_value DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000465 return false;
466 }
467 // <rdar://problem/8456377> DW_CFA_same_value unsupported
468 // "same value" means register was stored in frame, but its current
469 // value has not changed, so no need to restore from frame.
470 // We model this as if the register was never saved.
471 results->savedRegisters[reg].location = kRegisterUnused;
472 // set flag to disable conversion to compact unwind
473 results->sameValueUsed = true;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000474 _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000475 break;
476 case DW_CFA_register:
477 reg = addressSpace.getULEB128(p, instructionsEnd);
478 reg2 = addressSpace.getULEB128(p, instructionsEnd);
479 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000480 _LIBUNWIND_LOG0(
481 "malformed DW_CFA_register DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000482 return false;
483 }
484 if (reg2 > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000485 _LIBUNWIND_LOG0(
486 "malformed DW_CFA_register DWARF unwind, reg2 too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000487 return false;
488 }
489 results->savedRegisters[reg].location = kRegisterInRegister;
490 results->savedRegisters[reg].value = (int64_t)reg2;
491 // set flag to disable conversion to compact unwind
492 results->registersInOtherRegisters = true;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000493 _LIBUNWIND_TRACE_DWARF(
494 "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000495 break;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000496#if !defined(_LIBUNWIND_NO_HEAP)
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000497 case DW_CFA_remember_state:
498 // avoid operator new, because that would be an upward dependency
499 entry = (PrologInfoStackEntry *)malloc(sizeof(PrologInfoStackEntry));
500 if (entry != NULL) {
501 entry->next = rememberStack;
502 entry->info = *results;
503 rememberStack = entry;
504 } else {
505 return false;
506 }
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000507 _LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000508 break;
509 case DW_CFA_restore_state:
510 if (rememberStack != NULL) {
511 PrologInfoStackEntry *top = rememberStack;
512 *results = top->info;
513 rememberStack = top->next;
514 free((char *)top);
515 } else {
516 return false;
517 }
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000518 _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000519 break;
Peter Zotov0717a2e2015-11-09 06:57:29 +0000520#endif
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000521 case DW_CFA_def_cfa:
522 reg = addressSpace.getULEB128(p, instructionsEnd);
523 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd);
524 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000525 _LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000526 return false;
527 }
528 results->cfaRegister = (uint32_t)reg;
529 results->cfaRegisterOffset = (int32_t)offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000530 _LIBUNWIND_TRACE_DWARF(
531 "DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 ")\n", reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000532 break;
533 case DW_CFA_def_cfa_register:
534 reg = addressSpace.getULEB128(p, instructionsEnd);
535 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000536 _LIBUNWIND_LOG0(
537 "malformed DW_CFA_def_cfa_register DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000538 return false;
539 }
540 results->cfaRegister = (uint32_t)reg;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000541 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000542 break;
543 case DW_CFA_def_cfa_offset:
544 results->cfaRegisterOffset = (int32_t)
545 addressSpace.getULEB128(p, instructionsEnd);
546 results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000547 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n",
548 results->cfaRegisterOffset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000549 break;
550 case DW_CFA_def_cfa_expression:
551 results->cfaRegister = 0;
552 results->cfaExpression = (int64_t)p;
553 length = addressSpace.getULEB128(p, instructionsEnd);
whitequark443f65c2017-12-25 21:08:41 +0000554 assert(length < static_cast<pint_t>(~0) && "pointer overflow");
Saleem Abdulrasool92acbf12017-03-08 16:03:27 +0000555 p += static_cast<pint_t>(length);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000556 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64
557 ", length=%" PRIu64 ")\n",
558 results->cfaExpression, length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000559 break;
560 case DW_CFA_expression:
561 reg = addressSpace.getULEB128(p, instructionsEnd);
562 if (reg > kMaxRegisterNumber) {
whitequarka3b115c2017-12-25 13:42:41 +0000563 _LIBUNWIND_LOG0(
whitequarkb718c5f2017-12-25 13:27:56 +0000564 "malformed DW_CFA_expression DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000565 return false;
566 }
567 results->savedRegisters[reg].location = kRegisterAtExpression;
568 results->savedRegisters[reg].value = (int64_t)p;
569 length = addressSpace.getULEB128(p, instructionsEnd);
whitequark443f65c2017-12-25 21:08:41 +0000570 assert(length < static_cast<pint_t>(~0) && "pointer overflow");
Saleem Abdulrasool92acbf12017-03-08 16:03:27 +0000571 p += static_cast<pint_t>(length);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000572 _LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", "
573 "expression=0x%" PRIx64 ", "
574 "length=%" PRIu64 ")\n",
575 reg, results->savedRegisters[reg].value, length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000576 break;
577 case DW_CFA_offset_extended_sf:
578 reg = addressSpace.getULEB128(p, instructionsEnd);
579 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000580 _LIBUNWIND_LOG0(
581 "malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000582 return false;
583 }
584 offset =
585 addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
586 results->savedRegisters[reg].location = kRegisterInCFA;
587 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000588 _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", "
589 "offset=%" PRId64 ")\n",
590 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000591 break;
592 case DW_CFA_def_cfa_sf:
593 reg = addressSpace.getULEB128(p, instructionsEnd);
594 offset =
595 addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
596 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000597 _LIBUNWIND_LOG0(
598 "malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000599 return false;
600 }
601 results->cfaRegister = (uint32_t)reg;
602 results->cfaRegisterOffset = (int32_t)offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000603 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", "
604 "offset=%" PRId64 ")\n",
605 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000606 break;
607 case DW_CFA_def_cfa_offset_sf:
608 results->cfaRegisterOffset = (int32_t)
609 (addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor);
610 results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000611 _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n",
612 results->cfaRegisterOffset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000613 break;
614 case DW_CFA_val_offset:
615 reg = addressSpace.getULEB128(p, instructionsEnd);
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000616 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000617 _LIBUNWIND_LOG(
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000618 "malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64
619 ") out of range\n",
620 reg);
621 return false;
622 }
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000623 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
624 * cieInfo.dataAlignFactor;
625 results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
626 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000627 _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", "
628 "offset=%" PRId64 "\n",
629 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000630 break;
631 case DW_CFA_val_offset_sf:
632 reg = addressSpace.getULEB128(p, instructionsEnd);
633 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000634 _LIBUNWIND_LOG0(
635 "malformed DW_CFA_val_offset_sf DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000636 return false;
637 }
638 offset =
639 addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
640 results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
641 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000642 _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", "
643 "offset=%" PRId64 "\n",
644 reg, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000645 break;
646 case DW_CFA_val_expression:
647 reg = addressSpace.getULEB128(p, instructionsEnd);
648 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000649 _LIBUNWIND_LOG0(
650 "malformed DW_CFA_val_expression DWARF unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000651 return false;
652 }
653 results->savedRegisters[reg].location = kRegisterIsExpression;
654 results->savedRegisters[reg].value = (int64_t)p;
655 length = addressSpace.getULEB128(p, instructionsEnd);
whitequark443f65c2017-12-25 21:08:41 +0000656 assert(length < static_cast<pint_t>(~0) && "pointer overflow");
Saleem Abdulrasool92acbf12017-03-08 16:03:27 +0000657 p += static_cast<pint_t>(length);
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000658 _LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", "
659 "expression=0x%" PRIx64 ", length=%" PRIu64 ")\n",
660 reg, results->savedRegisters[reg].value, length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000661 break;
662 case DW_CFA_GNU_args_size:
663 length = addressSpace.getULEB128(p, instructionsEnd);
664 results->spExtraArgSize = (uint32_t)length;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000665 _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000666 break;
667 case DW_CFA_GNU_negative_offset_extended:
668 reg = addressSpace.getULEB128(p, instructionsEnd);
669 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000670 _LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF "
671 "unwind, reg too big");
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000672 return false;
673 }
674 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
675 * cieInfo.dataAlignFactor;
676 results->savedRegisters[reg].location = kRegisterInCFA;
677 results->savedRegisters[reg].value = -offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000678 _LIBUNWIND_TRACE_DWARF(
679 "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000680 break;
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000681
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000682#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC)
683 // The same constant is used to represent different instructions on
684 // AArch64 (negate_ra_state) and SPARC (window_save).
685 static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save,
686 "uses the same constant");
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000687 case DW_CFA_AARCH64_negate_ra_state:
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000688 switch (arch) {
689#if defined(_LIBUNWIND_TARGET_AARCH64)
690 case REGISTERS_ARM64:
691 results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1;
692 _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n");
693 break;
694#endif
695#if defined(_LIBUNWIND_TARGET_SPARC)
696 // case DW_CFA_GNU_window_save:
697 case REGISTERS_SPARC:
698 _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n");
699 for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) {
700 results->savedRegisters[reg].location = kRegisterInRegister;
701 results->savedRegisters[reg].value =
702 ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0;
703 }
704
705 for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
706 results->savedRegisters[reg].location = kRegisterInCFA;
707 results->savedRegisters[reg].value =
708 ((int64_t)reg - UNW_SPARC_L0) * 4;
709 }
710 break;
711#endif
712 }
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000713 break;
Daniel Cederman9f2f07a2019-01-14 10:15:20 +0000714#else
715 (void)arch;
Luke Cheeseman4d8e4312018-12-14 11:30:12 +0000716#endif
717
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000718 default:
719 operand = opcode & 0x3F;
720 switch (opcode & 0xC0) {
721 case DW_CFA_offset:
722 reg = operand;
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000723 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000724 _LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64
725 ") out of range",
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000726 reg);
727 return false;
728 }
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000729 offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
730 * cieInfo.dataAlignFactor;
731 results->savedRegisters[reg].location = kRegisterInCFA;
732 results->savedRegisters[reg].value = offset;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000733 _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n",
734 operand, offset);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000735 break;
736 case DW_CFA_advance_loc:
737 codeOffset += operand * cieInfo.codeAlignFactor;
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000738 _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n",
739 static_cast<uint64_t>(codeOffset));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000740 break;
741 case DW_CFA_restore:
742 reg = operand;
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000743 if (reg > kMaxRegisterNumber) {
whitequarkb718c5f2017-12-25 13:27:56 +0000744 _LIBUNWIND_LOG("malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64
745 ") out of range",
Martin Storsjo6ae660a2017-10-24 07:16:40 +0000746 reg);
747 return false;
748 }
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000749 results->savedRegisters[reg] = initialState.savedRegisters[reg];
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000750 _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n",
Saleem Abdulrasoolc73a3c32017-01-21 21:27:29 +0000751 static_cast<uint64_t>(operand));
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000752 break;
753 default:
Saleem Abdulrasoold1be5b02017-01-21 16:22:57 +0000754 _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode);
Saleem Abdulrasool17552662015-04-24 19:39:17 +0000755 return false;
756 }
757 }
758 }
759
760 return true;
761}
762
763} // namespace libunwind
764
765#endif // __DWARF_PARSER_HPP__