Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp
213845 views
//===----------------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"9#include "llvm/Support/Errc.h"10#include "llvm/Support/ErrorHandling.h"11#include "llvm/Support/raw_ostream.h"12#include <cassert>13#include <cinttypes>14#include <cstdint>15#include <optional>1617using namespace llvm;18using namespace dwarf;1920UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; }2122UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }2324UnwindLocation UnwindLocation::createSame() { return {Same}; }2526UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {27return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};28}2930UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {31return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};32}3334UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {35return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};36}3738UnwindLocation39UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,40std::optional<uint32_t> AddrSpace) {41return {RegPlusOffset, RegNum, Offset, AddrSpace, false};42}4344UnwindLocation45UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,46std::optional<uint32_t> AddrSpace) {47return {RegPlusOffset, RegNum, Offset, AddrSpace, true};48}4950UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {51return {Expr, false};52}5354UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {55return {Expr, true};56}5758bool UnwindLocation::operator==(const UnwindLocation &RHS) const {59if (Kind != RHS.Kind)60return false;61switch (Kind) {62case Unspecified:63case Undefined:64case Same:65return true;66case CFAPlusOffset:67return Offset == RHS.Offset && Dereference == RHS.Dereference;68case RegPlusOffset:69return RegNum == RHS.RegNum && Offset == RHS.Offset &&70Dereference == RHS.Dereference;71case DWARFExpr:72return *Expr == *RHS.Expr && Dereference == RHS.Dereference;73case Constant:74return Offset == RHS.Offset;75}76return false;77}7879Expected<UnwindTable::RowContainer>80llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row,81const RegisterLocations *InitialLocs) {82// All the unwinding rows parsed during processing of the CFI program.83UnwindTable::RowContainer Rows;8485// State consists of CFA value and register locations.86std::vector<std::pair<UnwindLocation, RegisterLocations>> States;87for (const CFIProgram::Instruction &Inst : CFIP) {88switch (Inst.Opcode) {89case dwarf::DW_CFA_set_loc: {90// The DW_CFA_set_loc instruction takes a single operand that91// represents a target address. The required action is to create a new92// table row using the specified address as the location. All other93// values in the new row are initially identical to the current row.94// The new location value is always greater than the current one. If95// the segment_size field of this FDE's CIE is non- zero, the initial96// location is preceded by a segment selector of the given length97llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, 0);98if (!NewAddress)99return NewAddress.takeError();100if (*NewAddress <= Row.getAddress())101return createStringError(102errc::invalid_argument,103"%s with adrress 0x%" PRIx64 " which must be greater than the "104"current row address 0x%" PRIx64,105CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress,106Row.getAddress());107Rows.push_back(Row);108Row.setAddress(*NewAddress);109break;110}111112case dwarf::DW_CFA_advance_loc:113case dwarf::DW_CFA_advance_loc1:114case dwarf::DW_CFA_advance_loc2:115case dwarf::DW_CFA_advance_loc4: {116// The DW_CFA_advance instruction takes a single operand that117// represents a constant delta. The required action is to create a new118// table row with a location value that is computed by taking the119// current entry’s location value and adding the value of delta *120// code_alignment_factor. All other values in the new row are initially121// identical to the current row.122Rows.push_back(Row);123llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, 0);124if (!Offset)125return Offset.takeError();126Row.slideAddress(*Offset);127break;128}129130case dwarf::DW_CFA_restore:131case dwarf::DW_CFA_restore_extended: {132// The DW_CFA_restore instruction takes a single operand (encoded with133// the opcode) that represents a register number. The required action134// is to change the rule for the indicated register to the rule135// assigned it by the initial_instructions in the CIE.136if (InitialLocs == nullptr)137return createStringError(138errc::invalid_argument, "%s encountered while parsing a CIE",139CFIP.callFrameString(Inst.Opcode).str().c_str());140llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);141if (!RegNum)142return RegNum.takeError();143if (std::optional<UnwindLocation> O =144InitialLocs->getRegisterLocation(*RegNum))145Row.getRegisterLocations().setRegisterLocation(*RegNum, *O);146else147Row.getRegisterLocations().removeRegisterLocation(*RegNum);148break;149}150151case dwarf::DW_CFA_offset:152case dwarf::DW_CFA_offset_extended:153case dwarf::DW_CFA_offset_extended_sf: {154llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);155if (!RegNum)156return RegNum.takeError();157llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);158if (!Offset)159return Offset.takeError();160Row.getRegisterLocations().setRegisterLocation(161*RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset));162break;163}164165case dwarf::DW_CFA_nop:166break;167168case dwarf::DW_CFA_remember_state:169States.push_back(170std::make_pair(Row.getCFAValue(), Row.getRegisterLocations()));171break;172173case dwarf::DW_CFA_restore_state:174if (States.empty())175return createStringError(errc::invalid_argument,176"DW_CFA_restore_state without a matching "177"previous DW_CFA_remember_state");178Row.getCFAValue() = States.back().first;179Row.getRegisterLocations() = States.back().second;180States.pop_back();181break;182183case dwarf::DW_CFA_GNU_window_save:184switch (CFIP.triple()) {185case Triple::aarch64:186case Triple::aarch64_be:187case Triple::aarch64_32: {188// DW_CFA_GNU_window_save is used for different things on different189// architectures. For aarch64 it is known as190// DW_CFA_AARCH64_negate_ra_state. The action is to toggle the191// value of the return address state between 1 and 0. If there is192// no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it193// should be initially set to 1.194constexpr uint32_t AArch64DWARFPAuthRaState = 34;195auto LRLoc = Row.getRegisterLocations().getRegisterLocation(196AArch64DWARFPAuthRaState);197if (LRLoc) {198if (LRLoc->getLocation() == UnwindLocation::Constant) {199// Toggle the constant value from 0 to 1 or 1 to 0.200LRLoc->setConstant(LRLoc->getConstant() ^ 1);201Row.getRegisterLocations().setRegisterLocation(202AArch64DWARFPAuthRaState, *LRLoc);203} else {204return createStringError(205errc::invalid_argument,206"%s encountered when existing rule for this register is not "207"a constant",208CFIP.callFrameString(Inst.Opcode).str().c_str());209}210} else {211Row.getRegisterLocations().setRegisterLocation(212AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1));213}214break;215}216217case Triple::sparc:218case Triple::sparcv9:219case Triple::sparcel:220for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) {221Row.getRegisterLocations().setRegisterLocation(222RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8));223}224break;225226default: {227return createStringError(228errc::not_supported,229"DW_CFA opcode %#x is not supported for architecture %s",230Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str());231232break;233}234}235break;236237case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {238constexpr uint32_t AArch64DWARFPAuthRaState = 34;239auto LRLoc = Row.getRegisterLocations().getRegisterLocation(240AArch64DWARFPAuthRaState);241if (LRLoc) {242if (LRLoc->getLocation() == UnwindLocation::Constant) {243// Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.244LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);245} else {246return createStringError(247errc::invalid_argument,248"%s encountered when existing rule for this register is not "249"a constant",250CFIP.callFrameString(Inst.Opcode).str().c_str());251}252} else {253Row.getRegisterLocations().setRegisterLocation(254AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));255}256break;257}258259case dwarf::DW_CFA_undefined: {260llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);261if (!RegNum)262return RegNum.takeError();263Row.getRegisterLocations().setRegisterLocation(264*RegNum, UnwindLocation::createUndefined());265break;266}267268case dwarf::DW_CFA_same_value: {269llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);270if (!RegNum)271return RegNum.takeError();272Row.getRegisterLocations().setRegisterLocation(273*RegNum, UnwindLocation::createSame());274break;275}276277case dwarf::DW_CFA_GNU_args_size:278break;279280case dwarf::DW_CFA_register: {281llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);282if (!RegNum)283return RegNum.takeError();284llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1);285if (!NewRegNum)286return NewRegNum.takeError();287Row.getRegisterLocations().setRegisterLocation(288*RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0));289break;290}291292case dwarf::DW_CFA_val_offset:293case dwarf::DW_CFA_val_offset_sf: {294llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);295if (!RegNum)296return RegNum.takeError();297llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);298if (!Offset)299return Offset.takeError();300Row.getRegisterLocations().setRegisterLocation(301*RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset));302break;303}304305case dwarf::DW_CFA_expression: {306llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);307if (!RegNum)308return RegNum.takeError();309Row.getRegisterLocations().setRegisterLocation(310*RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression));311break;312}313314case dwarf::DW_CFA_val_expression: {315llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);316if (!RegNum)317return RegNum.takeError();318Row.getRegisterLocations().setRegisterLocation(319*RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression));320break;321}322323case dwarf::DW_CFA_def_cfa_register: {324llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);325if (!RegNum)326return RegNum.takeError();327if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset)328Row.getCFAValue() =329UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0);330else331Row.getCFAValue().setRegister(*RegNum);332break;333}334335case dwarf::DW_CFA_def_cfa_offset:336case dwarf::DW_CFA_def_cfa_offset_sf: {337llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 0);338if (!Offset)339return Offset.takeError();340if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) {341return createStringError(342errc::invalid_argument,343"%s found when CFA rule was not RegPlusOffset",344CFIP.callFrameString(Inst.Opcode).str().c_str());345}346Row.getCFAValue().setOffset(*Offset);347break;348}349350case dwarf::DW_CFA_def_cfa:351case dwarf::DW_CFA_def_cfa_sf: {352llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);353if (!RegNum)354return RegNum.takeError();355llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);356if (!Offset)357return Offset.takeError();358Row.getCFAValue() =359UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset);360break;361}362363case dwarf::DW_CFA_LLVM_def_aspace_cfa:364case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {365llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);366if (!RegNum)367return RegNum.takeError();368llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);369if (!Offset)370return Offset.takeError();371llvm::Expected<uint32_t> CFAAddrSpace =372Inst.getOperandAsUnsigned(CFIP, 2);373if (!CFAAddrSpace)374return CFAAddrSpace.takeError();375Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(376*RegNum, *Offset, *CFAAddrSpace);377break;378}379380case dwarf::DW_CFA_def_cfa_expression:381Row.getCFAValue() =382UnwindLocation::createIsDWARFExpression(*Inst.Expression);383break;384}385}386return Rows;387}388389390