| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/DebugInfo/DIContext.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| using namespace llvm; | |
| using namespace dwarf; | |
| static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, | |
| unsigned RegNum) { | |
| if (DumpOpts.GetNameForDWARFReg) { | |
| auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); | |
| if (!RegName.empty()) { | |
| OS << RegName; | |
| return; | |
| } | |
| } | |
| OS << "reg" << RegNum; | |
| } | |
| /// Print an unwind location expression as text and use the register information | |
| /// if some is provided. | |
| /// | |
| /// \param R the unwind location to print. | |
| /// | |
| /// \param OS the stream to use for output. | |
| /// | |
| /// \param MRI register information that helps emit register names insteead | |
| /// of raw register numbers. | |
| /// | |
| /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame | |
| /// instead of from .debug_frame. This is needed for register number | |
| /// conversion because some register numbers differ between the two sections | |
| /// for certain architectures like x86. | |
| static void printUnwindLocation(const UnwindLocation &UL, raw_ostream &OS, | |
| DIDumpOptions DumpOpts) { | |
| if (UL.getDereference()) | |
| OS << '['; | |
| switch (UL.getLocation()) { | |
| case UnwindLocation::Unspecified: | |
| OS << "unspecified"; | |
| break; | |
| case UnwindLocation::Undefined: | |
| OS << "undefined"; | |
| break; | |
| case UnwindLocation::Same: | |
| OS << "same"; | |
| break; | |
| case UnwindLocation::CFAPlusOffset: | |
| OS << "CFA"; | |
| if (UL.getOffset() == 0) | |
| break; | |
| if (UL.getOffset() > 0) | |
| OS << "+"; | |
| OS << UL.getOffset(); | |
| break; | |
| case UnwindLocation::RegPlusOffset: | |
| printRegister(OS, DumpOpts, UL.getRegister()); | |
| if (UL.getOffset() == 0 && !UL.hasAddressSpace()) | |
| break; | |
| if (UL.getOffset() >= 0) | |
| OS << "+"; | |
| OS << UL.getOffset(); | |
| if (UL.hasAddressSpace()) | |
| OS << " in addrspace" << UL.getAddressSpace(); | |
| break; | |
| case UnwindLocation::DWARFExpr: { | |
| if (UL.getDWARFExpressionBytes()) { | |
| auto Expr = *UL.getDWARFExpressionBytes(); | |
| printDwarfExpression(&Expr, OS, DumpOpts, nullptr); | |
| } | |
| break; | |
| } | |
| case UnwindLocation::Constant: | |
| OS << UL.getOffset(); | |
| break; | |
| } | |
| if (UL.getDereference()) | |
| OS << ']'; | |
| } | |
| raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, | |
| const UnwindLocation &UL) { | |
| auto DumpOpts = DIDumpOptions(); | |
| printUnwindLocation(UL, OS, DumpOpts); | |
| return OS; | |
| } | |
| /// Print all registers + locations that are currently defined in a register | |
| /// locations. | |
| /// | |
| /// \param RL the register locations to print. | |
| /// | |
| /// \param OS the stream to use for output. | |
| /// | |
| /// \param MRI register information that helps emit register names insteead | |
| /// of raw register numbers. | |
| /// | |
| /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame | |
| /// instead of from .debug_frame. This is needed for register number | |
| /// conversion because some register numbers differ between the two sections | |
| /// for certain architectures like x86. | |
| static void printRegisterLocations(const RegisterLocations &RL, raw_ostream &OS, | |
| DIDumpOptions DumpOpts) { | |
| ListSeparator LS; | |
| for (uint32_t Reg : RL.getRegisters()) { | |
| auto Loc = *RL.getRegisterLocation(Reg); | |
| OS << LS; | |
| printRegister(OS, DumpOpts, Reg); | |
| OS << '='; | |
| printUnwindLocation(Loc, OS, DumpOpts); | |
| } | |
| } | |
| raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, | |
| const RegisterLocations &RL) { | |
| auto DumpOpts = DIDumpOptions(); | |
| printRegisterLocations(RL, OS, DumpOpts); | |
| return OS; | |
| } | |
| /// Print an UnwindRow to the stream. | |
| /// | |
| /// \param Row the UnwindRow to print. | |
| /// | |
| /// \param OS the stream to use for output. | |
| /// | |
| /// \param MRI register information that helps emit register names insteead | |
| /// of raw register numbers. | |
| /// | |
| /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame | |
| /// instead of from .debug_frame. This is needed for register number | |
| /// conversion because some register numbers differ between the two sections | |
| /// for certain architectures like x86. | |
| /// | |
| /// \param IndentLevel specify the indent level as an integer. The UnwindRow | |
| /// will be output to the stream preceded by 2 * IndentLevel number of spaces. | |
| static void printUnwindRow(const UnwindRow &Row, raw_ostream &OS, | |
| DIDumpOptions DumpOpts, unsigned IndentLevel) { | |
| OS.indent(2 * IndentLevel); | |
| if (Row.hasAddress()) | |
| OS << formatv("{0:x}: ", Row.getAddress()); | |
| OS << "CFA="; | |
| printUnwindLocation(Row.getCFAValue(), OS, DumpOpts); | |
| if (Row.getRegisterLocations().hasLocations()) { | |
| OS << ": "; | |
| printRegisterLocations(Row.getRegisterLocations(), OS, DumpOpts); | |
| } | |
| OS << "\n"; | |
| } | |
| raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { | |
| auto DumpOpts = DIDumpOptions(); | |
| printUnwindRow(Row, OS, DumpOpts, 0); | |
| return OS; | |
| } | |
| void llvm::dwarf::printUnwindTable(const UnwindTable &Rows, raw_ostream &OS, | |
| DIDumpOptions DumpOpts, | |
| unsigned IndentLevel) { | |
| for (const UnwindRow &Row : Rows) | |
| printUnwindRow(Row, OS, DumpOpts, IndentLevel); | |
| } | |
| raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { | |
| auto DumpOpts = DIDumpOptions(); | |
| printUnwindTable(Rows, OS, DumpOpts, 0); | |
| return OS; | |
| } |