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-exception 6 // 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 18 using namespace llvm; 19 using namespace dwarf; 20 21 UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } 22 23 UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } 24 25 UnwindLocation UnwindLocation::createSame() { return {Same}; } 26 27 UnwindLocation UnwindLocation::createIsConstant(int32_t Value) { 28 return {Constant, InvalidRegisterNumber, Value, std::nullopt, false}; 29 } 30 31 UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) { 32 return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false}; 33 } 34 35 UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) { 36 return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true}; 37 } 38 39 UnwindLocation 40 UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset, 41 std::optional<uint32_t> AddrSpace) { 42 return {RegPlusOffset, RegNum, Offset, AddrSpace, false}; 43 } 44 45 UnwindLocation 46 UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset, 47 std::optional<uint32_t> AddrSpace) { 48 return {RegPlusOffset, RegNum, Offset, AddrSpace, true}; 49 } 50 51 UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) { 52 return {Expr, false}; 53 } 54 55 UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { 56 return {Expr, true}; 57 } 58 59 bool 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 80 Expected<UnwindTable::RowContainer> 81 llvm::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 that 92 // represents a target address. The required action is to create a new 93 // table row using the specified address as the location. All other 94 // values in the new row are initially identical to the current row. 95 // The new location value is always greater than the current one. If 96 // the segment_size field of this FDE's CIE is non- zero, the initial 97 // location is preceded by a segment selector of the given length 98 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 that 118 // represents a constant delta. The required action is to create a new 119 // table row with a location value that is computed by taking the 120 // current entry’s location value and adding the value of delta * 121 // code_alignment_factor. All other values in the new row are initially 122 // 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 with 134 // the opcode) that represents a register number. The required action 135 // is to change the rule for the indicated register to the rule 136 // 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 else 148 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 different 190 // architectures. For aarch64 it is known as 191 // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the 192 // value of the return address state between 1 and 0. If there is 193 // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it 194 // 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 else 332 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