[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
This commit is contained in:
committed by
GitHub
parent
a9811340b7
commit
628274dadf
28
llvm/include/llvm/DebugInfo/DWARF/DWARFCFIPrinter.h
Normal file
28
llvm/include/llvm/DebugInfo/DWARF/DWARFCFIPrinter.h
Normal file
@@ -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<uint64_t> Address);
|
||||
|
||||
} // end namespace dwarf
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_DWARF_DWARFCFIPRINTER_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<uint64_t> 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<OperandType[MaxOperands]> getOperandTypes();
|
||||
|
||||
private:
|
||||
std::vector<Instruction> 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<OperandType[MaxOperands]> 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<uint64_t> &Address) const;
|
||||
};
|
||||
|
||||
} // end namespace dwarf
|
||||
|
||||
@@ -2,6 +2,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF
|
||||
DWARFAbbreviationDeclaration.cpp
|
||||
DWARFAddressRange.cpp
|
||||
DWARFAcceleratorTable.cpp
|
||||
DWARFCFIPrinter.cpp
|
||||
DWARFCFIProgram.cpp
|
||||
DWARFCompileUnit.cpp
|
||||
DWARFContext.cpp
|
||||
|
||||
121
llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp
Normal file
121
llvm/lib/DebugInfo/DWARF/DWARFCFIPrinter.cpp
Normal file
@@ -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 <cassert>
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
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<uint64_t> &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<uint64_t> 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';
|
||||
}
|
||||
}
|
||||
@@ -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<OperandType[MaxOperands]>(&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<uint64_t> &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<uint64_t> 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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<UnwindTable> 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<UnwindTable> RowsOrErr = UnwindTable::create(this))
|
||||
|
||||
@@ -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<ELFT>::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";
|
||||
|
||||
Reference in New Issue
Block a user