389 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"10#include "llvm/Support/Errc.h"11#include "llvm/Support/ErrorHandling.h"12#include "llvm/Support/raw_ostream.h"13#include <cassert>14#include <cinttypes>15#include <cstdint>16#include <optional>17 18using namespace llvm;19using namespace dwarf;20 21UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; }22 23UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }24 25UnwindLocation UnwindLocation::createSame() { return {Same}; }26 27UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {28 return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};29}30 31UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {32 return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};33}34 35UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {36 return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};37}38 39UnwindLocation40UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,41 std::optional<uint32_t> AddrSpace) {42 return {RegPlusOffset, RegNum, Offset, AddrSpace, false};43}44 45UnwindLocation46UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,47 std::optional<uint32_t> AddrSpace) {48 return {RegPlusOffset, RegNum, Offset, AddrSpace, true};49}50 51UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {52 return {Expr, false};53}54 55UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {56 return {Expr, true};57}58 59bool UnwindLocation::operator==(const UnwindLocation &RHS) const {60 if (Kind != RHS.Kind)61 return false;62 switch (Kind) {63 case Unspecified:64 case Undefined:65 case Same:66 return true;67 case CFAPlusOffset:68 return Offset == RHS.Offset && Dereference == RHS.Dereference;69 case RegPlusOffset:70 return RegNum == RHS.RegNum && Offset == RHS.Offset &&71 Dereference == RHS.Dereference;72 case DWARFExpr:73 return *Expr == *RHS.Expr && Dereference == RHS.Dereference;74 case Constant:75 return Offset == RHS.Offset;76 }77 return false;78}79 80Expected<UnwindTable::RowContainer>81llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row,82 const RegisterLocations *InitialLocs) {83 // All the unwinding rows parsed during processing of the CFI program.84 UnwindTable::RowContainer Rows;85 86 // State consists of CFA value and register locations.87 std::vector<std::pair<UnwindLocation, RegisterLocations>> States;88 for (const CFIProgram::Instruction &Inst : CFIP) {89 switch (Inst.Opcode) {90 case dwarf::DW_CFA_set_loc: {91 // The DW_CFA_set_loc instruction takes a single operand that92 // represents a target address. The required action is to create a new93 // table row using the specified address as the location. All other94 // values in the new row are initially identical to the current row.95 // The new location value is always greater than the current one. If96 // the segment_size field of this FDE's CIE is non- zero, the initial97 // location is preceded by a segment selector of the given length98 llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, 0);99 if (!NewAddress)100 return NewAddress.takeError();101 if (*NewAddress <= Row.getAddress())102 return createStringError(103 errc::invalid_argument,104 "%s with adrress 0x%" PRIx64 " which must be greater than the "105 "current row address 0x%" PRIx64,106 CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress,107 Row.getAddress());108 Rows.push_back(Row);109 Row.setAddress(*NewAddress);110 break;111 }112 113 case dwarf::DW_CFA_advance_loc:114 case dwarf::DW_CFA_advance_loc1:115 case dwarf::DW_CFA_advance_loc2:116 case dwarf::DW_CFA_advance_loc4: {117 // The DW_CFA_advance instruction takes a single operand that118 // represents a constant delta. The required action is to create a new119 // table row with a location value that is computed by taking the120 // current entry’s location value and adding the value of delta *121 // code_alignment_factor. All other values in the new row are initially122 // identical to the current row.123 Rows.push_back(Row);124 llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, 0);125 if (!Offset)126 return Offset.takeError();127 Row.slideAddress(*Offset);128 break;129 }130 131 case dwarf::DW_CFA_restore:132 case dwarf::DW_CFA_restore_extended: {133 // The DW_CFA_restore instruction takes a single operand (encoded with134 // the opcode) that represents a register number. The required action135 // is to change the rule for the indicated register to the rule136 // assigned it by the initial_instructions in the CIE.137 if (InitialLocs == nullptr)138 return createStringError(139 errc::invalid_argument, "%s encountered while parsing a CIE",140 CFIP.callFrameString(Inst.Opcode).str().c_str());141 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);142 if (!RegNum)143 return RegNum.takeError();144 if (std::optional<UnwindLocation> O =145 InitialLocs->getRegisterLocation(*RegNum))146 Row.getRegisterLocations().setRegisterLocation(*RegNum, *O);147 else148 Row.getRegisterLocations().removeRegisterLocation(*RegNum);149 break;150 }151 152 case dwarf::DW_CFA_offset:153 case dwarf::DW_CFA_offset_extended:154 case dwarf::DW_CFA_offset_extended_sf: {155 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);156 if (!RegNum)157 return RegNum.takeError();158 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);159 if (!Offset)160 return Offset.takeError();161 Row.getRegisterLocations().setRegisterLocation(162 *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset));163 break;164 }165 166 case dwarf::DW_CFA_nop:167 break;168 169 case dwarf::DW_CFA_remember_state:170 States.push_back(171 std::make_pair(Row.getCFAValue(), Row.getRegisterLocations()));172 break;173 174 case dwarf::DW_CFA_restore_state:175 if (States.empty())176 return createStringError(errc::invalid_argument,177 "DW_CFA_restore_state without a matching "178 "previous DW_CFA_remember_state");179 Row.getCFAValue() = States.back().first;180 Row.getRegisterLocations() = States.back().second;181 States.pop_back();182 break;183 184 case dwarf::DW_CFA_GNU_window_save:185 switch (CFIP.triple()) {186 case Triple::aarch64:187 case Triple::aarch64_be:188 case Triple::aarch64_32: {189 // DW_CFA_GNU_window_save is used for different things on different190 // architectures. For aarch64 it is known as191 // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the192 // value of the return address state between 1 and 0. If there is193 // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it194 // should be initially set to 1.195 constexpr uint32_t AArch64DWARFPAuthRaState = 34;196 auto LRLoc = Row.getRegisterLocations().getRegisterLocation(197 AArch64DWARFPAuthRaState);198 if (LRLoc) {199 if (LRLoc->getLocation() == UnwindLocation::Constant) {200 // Toggle the constant value from 0 to 1 or 1 to 0.201 LRLoc->setConstant(LRLoc->getConstant() ^ 1);202 Row.getRegisterLocations().setRegisterLocation(203 AArch64DWARFPAuthRaState, *LRLoc);204 } else {205 return createStringError(206 errc::invalid_argument,207 "%s encountered when existing rule for this register is not "208 "a constant",209 CFIP.callFrameString(Inst.Opcode).str().c_str());210 }211 } else {212 Row.getRegisterLocations().setRegisterLocation(213 AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1));214 }215 break;216 }217 218 case Triple::sparc:219 case Triple::sparcv9:220 case Triple::sparcel:221 for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) {222 Row.getRegisterLocations().setRegisterLocation(223 RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8));224 }225 break;226 227 default: {228 return createStringError(229 errc::not_supported,230 "DW_CFA opcode %#x is not supported for architecture %s",231 Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str());232 233 break;234 }235 }236 break;237 238 case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {239 constexpr uint32_t AArch64DWARFPAuthRaState = 34;240 auto LRLoc = Row.getRegisterLocations().getRegisterLocation(241 AArch64DWARFPAuthRaState);242 if (LRLoc) {243 if (LRLoc->getLocation() == UnwindLocation::Constant) {244 // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.245 LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);246 } else {247 return createStringError(248 errc::invalid_argument,249 "%s encountered when existing rule for this register is not "250 "a constant",251 CFIP.callFrameString(Inst.Opcode).str().c_str());252 }253 } else {254 Row.getRegisterLocations().setRegisterLocation(255 AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));256 }257 break;258 }259 260 case dwarf::DW_CFA_undefined: {261 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);262 if (!RegNum)263 return RegNum.takeError();264 Row.getRegisterLocations().setRegisterLocation(265 *RegNum, UnwindLocation::createUndefined());266 break;267 }268 269 case dwarf::DW_CFA_same_value: {270 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);271 if (!RegNum)272 return RegNum.takeError();273 Row.getRegisterLocations().setRegisterLocation(274 *RegNum, UnwindLocation::createSame());275 break;276 }277 278 case dwarf::DW_CFA_GNU_args_size:279 break;280 281 case dwarf::DW_CFA_register: {282 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);283 if (!RegNum)284 return RegNum.takeError();285 llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1);286 if (!NewRegNum)287 return NewRegNum.takeError();288 Row.getRegisterLocations().setRegisterLocation(289 *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0));290 break;291 }292 293 case dwarf::DW_CFA_val_offset:294 case dwarf::DW_CFA_val_offset_sf: {295 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);296 if (!RegNum)297 return RegNum.takeError();298 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);299 if (!Offset)300 return Offset.takeError();301 Row.getRegisterLocations().setRegisterLocation(302 *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset));303 break;304 }305 306 case dwarf::DW_CFA_expression: {307 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);308 if (!RegNum)309 return RegNum.takeError();310 Row.getRegisterLocations().setRegisterLocation(311 *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression));312 break;313 }314 315 case dwarf::DW_CFA_val_expression: {316 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);317 if (!RegNum)318 return RegNum.takeError();319 Row.getRegisterLocations().setRegisterLocation(320 *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression));321 break;322 }323 324 case dwarf::DW_CFA_def_cfa_register: {325 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);326 if (!RegNum)327 return RegNum.takeError();328 if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset)329 Row.getCFAValue() =330 UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0);331 else332 Row.getCFAValue().setRegister(*RegNum);333 break;334 }335 336 case dwarf::DW_CFA_def_cfa_offset:337 case dwarf::DW_CFA_def_cfa_offset_sf: {338 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 0);339 if (!Offset)340 return Offset.takeError();341 if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) {342 return createStringError(343 errc::invalid_argument,344 "%s found when CFA rule was not RegPlusOffset",345 CFIP.callFrameString(Inst.Opcode).str().c_str());346 }347 Row.getCFAValue().setOffset(*Offset);348 break;349 }350 351 case dwarf::DW_CFA_def_cfa:352 case dwarf::DW_CFA_def_cfa_sf: {353 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);354 if (!RegNum)355 return RegNum.takeError();356 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);357 if (!Offset)358 return Offset.takeError();359 Row.getCFAValue() =360 UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset);361 break;362 }363 364 case dwarf::DW_CFA_LLVM_def_aspace_cfa:365 case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {366 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);367 if (!RegNum)368 return RegNum.takeError();369 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);370 if (!Offset)371 return Offset.takeError();372 llvm::Expected<uint32_t> CFAAddrSpace =373 Inst.getOperandAsUnsigned(CFIP, 2);374 if (!CFAAddrSpace)375 return CFAAddrSpace.takeError();376 Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(377 *RegNum, *Offset, *CFAAddrSpace);378 break;379 }380 381 case dwarf::DW_CFA_def_cfa_expression:382 Row.getCFAValue() =383 UnwindLocation::createIsDWARFExpression(*Inst.Expression);384 break;385 }386 }387 return Rows;388}389