diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -367,6 +367,10 @@ Add symbol description to disassembly output. +.. option:: --traceback-table + + Decode traceback table for disassembly output. + BUGS ---- diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -451,7 +451,7 @@ bool hasVectorInfo() const; bool hasExtensionTable() const; - uint8_t getNumofGPRsSaved() const; + uint8_t getNumOfGPRsSaved() const; uint8_t getNumberOfFixedParms() const; diff --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp --- a/llvm/lib/Object/XCOFFObjectFile.cpp +++ b/llvm/lib/Object/XCOFFObjectFile.cpp @@ -1029,7 +1029,7 @@ return GETBITWITHMASK(4, HasVectorInfoMask); } -uint8_t XCOFFTracebackTable::getNumofGPRsSaved() const { +uint8_t XCOFFTracebackTable::getNumOfGPRsSaved() const { return GETBITWITHMASKSHIFT(4, GPRSavedMask, GPRSavedShift); } diff --git a/llvm/test/tools/llvm-objdump/XCOFF/Inputs/xcoff-invalid-traceback-table.o b/llvm/test/tools/llvm-objdump/XCOFF/Inputs/xcoff-invalid-traceback-table.o new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ diff --git a/llvm/tools/llvm-objdump/XCOFFDump.h b/llvm/tools/llvm-objdump/XCOFFDump.h --- a/llvm/tools/llvm-objdump/XCOFFDump.h +++ b/llvm/tools/llvm-objdump/XCOFFDump.h @@ -10,6 +10,7 @@ #define LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H #include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/FormattedStream.h" namespace llvm { @@ -28,6 +29,12 @@ Error getXCOFFRelocationValueString(const object::XCOFFObjectFile *Obj, const object::RelocationRef &RelRef, llvm::SmallVectorImpl &Result); + +void formatTracebackTableOutput(ArrayRef Bytes, uint64_t Address, + formatted_raw_ostream &OS); + +void dumpTracebackTable(ArrayRef Bytes, uint64_t Address, + formatted_raw_ostream &OS, uint64_t End); } // namespace objdump } // namespace llvm #endif diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp --- a/llvm/tools/llvm-objdump/XCOFFDump.cpp +++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp @@ -15,9 +15,13 @@ #include "llvm-objdump.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; +using namespace llvm::XCOFF; +using namespace llvm::support; Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj, const RelocationRef &Rel, @@ -86,3 +90,233 @@ return Result; } + +void objdump::formatTracebackTableOutput(ArrayRef Bytes, + uint64_t Address, + formatted_raw_ostream &OS) { + size_t Start = OS.tell(); + if (!NoLeadingAddr) + OS << format("%8" PRIx64 ":", Address); + if (!NoShowRawInsn) { + OS << ' '; + dumpBytes(Bytes, OS); + } + + // The output of printInst starts with a tab. Print some spaces so that + // the tab has 1 column and advances to the target tab stop. + + unsigned TabStop = NoShowRawInsn ? 16 : 40; + unsigned Column = OS.tell() - Start; + OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); +} + +std::string SourceLanguageIdentifier[] = { + "C", "FORTRAN", "Pascal", "ADA", "PL/I", + "BASIC", "LISP", "COBOL", "Modulas2", "C++", + "RPG", "PL8", "Assembler", "Java", "Objective C"}; + +#define PRINTBOOL(Prefix, Field) \ + OS << Prefix << " " << ((TbTable.Field()) ? "+" : "-") << #Field + +#define PRINTGET(Prefix, Field) \ + OS << Prefix << " " << #Field << "=" << (unsigned)(TbTable.get##Field()) + +#define SPLIT \ + OS << "\n"; \ + OS.indent(TabStop) + +void objdump::dumpTracebackTable(ArrayRef Bytes, uint64_t Address, + formatted_raw_ostream &OS, uint64_t End) { + uint64_t Index = 0; + unsigned TabStop = (NoShowRawInsn ? 16 : 40) - 1; + + // Print out backtrace table boundary. + formatTracebackTableOutput(Bytes.slice(Index, 4), Address, OS); + OS << "# Traceback table begin\n"; + Index += 4; + + uint64_t Size = End - Address; + + Expected TTOrErr = + XCOFFTracebackTable::create(Bytes.data() + Index, Size); + + if (!TTOrErr) { + OS << "\tWarning: Parse traceback table failure!(The value in error msg is " + "calculated based on traceback begin) \n"; + OS << "\tError msg: " << toString(TTOrErr.takeError()); + OS << "\n\tThe raw data of traceback table as : \n"; + uint64_t LastNoZero = Index; + + // The value of Size been changed in function XCOFFTracebackTable::create() + Size = End - Address; + + for (uint64_t I = Index; I < Size; I = I + 4) { + if (support::endian::read32be(Bytes.slice(I, 4).data()) != 0) + LastNoZero = (I + 4) > Size ? Size : (I + 4); + } + + if (Size - LastNoZero <= 4) + LastNoZero = Size; + + while (Index < LastNoZero) { + formatTracebackTableOutput(Bytes.slice(Index, 4), Address + Index, OS); + Index += 4; + OS << "\n"; + } + + // Print out all remaining zero as ... + if (Size - LastNoZero >= 8) + OS << "\t\t...\n"; + + return; + } + + XCOFFTracebackTable TbTable = *TTOrErr; + + // Print out the first byte of 8 bytes of mandatory fields. + formatTracebackTableOutput(Bytes.slice(Index, 4), Address + Index, OS); + OS << "# Version = " << (int)TbTable.getVersion(); + Index += 4; + + // Print out the second byte of 8 bytes of mandatory fields. + unsigned LangId = TbTable.getLanguageID(); + OS << "; Language = " + << (LangId < sizeof(SourceLanguageIdentifier) / sizeof(std::string *) + ? SourceLanguageIdentifier[LangId] + : "Unknown") + << "\n"; + + // Print out the third byte of 8 bytes of mandatory fields. + OS.indent(TabStop); + PRINTBOOL(" ;", isGlobalLinkage); + PRINTBOOL(",", isOutOfLineEpilogOrPrologue); + SPLIT; + PRINTBOOL(" ", hasTraceBackTableOffset); + PRINTBOOL(",", isInternalProcedure); + SPLIT; + PRINTBOOL(" ", hasControlledStorage); + PRINTBOOL(",", isTOCless); + SPLIT; + PRINTBOOL(" ", isFloatingPointPresent); + PRINTBOOL(",", isFloatingPointOperationLogOrAbortEnabled); + + // Print out the 4th byte of 8 bytes of mandatory fields. + SPLIT; + PRINTBOOL(" ;", isInterruptHandler); + PRINTBOOL(",", isFuncNamePresent); + PRINTBOOL(",", isAllocaUsed); + SPLIT; + PRINTGET(" ", OnConditionDirective); + PRINTBOOL(",", isCRSaved); + PRINTBOOL(",", isLRSaved); + OS << "\n"; + + formatTracebackTableOutput(Bytes.slice(Index, 4), Address + Index, OS); + Index += 4; + // Print out the 5th byte of 8 bytes of mandatory fields. + PRINTBOOL("#", isBackChainStored); + PRINTBOOL(",", isFixup); + PRINTGET(",", NumOfFPRsSaved); + + // Print out the 6th byte of 8 bytes of mandatory fields. + SPLIT; + PRINTBOOL(" ;", hasExtensionTable); + PRINTBOOL(",", hasVectorInfo); + PRINTGET(",", NumOfGPRsSaved); + + // Print out the 7th byte of 8 bytes of mandatory fields. + SPLIT; + PRINTGET(" ;", NumberOfFixedParms); + + // Print out the 8th byte of 8 bytes of mandatory fields. + PRINTGET(";", NumberOfFPParms); + PRINTBOOL(",", hasParmsOnStack); + +#define PRINTOPTIONAL(Field) \ + if (TbTable.get##Field()) { \ + OS << "\n"; \ + formatTracebackTableOutput(Bytes.slice(Index, 4), Address + Index, OS); \ + Index += 4; \ + OS << "# " << #Field << "=" << TbTable.get##Field().getValue(); \ + } + + PRINTOPTIONAL(ParmsType); + PRINTOPTIONAL(TraceBackTableOffset); + PRINTOPTIONAL(HandlerMask); + + PRINTOPTIONAL(NumOfCtlAnchors); + + if (TbTable.getControlledStorageInfoDisp()) { + SmallVector Disp = + TbTable.getControlledStorageInfoDisp().getValue(); + for (unsigned I = 0; I < Disp.size(); ++I) { + OS << "\n"; + formatTracebackTableOutput(Bytes.slice(Index, 4), Address + Index, OS); + Index += 4; + OS << " ControlledStorageInfoDisp[" << I << "]=" << Disp[I]; + } + } + + uint8_t RemainingBytes = 0; + + // Print out function name length and function name if there is. + if (TbTable.isFuncNamePresent()) { + uint16_t Len = 0; + bool HasPrinted = false; + uint16_t FunctionNameLen = TbTable.getFunctionName().getValue().size(); + + assert(FunctionNameLen > 0 && + "The length of function name must large than zero."); + + // Function name occupy two bytes. + while (Len < FunctionNameLen + 2) { + OS << "\n"; + formatTracebackTableOutput(Bytes.slice(Index + Len, 4), + Address + Index + Len, OS); + Len += 4; + + if (!HasPrinted) { + OS << "# FunctionNameLen =" << FunctionNameLen; + OS << "; FunctionName =" << TbTable.getFunctionName().getValue(); + HasPrinted = true; + } + } + + Index += Len; + RemainingBytes = Len - FunctionNameLen - 2; + } + + if (TbTable.isAllocaUsed()) { + if (RemainingBytes == 0) { + OS << "\n"; + formatTracebackTableOutput(Bytes.slice(Index, 5), Address + Index, OS); + Index += 4; + RemainingBytes = 4; + OS << "# "; + } else + OS << " ;"; + + OS << "AllocaRegister =" + << (unsigned int)TbTable.getAllocaRegister().getValue(); + --RemainingBytes; + } + + if (RemainingBytes != 0) + OS << " ; Padding"; + + OS << "\n"; + if (End == Address + Index) + return; + + // Print out all padding. + if (End - Address + Index >= 8) + OS << "\t\t...\n"; + else { + formatTracebackTableOutput(Bytes.slice(Index, 4), Address + Index, OS); + OS << "# Padding\n"; + } +} +#undef PRINTBOOL +#undef PRINTGET +#undef SPLIT +#undef PRINTOPTIONAL diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -45,6 +45,7 @@ extern cl::opt SectionHeaders; extern cl::opt SectionContents; extern cl::opt SymbolDescription; +extern cl::opt TracebackTable; extern cl::opt SymbolTable; extern cl::opt TripleName; extern cl::opt UnwindInfo; diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -142,6 +142,12 @@ "option is for XCOFF files only"), cl::init(false), cl::cat(ObjdumpCat)); +cl::opt objdump::TracebackTable( + "traceback-table", + cl::desc("Decode traceback table for disassembly. This " + "option is for XCOFF files only"), + cl::init(false), cl::cat(ObjdumpCat)); + static cl::list DisassembleSymbols("disassemble-symbols", cl::CommaSeparated, cl::desc("List of symbols to disassemble. " @@ -1559,7 +1565,7 @@ const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName); const StringRef Name = unwrapOrError(Symbol.getName(), FileName); - if (Obj->isXCOFF() && SymbolDescription) { + if (Obj->isXCOFF() && (SymbolDescription || TracebackTable)) { const auto *XCOFFObj = cast(Obj); DataRefImpl SymbolDRI = Symbol.getRawDataRefImpl(); @@ -1577,7 +1583,7 @@ static SymbolInfoTy createDummySymbolInfo(const ObjectFile *Obj, const uint64_t Addr, StringRef &Name, uint8_t Type) { - if (Obj->isXCOFF() && SymbolDescription) + if (Obj->isXCOFF() && (SymbolDescription || TracebackTable)) return SymbolInfoTy(Addr, Name, None, None, false); else return SymbolInfoTy(Addr, Name, Type); @@ -1939,6 +1945,12 @@ Symbols[SI].Type != ELF::STT_OBJECT && !DisassembleAll; bool DumpARMELFData = false; + bool DumpTracebackTableForXCOFFFunction = + Obj->isXCOFF() && Section.isText() && TracebackTable && + Symbols[SI].XCOFFSymInfo.StorageMappingClass && + (Symbols[SI].XCOFFSymInfo.StorageMappingClass.getValue() == + XCOFF::XMC_PR); + formatted_raw_ostream FOS(outs()); std::unordered_map AllLabels; @@ -1986,6 +1998,14 @@ } } + if (DumpTracebackTableForXCOFFFunction && + doesXCOFFTracebackTableBegin(Bytes.slice(Index, 4))) { + dumpTracebackTable(Bytes.slice(Index), + SectionAddr + Index + VMAAdjustment, FOS, End); + Index = End; + continue; + } + // Print local label if there's any. auto Iter = AllLabels.find(SectionAddr + Index); if (Iter != AllLabels.end())