From 628274dadf92995f4544d6134cba45d327d9eaaa Mon Sep 17 00:00:00 2001 From: Sterling-Augustine <56981066+Sterling-Augustine@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:35:47 -0700 Subject: [PATCH] [NFC] Extract Printing portions of DWARFCFIProgram to new files (#143762) CFIPrograms' most common uses are within debug frames, but it is not their only use. For example, some assembly writers encode them by hand into .cfi_escape directives. This PR extracts printing code for them into its own files, which avoids the need for the main class to depend on DWARFUnit, sections, and similar. One in a series of NFC DebugInfo/DWARF refactoring changes to layer it more cleanly, so that binary CFI parsing can be used from low-level code, (such as byte strings created via .cfi_escape) without circular dependencies. The final goal is to make a more limited dwarf library usable from lower-level code. More information can be found at https://discourse.llvm.org/t/rfc-debuginfo-dwarf-refactor-into-to-lower-and-higher-level-libraries/86665 --- .../llvm/DebugInfo/DWARF/DWARFCFIPrinter.h | 28 ++++ .../llvm/DebugInfo/DWARF/DWARFCFIProgram.h | 62 ++++----- llvm/lib/DebugInfo/DWARF/CMakeLists.txt | 1 + llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp | 121 ++++++++++++++++++ llvm/lib/DebugInfo/DWARF/DWARFCFIProgram.cpp | 94 -------------- llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 6 +- llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h | 5 +- 7 files changed, 184 insertions(+), 133 deletions(-) create mode 100644 llvm/include/llvm/DebugInfo/DWARF/DWARFCFIPrinter.h create mode 100644 llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFCFIPrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFCFIPrinter.h new file mode 100644 index 000000000000..32e8247ac4c2 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFCFIPrinter.h @@ -0,0 +1,28 @@ +//===- DWARFCFIPrinter.h ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_DWARF_DWARFCFIPRINTER_H +#define LLVM_DEBUGINFO_DWARF_DWARFCFIPRINTER_H + +#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h" + +namespace llvm { + +struct DIDumpOptions; + +namespace dwarf { + +void printCFIProgram(const CFIProgram &P, raw_ostream &OS, + const DIDumpOptions &DumpOpts, unsigned IndentLevel, + std::optional Address); + +} // end namespace dwarf + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_DWARF_DWARFCFIPRINTER_H diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFCFIProgram.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFCFIProgram.h index 24a0f389470d..ad7358c28f16 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFCFIProgram.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFCFIProgram.h @@ -24,6 +24,7 @@ namespace llvm { namespace dwarf { + /// Represent a sequence of Call Frame Information instructions that, when read /// in order, construct a table mapping PC to frame state. This can also be /// referred to as "CFI rules" in DWARF literature to avoid confusion with @@ -80,15 +81,37 @@ public: LLVM_ABI Error parse(DWARFDataExtractor Data, uint64_t *Offset, uint64_t EndOffset); - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel, - std::optional InitialLocation) const; - void addInstruction(const Instruction &I) { Instructions.push_back(I); } /// Get a DWARF CFI call frame string for the given DW_CFA opcode. LLVM_ABI StringRef callFrameString(unsigned Opcode) const; + /// Types of operands to CFI instructions + /// In DWARF, this type is implicitly tied to a CFI instruction opcode and + /// thus this type doesn't need to be explicitly written to the file (this is + /// not a DWARF encoding). The relationship of instrs to operand types can + /// be obtained from getOperandTypes() and is only used to simplify + /// instruction printing and error messages. + enum OperandType { + OT_Unset, + OT_None, + OT_Address, + OT_Offset, + OT_FactoredCodeOffset, + OT_SignedFactDataOffset, + OT_UnsignedFactDataOffset, + OT_Register, + OT_AddressSpace, + OT_Expression + }; + + /// Get the OperandType as a "const char *". + static const char *operandTypeString(OperandType OT); + + /// Retrieve the array describing the types of operands according to the enum + /// above. This is indexed by opcode. + static ArrayRef getOperandTypes(); + private: std::vector Instructions; const uint64_t CodeAlignmentFactor; @@ -121,37 +144,6 @@ private: Instructions.back().Ops.push_back(Operand2); Instructions.back().Ops.push_back(Operand3); } - - /// Types of operands to CFI instructions - /// In DWARF, this type is implicitly tied to a CFI instruction opcode and - /// thus this type doesn't need to be explicitly written to the file (this is - /// not a DWARF encoding). The relationship of instrs to operand types can - /// be obtained from getOperandTypes() and is only used to simplify - /// instruction printing. - enum OperandType { - OT_Unset, - OT_None, - OT_Address, - OT_Offset, - OT_FactoredCodeOffset, - OT_SignedFactDataOffset, - OT_UnsignedFactDataOffset, - OT_Register, - OT_AddressSpace, - OT_Expression - }; - - /// Get the OperandType as a "const char *". - static const char *operandTypeString(OperandType OT); - - /// Retrieve the array describing the types of operands according to the enum - /// above. This is indexed by opcode. - static ArrayRef getOperandTypes(); - - /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. - void printOperand(raw_ostream &OS, DIDumpOptions DumpOpts, - const Instruction &Instr, unsigned OperandIdx, - uint64_t Operand, std::optional &Address) const; }; } // end namespace dwarf diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt index cc9734f9f22b..86e74110b15e 100644 --- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -2,6 +2,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF DWARFAbbreviationDeclaration.cpp DWARFAddressRange.cpp DWARFAcceleratorTable.cpp + DWARFCFIPrinter.cpp DWARFCFIProgram.cpp DWARFCompileUnit.cpp DWARFContext.cpp diff --git a/llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp new file mode 100644 index 000000000000..e52f671e4fa1 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp @@ -0,0 +1,121 @@ +//===- DWARFCFIPrinter.cpp - Print the cfi-portions of .debug_frame -------===// +// +// 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/DWARFCFIPrinter.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; +using namespace dwarf; + +static void printRegister(raw_ostream &OS, const DIDumpOptions &DumpOpts, + unsigned RegNum) { + if (DumpOpts.GetNameForDWARFReg) { + auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); + if (!RegName.empty()) { + OS << RegName; + return; + } + } + OS << "reg" << RegNum; +} + +/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. +static void printOperand(raw_ostream &OS, const DIDumpOptions &DumpOpts, + const CFIProgram &P, + const CFIProgram::Instruction &Instr, + unsigned OperandIdx, uint64_t Operand, + std::optional &Address) { + assert(OperandIdx < CFIProgram::MaxOperands); + uint8_t Opcode = Instr.Opcode; + CFIProgram::OperandType Type = P.getOperandTypes()[Opcode][OperandIdx]; + + switch (Type) { + case CFIProgram::OT_Unset: { + OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; + auto OpcodeName = P.callFrameString(Opcode); + if (!OpcodeName.empty()) + OS << " " << OpcodeName; + else + OS << format(" Opcode %x", Opcode); + break; + } + case CFIProgram::OT_None: + break; + case CFIProgram::OT_Address: + OS << format(" %" PRIx64, Operand); + Address = Operand; + break; + case CFIProgram::OT_Offset: + // The offsets are all encoded in a unsigned form, but in practice + // consumers use them signed. It's most certainly legacy due to + // the lack of signed variants in the first Dwarf standards. + OS << format(" %+" PRId64, int64_t(Operand)); + break; + case CFIProgram::OT_FactoredCodeOffset: // Always Unsigned + if (P.codeAlign()) + OS << format(" %" PRId64, Operand * P.codeAlign()); + else + OS << format(" %" PRId64 "*code_alignment_factor", Operand); + if (Address && P.codeAlign()) { + *Address += Operand * P.codeAlign(); + OS << format(" to 0x%" PRIx64, *Address); + } + break; + case CFIProgram::OT_SignedFactDataOffset: + if (P.dataAlign()) + OS << format(" %" PRId64, int64_t(Operand) * P.dataAlign()); + else + OS << format(" %" PRId64 "*data_alignment_factor", int64_t(Operand)); + break; + case CFIProgram::OT_UnsignedFactDataOffset: + if (P.dataAlign()) + OS << format(" %" PRId64, Operand * P.dataAlign()); + else + OS << format(" %" PRId64 "*data_alignment_factor", Operand); + break; + case CFIProgram::OT_Register: + OS << ' '; + printRegister(OS, DumpOpts, Operand); + break; + case CFIProgram::OT_AddressSpace: + OS << format(" in addrspace%" PRId64, Operand); + break; + case CFIProgram::OT_Expression: + assert(Instr.Expression && "missing DWARFExpression object"); + OS << " "; + DWARFExpressionPrinter::print(&Instr.Expression.value(), OS, DumpOpts, + nullptr); + break; + } +} + +void llvm::dwarf::printCFIProgram(const CFIProgram &P, raw_ostream &OS, + const DIDumpOptions &DumpOpts, + unsigned IndentLevel, + std::optional Address) { + for (const auto &Instr : P) { + uint8_t Opcode = Instr.Opcode; + OS.indent(2 * IndentLevel); + OS << P.callFrameString(Opcode) << ":"; + for (size_t i = 0; i < Instr.Ops.size(); ++i) + printOperand(OS, DumpOpts, P, Instr, i, Instr.Ops[i], Address); + OS << '\n'; + } +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFCFIProgram.cpp b/llvm/lib/DebugInfo/DWARF/DWARFCFIProgram.cpp index 8d25599627c4..365b26b98a1e 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFCFIProgram.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFCFIProgram.cpp @@ -23,18 +23,6 @@ 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; -} - // See DWARF standard v3, section 7.23 const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0; const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; @@ -361,85 +349,3 @@ CFIProgram::getOperandTypes() { return ArrayRef(&OpTypes[0], DW_CFA_restore + 1); } - -/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. -void CFIProgram::printOperand(raw_ostream &OS, DIDumpOptions DumpOpts, - const Instruction &Instr, unsigned OperandIdx, - uint64_t Operand, - std::optional &Address) const { - assert(OperandIdx < MaxOperands); - uint8_t Opcode = Instr.Opcode; - OperandType Type = getOperandTypes()[Opcode][OperandIdx]; - - switch (Type) { - case OT_Unset: { - OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; - auto OpcodeName = callFrameString(Opcode); - if (!OpcodeName.empty()) - OS << " " << OpcodeName; - else - OS << format(" Opcode %x", Opcode); - break; - } - case OT_None: - break; - case OT_Address: - OS << format(" %" PRIx64, Operand); - Address = Operand; - break; - case OT_Offset: - // The offsets are all encoded in a unsigned form, but in practice - // consumers use them signed. It's most certainly legacy due to - // the lack of signed variants in the first Dwarf standards. - OS << format(" %+" PRId64, int64_t(Operand)); - break; - case OT_FactoredCodeOffset: // Always Unsigned - if (CodeAlignmentFactor) - OS << format(" %" PRId64, Operand * CodeAlignmentFactor); - else - OS << format(" %" PRId64 "*code_alignment_factor", Operand); - if (Address && CodeAlignmentFactor) { - *Address += Operand * CodeAlignmentFactor; - OS << format(" to 0x%" PRIx64, *Address); - } - break; - case OT_SignedFactDataOffset: - if (DataAlignmentFactor) - OS << format(" %" PRId64, int64_t(Operand) * DataAlignmentFactor); - else - OS << format(" %" PRId64 "*data_alignment_factor", int64_t(Operand)); - break; - case OT_UnsignedFactDataOffset: - if (DataAlignmentFactor) - OS << format(" %" PRId64, Operand * DataAlignmentFactor); - else - OS << format(" %" PRId64 "*data_alignment_factor", Operand); - break; - case OT_Register: - OS << ' '; - printRegister(OS, DumpOpts, Operand); - break; - case OT_AddressSpace: - OS << format(" in addrspace%" PRId64, Operand); - break; - case OT_Expression: - assert(Instr.Expression && "missing DWARFExpression object"); - OS << " "; - DWARFExpressionPrinter::print(&Instr.Expression.value(), OS, DumpOpts, - nullptr); - break; - } -} - -void CFIProgram::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel, - std::optional Address) const { - for (const auto &Instr : Instructions) { - uint8_t Opcode = Instr.Opcode; - OS.indent(2 * IndentLevel); - OS << callFrameString(Opcode) << ":"; - for (unsigned i = 0; i < Instr.Ops.size(); ++i) - printOperand(OS, DumpOpts, Instr, i, Instr.Ops[i], Address); - OS << '\n'; - } -} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index c46b14b4446f..9dff925073db 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h" #include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" @@ -602,7 +603,8 @@ void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << "\n"; } OS << "\n"; - CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, /*InitialLocation=*/{}); + printCFIProgram(CFIs, OS, DumpOpts, /*IndentLevel=*/1, + /*InitialLocation=*/{}); OS << "\n"; if (Expected RowsOrErr = UnwindTable::create(this)) @@ -630,7 +632,7 @@ void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << " Format: " << FormatString(IsDWARF64) << "\n"; if (LSDAAddress) OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress); - CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, InitialLocation); + printCFIProgram(CFIs, OS, DumpOpts, /*IndentLevel=*/1, InitialLocation); OS << "\n"; if (Expected RowsOrErr = UnwindTable::create(this)) diff --git a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h index 94a44e3afccb..85c4165de4aa 100644 --- a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h +++ b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -12,6 +12,7 @@ #include "llvm-readobj.h" #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" @@ -228,8 +229,8 @@ void PrinterContext::printEHFrame(const Elf_Shdr *EHFrameShdr) const { W.indent(); auto DumpOpts = DIDumpOptions(); DumpOpts.IsEH = true; - Entry.cfis().dump(W.getOStream(), DumpOpts, W.getIndentLevel(), - InitialLocation); + printCFIProgram(Entry.cfis(), W.getOStream(), DumpOpts, W.getIndentLevel(), + InitialLocation); W.unindent(); W.unindent(); W.getOStream() << "\n";