1349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //
80b57cec5SDimitry Andric // Parses DWARF CFIs (FDEs and CIEs).
90b57cec5SDimitry Andric //
100b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
110b57cec5SDimitry Andric
120b57cec5SDimitry Andric #ifndef __DWARF_PARSER_HPP__
130b57cec5SDimitry Andric #define __DWARF_PARSER_HPP__
140b57cec5SDimitry Andric
150b57cec5SDimitry Andric #include <inttypes.h>
160b57cec5SDimitry Andric #include <stdint.h>
170b57cec5SDimitry Andric #include <stdio.h>
180b57cec5SDimitry Andric #include <stdlib.h>
190b57cec5SDimitry Andric
200b57cec5SDimitry Andric #include "libunwind.h"
210b57cec5SDimitry Andric #include "dwarf2.h"
220b57cec5SDimitry Andric #include "Registers.hpp"
230b57cec5SDimitry Andric
240b57cec5SDimitry Andric #include "config.h"
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric namespace libunwind {
270b57cec5SDimitry Andric
280b57cec5SDimitry Andric /// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
290b57cec5SDimitry Andric /// See DWARF Spec for details:
300b57cec5SDimitry Andric /// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
310b57cec5SDimitry Andric ///
320b57cec5SDimitry Andric template <typename A>
330b57cec5SDimitry Andric class CFI_Parser {
340b57cec5SDimitry Andric public:
350b57cec5SDimitry Andric typedef typename A::pint_t pint_t;
360b57cec5SDimitry Andric
370b57cec5SDimitry Andric /// Information encoded in a CIE (Common Information Entry)
380b57cec5SDimitry Andric struct CIE_Info {
390b57cec5SDimitry Andric pint_t cieStart;
400b57cec5SDimitry Andric pint_t cieLength;
410b57cec5SDimitry Andric pint_t cieInstructions;
420b57cec5SDimitry Andric uint8_t pointerEncoding;
430b57cec5SDimitry Andric uint8_t lsdaEncoding;
440b57cec5SDimitry Andric uint8_t personalityEncoding;
450b57cec5SDimitry Andric uint8_t personalityOffsetInCIE;
460b57cec5SDimitry Andric pint_t personality;
470b57cec5SDimitry Andric uint32_t codeAlignFactor;
480b57cec5SDimitry Andric int dataAlignFactor;
490b57cec5SDimitry Andric bool isSignalFrame;
500b57cec5SDimitry Andric bool fdesHaveAugmentationData;
510b57cec5SDimitry Andric uint8_t returnAddressRegister;
520b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_AARCH64)
530b57cec5SDimitry Andric bool addressesSignedWithBKey;
54*bdd1243dSDimitry Andric bool mteTaggedFrame;
550b57cec5SDimitry Andric #endif
560b57cec5SDimitry Andric };
570b57cec5SDimitry Andric
580b57cec5SDimitry Andric /// Information about an FDE (Frame Description Entry)
590b57cec5SDimitry Andric struct FDE_Info {
600b57cec5SDimitry Andric pint_t fdeStart;
610b57cec5SDimitry Andric pint_t fdeLength;
620b57cec5SDimitry Andric pint_t fdeInstructions;
630b57cec5SDimitry Andric pint_t pcStart;
640b57cec5SDimitry Andric pint_t pcEnd;
650b57cec5SDimitry Andric pint_t lsda;
660b57cec5SDimitry Andric };
670b57cec5SDimitry Andric
680b57cec5SDimitry Andric enum {
690b57cec5SDimitry Andric kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER
700b57cec5SDimitry Andric };
710b57cec5SDimitry Andric enum RegisterSavedWhere {
720b57cec5SDimitry Andric kRegisterUnused,
730b57cec5SDimitry Andric kRegisterInCFA,
74d56accc7SDimitry Andric kRegisterInCFADecrypt, // sparc64 specific
750b57cec5SDimitry Andric kRegisterOffsetFromCFA,
760b57cec5SDimitry Andric kRegisterInRegister,
770b57cec5SDimitry Andric kRegisterAtExpression,
780b57cec5SDimitry Andric kRegisterIsExpression
790b57cec5SDimitry Andric };
800b57cec5SDimitry Andric struct RegisterLocation {
810b57cec5SDimitry Andric RegisterSavedWhere location;
825ffd83dbSDimitry Andric bool initialStateSaved;
830b57cec5SDimitry Andric int64_t value;
840b57cec5SDimitry Andric };
850b57cec5SDimitry Andric /// Information about a frame layout and registers saved determined
860b57cec5SDimitry Andric /// by "running" the DWARF FDE "instructions"
870b57cec5SDimitry Andric struct PrologInfo {
880b57cec5SDimitry Andric uint32_t cfaRegister;
890b57cec5SDimitry Andric int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
900b57cec5SDimitry Andric int64_t cfaExpression; // CFA = expression
910b57cec5SDimitry Andric uint32_t spExtraArgSize;
920b57cec5SDimitry Andric RegisterLocation savedRegisters[kMaxRegisterNumber + 1];
935ffd83dbSDimitry Andric enum class InitializeTime { kLazy, kNormal };
945ffd83dbSDimitry Andric
955ffd83dbSDimitry Andric // When saving registers, this data structure is lazily initialized.
PrologInfolibunwind::CFI_Parser::PrologInfo965ffd83dbSDimitry Andric PrologInfo(InitializeTime IT = InitializeTime::kNormal) {
975ffd83dbSDimitry Andric if (IT == InitializeTime::kNormal)
985ffd83dbSDimitry Andric memset(this, 0, sizeof(*this));
995ffd83dbSDimitry Andric }
checkSaveRegisterlibunwind::CFI_Parser::PrologInfo1005ffd83dbSDimitry Andric void checkSaveRegister(uint64_t reg, PrologInfo &initialState) {
1015ffd83dbSDimitry Andric if (!savedRegisters[reg].initialStateSaved) {
1025ffd83dbSDimitry Andric initialState.savedRegisters[reg] = savedRegisters[reg];
1035ffd83dbSDimitry Andric savedRegisters[reg].initialStateSaved = true;
1045ffd83dbSDimitry Andric }
1055ffd83dbSDimitry Andric }
setRegisterlibunwind::CFI_Parser::PrologInfo1065ffd83dbSDimitry Andric void setRegister(uint64_t reg, RegisterSavedWhere newLocation,
1075ffd83dbSDimitry Andric int64_t newValue, PrologInfo &initialState) {
1085ffd83dbSDimitry Andric checkSaveRegister(reg, initialState);
1095ffd83dbSDimitry Andric savedRegisters[reg].location = newLocation;
1105ffd83dbSDimitry Andric savedRegisters[reg].value = newValue;
1115ffd83dbSDimitry Andric }
setRegisterLocationlibunwind::CFI_Parser::PrologInfo1125ffd83dbSDimitry Andric void setRegisterLocation(uint64_t reg, RegisterSavedWhere newLocation,
1135ffd83dbSDimitry Andric PrologInfo &initialState) {
1145ffd83dbSDimitry Andric checkSaveRegister(reg, initialState);
1155ffd83dbSDimitry Andric savedRegisters[reg].location = newLocation;
1165ffd83dbSDimitry Andric }
setRegisterValuelibunwind::CFI_Parser::PrologInfo1175ffd83dbSDimitry Andric void setRegisterValue(uint64_t reg, int64_t newValue,
1185ffd83dbSDimitry Andric PrologInfo &initialState) {
1195ffd83dbSDimitry Andric checkSaveRegister(reg, initialState);
1205ffd83dbSDimitry Andric savedRegisters[reg].value = newValue;
1215ffd83dbSDimitry Andric }
restoreRegisterToInitialStatelibunwind::CFI_Parser::PrologInfo1225ffd83dbSDimitry Andric void restoreRegisterToInitialState(uint64_t reg, PrologInfo &initialState) {
1235ffd83dbSDimitry Andric if (savedRegisters[reg].initialStateSaved)
1245ffd83dbSDimitry Andric savedRegisters[reg] = initialState.savedRegisters[reg];
1255ffd83dbSDimitry Andric // else the register still holds its initial state
1265ffd83dbSDimitry Andric }
1270b57cec5SDimitry Andric };
1280b57cec5SDimitry Andric
1290b57cec5SDimitry Andric struct PrologInfoStackEntry {
PrologInfoStackEntrylibunwind::CFI_Parser::PrologInfoStackEntry1300b57cec5SDimitry Andric PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i)
1310b57cec5SDimitry Andric : next(n), info(i) {}
1320b57cec5SDimitry Andric PrologInfoStackEntry *next;
1330b57cec5SDimitry Andric PrologInfo info;
1340b57cec5SDimitry Andric };
1350b57cec5SDimitry Andric
136e8d8bef9SDimitry Andric struct RememberStack {
137e8d8bef9SDimitry Andric PrologInfoStackEntry *entry;
RememberStacklibunwind::CFI_Parser::RememberStack138e8d8bef9SDimitry Andric RememberStack() : entry(nullptr) {}
~RememberStacklibunwind::CFI_Parser::RememberStack139e8d8bef9SDimitry Andric ~RememberStack() {
140e8d8bef9SDimitry Andric #if defined(_LIBUNWIND_REMEMBER_CLEANUP_NEEDED)
141e8d8bef9SDimitry Andric // Clean up rememberStack. Even in the case where every
142e8d8bef9SDimitry Andric // DW_CFA_remember_state is paired with a DW_CFA_restore_state,
143e8d8bef9SDimitry Andric // parseInstructions can skip restore opcodes if it reaches the target PC
144e8d8bef9SDimitry Andric // and stops interpreting, so we have to make sure we don't leak memory.
145e8d8bef9SDimitry Andric while (entry) {
146e8d8bef9SDimitry Andric PrologInfoStackEntry *next = entry->next;
147e8d8bef9SDimitry Andric _LIBUNWIND_REMEMBER_FREE(entry);
148e8d8bef9SDimitry Andric entry = next;
149e8d8bef9SDimitry Andric }
150e8d8bef9SDimitry Andric #endif
151e8d8bef9SDimitry Andric }
152e8d8bef9SDimitry Andric };
153e8d8bef9SDimitry Andric
1540b57cec5SDimitry Andric static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
15504eeddc0SDimitry Andric size_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo,
1560b57cec5SDimitry Andric CIE_Info *cieInfo);
1570b57cec5SDimitry Andric static const char *decodeFDE(A &addressSpace, pint_t fdeStart,
158349cc55cSDimitry Andric FDE_Info *fdeInfo, CIE_Info *cieInfo,
159349cc55cSDimitry Andric bool useCIEInfo = false);
1600b57cec5SDimitry Andric static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo,
1610b57cec5SDimitry Andric const CIE_Info &cieInfo, pint_t upToPC,
1620b57cec5SDimitry Andric int arch, PrologInfo *results);
1630b57cec5SDimitry Andric
1640b57cec5SDimitry Andric static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo);
1650b57cec5SDimitry Andric };
1660b57cec5SDimitry Andric
167349cc55cSDimitry Andric /// Parse a FDE into a CIE_Info and an FDE_Info. If useCIEInfo is
168349cc55cSDimitry Andric /// true, treat cieInfo as already-parsed CIE_Info (whose start offset
169349cc55cSDimitry Andric /// must match the one specified by the FDE) rather than parsing the
170349cc55cSDimitry Andric /// one indicated within the FDE.
1710b57cec5SDimitry Andric template <typename A>
decodeFDE(A & addressSpace,pint_t fdeStart,FDE_Info * fdeInfo,CIE_Info * cieInfo,bool useCIEInfo)1720b57cec5SDimitry Andric const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart,
173349cc55cSDimitry Andric FDE_Info *fdeInfo, CIE_Info *cieInfo,
174349cc55cSDimitry Andric bool useCIEInfo) {
1750b57cec5SDimitry Andric pint_t p = fdeStart;
1760b57cec5SDimitry Andric pint_t cfiLength = (pint_t)addressSpace.get32(p);
1770b57cec5SDimitry Andric p += 4;
1780b57cec5SDimitry Andric if (cfiLength == 0xffffffff) {
1790b57cec5SDimitry Andric // 0xffffffff means length is really next 8 bytes
1800b57cec5SDimitry Andric cfiLength = (pint_t)addressSpace.get64(p);
1810b57cec5SDimitry Andric p += 8;
1820b57cec5SDimitry Andric }
1830b57cec5SDimitry Andric if (cfiLength == 0)
184e8d8bef9SDimitry Andric return "FDE has zero length"; // zero terminator
1850b57cec5SDimitry Andric uint32_t ciePointer = addressSpace.get32(p);
1860b57cec5SDimitry Andric if (ciePointer == 0)
1870b57cec5SDimitry Andric return "FDE is really a CIE"; // this is a CIE not an FDE
1880b57cec5SDimitry Andric pint_t nextCFI = p + cfiLength;
1890b57cec5SDimitry Andric pint_t cieStart = p - ciePointer;
190349cc55cSDimitry Andric if (useCIEInfo) {
191349cc55cSDimitry Andric if (cieInfo->cieStart != cieStart)
192349cc55cSDimitry Andric return "CIE start does not match";
193349cc55cSDimitry Andric } else {
1940b57cec5SDimitry Andric const char *err = parseCIE(addressSpace, cieStart, cieInfo);
1950b57cec5SDimitry Andric if (err != NULL)
1960b57cec5SDimitry Andric return err;
197349cc55cSDimitry Andric }
1980b57cec5SDimitry Andric p += 4;
1990b57cec5SDimitry Andric // Parse pc begin and range.
2000b57cec5SDimitry Andric pint_t pcStart =
2010b57cec5SDimitry Andric addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
2020b57cec5SDimitry Andric pint_t pcRange =
2030b57cec5SDimitry Andric addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
2040b57cec5SDimitry Andric // Parse rest of info.
2050b57cec5SDimitry Andric fdeInfo->lsda = 0;
2060b57cec5SDimitry Andric // Check for augmentation length.
2070b57cec5SDimitry Andric if (cieInfo->fdesHaveAugmentationData) {
2080b57cec5SDimitry Andric pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
2090b57cec5SDimitry Andric pint_t endOfAug = p + augLen;
2100b57cec5SDimitry Andric if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
2110b57cec5SDimitry Andric // Peek at value (without indirection). Zero means no LSDA.
2120b57cec5SDimitry Andric pint_t lsdaStart = p;
2130b57cec5SDimitry Andric if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) !=
2140b57cec5SDimitry Andric 0) {
2150b57cec5SDimitry Andric // Reset pointer and re-parse LSDA address.
2160b57cec5SDimitry Andric p = lsdaStart;
2170b57cec5SDimitry Andric fdeInfo->lsda =
2180b57cec5SDimitry Andric addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
2190b57cec5SDimitry Andric }
2200b57cec5SDimitry Andric }
2210b57cec5SDimitry Andric p = endOfAug;
2220b57cec5SDimitry Andric }
2230b57cec5SDimitry Andric fdeInfo->fdeStart = fdeStart;
2240b57cec5SDimitry Andric fdeInfo->fdeLength = nextCFI - fdeStart;
2250b57cec5SDimitry Andric fdeInfo->fdeInstructions = p;
2260b57cec5SDimitry Andric fdeInfo->pcStart = pcStart;
2270b57cec5SDimitry Andric fdeInfo->pcEnd = pcStart + pcRange;
2280b57cec5SDimitry Andric return NULL; // success
2290b57cec5SDimitry Andric }
2300b57cec5SDimitry Andric
2310b57cec5SDimitry Andric /// Scan an eh_frame section to find an FDE for a pc
2320b57cec5SDimitry Andric template <typename A>
findFDE(A & addressSpace,pint_t pc,pint_t ehSectionStart,size_t sectionLength,pint_t fdeHint,FDE_Info * fdeInfo,CIE_Info * cieInfo)2330b57cec5SDimitry Andric bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
23404eeddc0SDimitry Andric size_t sectionLength, pint_t fdeHint,
2350b57cec5SDimitry Andric FDE_Info *fdeInfo, CIE_Info *cieInfo) {
2360b57cec5SDimitry Andric //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
2370b57cec5SDimitry Andric pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
23804eeddc0SDimitry Andric const pint_t ehSectionEnd = (sectionLength == SIZE_MAX)
239e8d8bef9SDimitry Andric ? static_cast<pint_t>(-1)
240e8d8bef9SDimitry Andric : (ehSectionStart + sectionLength);
2410b57cec5SDimitry Andric while (p < ehSectionEnd) {
2420b57cec5SDimitry Andric pint_t currentCFI = p;
2430b57cec5SDimitry Andric //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
2440b57cec5SDimitry Andric pint_t cfiLength = addressSpace.get32(p);
2450b57cec5SDimitry Andric p += 4;
2460b57cec5SDimitry Andric if (cfiLength == 0xffffffff) {
2470b57cec5SDimitry Andric // 0xffffffff means length is really next 8 bytes
2480b57cec5SDimitry Andric cfiLength = (pint_t)addressSpace.get64(p);
2490b57cec5SDimitry Andric p += 8;
2500b57cec5SDimitry Andric }
2510b57cec5SDimitry Andric if (cfiLength == 0)
252e8d8bef9SDimitry Andric return false; // zero terminator
2530b57cec5SDimitry Andric uint32_t id = addressSpace.get32(p);
2540b57cec5SDimitry Andric if (id == 0) {
2550b57cec5SDimitry Andric // Skip over CIEs.
2560b57cec5SDimitry Andric p += cfiLength;
2570b57cec5SDimitry Andric } else {
2580b57cec5SDimitry Andric // Process FDE to see if it covers pc.
2590b57cec5SDimitry Andric pint_t nextCFI = p + cfiLength;
2600b57cec5SDimitry Andric uint32_t ciePointer = addressSpace.get32(p);
2610b57cec5SDimitry Andric pint_t cieStart = p - ciePointer;
2620b57cec5SDimitry Andric // Validate pointer to CIE is within section.
2630b57cec5SDimitry Andric if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) {
2640b57cec5SDimitry Andric if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) {
2650b57cec5SDimitry Andric p += 4;
2660b57cec5SDimitry Andric // Parse pc begin and range.
2670b57cec5SDimitry Andric pint_t pcStart =
2680b57cec5SDimitry Andric addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
2690b57cec5SDimitry Andric pint_t pcRange = addressSpace.getEncodedP(
2700b57cec5SDimitry Andric p, nextCFI, cieInfo->pointerEncoding & 0x0F);
2710b57cec5SDimitry Andric // Test if pc is within the function this FDE covers.
2720b57cec5SDimitry Andric if ((pcStart < pc) && (pc <= pcStart + pcRange)) {
2730b57cec5SDimitry Andric // parse rest of info
2740b57cec5SDimitry Andric fdeInfo->lsda = 0;
2750b57cec5SDimitry Andric // check for augmentation length
2760b57cec5SDimitry Andric if (cieInfo->fdesHaveAugmentationData) {
2770b57cec5SDimitry Andric pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
2780b57cec5SDimitry Andric pint_t endOfAug = p + augLen;
2790b57cec5SDimitry Andric if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
2800b57cec5SDimitry Andric // Peek at value (without indirection). Zero means no LSDA.
2810b57cec5SDimitry Andric pint_t lsdaStart = p;
2820b57cec5SDimitry Andric if (addressSpace.getEncodedP(
2830b57cec5SDimitry Andric p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) {
2840b57cec5SDimitry Andric // Reset pointer and re-parse LSDA address.
2850b57cec5SDimitry Andric p = lsdaStart;
2860b57cec5SDimitry Andric fdeInfo->lsda = addressSpace
2870b57cec5SDimitry Andric .getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
2880b57cec5SDimitry Andric }
2890b57cec5SDimitry Andric }
2900b57cec5SDimitry Andric p = endOfAug;
2910b57cec5SDimitry Andric }
2920b57cec5SDimitry Andric fdeInfo->fdeStart = currentCFI;
2930b57cec5SDimitry Andric fdeInfo->fdeLength = nextCFI - currentCFI;
2940b57cec5SDimitry Andric fdeInfo->fdeInstructions = p;
2950b57cec5SDimitry Andric fdeInfo->pcStart = pcStart;
2960b57cec5SDimitry Andric fdeInfo->pcEnd = pcStart + pcRange;
2970b57cec5SDimitry Andric return true;
2980b57cec5SDimitry Andric } else {
2990b57cec5SDimitry Andric // pc is not in begin/range, skip this FDE
3000b57cec5SDimitry Andric }
3010b57cec5SDimitry Andric } else {
3020b57cec5SDimitry Andric // Malformed CIE, now augmentation describing pc range encoding.
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric } else {
3050b57cec5SDimitry Andric // malformed FDE. CIE is bad
3060b57cec5SDimitry Andric }
3070b57cec5SDimitry Andric p = nextCFI;
3080b57cec5SDimitry Andric }
3090b57cec5SDimitry Andric }
3100b57cec5SDimitry Andric return false;
3110b57cec5SDimitry Andric }
3120b57cec5SDimitry Andric
3130b57cec5SDimitry Andric /// Extract info from a CIE
3140b57cec5SDimitry Andric template <typename A>
parseCIE(A & addressSpace,pint_t cie,CIE_Info * cieInfo)3150b57cec5SDimitry Andric const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
3160b57cec5SDimitry Andric CIE_Info *cieInfo) {
3170b57cec5SDimitry Andric cieInfo->pointerEncoding = 0;
3180b57cec5SDimitry Andric cieInfo->lsdaEncoding = DW_EH_PE_omit;
3190b57cec5SDimitry Andric cieInfo->personalityEncoding = 0;
3200b57cec5SDimitry Andric cieInfo->personalityOffsetInCIE = 0;
3210b57cec5SDimitry Andric cieInfo->personality = 0;
3220b57cec5SDimitry Andric cieInfo->codeAlignFactor = 0;
3230b57cec5SDimitry Andric cieInfo->dataAlignFactor = 0;
3240b57cec5SDimitry Andric cieInfo->isSignalFrame = false;
3250b57cec5SDimitry Andric cieInfo->fdesHaveAugmentationData = false;
3260b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_AARCH64)
3270b57cec5SDimitry Andric cieInfo->addressesSignedWithBKey = false;
328*bdd1243dSDimitry Andric cieInfo->mteTaggedFrame = false;
3290b57cec5SDimitry Andric #endif
3300b57cec5SDimitry Andric cieInfo->cieStart = cie;
3310b57cec5SDimitry Andric pint_t p = cie;
3320b57cec5SDimitry Andric pint_t cieLength = (pint_t)addressSpace.get32(p);
3330b57cec5SDimitry Andric p += 4;
3340b57cec5SDimitry Andric pint_t cieContentEnd = p + cieLength;
3350b57cec5SDimitry Andric if (cieLength == 0xffffffff) {
3360b57cec5SDimitry Andric // 0xffffffff means length is really next 8 bytes
3370b57cec5SDimitry Andric cieLength = (pint_t)addressSpace.get64(p);
3380b57cec5SDimitry Andric p += 8;
3390b57cec5SDimitry Andric cieContentEnd = p + cieLength;
3400b57cec5SDimitry Andric }
3410b57cec5SDimitry Andric if (cieLength == 0)
3420b57cec5SDimitry Andric return NULL;
3430b57cec5SDimitry Andric // CIE ID is always 0
3440b57cec5SDimitry Andric if (addressSpace.get32(p) != 0)
3450b57cec5SDimitry Andric return "CIE ID is not zero";
3460b57cec5SDimitry Andric p += 4;
3470b57cec5SDimitry Andric // Version is always 1 or 3
3480b57cec5SDimitry Andric uint8_t version = addressSpace.get8(p);
3490b57cec5SDimitry Andric if ((version != 1) && (version != 3))
3500b57cec5SDimitry Andric return "CIE version is not 1 or 3";
3510b57cec5SDimitry Andric ++p;
3520b57cec5SDimitry Andric // save start of augmentation string and find end
3530b57cec5SDimitry Andric pint_t strStart = p;
3540b57cec5SDimitry Andric while (addressSpace.get8(p) != 0)
3550b57cec5SDimitry Andric ++p;
3560b57cec5SDimitry Andric ++p;
357*bdd1243dSDimitry Andric // parse code alignment factor
3580b57cec5SDimitry Andric cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd);
3590b57cec5SDimitry Andric // parse data alignment factor
3600b57cec5SDimitry Andric cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd);
3610b57cec5SDimitry Andric // parse return address register
362e8d8bef9SDimitry Andric uint64_t raReg = (version == 1) ? addressSpace.get8(p++)
363e8d8bef9SDimitry Andric : addressSpace.getULEB128(p, cieContentEnd);
3640b57cec5SDimitry Andric assert(raReg < 255 && "return address register too large");
3650b57cec5SDimitry Andric cieInfo->returnAddressRegister = (uint8_t)raReg;
3660b57cec5SDimitry Andric // parse augmentation data based on augmentation string
3670b57cec5SDimitry Andric const char *result = NULL;
3680b57cec5SDimitry Andric if (addressSpace.get8(strStart) == 'z') {
3690b57cec5SDimitry Andric // parse augmentation data length
3700b57cec5SDimitry Andric addressSpace.getULEB128(p, cieContentEnd);
3710b57cec5SDimitry Andric for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) {
3720b57cec5SDimitry Andric switch (addressSpace.get8(s)) {
3730b57cec5SDimitry Andric case 'z':
3740b57cec5SDimitry Andric cieInfo->fdesHaveAugmentationData = true;
3750b57cec5SDimitry Andric break;
3760b57cec5SDimitry Andric case 'P':
3770b57cec5SDimitry Andric cieInfo->personalityEncoding = addressSpace.get8(p);
3780b57cec5SDimitry Andric ++p;
3790b57cec5SDimitry Andric cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie);
3800b57cec5SDimitry Andric cieInfo->personality = addressSpace
3810b57cec5SDimitry Andric .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
3820b57cec5SDimitry Andric break;
3830b57cec5SDimitry Andric case 'L':
3840b57cec5SDimitry Andric cieInfo->lsdaEncoding = addressSpace.get8(p);
3850b57cec5SDimitry Andric ++p;
3860b57cec5SDimitry Andric break;
3870b57cec5SDimitry Andric case 'R':
3880b57cec5SDimitry Andric cieInfo->pointerEncoding = addressSpace.get8(p);
3890b57cec5SDimitry Andric ++p;
3900b57cec5SDimitry Andric break;
3910b57cec5SDimitry Andric case 'S':
3920b57cec5SDimitry Andric cieInfo->isSignalFrame = true;
3930b57cec5SDimitry Andric break;
3940b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_AARCH64)
3950b57cec5SDimitry Andric case 'B':
3960b57cec5SDimitry Andric cieInfo->addressesSignedWithBKey = true;
3970b57cec5SDimitry Andric break;
398*bdd1243dSDimitry Andric case 'G':
399*bdd1243dSDimitry Andric cieInfo->mteTaggedFrame = true;
400*bdd1243dSDimitry Andric break;
4010b57cec5SDimitry Andric #endif
4020b57cec5SDimitry Andric default:
4030b57cec5SDimitry Andric // ignore unknown letters
4040b57cec5SDimitry Andric break;
4050b57cec5SDimitry Andric }
4060b57cec5SDimitry Andric }
4070b57cec5SDimitry Andric }
4080b57cec5SDimitry Andric cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
4090b57cec5SDimitry Andric cieInfo->cieInstructions = p;
4100b57cec5SDimitry Andric return result;
4110b57cec5SDimitry Andric }
4120b57cec5SDimitry Andric
4130b57cec5SDimitry Andric
414*bdd1243dSDimitry Andric /// "run" the DWARF instructions and create the abstract PrologInfo for an FDE
4150b57cec5SDimitry Andric template <typename A>
parseFDEInstructions(A & addressSpace,const FDE_Info & fdeInfo,const CIE_Info & cieInfo,pint_t upToPC,int arch,PrologInfo * results)4160b57cec5SDimitry Andric bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
4170b57cec5SDimitry Andric const FDE_Info &fdeInfo,
4180b57cec5SDimitry Andric const CIE_Info &cieInfo, pint_t upToPC,
4190b57cec5SDimitry Andric int arch, PrologInfo *results) {
420e8d8bef9SDimitry Andric // Alloca is used for the allocation of the rememberStack entries. It removes
421e8d8bef9SDimitry Andric // the dependency on new/malloc but the below for loop can not be refactored
422e8d8bef9SDimitry Andric // into functions. Entry could be saved during the processing of a CIE and
423e8d8bef9SDimitry Andric // restored by an FDE.
424e8d8bef9SDimitry Andric RememberStack rememberStack;
4250b57cec5SDimitry Andric
426e8d8bef9SDimitry Andric struct ParseInfo {
427e8d8bef9SDimitry Andric pint_t instructions;
428e8d8bef9SDimitry Andric pint_t instructionsEnd;
429e8d8bef9SDimitry Andric pint_t pcoffset;
430e8d8bef9SDimitry Andric };
4315ffd83dbSDimitry Andric
432e8d8bef9SDimitry Andric ParseInfo parseInfoArray[] = {
433e8d8bef9SDimitry Andric {cieInfo.cieInstructions, cieInfo.cieStart + cieInfo.cieLength,
434e8d8bef9SDimitry Andric (pint_t)(-1)},
435e8d8bef9SDimitry Andric {fdeInfo.fdeInstructions, fdeInfo.fdeStart + fdeInfo.fdeLength,
436e8d8bef9SDimitry Andric upToPC - fdeInfo.pcStart}};
4375ffd83dbSDimitry Andric
438e8d8bef9SDimitry Andric for (const auto &info : parseInfoArray) {
439e8d8bef9SDimitry Andric pint_t p = info.instructions;
440e8d8bef9SDimitry Andric pint_t instructionsEnd = info.instructionsEnd;
441e8d8bef9SDimitry Andric pint_t pcoffset = info.pcoffset;
4420b57cec5SDimitry Andric pint_t codeOffset = 0;
443e8d8bef9SDimitry Andric
4445ffd83dbSDimitry Andric // initialState initialized as registers in results are modified. Use
4455ffd83dbSDimitry Andric // PrologInfo accessor functions to avoid reading uninitialized data.
4465ffd83dbSDimitry Andric PrologInfo initialState(PrologInfo::InitializeTime::kLazy);
4470b57cec5SDimitry Andric
448e8d8bef9SDimitry Andric _LIBUNWIND_TRACE_DWARF("parseFDEInstructions(instructions=0x%0" PRIx64
449e8d8bef9SDimitry Andric ")\n",
4500b57cec5SDimitry Andric static_cast<uint64_t>(instructionsEnd));
4510b57cec5SDimitry Andric
4520b57cec5SDimitry Andric // see DWARF Spec, section 6.4.2 for details on unwind opcodes
4530b57cec5SDimitry Andric while ((p < instructionsEnd) && (codeOffset < pcoffset)) {
4540b57cec5SDimitry Andric uint64_t reg;
4550b57cec5SDimitry Andric uint64_t reg2;
4560b57cec5SDimitry Andric int64_t offset;
4570b57cec5SDimitry Andric uint64_t length;
4580b57cec5SDimitry Andric uint8_t opcode = addressSpace.get8(p);
4590b57cec5SDimitry Andric uint8_t operand;
460e8d8bef9SDimitry Andric
4610b57cec5SDimitry Andric ++p;
4620b57cec5SDimitry Andric switch (opcode) {
4630b57cec5SDimitry Andric case DW_CFA_nop:
4640b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n");
4650b57cec5SDimitry Andric break;
4660b57cec5SDimitry Andric case DW_CFA_set_loc:
467e8d8bef9SDimitry Andric codeOffset = addressSpace.getEncodedP(p, instructionsEnd,
468e8d8bef9SDimitry Andric cieInfo.pointerEncoding);
4690b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n");
4700b57cec5SDimitry Andric break;
4710b57cec5SDimitry Andric case DW_CFA_advance_loc1:
4720b57cec5SDimitry Andric codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
4730b57cec5SDimitry Andric p += 1;
4740b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n",
4750b57cec5SDimitry Andric static_cast<uint64_t>(codeOffset));
4760b57cec5SDimitry Andric break;
4770b57cec5SDimitry Andric case DW_CFA_advance_loc2:
4780b57cec5SDimitry Andric codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
4790b57cec5SDimitry Andric p += 2;
4800b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n",
4810b57cec5SDimitry Andric static_cast<uint64_t>(codeOffset));
4820b57cec5SDimitry Andric break;
4830b57cec5SDimitry Andric case DW_CFA_advance_loc4:
4840b57cec5SDimitry Andric codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
4850b57cec5SDimitry Andric p += 4;
4860b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n",
4870b57cec5SDimitry Andric static_cast<uint64_t>(codeOffset));
4880b57cec5SDimitry Andric break;
4890b57cec5SDimitry Andric case DW_CFA_offset_extended:
4900b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
491e8d8bef9SDimitry Andric offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
492e8d8bef9SDimitry Andric cieInfo.dataAlignFactor;
4930b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
4940b57cec5SDimitry Andric _LIBUNWIND_LOG0(
4950b57cec5SDimitry Andric "malformed DW_CFA_offset_extended DWARF unwind, reg too big");
4960b57cec5SDimitry Andric return false;
4970b57cec5SDimitry Andric }
4985ffd83dbSDimitry Andric results->setRegister(reg, kRegisterInCFA, offset, initialState);
4990b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", "
5000b57cec5SDimitry Andric "offset=%" PRId64 ")\n",
5010b57cec5SDimitry Andric reg, offset);
5020b57cec5SDimitry Andric break;
5030b57cec5SDimitry Andric case DW_CFA_restore_extended:
5040b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
5050b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
5060b57cec5SDimitry Andric _LIBUNWIND_LOG0(
5070b57cec5SDimitry Andric "malformed DW_CFA_restore_extended DWARF unwind, reg too big");
5080b57cec5SDimitry Andric return false;
5090b57cec5SDimitry Andric }
5105ffd83dbSDimitry Andric results->restoreRegisterToInitialState(reg, initialState);
511e8d8bef9SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n",
512e8d8bef9SDimitry Andric reg);
5130b57cec5SDimitry Andric break;
5140b57cec5SDimitry Andric case DW_CFA_undefined:
5150b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
5160b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
5170b57cec5SDimitry Andric _LIBUNWIND_LOG0(
5180b57cec5SDimitry Andric "malformed DW_CFA_undefined DWARF unwind, reg too big");
5190b57cec5SDimitry Andric return false;
5200b57cec5SDimitry Andric }
5215866c369SDimitry Andric results->setRegisterLocation(reg, kRegisterUnused, initialState);
5220b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg);
5230b57cec5SDimitry Andric break;
5240b57cec5SDimitry Andric case DW_CFA_same_value:
5250b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
5260b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
5270b57cec5SDimitry Andric _LIBUNWIND_LOG0(
5280b57cec5SDimitry Andric "malformed DW_CFA_same_value DWARF unwind, reg too big");
5290b57cec5SDimitry Andric return false;
5300b57cec5SDimitry Andric }
5310b57cec5SDimitry Andric // <rdar://problem/8456377> DW_CFA_same_value unsupported
5320b57cec5SDimitry Andric // "same value" means register was stored in frame, but its current
5330b57cec5SDimitry Andric // value has not changed, so no need to restore from frame.
5340b57cec5SDimitry Andric // We model this as if the register was never saved.
5355ffd83dbSDimitry Andric results->setRegisterLocation(reg, kRegisterUnused, initialState);
5360b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg);
5370b57cec5SDimitry Andric break;
5380b57cec5SDimitry Andric case DW_CFA_register:
5390b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
5400b57cec5SDimitry Andric reg2 = addressSpace.getULEB128(p, instructionsEnd);
5410b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
5420b57cec5SDimitry Andric _LIBUNWIND_LOG0(
5430b57cec5SDimitry Andric "malformed DW_CFA_register DWARF unwind, reg too big");
5440b57cec5SDimitry Andric return false;
5450b57cec5SDimitry Andric }
5460b57cec5SDimitry Andric if (reg2 > kMaxRegisterNumber) {
5470b57cec5SDimitry Andric _LIBUNWIND_LOG0(
5480b57cec5SDimitry Andric "malformed DW_CFA_register DWARF unwind, reg2 too big");
5490b57cec5SDimitry Andric return false;
5500b57cec5SDimitry Andric }
5515ffd83dbSDimitry Andric results->setRegister(reg, kRegisterInRegister, (int64_t)reg2,
5525ffd83dbSDimitry Andric initialState);
5530b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF(
5540b57cec5SDimitry Andric "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2);
5550b57cec5SDimitry Andric break;
556e8d8bef9SDimitry Andric case DW_CFA_remember_state: {
557e8d8bef9SDimitry Andric // Avoid operator new because that would be an upward dependency.
558e8d8bef9SDimitry Andric // Avoid malloc because it needs heap allocation.
559e8d8bef9SDimitry Andric PrologInfoStackEntry *entry =
560e8d8bef9SDimitry Andric (PrologInfoStackEntry *)_LIBUNWIND_REMEMBER_ALLOC(
561e8d8bef9SDimitry Andric sizeof(PrologInfoStackEntry));
5620b57cec5SDimitry Andric if (entry != NULL) {
563e8d8bef9SDimitry Andric entry->next = rememberStack.entry;
5640b57cec5SDimitry Andric entry->info = *results;
565e8d8bef9SDimitry Andric rememberStack.entry = entry;
5660b57cec5SDimitry Andric } else {
5670b57cec5SDimitry Andric return false;
5680b57cec5SDimitry Andric }
5690b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n");
5700b57cec5SDimitry Andric break;
571e8d8bef9SDimitry Andric }
5720b57cec5SDimitry Andric case DW_CFA_restore_state:
573e8d8bef9SDimitry Andric if (rememberStack.entry != NULL) {
574e8d8bef9SDimitry Andric PrologInfoStackEntry *top = rememberStack.entry;
5750b57cec5SDimitry Andric *results = top->info;
576e8d8bef9SDimitry Andric rememberStack.entry = top->next;
577e8d8bef9SDimitry Andric _LIBUNWIND_REMEMBER_FREE(top);
5780b57cec5SDimitry Andric } else {
5790b57cec5SDimitry Andric return false;
5800b57cec5SDimitry Andric }
5810b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n");
5820b57cec5SDimitry Andric break;
5830b57cec5SDimitry Andric case DW_CFA_def_cfa:
5840b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
5850b57cec5SDimitry Andric offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd);
5860b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
5870b57cec5SDimitry Andric _LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big");
5880b57cec5SDimitry Andric return false;
5890b57cec5SDimitry Andric }
5900b57cec5SDimitry Andric results->cfaRegister = (uint32_t)reg;
5910b57cec5SDimitry Andric results->cfaRegisterOffset = (int32_t)offset;
592e8d8bef9SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64
593e8d8bef9SDimitry Andric ")\n",
594e8d8bef9SDimitry Andric reg, offset);
5950b57cec5SDimitry Andric break;
5960b57cec5SDimitry Andric case DW_CFA_def_cfa_register:
5970b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
5980b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
5990b57cec5SDimitry Andric _LIBUNWIND_LOG0(
6000b57cec5SDimitry Andric "malformed DW_CFA_def_cfa_register DWARF unwind, reg too big");
6010b57cec5SDimitry Andric return false;
6020b57cec5SDimitry Andric }
6030b57cec5SDimitry Andric results->cfaRegister = (uint32_t)reg;
6040b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg);
6050b57cec5SDimitry Andric break;
6060b57cec5SDimitry Andric case DW_CFA_def_cfa_offset:
607e8d8bef9SDimitry Andric results->cfaRegisterOffset =
608e8d8bef9SDimitry Andric (int32_t)addressSpace.getULEB128(p, instructionsEnd);
6090b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n",
6100b57cec5SDimitry Andric results->cfaRegisterOffset);
6110b57cec5SDimitry Andric break;
6120b57cec5SDimitry Andric case DW_CFA_def_cfa_expression:
6130b57cec5SDimitry Andric results->cfaRegister = 0;
6140b57cec5SDimitry Andric results->cfaExpression = (int64_t)p;
6150b57cec5SDimitry Andric length = addressSpace.getULEB128(p, instructionsEnd);
6160b57cec5SDimitry Andric assert(length < static_cast<pint_t>(~0) && "pointer overflow");
6170b57cec5SDimitry Andric p += static_cast<pint_t>(length);
6180b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64
6190b57cec5SDimitry Andric ", length=%" PRIu64 ")\n",
6200b57cec5SDimitry Andric results->cfaExpression, length);
6210b57cec5SDimitry Andric break;
6220b57cec5SDimitry Andric case DW_CFA_expression:
6230b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
6240b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
6250b57cec5SDimitry Andric _LIBUNWIND_LOG0(
6260b57cec5SDimitry Andric "malformed DW_CFA_expression DWARF unwind, reg too big");
6270b57cec5SDimitry Andric return false;
6280b57cec5SDimitry Andric }
6295ffd83dbSDimitry Andric results->setRegister(reg, kRegisterAtExpression, (int64_t)p,
6305ffd83dbSDimitry Andric initialState);
6310b57cec5SDimitry Andric length = addressSpace.getULEB128(p, instructionsEnd);
6320b57cec5SDimitry Andric assert(length < static_cast<pint_t>(~0) && "pointer overflow");
6330b57cec5SDimitry Andric p += static_cast<pint_t>(length);
6340b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", "
6350b57cec5SDimitry Andric "expression=0x%" PRIx64 ", "
6360b57cec5SDimitry Andric "length=%" PRIu64 ")\n",
6370b57cec5SDimitry Andric reg, results->savedRegisters[reg].value, length);
6380b57cec5SDimitry Andric break;
6390b57cec5SDimitry Andric case DW_CFA_offset_extended_sf:
6400b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
6410b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
6420b57cec5SDimitry Andric _LIBUNWIND_LOG0(
6430b57cec5SDimitry Andric "malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big");
6440b57cec5SDimitry Andric return false;
6450b57cec5SDimitry Andric }
646e8d8bef9SDimitry Andric offset = addressSpace.getSLEB128(p, instructionsEnd) *
647e8d8bef9SDimitry Andric cieInfo.dataAlignFactor;
6485ffd83dbSDimitry Andric results->setRegister(reg, kRegisterInCFA, offset, initialState);
6490b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", "
6500b57cec5SDimitry Andric "offset=%" PRId64 ")\n",
6510b57cec5SDimitry Andric reg, offset);
6520b57cec5SDimitry Andric break;
6530b57cec5SDimitry Andric case DW_CFA_def_cfa_sf:
6540b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
655e8d8bef9SDimitry Andric offset = addressSpace.getSLEB128(p, instructionsEnd) *
656e8d8bef9SDimitry Andric cieInfo.dataAlignFactor;
6570b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
6580b57cec5SDimitry Andric _LIBUNWIND_LOG0(
6590b57cec5SDimitry Andric "malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big");
6600b57cec5SDimitry Andric return false;
6610b57cec5SDimitry Andric }
6620b57cec5SDimitry Andric results->cfaRegister = (uint32_t)reg;
6630b57cec5SDimitry Andric results->cfaRegisterOffset = (int32_t)offset;
6640b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", "
6650b57cec5SDimitry Andric "offset=%" PRId64 ")\n",
6660b57cec5SDimitry Andric reg, offset);
6670b57cec5SDimitry Andric break;
6680b57cec5SDimitry Andric case DW_CFA_def_cfa_offset_sf:
669e8d8bef9SDimitry Andric results->cfaRegisterOffset =
670e8d8bef9SDimitry Andric (int32_t)(addressSpace.getSLEB128(p, instructionsEnd) *
671e8d8bef9SDimitry Andric cieInfo.dataAlignFactor);
6720b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n",
6730b57cec5SDimitry Andric results->cfaRegisterOffset);
6740b57cec5SDimitry Andric break;
6750b57cec5SDimitry Andric case DW_CFA_val_offset:
6760b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
6770b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
6780b57cec5SDimitry Andric _LIBUNWIND_LOG(
6790b57cec5SDimitry Andric "malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64
6800b57cec5SDimitry Andric ") out of range\n",
6810b57cec5SDimitry Andric reg);
6820b57cec5SDimitry Andric return false;
6830b57cec5SDimitry Andric }
684e8d8bef9SDimitry Andric offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
685e8d8bef9SDimitry Andric cieInfo.dataAlignFactor;
6865ffd83dbSDimitry Andric results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState);
6870b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", "
6880b57cec5SDimitry Andric "offset=%" PRId64 "\n",
6890b57cec5SDimitry Andric reg, offset);
6900b57cec5SDimitry Andric break;
6910b57cec5SDimitry Andric case DW_CFA_val_offset_sf:
6920b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
6930b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
6940b57cec5SDimitry Andric _LIBUNWIND_LOG0(
6950b57cec5SDimitry Andric "malformed DW_CFA_val_offset_sf DWARF unwind, reg too big");
6960b57cec5SDimitry Andric return false;
6970b57cec5SDimitry Andric }
698e8d8bef9SDimitry Andric offset = addressSpace.getSLEB128(p, instructionsEnd) *
699e8d8bef9SDimitry Andric cieInfo.dataAlignFactor;
7005ffd83dbSDimitry Andric results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState);
7010b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", "
7020b57cec5SDimitry Andric "offset=%" PRId64 "\n",
7030b57cec5SDimitry Andric reg, offset);
7040b57cec5SDimitry Andric break;
7050b57cec5SDimitry Andric case DW_CFA_val_expression:
7060b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
7070b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
7080b57cec5SDimitry Andric _LIBUNWIND_LOG0(
7090b57cec5SDimitry Andric "malformed DW_CFA_val_expression DWARF unwind, reg too big");
7100b57cec5SDimitry Andric return false;
7110b57cec5SDimitry Andric }
7125ffd83dbSDimitry Andric results->setRegister(reg, kRegisterIsExpression, (int64_t)p,
7135ffd83dbSDimitry Andric initialState);
7140b57cec5SDimitry Andric length = addressSpace.getULEB128(p, instructionsEnd);
7150b57cec5SDimitry Andric assert(length < static_cast<pint_t>(~0) && "pointer overflow");
7160b57cec5SDimitry Andric p += static_cast<pint_t>(length);
7170b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", "
718e8d8bef9SDimitry Andric "expression=0x%" PRIx64 ", length=%" PRIu64
719e8d8bef9SDimitry Andric ")\n",
7200b57cec5SDimitry Andric reg, results->savedRegisters[reg].value, length);
7210b57cec5SDimitry Andric break;
7220b57cec5SDimitry Andric case DW_CFA_GNU_args_size:
7230b57cec5SDimitry Andric length = addressSpace.getULEB128(p, instructionsEnd);
7240b57cec5SDimitry Andric results->spExtraArgSize = (uint32_t)length;
7250b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length);
7260b57cec5SDimitry Andric break;
7270b57cec5SDimitry Andric case DW_CFA_GNU_negative_offset_extended:
7280b57cec5SDimitry Andric reg = addressSpace.getULEB128(p, instructionsEnd);
7290b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
7300b57cec5SDimitry Andric _LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF "
7310b57cec5SDimitry Andric "unwind, reg too big");
7320b57cec5SDimitry Andric return false;
7330b57cec5SDimitry Andric }
734e8d8bef9SDimitry Andric offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
735e8d8bef9SDimitry Andric cieInfo.dataAlignFactor;
7365ffd83dbSDimitry Andric results->setRegister(reg, kRegisterInCFA, -offset, initialState);
7370b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF(
7380b57cec5SDimitry Andric "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset);
7390b57cec5SDimitry Andric break;
7400b57cec5SDimitry Andric
741d56accc7SDimitry Andric #if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) || \
742d56accc7SDimitry Andric defined(_LIBUNWIND_TARGET_SPARC64)
7430b57cec5SDimitry Andric // The same constant is used to represent different instructions on
7440b57cec5SDimitry Andric // AArch64 (negate_ra_state) and SPARC (window_save).
7450b57cec5SDimitry Andric static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save,
7460b57cec5SDimitry Andric "uses the same constant");
7470b57cec5SDimitry Andric case DW_CFA_AARCH64_negate_ra_state:
7480b57cec5SDimitry Andric switch (arch) {
7490b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_AARCH64)
7505ffd83dbSDimitry Andric case REGISTERS_ARM64: {
7515ffd83dbSDimitry Andric int64_t value =
752349cc55cSDimitry Andric results->savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value ^ 0x1;
753349cc55cSDimitry Andric results->setRegisterValue(UNW_AARCH64_RA_SIGN_STATE, value,
754e8d8bef9SDimitry Andric initialState);
7550b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n");
7565ffd83dbSDimitry Andric } break;
7570b57cec5SDimitry Andric #endif
7585ffd83dbSDimitry Andric
7590b57cec5SDimitry Andric #if defined(_LIBUNWIND_TARGET_SPARC)
7600b57cec5SDimitry Andric // case DW_CFA_GNU_window_save:
7610b57cec5SDimitry Andric case REGISTERS_SPARC:
7620b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n");
7630b57cec5SDimitry Andric for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) {
7645ffd83dbSDimitry Andric results->setRegister(reg, kRegisterInRegister,
7655ffd83dbSDimitry Andric ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0,
7665ffd83dbSDimitry Andric initialState);
7670b57cec5SDimitry Andric }
7680b57cec5SDimitry Andric
7690b57cec5SDimitry Andric for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
7705ffd83dbSDimitry Andric results->setRegister(reg, kRegisterInCFA,
771e8d8bef9SDimitry Andric ((int64_t)reg - UNW_SPARC_L0) * 4,
772e8d8bef9SDimitry Andric initialState);
7730b57cec5SDimitry Andric }
7740b57cec5SDimitry Andric break;
7750b57cec5SDimitry Andric #endif
776d56accc7SDimitry Andric
777d56accc7SDimitry Andric #if defined(_LIBUNWIND_TARGET_SPARC64)
778d56accc7SDimitry Andric // case DW_CFA_GNU_window_save:
779d56accc7SDimitry Andric case REGISTERS_SPARC64:
780d56accc7SDimitry Andric // Don't save %o0-%o7 on sparc64.
781d56accc7SDimitry Andric // https://reviews.llvm.org/D32450#736405
782d56accc7SDimitry Andric
783d56accc7SDimitry Andric for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
784d56accc7SDimitry Andric if (reg == UNW_SPARC_I7)
785d56accc7SDimitry Andric results->setRegister(
786d56accc7SDimitry Andric reg, kRegisterInCFADecrypt,
787d56accc7SDimitry Andric static_cast<int64_t>((reg - UNW_SPARC_L0) * sizeof(pint_t)),
788d56accc7SDimitry Andric initialState);
789d56accc7SDimitry Andric else
790d56accc7SDimitry Andric results->setRegister(
791d56accc7SDimitry Andric reg, kRegisterInCFA,
792d56accc7SDimitry Andric static_cast<int64_t>((reg - UNW_SPARC_L0) * sizeof(pint_t)),
793d56accc7SDimitry Andric initialState);
794d56accc7SDimitry Andric }
795d56accc7SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save\n");
796d56accc7SDimitry Andric break;
797d56accc7SDimitry Andric #endif
7980b57cec5SDimitry Andric }
7990b57cec5SDimitry Andric break;
800d56accc7SDimitry Andric
8010b57cec5SDimitry Andric #else
8020b57cec5SDimitry Andric (void)arch;
8030b57cec5SDimitry Andric #endif
8040b57cec5SDimitry Andric
8050b57cec5SDimitry Andric default:
8060b57cec5SDimitry Andric operand = opcode & 0x3F;
8070b57cec5SDimitry Andric switch (opcode & 0xC0) {
8080b57cec5SDimitry Andric case DW_CFA_offset:
8090b57cec5SDimitry Andric reg = operand;
8100b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
8110b57cec5SDimitry Andric _LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64
8120b57cec5SDimitry Andric ") out of range",
8130b57cec5SDimitry Andric reg);
8140b57cec5SDimitry Andric return false;
8150b57cec5SDimitry Andric }
816e8d8bef9SDimitry Andric offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
817e8d8bef9SDimitry Andric cieInfo.dataAlignFactor;
8185ffd83dbSDimitry Andric results->setRegister(reg, kRegisterInCFA, offset, initialState);
8190b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n",
8200b57cec5SDimitry Andric operand, offset);
8210b57cec5SDimitry Andric break;
8220b57cec5SDimitry Andric case DW_CFA_advance_loc:
8230b57cec5SDimitry Andric codeOffset += operand * cieInfo.codeAlignFactor;
8240b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n",
8250b57cec5SDimitry Andric static_cast<uint64_t>(codeOffset));
8260b57cec5SDimitry Andric break;
8270b57cec5SDimitry Andric case DW_CFA_restore:
8280b57cec5SDimitry Andric reg = operand;
8290b57cec5SDimitry Andric if (reg > kMaxRegisterNumber) {
830e8d8bef9SDimitry Andric _LIBUNWIND_LOG(
831e8d8bef9SDimitry Andric "malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64
8320b57cec5SDimitry Andric ") out of range",
8330b57cec5SDimitry Andric reg);
8340b57cec5SDimitry Andric return false;
8350b57cec5SDimitry Andric }
8365ffd83dbSDimitry Andric results->restoreRegisterToInitialState(reg, initialState);
8370b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n",
8380b57cec5SDimitry Andric static_cast<uint64_t>(operand));
8390b57cec5SDimitry Andric break;
8400b57cec5SDimitry Andric default:
8410b57cec5SDimitry Andric _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode);
8420b57cec5SDimitry Andric return false;
8430b57cec5SDimitry Andric }
8440b57cec5SDimitry Andric }
8450b57cec5SDimitry Andric }
846e8d8bef9SDimitry Andric }
8470b57cec5SDimitry Andric return true;
8480b57cec5SDimitry Andric }
8490b57cec5SDimitry Andric
8500b57cec5SDimitry Andric } // namespace libunwind
8510b57cec5SDimitry Andric
8520b57cec5SDimitry Andric #endif // __DWARF_PARSER_HPP__
853