From 916a94b870042772568fca7995cf45aef7a6e333 Mon Sep 17 00:00:00 2001 From: Bill Wendling Date: Fri, 17 Jun 2011 20:35:21 +0000 Subject: [PATCH] Add an option that allows one to "decode" the LSDA. The LSDA is a bit difficult for the non-initiated to read. Even with comments, it's not always clear what's going on. This wraps the ASM streamer in a class that retains the LSDA and then emits a human-readable description of what's going on in it. So instead of having to make sense of: Lexception1: .byte 255 .byte 155 .byte 168 .space 1 .byte 3 .byte 26 Lset0 = Ltmp7-Leh_func_begin1 .long Lset0 Lset1 = Ltmp812-Ltmp7 .long Lset1 Lset2 = Ltmp913-Leh_func_begin1 .long Lset2 .byte 3 Lset3 = Ltmp812-Leh_func_begin1 .long Lset3 Lset4 = Leh_func_end1-Ltmp812 .long Lset4 .long 0 .byte 0 .byte 1 .byte 0 .byte 2 .byte 125 .long __ZTIi@GOTPCREL+4 .long __ZTIPKc@GOTPCREL+4 you can read this instead: ## Exception Handling Table: Lexception1 ## @LPStart Encoding: omit ## @TType Encoding: indirect pcrel sdata4 ## @TType Base: 40 bytes ## @CallSite Encoding: udata4 ## @Action Table Size: 26 bytes ## Action 1: ## A throw between Ltmp7 and Ltmp812 jumps to Ltmp913 on an exception. ## For type(s): __ZTIi@GOTPCREL+4 __ZTIPKc@GOTPCREL+4 ## Action 2: ## A throw between Ltmp812 and Leh_func_end1 does not have a landing pad. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@133286 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/MC/MCStreamer.h | 6 +- include/llvm/Target/TargetRegistry.h | 11 +- lib/CodeGen/LLVMTargetMachine.cpp | 5 +- lib/MC/MCAsmStreamer.cpp | 368 ++++++++++++++++++++++++++- lib/Target/PTX/PTXMCAsmStreamer.cpp | 3 +- lib/Target/PTX/PTXTargetMachine.cpp | 3 +- tools/llvm-mc/llvm-mc.cpp | 16 +- 7 files changed, 395 insertions(+), 17 deletions(-) diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h index c05a9251dc7..6f017136c2d 100644 --- a/include/llvm/MC/MCStreamer.h +++ b/include/llvm/MC/MCStreamer.h @@ -547,6 +547,9 @@ namespace llvm { /// /// \param ShowInst - Whether to show the MCInst representation inline with /// the assembly. + /// + /// \param DecodeLSDA - If true, emit comments that translates the LSDA into a + /// human readable format. Only usable with CFI. MCStreamer *createAsmStreamer(MCContext &Ctx, formatted_raw_ostream &OS, bool isVerboseAsm, bool useLoc, @@ -554,7 +557,8 @@ namespace llvm { MCInstPrinter *InstPrint = 0, MCCodeEmitter *CE = 0, TargetAsmBackend *TAB = 0, - bool ShowInst = false); + bool ShowInst = false, + bool DecodeLSDA = false); /// createMachOStreamer - Create a machine code streamer which will generate /// Mach-O format object files. diff --git a/include/llvm/Target/TargetRegistry.h b/include/llvm/Target/TargetRegistry.h index a464822893b..0bec8bc1ecf 100644 --- a/include/llvm/Target/TargetRegistry.h +++ b/include/llvm/Target/TargetRegistry.h @@ -47,7 +47,8 @@ namespace llvm { MCInstPrinter *InstPrint, MCCodeEmitter *CE, TargetAsmBackend *TAB, - bool ShowInst); + bool ShowInst, + bool DecodeLSDA); /// Target - Wrapper for Target specific information. /// @@ -100,7 +101,8 @@ namespace llvm { MCInstPrinter *InstPrint, MCCodeEmitter *CE, TargetAsmBackend *TAB, - bool ShowInst); + bool ShowInst, + bool DecodeLSDA); private: /// Next - The next registered target in the linked list, maintained by the @@ -334,10 +336,11 @@ namespace llvm { MCInstPrinter *InstPrint, MCCodeEmitter *CE, TargetAsmBackend *TAB, - bool ShowInst) const { + bool ShowInst, + bool DecodeLSDA) const { // AsmStreamerCtorFn is default to llvm::createAsmStreamer return AsmStreamerCtorFn(Ctx, OS, isVerboseAsm, useLoc, useCFI, - InstPrint, CE, TAB, ShowInst); + InstPrint, CE, TAB, ShowInst, DecodeLSDA); } /// @} diff --git a/lib/CodeGen/LLVMTargetMachine.cpp b/lib/CodeGen/LLVMTargetMachine.cpp index b98fbed695b..4ed959e120e 100644 --- a/lib/CodeGen/LLVMTargetMachine.cpp +++ b/lib/CodeGen/LLVMTargetMachine.cpp @@ -72,6 +72,8 @@ static cl::opt ShowMCEncoding("show-mc-encoding", cl::Hidden, cl::desc("Show encoding in .s output")); static cl::opt ShowMCInst("show-mc-inst", cl::Hidden, cl::desc("Show instruction structure in .s output")); +static cl::opt DecodeMCLSDA("decode-mc-lsda", cl::Hidden, + cl::desc("Print LSDA in human readable format in .s output")); static cl::opt EnableMCLogging("enable-mc-api-logging", cl::Hidden, cl::desc("Enable MC API logging")); static cl::opt VerifyMachineCode("verify-machineinstrs", cl::Hidden, @@ -152,7 +154,8 @@ bool LLVMTargetMachine::addPassesToEmitFile(PassManagerBase &PM, hasMCUseCFI(), InstPrinter, MCE, TAB, - ShowMCInst); + ShowMCInst, + DecodeMCLSDA); AsmStreamer.reset(S); break; } diff --git a/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index e8b09fcaced..01de450d971 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -19,6 +19,7 @@ #include "llvm/MC/MCSymbol.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" @@ -33,8 +34,10 @@ using namespace llvm; namespace { class MCAsmStreamer : public MCStreamer { +protected: formatted_raw_ostream &OS; const MCAsmInfo &MAI; +private: OwningPtr InstPrinter; OwningPtr Emitter; OwningPtr AsmBackend; @@ -1242,12 +1245,371 @@ void MCAsmStreamer::Finish() { EmitFrames(false); } +//===----------------------------------------------------------------------===// +/// MCLSDADecoderAsmStreamer - This is identical to the MCAsmStreamer, but +/// outputs a description of the LSDA in a human readable format. +/// +namespace { + +class MCLSDADecoderAsmStreamer : public MCAsmStreamer { + const MCSymbol *PersonalitySymbol; + const MCSymbol *LSDASymbol; + bool InLSDA; + bool ReadingULEB128; + + uint64_t BytesRead; + uint64_t ActionTableBytes; + uint64_t LSDASize; + + SmallVector ULEB128Value; + std::vector LSDAEncoding; + std::vector Assignments; + + /// GetULEB128Value - A helper function to convert the value in the + /// ULEB128Value vector into a uint64_t. + uint64_t GetULEB128Value(SmallVectorImpl &ULEB128Value) { + uint64_t Val = 0; + for (unsigned i = 0, e = ULEB128Value.size(); i != e; ++i) + Val |= (ULEB128Value[i] & 0x7F) << (7 * i); + return Val; + } + + /// ResetState - Reset the state variables. + void ResetState() { + PersonalitySymbol = 0; + LSDASymbol = 0; + LSDASize = 0; + BytesRead = 0; + ActionTableBytes = 0; + InLSDA = false; + ReadingULEB128 = false; + ULEB128Value.clear(); + LSDAEncoding.clear(); + Assignments.clear(); + } + + void EmitEHTableDescription(); + + const char *DecodeDWARFEncoding(unsigned Encoding) { + switch (Encoding) { + case dwarf::DW_EH_PE_absptr: return "absptr"; + case dwarf::DW_EH_PE_omit: return "omit"; + case dwarf::DW_EH_PE_pcrel: return "pcrel"; + case dwarf::DW_EH_PE_udata4: return "udata4"; + case dwarf::DW_EH_PE_udata8: return "udata8"; + case dwarf::DW_EH_PE_sdata4: return "sdata4"; + case dwarf::DW_EH_PE_sdata8: return "sdata8"; + case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata4: return "pcrel udata4"; + case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata4: return "pcrel sdata4"; + case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata8: return "pcrel udata8"; + case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata8: return "pcrel sdata8"; + case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata4: + return "indirect pcrel udata4"; + case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata4: + return "indirect pcrel sdata4"; + case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata8: + return "indirect pcrel udata8"; + case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata8: + return "indirect pcrel sdata8"; + } + + return ""; + } +public: + MCLSDADecoderAsmStreamer(MCContext &Context, formatted_raw_ostream &os, + bool isVerboseAsm, bool useLoc, bool useCFI, + MCInstPrinter *printer, MCCodeEmitter *emitter, + TargetAsmBackend *asmbackend, + bool showInst) + : MCAsmStreamer(Context, os, isVerboseAsm, useLoc, useCFI, + printer, emitter, asmbackend, showInst) { + ResetState(); + } + ~MCLSDADecoderAsmStreamer() {} + + virtual void Finish() { + ResetState(); + MCAsmStreamer::Finish(); + } + + virtual void EmitLabel(MCSymbol *Symbol) { + if (Symbol == LSDASymbol) + InLSDA = true; + MCAsmStreamer::EmitLabel(Symbol); + } + virtual void EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) { + if (InLSDA) + Assignments.push_back(Value); + MCAsmStreamer::EmitAssignment(Symbol, Value); + } + virtual void EmitIntValue(uint64_t Value, unsigned Size, + unsigned AddrSpace = 0); + virtual void EmitValueImpl(const MCExpr *Value, unsigned Size, + unsigned AddrSpace); + virtual void EmitFill(uint64_t NumBytes, uint8_t FillValue, + unsigned AddrSpace); + virtual void EmitCFIPersonality(const MCSymbol *Sym, unsigned Encoding) { + PersonalitySymbol = Sym; + MCAsmStreamer::EmitCFIPersonality(Sym, Encoding); + } + virtual void EmitCFILsda(const MCSymbol *Sym, unsigned Encoding) { + LSDASymbol = Sym; + MCAsmStreamer::EmitCFILsda(Sym, Encoding); + } +}; + +} // end anonymous namespace + +void MCLSDADecoderAsmStreamer::EmitIntValue(uint64_t Value, unsigned Size, + unsigned AddrSpace) { + if (!InLSDA) + return MCAsmStreamer::EmitIntValue(Value, Size, AddrSpace); + + BytesRead += Size; + + // We place the LSDA into the LSDAEncoding vector for later analysis. If we + // have a ULEB128, we read that in separate iterations through here and then + // get its value. + if (!ReadingULEB128) { + LSDAEncoding.push_back(Value); + int EncodingSize = LSDAEncoding.size(); + + if (EncodingSize == 1 || EncodingSize == 3) { + // The LPStart and TType encodings. + if (Value != dwarf::DW_EH_PE_omit) { + // The encoding is next and is a ULEB128 value. + ReadingULEB128 = true; + ULEB128Value.clear(); + } else { + // The encoding was omitted. Put a 0 here as a placeholder. + LSDAEncoding.push_back(0); + } + } else if (EncodingSize == 5) { + // The next value is a ULEB128 value that tells us how long the call site + // table is -- where the start of the action tab + ReadingULEB128 = true; + ULEB128Value.clear(); + } + + InLSDA = (LSDASize == 0 || BytesRead < LSDASize); + } else { + // We're reading a ULEB128. Make it so! + assert(Size == 1 && "Non-byte representation of a ULEB128?"); + ULEB128Value.push_back(Value); + + if ((Value & 0x80) == 0) { + uint64_t Val = GetULEB128Value(ULEB128Value); + LSDAEncoding.push_back(Val); + ULEB128Value.clear(); + ReadingULEB128 = false; + + if (LSDAEncoding.size() == 4) + // The fourth value tells us where the bottom of the type table is. + LSDASize = BytesRead + LSDAEncoding[3]; + else if (LSDAEncoding.size() == 6) + // The sixth value tells us where the start of the action table is. + ActionTableBytes = BytesRead; + } + } + + MCAsmStreamer::EmitValueImpl(MCConstantExpr::Create(Value, getContext()), + Size, AddrSpace); + + if (LSDASize != 0 && !InLSDA) + EmitEHTableDescription(); +} + +void MCLSDADecoderAsmStreamer::EmitValueImpl(const MCExpr *Value, + unsigned Size, + unsigned AddrSpace) { + if (InLSDA && LSDASize != 0) { + assert(BytesRead + Size <= LSDASize && "EH table too small!"); + + if (BytesRead > uint64_t(LSDAEncoding[5]) + ActionTableBytes) + // Insert the type values. + Assignments.push_back(Value); + + LSDAEncoding.push_back(Assignments.size()); + BytesRead += Size; + InLSDA = (LSDASize == 0 || BytesRead < LSDASize); + } + + MCAsmStreamer::EmitValueImpl(Value, Size, AddrSpace); + + if (LSDASize != 0 && !InLSDA) + EmitEHTableDescription(); +} + +void MCLSDADecoderAsmStreamer::EmitFill(uint64_t NumBytes, uint8_t FillValue, + unsigned AddrSpace) { + if (InLSDA && ReadingULEB128) { + for (uint64_t I = NumBytes; I != 0; --I) + ULEB128Value.push_back(FillValue); + + BytesRead += NumBytes; + + if ((FillValue & 0x80) == 0) { + uint64_t Val = GetULEB128Value(ULEB128Value); + LSDAEncoding.push_back(Val); + ULEB128Value.clear(); + ReadingULEB128 = false; + + if (LSDAEncoding.size() == 4) + // The fourth value tells us where the bottom of the type table is. + LSDASize = BytesRead + LSDAEncoding[3]; + else if (LSDAEncoding.size() == 6) + // The sixth value tells us where the start of the action table is. + ActionTableBytes = BytesRead; + } + } + + MCAsmStreamer::EmitFill(NumBytes, FillValue, AddrSpace); +} + +/// EmitEHTableDescription - Emit a human readable version of the LSDA. +void MCLSDADecoderAsmStreamer::EmitEHTableDescription() { + assert(LSDAEncoding.size() > 6 && "Invalid LSDA!"); + + // Emit header information. + StringRef C = MAI.getCommentString(); +#define CMT OS << C << ' ' + CMT << "Exception Handling Table: " << LSDASymbol->getName() << "\n"; + CMT << " @LPStart Encoding: " << DecodeDWARFEncoding(LSDAEncoding[0]) << "\n"; + if (LSDAEncoding[1]) + CMT << "@LPStart: 0x" << LSDAEncoding[1] << "\n"; + CMT << " @TType Encoding: " << DecodeDWARFEncoding(LSDAEncoding[2]) << "\n"; + CMT << " @TType Base: " << LSDAEncoding[3] << " bytes\n"; + CMT << "@CallSite Encoding: " << DecodeDWARFEncoding(LSDAEncoding[4]) << "\n"; + CMT << "@Action Table Size: " << LSDAEncoding[5] << " bytes\n\n"; + + bool isSjLjEH = (MAI.getExceptionHandlingType() == ExceptionHandling::ARM); + + int64_t CallSiteTableSize = LSDAEncoding[5]; + unsigned CallSiteEntrySize; + if (!isSjLjEH) + CallSiteEntrySize = 4 + // Region start. + 4 + // Region end. + 4 + // Landing pad. + 1; // TType index. + else + CallSiteEntrySize = 1 + // Call index. + 1 + // Landing pad. + 1; // TType index. + + unsigned NumEntries = CallSiteTableSize / CallSiteEntrySize; + assert(CallSiteTableSize % CallSiteEntrySize == 0 && + "The action table size is not a multiple of what it should be!"); + unsigned TTypeIdx = 5 + // Action table size index. + (isSjLjEH ? 3 : 4) * NumEntries + // Action table entries. + 1; // Just because. + + // Emit the action table. + unsigned Action = 1; + for (unsigned I = 6; I < TTypeIdx; ) { + CMT << "Action " << Action++ << ":\n"; + + // The beginning of the throwing region. + uint64_t Idx = LSDAEncoding[I++]; + + if (!isSjLjEH) { + CMT << " A throw between " + << *cast(Assignments[Idx - 1])->getLHS() << " and "; + + // The end of the throwing region. + Idx = LSDAEncoding[I++]; + OS << *cast(Assignments[Idx - 1])->getLHS(); + } else { + CMT << " A throw from call " << *Assignments[Idx - 1]; + } + + // The landing pad. + Idx = LSDAEncoding[I++]; + if (Idx) { + OS << " jumps to " + << *cast(Assignments[Idx - 1])->getLHS() + << " on an exception.\n"; + } else { + OS << " does not have a landing pad.\n"; + ++I; + continue; + } + + // The index into the action table. + Idx = LSDAEncoding[I++]; + if (!Idx) { + CMT << " :cleanup:\n"; + continue; + } + + // A semi-graphical representation of what the different indexes are in the + // loop below. + // + // Idx - Index into the action table. + // Action - Index into the type table from the type table base. + // Next - Offset from Idx to the next action type. + // + // Idx---. + // | + // v + // [call site table] _1 _2 _3 + // TTypeIdx--> ......................... + // [action 1] _1 _2 + // [action 2] _1 _2 + // ... + // [action n] _1 _2 + // [type m] ^ + // ... | + // [type 1] `---Next + // + + int Action = LSDAEncoding[TTypeIdx + Idx - 1]; + if ((Action & 0x40) != 0) + // Ignore exception specifications. + continue; + + // Emit the types that are caught by this exception. + CMT << " For type(s): "; + for (;;) { + if ((Action & 0x40) != 0) + // Ignore exception specifications. + break; + + if (uint64_t Ty = LSDAEncoding[LSDAEncoding.size() - Action]) { + OS << " " << *Assignments[Ty - 1]; + + // Types can be chained together. Typically, it's a negative offset from + // the current type to a different one in the type table. + int Next = LSDAEncoding[TTypeIdx + Idx]; + if (Next == 0) + break; + if ((Next & 0x40) != 0) + Next = (int)(signed char)(Next | 0x80); + Idx += Next + 1; + Action = LSDAEncoding[TTypeIdx + Idx - 1]; + continue; + } else { + OS << " :catchall:"; + } + break; + } + + OS << "\n"; + } + + OS << "\n"; + ResetState(); +} + MCStreamer *llvm::createAsmStreamer(MCContext &Context, formatted_raw_ostream &OS, bool isVerboseAsm, bool useLoc, - bool useCFI, - MCInstPrinter *IP, MCCodeEmitter *CE, - TargetAsmBackend *TAB, bool ShowInst) { + bool useCFI, MCInstPrinter *IP, + MCCodeEmitter *CE, TargetAsmBackend *TAB, + bool ShowInst, bool DecodeLSDA) { + if (DecodeLSDA) + return new MCLSDADecoderAsmStreamer(Context, OS, isVerboseAsm, useLoc, + useCFI, IP, CE, TAB, ShowInst); + return new MCAsmStreamer(Context, OS, isVerboseAsm, useLoc, useCFI, IP, CE, TAB, ShowInst); } diff --git a/lib/Target/PTX/PTXMCAsmStreamer.cpp b/lib/Target/PTX/PTXMCAsmStreamer.cpp index 1574670b6e9..bc37b6a1fb2 100644 --- a/lib/Target/PTX/PTXMCAsmStreamer.cpp +++ b/lib/Target/PTX/PTXMCAsmStreamer.cpp @@ -533,7 +533,8 @@ namespace llvm { bool isVerboseAsm, bool useLoc, bool useCFI, MCInstPrinter *IP, MCCodeEmitter *CE, TargetAsmBackend *TAB, - bool ShowInst) { + bool ShowInst, + bool /*DecodeLSDA*/) { return new PTXMCAsmStreamer(Context, OS, isVerboseAsm, useLoc, IP, CE, ShowInst); } diff --git a/lib/Target/PTX/PTXTargetMachine.cpp b/lib/Target/PTX/PTXTargetMachine.cpp index 1b737c9d863..31ed09c3e9c 100644 --- a/lib/Target/PTX/PTXTargetMachine.cpp +++ b/lib/Target/PTX/PTXTargetMachine.cpp @@ -27,7 +27,8 @@ namespace llvm { MCInstPrinter *InstPrint, MCCodeEmitter *CE, TargetAsmBackend *TAB, - bool ShowInst); + bool ShowInst, + bool DecodeLSDA); } extern "C" void LLVMInitializePTXTarget() { diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index eb23a1a4671..48e2a61af83 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -61,6 +61,10 @@ static cl::opt ShowInstOperands("show-inst-operands", cl::desc("Show instructions operands as parsed")); +static cl::opt +DecodeLSDA("decode-lsda", + cl::desc("Print LSDA in human readable format")); + static cl::opt OutputAsmVariant("output-asm-variant", cl::desc("Syntax variant to use for output printing")); @@ -97,7 +101,7 @@ IncludeDirs("I", cl::desc("Directory of include files"), static cl::opt ArchName("arch", cl::desc("Target arch to assemble for, " - "see -version for available targets")); + "see -version for available targets")); static cl::opt TripleName("triple", cl::desc("Target triple to assemble for, " @@ -110,12 +114,11 @@ MCPU("mcpu", cl::init("")); static cl::opt -NoInitialTextSection("n", cl::desc( - "Don't assume assembly file starts in the text section")); +NoInitialTextSection("n", cl::desc("Don't assume assembly file starts " + "in the text section")); static cl::opt -SaveTempLabels("L", cl::desc( - "Don't discard temporary labels")); +SaveTempLabels("L", cl::desc("Don't discard temporary labels")); enum ActionType { AC_AsLex, @@ -358,7 +361,8 @@ static int AssembleInput(const char *ProgName) { Str.reset(TheTarget->createAsmStreamer(Ctx, FOS, /*asmverbose*/true, /*useLoc*/ true, /*useCFI*/ true, IP, CE, TAB, - ShowInst)); + ShowInst, + DecodeLSDA)); } else if (FileType == OFT_Null) { Str.reset(createNullStreamer(Ctx)); } else { -- 2.34.1