| 1 | //===- DWARFDebugFrame.h - Parsing of .debug_frame ------------------------===// |
| 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/DWARFDebugFrame.h" |
| 10 | #include "llvm/ADT/DenseMap.h" |
| 11 | #include "llvm/ADT/StringExtras.h" |
| 12 | #include "llvm/ADT/StringRef.h" |
| 13 | #include "llvm/BinaryFormat/Dwarf.h" |
| 14 | #include "llvm/DebugInfo/DIContext.h" |
| 15 | #include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h" |
| 16 | #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" |
| 17 | #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" |
| 18 | #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" |
| 19 | #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" |
| 20 | #include "llvm/Support/Compiler.h" |
| 21 | #include "llvm/Support/DataExtractor.h" |
| 22 | #include "llvm/Support/Errc.h" |
| 23 | #include "llvm/Support/Error.h" |
| 24 | #include "llvm/Support/ErrorHandling.h" |
| 25 | #include "llvm/Support/Format.h" |
| 26 | #include "llvm/Support/raw_ostream.h" |
| 27 | #include <cassert> |
| 28 | #include <cinttypes> |
| 29 | #include <cstdint> |
| 30 | #include <optional> |
| 31 | |
| 32 | using namespace llvm; |
| 33 | using namespace dwarf; |
| 34 | |
| 35 | static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, |
| 36 | unsigned RegNum) { |
| 37 | if (DumpOpts.GetNameForDWARFReg) { |
| 38 | auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); |
| 39 | if (!RegName.empty()) { |
| 40 | OS << RegName; |
| 41 | return; |
| 42 | } |
| 43 | } |
| 44 | OS << "reg" << RegNum; |
| 45 | } |
| 46 | |
| 47 | UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } |
| 48 | |
| 49 | UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } |
| 50 | |
| 51 | UnwindLocation UnwindLocation::createSame() { return {Same}; } |
| 52 | |
| 53 | UnwindLocation UnwindLocation::createIsConstant(int32_t Value) { |
| 54 | return {Constant, InvalidRegisterNumber, Value, std::nullopt, false}; |
| 55 | } |
| 56 | |
| 57 | UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) { |
| 58 | return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false}; |
| 59 | } |
| 60 | |
| 61 | UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) { |
| 62 | return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true}; |
| 63 | } |
| 64 | |
| 65 | UnwindLocation |
| 66 | UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset, |
| 67 | std::optional<uint32_t> AddrSpace) { |
| 68 | return {RegPlusOffset, RegNum, Offset, AddrSpace, false}; |
| 69 | } |
| 70 | |
| 71 | UnwindLocation |
| 72 | UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset, |
| 73 | std::optional<uint32_t> AddrSpace) { |
| 74 | return {RegPlusOffset, RegNum, Offset, AddrSpace, true}; |
| 75 | } |
| 76 | |
| 77 | UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) { |
| 78 | return {Expr, false}; |
| 79 | } |
| 80 | |
| 81 | UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { |
| 82 | return {Expr, true}; |
| 83 | } |
| 84 | |
| 85 | void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { |
| 86 | if (Dereference) |
| 87 | OS << '['; |
| 88 | switch (Kind) { |
| 89 | case Unspecified: |
| 90 | OS << "unspecified" ; |
| 91 | break; |
| 92 | case Undefined: |
| 93 | OS << "undefined" ; |
| 94 | break; |
| 95 | case Same: |
| 96 | OS << "same" ; |
| 97 | break; |
| 98 | case CFAPlusOffset: |
| 99 | OS << "CFA" ; |
| 100 | if (Offset == 0) |
| 101 | break; |
| 102 | if (Offset > 0) |
| 103 | OS << "+" ; |
| 104 | OS << Offset; |
| 105 | break; |
| 106 | case RegPlusOffset: |
| 107 | printRegister(OS, DumpOpts, RegNum); |
| 108 | if (Offset == 0 && !AddrSpace) |
| 109 | break; |
| 110 | if (Offset >= 0) |
| 111 | OS << "+" ; |
| 112 | OS << Offset; |
| 113 | if (AddrSpace) |
| 114 | OS << " in addrspace" << *AddrSpace; |
| 115 | break; |
| 116 | case DWARFExpr: { |
| 117 | if (Expr) |
| 118 | printDwarfExpression(E: &Expr.value(), OS, DumpOpts, U: nullptr); |
| 119 | break; |
| 120 | } |
| 121 | case Constant: |
| 122 | OS << Offset; |
| 123 | break; |
| 124 | } |
| 125 | if (Dereference) |
| 126 | OS << ']'; |
| 127 | } |
| 128 | |
| 129 | raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, |
| 130 | const UnwindLocation &UL) { |
| 131 | auto DumpOpts = DIDumpOptions(); |
| 132 | UL.dump(OS, DumpOpts); |
| 133 | return OS; |
| 134 | } |
| 135 | |
| 136 | bool UnwindLocation::operator==(const UnwindLocation &RHS) const { |
| 137 | if (Kind != RHS.Kind) |
| 138 | return false; |
| 139 | switch (Kind) { |
| 140 | case Unspecified: |
| 141 | case Undefined: |
| 142 | case Same: |
| 143 | return true; |
| 144 | case CFAPlusOffset: |
| 145 | return Offset == RHS.Offset && Dereference == RHS.Dereference; |
| 146 | case RegPlusOffset: |
| 147 | return RegNum == RHS.RegNum && Offset == RHS.Offset && |
| 148 | Dereference == RHS.Dereference; |
| 149 | case DWARFExpr: |
| 150 | return *Expr == *RHS.Expr && Dereference == RHS.Dereference; |
| 151 | case Constant: |
| 152 | return Offset == RHS.Offset; |
| 153 | } |
| 154 | return false; |
| 155 | } |
| 156 | |
| 157 | void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { |
| 158 | bool First = true; |
| 159 | for (const auto &RegLocPair : Locations) { |
| 160 | if (First) |
| 161 | First = false; |
| 162 | else |
| 163 | OS << ", " ; |
| 164 | printRegister(OS, DumpOpts, RegNum: RegLocPair.first); |
| 165 | OS << '='; |
| 166 | RegLocPair.second.dump(OS, DumpOpts); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, |
| 171 | const RegisterLocations &RL) { |
| 172 | auto DumpOpts = DIDumpOptions(); |
| 173 | RL.dump(OS, DumpOpts); |
| 174 | return OS; |
| 175 | } |
| 176 | |
| 177 | void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts, |
| 178 | unsigned IndentLevel) const { |
| 179 | OS.indent(NumSpaces: 2 * IndentLevel); |
| 180 | if (hasAddress()) |
| 181 | OS << format(Fmt: "0x%" PRIx64 ": " , Vals: *Address); |
| 182 | OS << "CFA=" ; |
| 183 | CFAValue.dump(OS, DumpOpts); |
| 184 | if (RegLocs.hasLocations()) { |
| 185 | OS << ": " ; |
| 186 | RegLocs.dump(OS, DumpOpts); |
| 187 | } |
| 188 | OS << "\n" ; |
| 189 | } |
| 190 | |
| 191 | raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { |
| 192 | auto DumpOpts = DIDumpOptions(); |
| 193 | Row.dump(OS, DumpOpts, IndentLevel: 0); |
| 194 | return OS; |
| 195 | } |
| 196 | |
| 197 | void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts, |
| 198 | unsigned IndentLevel) const { |
| 199 | for (const UnwindRow &Row : Rows) |
| 200 | Row.dump(OS, DumpOpts, IndentLevel); |
| 201 | } |
| 202 | |
| 203 | raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { |
| 204 | auto DumpOpts = DIDumpOptions(); |
| 205 | Rows.dump(OS, DumpOpts, IndentLevel: 0); |
| 206 | return OS; |
| 207 | } |
| 208 | |
| 209 | Expected<UnwindTable> llvm::dwarf::createUnwindTable(const FDE *Fde) { |
| 210 | const CIE *Cie = Fde->getLinkedCIE(); |
| 211 | if (Cie == nullptr) |
| 212 | return createStringError(EC: errc::invalid_argument, |
| 213 | Fmt: "unable to get CIE for FDE at offset 0x%" PRIx64, |
| 214 | Vals: Fde->getOffset()); |
| 215 | |
| 216 | // Rows will be empty if there are no CFI instructions. |
| 217 | if (Cie->cfis().empty() && Fde->cfis().empty()) |
| 218 | return UnwindTable({}); |
| 219 | |
| 220 | UnwindTable::RowContainer CieRows; |
| 221 | UnwindRow Row; |
| 222 | Row.setAddress(Fde->getInitialLocation()); |
| 223 | if (Error CieError = parseRows(CFIP: Cie->cfis(), CurrRow&: Row, InitialLocs: nullptr).moveInto(Value&: CieRows)) |
| 224 | return std::move(CieError); |
| 225 | // We need to save the initial locations of registers from the CIE parsing |
| 226 | // in case we run into DW_CFA_restore or DW_CFA_restore_extended opcodes. |
| 227 | UnwindTable::RowContainer FdeRows; |
| 228 | const RegisterLocations InitialLocs = Row.getRegisterLocations(); |
| 229 | if (Error FdeError = |
| 230 | parseRows(CFIP: Fde->cfis(), CurrRow&: Row, InitialLocs: &InitialLocs).moveInto(Value&: FdeRows)) |
| 231 | return std::move(FdeError); |
| 232 | |
| 233 | UnwindTable::RowContainer AllRows; |
| 234 | AllRows.insert(position: AllRows.end(), first: CieRows.begin(), last: CieRows.end()); |
| 235 | AllRows.insert(position: AllRows.end(), first: FdeRows.begin(), last: FdeRows.end()); |
| 236 | |
| 237 | // May be all the CFI instructions were DW_CFA_nop amd Row becomes empty. |
| 238 | // Do not add that to the unwind table. |
| 239 | if (Row.getRegisterLocations().hasLocations() || |
| 240 | Row.getCFAValue().getLocation() != UnwindLocation::Unspecified) |
| 241 | AllRows.push_back(x: Row); |
| 242 | return UnwindTable(std::move(AllRows)); |
| 243 | } |
| 244 | |
| 245 | Expected<UnwindTable> llvm::dwarf::createUnwindTable(const CIE *Cie) { |
| 246 | // Rows will be empty if there are no CFI instructions. |
| 247 | if (Cie->cfis().empty()) |
| 248 | return UnwindTable({}); |
| 249 | |
| 250 | UnwindTable::RowContainer Rows; |
| 251 | UnwindRow Row; |
| 252 | if (Error CieError = parseRows(CFIP: Cie->cfis(), CurrRow&: Row, InitialLocs: nullptr).moveInto(Value&: Rows)) |
| 253 | return std::move(CieError); |
| 254 | // May be all the CFI instructions were DW_CFA_nop amd Row becomes empty. |
| 255 | // Do not add that to the unwind table. |
| 256 | if (Row.getRegisterLocations().hasLocations() || |
| 257 | Row.getCFAValue().getLocation() != UnwindLocation::Unspecified) |
| 258 | Rows.push_back(x: Row); |
| 259 | return UnwindTable(std::move(Rows)); |
| 260 | } |
| 261 | |
| 262 | Expected<UnwindTable::RowContainer> |
| 263 | llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row, |
| 264 | const RegisterLocations *InitialLocs) { |
| 265 | // All the unwinding rows parsed during processing of the CFI program. |
| 266 | UnwindTable::RowContainer Rows; |
| 267 | |
| 268 | // State consists of CFA value and register locations. |
| 269 | std::vector<std::pair<UnwindLocation, RegisterLocations>> States; |
| 270 | for (const CFIProgram::Instruction &Inst : CFIP) { |
| 271 | switch (Inst.Opcode) { |
| 272 | case dwarf::DW_CFA_set_loc: { |
| 273 | // The DW_CFA_set_loc instruction takes a single operand that |
| 274 | // represents a target address. The required action is to create a new |
| 275 | // table row using the specified address as the location. All other |
| 276 | // values in the new row are initially identical to the current row. |
| 277 | // The new location value is always greater than the current one. If |
| 278 | // the segment_size field of this FDE's CIE is non- zero, the initial |
| 279 | // location is preceded by a segment selector of the given length |
| 280 | llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 281 | if (!NewAddress) |
| 282 | return NewAddress.takeError(); |
| 283 | if (*NewAddress <= Row.getAddress()) |
| 284 | return createStringError( |
| 285 | EC: errc::invalid_argument, |
| 286 | Fmt: "%s with adrress 0x%" PRIx64 " which must be greater than the " |
| 287 | "current row address 0x%" PRIx64, |
| 288 | Vals: CFIP.callFrameString(Opcode: Inst.Opcode).str().c_str(), Vals: *NewAddress, |
| 289 | Vals: Row.getAddress()); |
| 290 | Rows.push_back(x: Row); |
| 291 | Row.setAddress(*NewAddress); |
| 292 | break; |
| 293 | } |
| 294 | |
| 295 | case dwarf::DW_CFA_advance_loc: |
| 296 | case dwarf::DW_CFA_advance_loc1: |
| 297 | case dwarf::DW_CFA_advance_loc2: |
| 298 | case dwarf::DW_CFA_advance_loc4: { |
| 299 | // The DW_CFA_advance instruction takes a single operand that |
| 300 | // represents a constant delta. The required action is to create a new |
| 301 | // table row with a location value that is computed by taking the |
| 302 | // current entry’s location value and adding the value of delta * |
| 303 | // code_alignment_factor. All other values in the new row are initially |
| 304 | // identical to the current row. |
| 305 | Rows.push_back(x: Row); |
| 306 | llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 307 | if (!Offset) |
| 308 | return Offset.takeError(); |
| 309 | Row.slideAddress(Offset: *Offset); |
| 310 | break; |
| 311 | } |
| 312 | |
| 313 | case dwarf::DW_CFA_restore: |
| 314 | case dwarf::DW_CFA_restore_extended: { |
| 315 | // The DW_CFA_restore instruction takes a single operand (encoded with |
| 316 | // the opcode) that represents a register number. The required action |
| 317 | // is to change the rule for the indicated register to the rule |
| 318 | // assigned it by the initial_instructions in the CIE. |
| 319 | if (InitialLocs == nullptr) |
| 320 | return createStringError( |
| 321 | EC: errc::invalid_argument, Fmt: "%s encountered while parsing a CIE" , |
| 322 | Vals: CFIP.callFrameString(Opcode: Inst.Opcode).str().c_str()); |
| 323 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 324 | if (!RegNum) |
| 325 | return RegNum.takeError(); |
| 326 | if (std::optional<UnwindLocation> O = |
| 327 | InitialLocs->getRegisterLocation(RegNum: *RegNum)) |
| 328 | Row.getRegisterLocations().setRegisterLocation(RegNum: *RegNum, Location: *O); |
| 329 | else |
| 330 | Row.getRegisterLocations().removeRegisterLocation(RegNum: *RegNum); |
| 331 | break; |
| 332 | } |
| 333 | |
| 334 | case dwarf::DW_CFA_offset: |
| 335 | case dwarf::DW_CFA_offset_extended: |
| 336 | case dwarf::DW_CFA_offset_extended_sf: { |
| 337 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 338 | if (!RegNum) |
| 339 | return RegNum.takeError(); |
| 340 | llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, OperandIdx: 1); |
| 341 | if (!Offset) |
| 342 | return Offset.takeError(); |
| 343 | Row.getRegisterLocations().setRegisterLocation( |
| 344 | RegNum: *RegNum, Location: UnwindLocation::createAtCFAPlusOffset(Offset: *Offset)); |
| 345 | break; |
| 346 | } |
| 347 | |
| 348 | case dwarf::DW_CFA_nop: |
| 349 | break; |
| 350 | |
| 351 | case dwarf::DW_CFA_remember_state: |
| 352 | States.push_back( |
| 353 | x: std::make_pair(x&: Row.getCFAValue(), y&: Row.getRegisterLocations())); |
| 354 | break; |
| 355 | |
| 356 | case dwarf::DW_CFA_restore_state: |
| 357 | if (States.empty()) |
| 358 | return createStringError(EC: errc::invalid_argument, |
| 359 | S: "DW_CFA_restore_state without a matching " |
| 360 | "previous DW_CFA_remember_state" ); |
| 361 | Row.getCFAValue() = States.back().first; |
| 362 | Row.getRegisterLocations() = States.back().second; |
| 363 | States.pop_back(); |
| 364 | break; |
| 365 | |
| 366 | case dwarf::DW_CFA_GNU_window_save: |
| 367 | switch (CFIP.triple()) { |
| 368 | case Triple::aarch64: |
| 369 | case Triple::aarch64_be: |
| 370 | case Triple::aarch64_32: { |
| 371 | // DW_CFA_GNU_window_save is used for different things on different |
| 372 | // architectures. For aarch64 it is known as |
| 373 | // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the |
| 374 | // value of the return address state between 1 and 0. If there is |
| 375 | // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it |
| 376 | // should be initially set to 1. |
| 377 | constexpr uint32_t AArch64DWARFPAuthRaState = 34; |
| 378 | auto LRLoc = Row.getRegisterLocations().getRegisterLocation( |
| 379 | RegNum: AArch64DWARFPAuthRaState); |
| 380 | if (LRLoc) { |
| 381 | if (LRLoc->getLocation() == UnwindLocation::Constant) { |
| 382 | // Toggle the constant value from 0 to 1 or 1 to 0. |
| 383 | LRLoc->setConstant(LRLoc->getConstant() ^ 1); |
| 384 | Row.getRegisterLocations().setRegisterLocation( |
| 385 | RegNum: AArch64DWARFPAuthRaState, Location: *LRLoc); |
| 386 | } else { |
| 387 | return createStringError( |
| 388 | EC: errc::invalid_argument, |
| 389 | Fmt: "%s encountered when existing rule for this register is not " |
| 390 | "a constant" , |
| 391 | Vals: CFIP.callFrameString(Opcode: Inst.Opcode).str().c_str()); |
| 392 | } |
| 393 | } else { |
| 394 | Row.getRegisterLocations().setRegisterLocation( |
| 395 | RegNum: AArch64DWARFPAuthRaState, Location: UnwindLocation::createIsConstant(Value: 1)); |
| 396 | } |
| 397 | break; |
| 398 | } |
| 399 | |
| 400 | case Triple::sparc: |
| 401 | case Triple::sparcv9: |
| 402 | case Triple::sparcel: |
| 403 | for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) { |
| 404 | Row.getRegisterLocations().setRegisterLocation( |
| 405 | RegNum, Location: UnwindLocation::createAtCFAPlusOffset(Offset: (RegNum - 16) * 8)); |
| 406 | } |
| 407 | break; |
| 408 | |
| 409 | default: { |
| 410 | return createStringError( |
| 411 | EC: errc::not_supported, |
| 412 | Fmt: "DW_CFA opcode %#x is not supported for architecture %s" , |
| 413 | Vals: Inst.Opcode, Vals: Triple::getArchTypeName(Kind: CFIP.triple()).str().c_str()); |
| 414 | |
| 415 | break; |
| 416 | } |
| 417 | } |
| 418 | break; |
| 419 | |
| 420 | case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: { |
| 421 | constexpr uint32_t AArch64DWARFPAuthRaState = 34; |
| 422 | auto LRLoc = Row.getRegisterLocations().getRegisterLocation( |
| 423 | RegNum: AArch64DWARFPAuthRaState); |
| 424 | if (LRLoc) { |
| 425 | if (LRLoc->getLocation() == UnwindLocation::Constant) { |
| 426 | // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0. |
| 427 | LRLoc->setConstant(LRLoc->getConstant() ^ 0x3); |
| 428 | } else { |
| 429 | return createStringError( |
| 430 | EC: errc::invalid_argument, |
| 431 | Fmt: "%s encountered when existing rule for this register is not " |
| 432 | "a constant" , |
| 433 | Vals: CFIP.callFrameString(Opcode: Inst.Opcode).str().c_str()); |
| 434 | } |
| 435 | } else { |
| 436 | Row.getRegisterLocations().setRegisterLocation( |
| 437 | RegNum: AArch64DWARFPAuthRaState, Location: UnwindLocation::createIsConstant(Value: 0x3)); |
| 438 | } |
| 439 | break; |
| 440 | } |
| 441 | |
| 442 | case dwarf::DW_CFA_undefined: { |
| 443 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 444 | if (!RegNum) |
| 445 | return RegNum.takeError(); |
| 446 | Row.getRegisterLocations().setRegisterLocation( |
| 447 | RegNum: *RegNum, Location: UnwindLocation::createUndefined()); |
| 448 | break; |
| 449 | } |
| 450 | |
| 451 | case dwarf::DW_CFA_same_value: { |
| 452 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 453 | if (!RegNum) |
| 454 | return RegNum.takeError(); |
| 455 | Row.getRegisterLocations().setRegisterLocation( |
| 456 | RegNum: *RegNum, Location: UnwindLocation::createSame()); |
| 457 | break; |
| 458 | } |
| 459 | |
| 460 | case dwarf::DW_CFA_GNU_args_size: |
| 461 | break; |
| 462 | |
| 463 | case dwarf::DW_CFA_register: { |
| 464 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 465 | if (!RegNum) |
| 466 | return RegNum.takeError(); |
| 467 | llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 1); |
| 468 | if (!NewRegNum) |
| 469 | return NewRegNum.takeError(); |
| 470 | Row.getRegisterLocations().setRegisterLocation( |
| 471 | RegNum: *RegNum, Location: UnwindLocation::createIsRegisterPlusOffset(RegNum: *NewRegNum, Offset: 0)); |
| 472 | break; |
| 473 | } |
| 474 | |
| 475 | case dwarf::DW_CFA_val_offset: |
| 476 | case dwarf::DW_CFA_val_offset_sf: { |
| 477 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 478 | if (!RegNum) |
| 479 | return RegNum.takeError(); |
| 480 | llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, OperandIdx: 1); |
| 481 | if (!Offset) |
| 482 | return Offset.takeError(); |
| 483 | Row.getRegisterLocations().setRegisterLocation( |
| 484 | RegNum: *RegNum, Location: UnwindLocation::createIsCFAPlusOffset(Offset: *Offset)); |
| 485 | break; |
| 486 | } |
| 487 | |
| 488 | case dwarf::DW_CFA_expression: { |
| 489 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 490 | if (!RegNum) |
| 491 | return RegNum.takeError(); |
| 492 | Row.getRegisterLocations().setRegisterLocation( |
| 493 | RegNum: *RegNum, Location: UnwindLocation::createAtDWARFExpression(Expr: *Inst.Expression)); |
| 494 | break; |
| 495 | } |
| 496 | |
| 497 | case dwarf::DW_CFA_val_expression: { |
| 498 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 499 | if (!RegNum) |
| 500 | return RegNum.takeError(); |
| 501 | Row.getRegisterLocations().setRegisterLocation( |
| 502 | RegNum: *RegNum, Location: UnwindLocation::createIsDWARFExpression(Expr: *Inst.Expression)); |
| 503 | break; |
| 504 | } |
| 505 | |
| 506 | case dwarf::DW_CFA_def_cfa_register: { |
| 507 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 508 | if (!RegNum) |
| 509 | return RegNum.takeError(); |
| 510 | if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) |
| 511 | Row.getCFAValue() = |
| 512 | UnwindLocation::createIsRegisterPlusOffset(RegNum: *RegNum, Offset: 0); |
| 513 | else |
| 514 | Row.getCFAValue().setRegister(*RegNum); |
| 515 | break; |
| 516 | } |
| 517 | |
| 518 | case dwarf::DW_CFA_def_cfa_offset: |
| 519 | case dwarf::DW_CFA_def_cfa_offset_sf: { |
| 520 | llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, OperandIdx: 0); |
| 521 | if (!Offset) |
| 522 | return Offset.takeError(); |
| 523 | if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) { |
| 524 | return createStringError( |
| 525 | EC: errc::invalid_argument, |
| 526 | Fmt: "%s found when CFA rule was not RegPlusOffset" , |
| 527 | Vals: CFIP.callFrameString(Opcode: Inst.Opcode).str().c_str()); |
| 528 | } |
| 529 | Row.getCFAValue().setOffset(*Offset); |
| 530 | break; |
| 531 | } |
| 532 | |
| 533 | case dwarf::DW_CFA_def_cfa: |
| 534 | case dwarf::DW_CFA_def_cfa_sf: { |
| 535 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 536 | if (!RegNum) |
| 537 | return RegNum.takeError(); |
| 538 | llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, OperandIdx: 1); |
| 539 | if (!Offset) |
| 540 | return Offset.takeError(); |
| 541 | Row.getCFAValue() = |
| 542 | UnwindLocation::createIsRegisterPlusOffset(RegNum: *RegNum, Offset: *Offset); |
| 543 | break; |
| 544 | } |
| 545 | |
| 546 | case dwarf::DW_CFA_LLVM_def_aspace_cfa: |
| 547 | case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: { |
| 548 | llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, OperandIdx: 0); |
| 549 | if (!RegNum) |
| 550 | return RegNum.takeError(); |
| 551 | llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, OperandIdx: 1); |
| 552 | if (!Offset) |
| 553 | return Offset.takeError(); |
| 554 | llvm::Expected<uint32_t> CFAAddrSpace = |
| 555 | Inst.getOperandAsUnsigned(CFIP, OperandIdx: 2); |
| 556 | if (!CFAAddrSpace) |
| 557 | return CFAAddrSpace.takeError(); |
| 558 | Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset( |
| 559 | RegNum: *RegNum, Offset: *Offset, AddrSpace: *CFAAddrSpace); |
| 560 | break; |
| 561 | } |
| 562 | |
| 563 | case dwarf::DW_CFA_def_cfa_expression: |
| 564 | Row.getCFAValue() = |
| 565 | UnwindLocation::createIsDWARFExpression(Expr: *Inst.Expression); |
| 566 | break; |
| 567 | } |
| 568 | } |
| 569 | return Rows; |
| 570 | } |
| 571 | |
| 572 | // Returns the CIE identifier to be used by the requested format. |
| 573 | // CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5. |
| 574 | // For CIE ID in .eh_frame sections see |
| 575 | // https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html |
| 576 | constexpr uint64_t getCIEId(bool IsDWARF64, bool IsEH) { |
| 577 | if (IsEH) |
| 578 | return 0; |
| 579 | if (IsDWARF64) |
| 580 | return DW64_CIE_ID; |
| 581 | return DW_CIE_ID; |
| 582 | } |
| 583 | |
| 584 | void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { |
| 585 | // A CIE with a zero length is a terminator entry in the .eh_frame section. |
| 586 | if (DumpOpts.IsEH && Length == 0) { |
| 587 | OS << format(Fmt: "%08" PRIx64, Vals: Offset) << " ZERO terminator\n" ; |
| 588 | return; |
| 589 | } |
| 590 | |
| 591 | OS << format(Fmt: "%08" PRIx64, Vals: Offset) |
| 592 | << format(Fmt: " %0*" PRIx64, Vals: IsDWARF64 ? 16 : 8, Vals: Length) |
| 593 | << format(Fmt: " %0*" PRIx64, Vals: IsDWARF64 && !DumpOpts.IsEH ? 16 : 8, |
| 594 | Vals: getCIEId(IsDWARF64, IsEH: DumpOpts.IsEH)) |
| 595 | << " CIE\n" |
| 596 | << " Format: " << FormatString(IsDWARF64) << "\n" ; |
| 597 | if (DumpOpts.IsEH && Version != 1) |
| 598 | OS << "WARNING: unsupported CIE version\n" ; |
| 599 | OS << format(Fmt: " Version: %d\n" , Vals: Version) |
| 600 | << " Augmentation: \"" << Augmentation << "\"\n" ; |
| 601 | if (Version >= 4) { |
| 602 | OS << format(Fmt: " Address size: %u\n" , Vals: (uint32_t)AddressSize); |
| 603 | OS << format(Fmt: " Segment desc size: %u\n" , |
| 604 | Vals: (uint32_t)SegmentDescriptorSize); |
| 605 | } |
| 606 | OS << format(Fmt: " Code alignment factor: %u\n" , Vals: (uint32_t)CodeAlignmentFactor); |
| 607 | OS << format(Fmt: " Data alignment factor: %d\n" , Vals: (int32_t)DataAlignmentFactor); |
| 608 | OS << format(Fmt: " Return address column: %d\n" , Vals: (int32_t)ReturnAddressRegister); |
| 609 | if (Personality) |
| 610 | OS << format(Fmt: " Personality Address: %016" PRIx64 "\n" , Vals: *Personality); |
| 611 | if (!AugmentationData.empty()) { |
| 612 | OS << " Augmentation data: " ; |
| 613 | for (uint8_t Byte : AugmentationData) |
| 614 | OS << ' ' << hexdigit(X: Byte >> 4) << hexdigit(X: Byte & 0xf); |
| 615 | OS << "\n" ; |
| 616 | } |
| 617 | OS << "\n" ; |
| 618 | printCFIProgram(P: CFIs, OS, DumpOpts, /*IndentLevel=*/1, |
| 619 | /*InitialLocation=*/Address: {}); |
| 620 | OS << "\n" ; |
| 621 | |
| 622 | if (Expected<UnwindTable> RowsOrErr = createUnwindTable(Cie: this)) |
| 623 | RowsOrErr->dump(OS, DumpOpts, IndentLevel: 1); |
| 624 | else { |
| 625 | DumpOpts.RecoverableErrorHandler(joinErrors( |
| 626 | E1: createStringError(EC: errc::invalid_argument, |
| 627 | S: "decoding the CIE opcodes into rows failed" ), |
| 628 | E2: RowsOrErr.takeError())); |
| 629 | } |
| 630 | OS << "\n" ; |
| 631 | } |
| 632 | |
| 633 | void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { |
| 634 | OS << format(Fmt: "%08" PRIx64, Vals: Offset) |
| 635 | << format(Fmt: " %0*" PRIx64, Vals: IsDWARF64 ? 16 : 8, Vals: Length) |
| 636 | << format(Fmt: " %0*" PRIx64, Vals: IsDWARF64 && !DumpOpts.IsEH ? 16 : 8, Vals: CIEPointer) |
| 637 | << " FDE cie=" ; |
| 638 | if (LinkedCIE) |
| 639 | OS << format(Fmt: "%08" PRIx64, Vals: LinkedCIE->getOffset()); |
| 640 | else |
| 641 | OS << "<invalid offset>" ; |
| 642 | OS << format(Fmt: " pc=%08" PRIx64 "...%08" PRIx64 "\n" , Vals: InitialLocation, |
| 643 | Vals: InitialLocation + AddressRange); |
| 644 | OS << " Format: " << FormatString(IsDWARF64) << "\n" ; |
| 645 | if (LSDAAddress) |
| 646 | OS << format(Fmt: " LSDA Address: %016" PRIx64 "\n" , Vals: *LSDAAddress); |
| 647 | printCFIProgram(P: CFIs, OS, DumpOpts, /*IndentLevel=*/1, Address: InitialLocation); |
| 648 | OS << "\n" ; |
| 649 | |
| 650 | if (Expected<UnwindTable> RowsOrErr = createUnwindTable(Fde: this)) |
| 651 | RowsOrErr->dump(OS, DumpOpts, IndentLevel: 1); |
| 652 | else { |
| 653 | DumpOpts.RecoverableErrorHandler(joinErrors( |
| 654 | E1: createStringError(EC: errc::invalid_argument, |
| 655 | S: "decoding the FDE opcodes into rows failed" ), |
| 656 | E2: RowsOrErr.takeError())); |
| 657 | } |
| 658 | OS << "\n" ; |
| 659 | } |
| 660 | |
| 661 | DWARFDebugFrame::DWARFDebugFrame(Triple::ArchType Arch, |
| 662 | bool IsEH, uint64_t EHFrameAddress) |
| 663 | : Arch(Arch), IsEH(IsEH), EHFrameAddress(EHFrameAddress) {} |
| 664 | |
| 665 | DWARFDebugFrame::~DWARFDebugFrame() = default; |
| 666 | |
| 667 | static void LLVM_ATTRIBUTE_UNUSED (DataExtractor Data, |
| 668 | uint64_t Offset, int Length) { |
| 669 | errs() << "DUMP: " ; |
| 670 | for (int i = 0; i < Length; ++i) { |
| 671 | uint8_t c = Data.getU8(offset_ptr: &Offset); |
| 672 | errs().write_hex(N: c); errs() << " " ; |
| 673 | } |
| 674 | errs() << "\n" ; |
| 675 | } |
| 676 | |
| 677 | Error DWARFDebugFrame::(DWARFDataExtractor Data) { |
| 678 | uint64_t Offset = 0; |
| 679 | DenseMap<uint64_t, CIE *> CIEs; |
| 680 | |
| 681 | while (Data.isValidOffset(offset: Offset)) { |
| 682 | uint64_t StartOffset = Offset; |
| 683 | |
| 684 | uint64_t Length; |
| 685 | DwarfFormat Format; |
| 686 | std::tie(args&: Length, args&: Format) = Data.getInitialLength(Off: &Offset); |
| 687 | bool IsDWARF64 = Format == DWARF64; |
| 688 | |
| 689 | // If the Length is 0, then this CIE is a terminator. We add it because some |
| 690 | // dumper tools might need it to print something special for such entries |
| 691 | // (e.g. llvm-objdump --dwarf=frames prints "ZERO terminator"). |
| 692 | if (Length == 0) { |
| 693 | auto Cie = std::make_unique<CIE>( |
| 694 | args&: IsDWARF64, args&: StartOffset, args: 0, args: 0, args: SmallString<8>(), args: 0, args: 0, args: 0, args: 0, args: 0, |
| 695 | args: SmallString<8>(), args: 0, args: 0, args: std::nullopt, args: std::nullopt, args: Arch); |
| 696 | CIEs[StartOffset] = Cie.get(); |
| 697 | Entries.push_back(x: std::move(Cie)); |
| 698 | break; |
| 699 | } |
| 700 | |
| 701 | // At this point, Offset points to the next field after Length. |
| 702 | // Length is the structure size excluding itself. Compute an offset one |
| 703 | // past the end of the structure (needed to know how many instructions to |
| 704 | // read). |
| 705 | uint64_t StartStructureOffset = Offset; |
| 706 | uint64_t EndStructureOffset = Offset + Length; |
| 707 | |
| 708 | // The Id field's size depends on the DWARF format |
| 709 | Error Err = Error::success(); |
| 710 | uint64_t Id = Data.getRelocatedValue(Size: (IsDWARF64 && !IsEH) ? 8 : 4, Off: &Offset, |
| 711 | /*SectionIndex=*/nullptr, Err: &Err); |
| 712 | if (Err) |
| 713 | return Err; |
| 714 | |
| 715 | if (Id == getCIEId(IsDWARF64, IsEH)) { |
| 716 | uint8_t Version = Data.getU8(offset_ptr: &Offset); |
| 717 | const char *Augmentation = Data.getCStr(OffsetPtr: &Offset); |
| 718 | StringRef AugmentationString(Augmentation ? Augmentation : "" ); |
| 719 | uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : |
| 720 | Data.getU8(offset_ptr: &Offset); |
| 721 | Data.setAddressSize(AddressSize); |
| 722 | uint8_t SegmentDescriptorSize = Version < 4 ? 0 : Data.getU8(offset_ptr: &Offset); |
| 723 | uint64_t CodeAlignmentFactor = Data.getULEB128(offset_ptr: &Offset); |
| 724 | int64_t DataAlignmentFactor = Data.getSLEB128(OffsetPtr: &Offset); |
| 725 | uint64_t ReturnAddressRegister = |
| 726 | Version == 1 ? Data.getU8(offset_ptr: &Offset) : Data.getULEB128(offset_ptr: &Offset); |
| 727 | |
| 728 | // Parse the augmentation data for EH CIEs |
| 729 | StringRef AugmentationData("" ); |
| 730 | uint32_t FDEPointerEncoding = DW_EH_PE_absptr; |
| 731 | uint32_t LSDAPointerEncoding = DW_EH_PE_omit; |
| 732 | std::optional<uint64_t> Personality; |
| 733 | std::optional<uint32_t> PersonalityEncoding; |
| 734 | if (IsEH) { |
| 735 | std::optional<uint64_t> AugmentationLength; |
| 736 | uint64_t StartAugmentationOffset; |
| 737 | uint64_t EndAugmentationOffset; |
| 738 | |
| 739 | // Walk the augmentation string to get all the augmentation data. |
| 740 | for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) { |
| 741 | switch (AugmentationString[i]) { |
| 742 | default: |
| 743 | return createStringError( |
| 744 | EC: errc::invalid_argument, |
| 745 | Fmt: "unknown augmentation character %c in entry at 0x%" PRIx64, |
| 746 | Vals: AugmentationString[i], Vals: StartOffset); |
| 747 | case 'L': |
| 748 | LSDAPointerEncoding = Data.getU8(offset_ptr: &Offset); |
| 749 | break; |
| 750 | case 'P': { |
| 751 | if (Personality) |
| 752 | return createStringError( |
| 753 | EC: errc::invalid_argument, |
| 754 | Fmt: "duplicate personality in entry at 0x%" PRIx64, Vals: StartOffset); |
| 755 | PersonalityEncoding = Data.getU8(offset_ptr: &Offset); |
| 756 | Personality = Data.getEncodedPointer( |
| 757 | Offset: &Offset, Encoding: *PersonalityEncoding, |
| 758 | PCRelOffset: EHFrameAddress ? EHFrameAddress + Offset : 0); |
| 759 | break; |
| 760 | } |
| 761 | case 'R': |
| 762 | FDEPointerEncoding = Data.getU8(offset_ptr: &Offset); |
| 763 | break; |
| 764 | case 'S': |
| 765 | // Current frame is a signal trampoline. |
| 766 | break; |
| 767 | case 'z': |
| 768 | if (i) |
| 769 | return createStringError( |
| 770 | EC: errc::invalid_argument, |
| 771 | Fmt: "'z' must be the first character at 0x%" PRIx64, Vals: StartOffset); |
| 772 | // Parse the augmentation length first. We only parse it if |
| 773 | // the string contains a 'z'. |
| 774 | AugmentationLength = Data.getULEB128(offset_ptr: &Offset); |
| 775 | StartAugmentationOffset = Offset; |
| 776 | EndAugmentationOffset = Offset + *AugmentationLength; |
| 777 | break; |
| 778 | case 'B': |
| 779 | // B-Key is used for signing functions associated with this |
| 780 | // augmentation string |
| 781 | break; |
| 782 | // This stack frame contains MTE tagged data, so needs to be |
| 783 | // untagged on unwind. |
| 784 | case 'G': |
| 785 | break; |
| 786 | } |
| 787 | } |
| 788 | |
| 789 | if (AugmentationLength) { |
| 790 | if (Offset != EndAugmentationOffset) |
| 791 | return createStringError(EC: errc::invalid_argument, |
| 792 | Fmt: "parsing augmentation data at 0x%" PRIx64 |
| 793 | " failed" , |
| 794 | Vals: StartOffset); |
| 795 | AugmentationData = Data.getData().slice(Start: StartAugmentationOffset, |
| 796 | End: EndAugmentationOffset); |
| 797 | } |
| 798 | } |
| 799 | |
| 800 | auto Cie = std::make_unique<CIE>( |
| 801 | args&: IsDWARF64, args&: StartOffset, args&: Length, args&: Version, args&: AugmentationString, |
| 802 | args&: AddressSize, args&: SegmentDescriptorSize, args&: CodeAlignmentFactor, |
| 803 | args&: DataAlignmentFactor, args&: ReturnAddressRegister, args&: AugmentationData, |
| 804 | args&: FDEPointerEncoding, args&: LSDAPointerEncoding, args&: Personality, |
| 805 | args&: PersonalityEncoding, args: Arch); |
| 806 | CIEs[StartOffset] = Cie.get(); |
| 807 | Entries.emplace_back(args: std::move(Cie)); |
| 808 | } else { |
| 809 | // FDE |
| 810 | uint64_t CIEPointer = Id; |
| 811 | uint64_t InitialLocation = 0; |
| 812 | uint64_t AddressRange = 0; |
| 813 | std::optional<uint64_t> LSDAAddress; |
| 814 | CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer]; |
| 815 | |
| 816 | if (IsEH) { |
| 817 | // The address size is encoded in the CIE we reference. |
| 818 | if (!Cie) |
| 819 | return createStringError(EC: errc::invalid_argument, |
| 820 | Fmt: "parsing FDE data at 0x%" PRIx64 |
| 821 | " failed due to missing CIE" , |
| 822 | Vals: StartOffset); |
| 823 | if (auto Val = |
| 824 | Data.getEncodedPointer(Offset: &Offset, Encoding: Cie->getFDEPointerEncoding(), |
| 825 | PCRelOffset: EHFrameAddress + Offset)) { |
| 826 | InitialLocation = *Val; |
| 827 | } |
| 828 | if (auto Val = Data.getEncodedPointer( |
| 829 | Offset: &Offset, Encoding: Cie->getFDEPointerEncoding(), PCRelOffset: 0)) { |
| 830 | AddressRange = *Val; |
| 831 | } |
| 832 | |
| 833 | StringRef AugmentationString = Cie->getAugmentationString(); |
| 834 | if (!AugmentationString.empty()) { |
| 835 | // Parse the augmentation length and data for this FDE. |
| 836 | uint64_t AugmentationLength = Data.getULEB128(offset_ptr: &Offset); |
| 837 | |
| 838 | uint64_t EndAugmentationOffset = Offset + AugmentationLength; |
| 839 | |
| 840 | // Decode the LSDA if the CIE augmentation string said we should. |
| 841 | if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) { |
| 842 | LSDAAddress = Data.getEncodedPointer( |
| 843 | Offset: &Offset, Encoding: Cie->getLSDAPointerEncoding(), |
| 844 | PCRelOffset: EHFrameAddress ? Offset + EHFrameAddress : 0); |
| 845 | } |
| 846 | |
| 847 | if (Offset != EndAugmentationOffset) |
| 848 | return createStringError(EC: errc::invalid_argument, |
| 849 | Fmt: "parsing augmentation data at 0x%" PRIx64 |
| 850 | " failed" , |
| 851 | Vals: StartOffset); |
| 852 | } |
| 853 | } else { |
| 854 | InitialLocation = Data.getRelocatedAddress(Off: &Offset); |
| 855 | AddressRange = Data.getRelocatedAddress(Off: &Offset); |
| 856 | } |
| 857 | |
| 858 | Entries.emplace_back(args: new FDE(IsDWARF64, StartOffset, Length, CIEPointer, |
| 859 | InitialLocation, AddressRange, Cie, |
| 860 | LSDAAddress, Arch)); |
| 861 | } |
| 862 | |
| 863 | if (Error E = |
| 864 | Entries.back()->cfis().parse(Data, Offset: &Offset, EndOffset: EndStructureOffset)) |
| 865 | return E; |
| 866 | |
| 867 | if (Offset != EndStructureOffset) |
| 868 | return createStringError( |
| 869 | EC: errc::invalid_argument, |
| 870 | Fmt: "parsing entry instructions at 0x%" PRIx64 " failed" , Vals: StartOffset); |
| 871 | } |
| 872 | |
| 873 | return Error::success(); |
| 874 | } |
| 875 | |
| 876 | FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { |
| 877 | auto It = partition_point(Range: Entries, P: [=](const std::unique_ptr<FrameEntry> &E) { |
| 878 | return E->getOffset() < Offset; |
| 879 | }); |
| 880 | if (It != Entries.end() && (*It)->getOffset() == Offset) |
| 881 | return It->get(); |
| 882 | return nullptr; |
| 883 | } |
| 884 | |
| 885 | void DWARFDebugFrame::dump(raw_ostream &OS, DIDumpOptions DumpOpts, |
| 886 | std::optional<uint64_t> Offset) const { |
| 887 | DumpOpts.IsEH = IsEH; |
| 888 | if (Offset) { |
| 889 | if (auto *Entry = getEntryAtOffset(Offset: *Offset)) |
| 890 | Entry->dump(OS, DumpOpts); |
| 891 | return; |
| 892 | } |
| 893 | |
| 894 | OS << "\n" ; |
| 895 | for (const auto &Entry : Entries) |
| 896 | Entry->dump(OS, DumpOpts); |
| 897 | } |
| 898 | |