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 @@ -435,6 +435,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 @@ -835,17 +835,20 @@ class XCOFFTracebackTable { const uint8_t *const TBPtr; + bool Is64BitObj; Optional> ParmsType; Optional TraceBackTableOffset; Optional HandlerMask; Optional NumOfCtlAnchors; - Optional> ControlledStorageInfoDisp; + Optional> ControlledStorageInfoDisp; Optional FunctionName; Optional AllocaRegister; Optional VecExt; Optional ExtensionTable; + Optional EhInfoDisp; - XCOFFTracebackTable(const uint8_t *Ptr, uint64_t &Size, Error &Err); + XCOFFTracebackTable(const uint8_t *Ptr, uint64_t &Size, Error &Err, + bool Is64Bit = false); public: /// Parse an XCOFF Traceback Table from \a Ptr with \a Size bytes. @@ -861,8 +864,8 @@ /// If the XCOFF Traceback Table is not parsed successfully or there are /// extra bytes that are not recognized, \a Size will be updated to be the /// size up to the end of the last successfully parsed field of the table. - static Expected create(const uint8_t *Ptr, - uint64_t &Size); + static Expected + create(const uint8_t *Ptr, uint64_t &Size, bool Is64Bits = false); uint8_t getVersion() const; uint8_t getLanguageID() const; @@ -901,13 +904,14 @@ } const Optional &getHandlerMask() const { return HandlerMask; } const Optional &getNumOfCtlAnchors() { return NumOfCtlAnchors; } - const Optional> &getControlledStorageInfoDisp() { + const Optional> &getControlledStorageInfoDisp() { return ControlledStorageInfoDisp; } const Optional &getFunctionName() const { return FunctionName; } const Optional &getAllocaRegister() const { return AllocaRegister; } const Optional &getVectorExt() const { return VecExt; } const Optional &getExtensionTable() const { return ExtensionTable; } + const Optional &getEhInfoDisp() const { return EhInfoDisp; } }; bool doesXCOFFTracebackTableBegin(ArrayRef Bytes); diff --git a/llvm/lib/BinaryFormat/XCOFF.cpp b/llvm/lib/BinaryFormat/XCOFF.cpp --- a/llvm/lib/BinaryFormat/XCOFF.cpp +++ b/llvm/lib/BinaryFormat/XCOFF.cpp @@ -176,7 +176,7 @@ Res += "TB_LONGTBTABLE2 "; // Two of the bits that haven't got used in the mask. - if (Flag & 0x06) + if (Flag & 0x06 || Flag == 0x0) Res += "Unknown "; // Pop the last space. 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 @@ -1394,18 +1394,18 @@ #undef GETVALUEWITHMASK #undef GETVALUEWITHMASKSHIFT -Expected XCOFFTracebackTable::create(const uint8_t *Ptr, - uint64_t &Size) { +Expected +XCOFFTracebackTable::create(const uint8_t *Ptr, uint64_t &Size, bool Is64Bit) { Error Err = Error::success(); - XCOFFTracebackTable TBT(Ptr, Size, Err); + XCOFFTracebackTable TBT(Ptr, Size, Err, Is64Bit); if (Err) return std::move(Err); return TBT; } XCOFFTracebackTable::XCOFFTracebackTable(const uint8_t *Ptr, uint64_t &Size, - Error &Err) - : TBPtr(Ptr) { + Error &Err, bool Is64Bit) + : TBPtr(Ptr), Is64BitObj(Is64Bit) { ErrorAsOutParameter EAO(&Err); DataExtractor DE(ArrayRef(Ptr, Size), /*IsLittleEndian=*/false, /*AddressSize=*/0); @@ -1431,10 +1431,10 @@ if (Cur && hasControlledStorage()) { NumOfCtlAnchors = DE.getU32(Cur); if (Cur && NumOfCtlAnchors) { - SmallVector Disp; - Disp.reserve(*NumOfCtlAnchors); + SmallVector Disp; + Disp.reserve(NumOfCtlAnchors.value()); for (uint32_t I = 0; I < NumOfCtlAnchors && Cur; ++I) - Disp.push_back(DE.getU32(Cur)); + Disp.push_back(Is64BitObj ? DE.getU64(Cur) : DE.getU32(Cur)); if (Cur) ControlledStorageInfoDisp = std::move(Disp); } @@ -1460,6 +1460,8 @@ } VecExt = TBVecExtOrErr.get(); VectorParmsNum = VecExt->getNumberOfVectorParms(); + // Skip two bytes of padding after vector info. + DE.skip(Cur, 2); } } @@ -1480,9 +1482,15 @@ ParmsType = ParmsTypeOrError.get(); } - if (Cur && hasExtensionTable()) + if (Cur && hasExtensionTable()) { ExtensionTable = DE.getU8(Cur); + if (ExtensionTable.value() & ExtendedTBTableFlag::TB_EH_INFO) { + // eh_info displacement must be 4-byte aligned. + Cur.seek(alignTo(Cur.tell(), 4)); + EhInfoDisp = Is64BitObj ? DE.getU64(Cur) : DE.getU32(Cur); + } + } if (!Cur) Err = Cur.takeError(); diff --git a/llvm/test/CodeGen/PowerPC/aix-emit-tracebacktable-clobber-register.ll b/llvm/test/CodeGen/PowerPC/aix-emit-tracebacktable-clobber-register.ll --- a/llvm/test/CodeGen/PowerPC/aix-emit-tracebacktable-clobber-register.ll +++ b/llvm/test/CodeGen/PowerPC/aix-emit-tracebacktable-clobber-register.ll @@ -6,6 +6,16 @@ ; RUN: -mcpu=pwr4 -mattr=+altivec -vec-extabi < %s | \ ; RUN: FileCheck --check-prefixes=CHECK-FUNC,COMMON %s +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=+altivec -vec-extabi -xcoff-traceback-table=true -filetype=obj -o %t.o < %s +; RUN: llvm-objdump -D --traceback-table --symbol-description %t.o | \ +; RUN: FileCheck --check-prefixes=OBJ-DIS,NO-FUNC-SEC %s + +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=+altivec -vec-extabi -xcoff-traceback-table=true -function-sections -filetype=obj -o %t_func.o < %s +; RUN: llvm-objdump -D --traceback-table --symbol-description %t_func.o | \ +; RUN: FileCheck --check-prefixes=OBJ-DIS,FUNC-SEC %s + define float @bar() #0 { entry: %fvalue = alloca float, align 4 @@ -95,3 +105,49 @@ ; COMMON: .toc ; COMMON: L..C2: ; COMMON-NEXT: .tc __ehinfo.1[TC],__ehinfo.1 + + +; OBJ-DIS: 9c: 00 00 00 00 # Traceback table begin +; OBJ-DIS-NEXT: a0: 00 # Version = 0 +; OBJ-DIS-NEXT: a1: 09 # Language = CPlusPlus +; OBJ-DIS-NEXT: a2: 22 # -isGlobalLinkage, -isOutOfLineEpilogOrPrologue +; OBJ-DIS-NEXT: +hasTraceBackTableOffset, -isInternalProcedure +; OBJ-DIS-NEXT: -hasControlledStorage, -isTOCless +; OBJ-DIS-NEXT: +isFloatingPointPresent +; OBJ-DIS-NEXT: -isFloatingPointOperationLogOrAbortEnabled +; OBJ-DIS-NEXT: a3: 40 # -isInterruptHandler, +isFuncNamePresent, -isAllocaUsed +; OBJ-DIS-NEXT: OnConditionDirective = 0, -isCRSaved, -isLRSaved +; OBJ-DIS-NEXT: a4: 85 # +isBackChainStored, -isFixup, NumOfFPRsSaved = 5 +; OBJ-DIS-NEXT: a5: 04 # -hasExtensionTable, -hasVectorInfo, NumOfGPRsSaved = 4 +; OBJ-DIS-NEXT: a6: 00 # NumberOfFixedParms = 0 +; OBJ-DIS-NEXT: a7: 01 # NumberOfFPParms = 0, +hasParmsOnStack +; OBJ-DIS-NEXT: a8: 00 00 00 9c # TraceBackTableOffset = 156 +; OBJ-DIS-NEXT: ac: 00 03 # FunctionNameLen = 3 +; OBJ-DIS-NEXT: ae: 62 61 72 # FunctionName = bar +; NO-FUNC-SEC-NEXT: b1: 60 00 00 # Paddings +; FUNC-SEC-NEXT: ... + +; OBJ-DIS: 11c: 00 00 00 00 # Traceback table begin +; OBJ-DIS-NEXT: 120: 00 # Version = 0 +; OBJ-DIS-NEXT: 121: 09 # Language = CPlusPlus +; OBJ-DIS-NEXT: 122: 20 # -isGlobalLinkage, -isOutOfLineEpilogOrPrologue +; OBJ-DIS-NEXT: +hasTraceBackTableOffset, -isInternalProcedure +; OBJ-DIS-NEXT: -hasControlledStorage, -isTOCless +; OBJ-DIS-NEXT: -isFloatingPointPresent +; OBJ-DIS-NEXT: -isFloatingPointOperationLogOrAbortEnabled +; OBJ-DIS-NEXT: 123: 40 # -isInterruptHandler, +isFuncNamePresent, -isAllocaUsed +; OBJ-DIS-NEXT: OnConditionDirective = 0, -isCRSaved, -isLRSaved +; OBJ-DIS-NEXT: 124: 80 # +isBackChainStored, -isFixup, NumOfFPRsSaved = 0 +; OBJ-DIS-NEXT: 125: c0 # +hasExtensionTable, +hasVectorInfo, NumOfGPRsSaved = 0 +; OBJ-DIS-NEXT: 126: 00 # NumberOfFixedParms = 0 +; OBJ-DIS-NEXT: 127: 01 # NumberOfFPParms = 0, +hasParmsOnStack +; OBJ-DIS-NEXT: 128: 00 00 00 5c # TraceBackTableOffset = 92 +; OBJ-DIS-NEXT: 12c: 00 04 # FunctionNameLen = 4 +; OBJ-DIS-NEXT: 12e: 66 6f 6f 76 # FunctionName = foov +; OBJ-DIS-NEXT: 132: 12 # NumberOfVRSaved = 4, +isVRSavedOnStack, -hasVarArgs +; OBJ-DIS-NEXT: 133: 01 # NumberOfVectorParms = 0, +hasVMXInstruction +; OBJ-DIS-NEXT: 134: 00 00 00 00 # VectorParmsInfoString = +; OBJ-DIS-NEXT: 138: 00 00 # Padding +; OBJ-DIS-NEXT: 13a: 08 # ExtensionTable = TB_EH_INFO +; OBJ-DIS-NEXT: 13b: 00 # Alignment padding for except info displacement +; OBJ-DIS-NEXT: 13c: 00 00 00 08 # Except info displacement diff --git a/llvm/test/tools/llvm-objdump/XCOFF/disassemble-invalid-traceback-table.test b/llvm/test/tools/llvm-objdump/XCOFF/disassemble-invalid-traceback-table.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/XCOFF/disassemble-invalid-traceback-table.test @@ -0,0 +1,36 @@ +## Test that parsing of an invalid XCOFF traceback table having a too +## big function name length causes an out of range error. + +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-objdump -D --traceback-table --symbol-description %t.o 2>&1 | \ +# RUN: FileCheck --implicit-check-not warning: --check-prefixes=WARN %s + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + SectionData: "4e8000200000000000002240800102075a000000000000880073666f6f0000000000000000000000" + +Symbols: + - Name: .AddNum + Value: 0 + Section: .text + Type: 0x20 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_PR + +# WARN: {{.*}}: warning: '{{.*}}': failure parsing traceback table with address: 0x4 +# WARN-NEXT: >>> unexpected end of data at offset 0x24 while reading [0x12, 0x85) +# WARN-NEXT: >>> Raw traceback table data is: +# WARN-NEXT: 8: 00 00 22 40 +# WARN-NEXT: c: 80 01 02 07 +# WARN-NEXT: 10: 5a 00 00 00 +# WARN-NEXT: 14: 00 00 00 88 +# WARN-NEXT: 18: 00 73 66 6f +# WARN-NEXT: 1c: 6f 00 00 00 +# WARN-NEXT: ... diff --git a/llvm/test/tools/llvm-objdump/XCOFF/disassemble-traceback-table.test b/llvm/test/tools/llvm-objdump/XCOFF/disassemble-traceback-table.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/XCOFF/disassemble-traceback-table.test @@ -0,0 +1,90 @@ +## Test that "llvm-objdump --traceback-table" decodes the ControlledStorageInfo, +## AllocaRegister, and extension table of the traceback table which cannot +## currently be generated by llc. + +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-objdump -D --traceback-table --symbol-description %t.o | \ +# RUN: FileCheck %s + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + SectionData: "9421ffc0000000000000204080000201000000000000000400064164644e756d0000000093e1fffc0000000000002a6080c202072c90000000000004000000036f0000010000000a000001000003666f6f1f0203c000000000002000" + +Symbols: + - Name: .AddNum + Value: 0 + Section: .text + Type: 0x20 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_PR + - Name: .foo + Value: 0x24 + Section: .text + Type: 0x20 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_PR + +# CHECK: 00000000 (idx: 0) .AddNum: +# CHECK-NEXT: 0: 94 21 ff c0 stwu 1, -64(1) +# CHECK-NEXT: 4: 00 00 00 00 # Traceback table begin +# CHECK-NEXT: 8: 00 # Version = 0 +# CHECK-NEXT: 9: 00 # Language = C +# CHECK-NEXT: a: 20 # -isGlobalLinkage, -isOutOfLineEpilogOrPrologue +# CHECK-NEXT: +hasTraceBackTableOffset, -isInternalProcedure +# CHECK-NEXT: -hasControlledStorage, -isTOCless +# CHECK-NEXT: -isFloatingPointPresent +# CHECK-NEXT: -isFloatingPointOperationLogOrAbortEnabled +# CHECK-NEXT: b: 40 # -isInterruptHandler, +isFuncNamePresent, -isAllocaUsed +# CHECK-NEXT: OnConditionDirective = 0, -isCRSaved, -isLRSaved +# CHECK-NEXT: c: 80 # +isBackChainStored, -isFixup, NumOfFPRsSaved = 0 +# CHECK-NEXT: d: 00 # -hasExtensionTable, -hasVectorInfo, NumOfGPRsSaved = 0 +# CHECK-NEXT: e: 02 # NumberOfFixedParms = 2 +# CHECK-NEXT: f: 01 # NumberOfFPParms = 0, +hasParmsOnStack +# CHECK-NEXT: 10: 00 00 00 00 # ParmsType = i, i +# CHECK-NEXT: 14: 00 00 00 04 # TraceBackTableOffset = 4 +# CHECK-NEXT: 18: 00 06 # FunctionNameLen = 6 +# CHECK-NEXT: 1a: 41 64 64 4e # FunctionName = AddNum +# CHECK-NEXT: 1e: 75 6d +# CHECK-NEXT: 20: 00 00 00 00 # Paddings + +# CHECK: 00000024 (idx: 2) .foo: +# CHECK-NEXT: 24: 93 e1 ff fc stw 31, -4(1) +# CHECK-NEXT: 28: 00 00 00 00 # Traceback table begin +# CHECK-NEXT: 2c: 00 # Version = 0 +# CHECK-NEXT: 2d: 00 # Language = C +# CHECK-NEXT: 2e: 2a # -isGlobalLinkage, -isOutOfLineEpilogOrPrologue +# CHECK-NEXT: +hasTraceBackTableOffset, -isInternalProcedure +# CHECK-NEXT: +hasControlledStorage, -isTOCless +# CHECK-NEXT: +isFloatingPointPresent +# CHECK-NEXT: -isFloatingPointOperationLogOrAbortEnabled +# CHECK-NEXT: 2f: 60 # -isInterruptHandler, +isFuncNamePresent, +isAllocaUsed +# CHECK-NEXT: OnConditionDirective = 0, -isCRSaved, -isLRSaved +# CHECK-NEXT: 30: 80 # +isBackChainStored, -isFixup, NumOfFPRsSaved = 0 +# CHECK-NEXT: 31: c2 # +hasExtensionTable, +hasVectorInfo, NumOfGPRsSaved = 2 +# CHECK-NEXT: 32: 02 # NumberOfFixedParms = 2 +# CHECK-NEXT: 33: 07 # NumberOfFPParms = 3, +hasParmsOnStack +# CHECK-NEXT: 34: 2c 90 00 00 # ParmsType = i, f, d, i, f, v +# CHECK-NEXT: 38: 00 00 00 04 # TraceBackTableOffset = 4 +# CHECK-NEXT: 3c: 00 00 00 03 # NumOfCtlAnchors = 3 +# CHECK-NEXT: 40: 6f 00 00 01 ControlledStorageInfoDisp[0] = 1862270977 +# CHECK-NEXT: 44: 00 00 00 0a ControlledStorageInfoDisp[1] = 10 +# CHECK-NEXT: 48: 00 00 01 00 ControlledStorageInfoDisp[2] = 256 +# CHECK-NEXT: 4c: 00 03 # FunctionNameLen = 3 +# CHECK-NEXT: 4e: 66 6f 6f # FunctionName = foo +# CHECK-NEXT: 51: 1f # AllocaRegister = 31 +# CHECK-NEXT: 52: 02 # NumberOfVRSaved = 0, +isVRSavedOnStack, -hasVarArgs +# CHECK-NEXT: 53: 03 # NumberOfVectorParms = 1, +hasVMXInstruction +# CHECK-NEXT: 54: c0 00 00 00 # VectorParmsInfoString = vf +# CHECK-NEXT: 58: 00 00 # Padding +# CHECK-NEXT: 5a: 20 # ExtensionTable = TB_SSP_CANARY +# CHECK-NEXT: 5b: 00 # Paddings diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -65,7 +65,11 @@ def symbol_description : Flag<["--"], "symbol-description">, HelpText<"Add symbol description for disassembly. This " - "option is for XCOFF files only.">; + "option is for XCOFF files only">; + +def traceback_table : Flag<["--"], "traceback-table">, + HelpText<"Decode traceback table for disassembly. This " + "option is for XCOFF files only">; def disassemble_symbols_EQ : Joined<["--"], "disassemble-symbols=">, HelpText<"List of symbols to disassemble. " diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp --- a/llvm/tools/llvm-objdump/SourcePrinter.cpp +++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -26,10 +26,6 @@ namespace llvm { namespace objdump { -unsigned getInstStartColumn(const MCSubtargetInfo &STI) { - return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; -} - bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) { if (LocExpr.Range == None) return false; 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 @@ -9,7 +9,9 @@ #ifndef LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H #define LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/FormattedStream.h" namespace llvm { @@ -32,6 +34,11 @@ Error getXCOFFRelocationValueString(const object::XCOFFObjectFile &Obj, const object::RelocationRef &RelRef, llvm::SmallVectorImpl &Result); + +void dumpTracebackTable(ArrayRef Bytes, uint64_t Address, + formatted_raw_ostream &OS, uint64_t End, + const MCSubtargetInfo &STI, + const object::ObjectFile *Obj); } // 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,14 @@ #include "llvm-objdump.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/Support/Casting.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, @@ -109,3 +114,279 @@ return Result; } + +#define PRINTBOOL(Prefix, Obj, Field) \ + OS << Prefix << " " << ((Obj.Field()) ? "+" : "-") << #Field + +#define PRINTGET(Prefix, Obj, Field) \ + OS << Prefix << " " << #Field << " = " << (unsigned)(Obj.get##Field()) + +#define SPLIT \ + OS << "\n"; \ + OS.indent(TabStop) + +#define PRINTOPTIONAL(Field) \ + if (TbTable.get##Field()) { \ + OS << "\n"; \ + printRawData(Bytes.slice(Index, 4), Address + Index, OS, STI); \ + Index += 4; \ + OS << "\t# " << #Field << " = " << TbTable.get##Field().value(); \ + } + +void objdump::dumpTracebackTable(ArrayRef Bytes, uint64_t Address, + formatted_raw_ostream &OS, uint64_t End, + const MCSubtargetInfo &STI, + const ObjectFile *Obj) { + uint64_t Index = 0; + unsigned TabStop = getInstStartColumn(STI) - 1; + // Print traceback table boundary. + printRawData(Bytes.slice(Index, 4), Address, OS, STI); + OS << "\t# Traceback table begin\n"; + Index += 4; + + uint64_t Size = End - Address; + bool Is64Bit = cast(Obj)->is64Bit(); + + Expected TTOrErr = + XCOFFTracebackTable::create(Bytes.data() + Index, Size, Is64Bit); + + if (!TTOrErr) { + reportWarning(Twine("failure parsing traceback table with address: 0x") + + utohexstr(Address) + " \n>>> " + + toString(TTOrErr.takeError()) + + "\n>>> Raw traceback table data is:", + Obj->getFileName()); + + uint64_t LastNoZero = Index; + + // The value of Size has 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; + + formatted_raw_ostream FOS(errs()); + while (Index < LastNoZero) { + printRawData(Bytes.slice(Index, 4), Address + Index, FOS, STI); + Index += 4; + errs() << "\n"; + } + + // Print all remaining zero as ... + if (Size - LastNoZero >= 8) + errs() << "\t\t...\n"; + + return; + } + + XCOFFTracebackTable TbTable = *TTOrErr; + + auto PrintBytes = [&](uint64_t N) { + printRawData(Bytes.slice(Index, N), Address + Index, OS, STI); + Index += N; + }; + + // Print the first byte of 8 bytes of mandatory fields. + PrintBytes(1); + OS << format("\t# Version = %i", TbTable.getVersion()) << "\n"; + + // Print the second byte of 8 bytes of mandatory fields. + PrintBytes(1); + TracebackTable::LanguageID LangId = + static_cast(TbTable.getLanguageID()); + OS << "\t# Language = " << getNameForTracebackTableLanguageId(LangId) << "\n"; + + // Print the third byte of 8 bytes of mandatory fields. + PrintBytes(1); + PRINTBOOL("\t#", TbTable, isGlobalLinkage); + PRINTBOOL(",", TbTable, isOutOfLineEpilogOrPrologue); + SPLIT; + PRINTBOOL("\t ", TbTable, hasTraceBackTableOffset); + PRINTBOOL(",", TbTable, isInternalProcedure); + SPLIT; + PRINTBOOL("\t ", TbTable, hasControlledStorage); + PRINTBOOL(",", TbTable, isTOCless); + SPLIT; + PRINTBOOL("\t ", TbTable, isFloatingPointPresent); + SPLIT; + PRINTBOOL("\t ", TbTable, isFloatingPointOperationLogOrAbortEnabled); + OS << "\n"; + + // Print the 4th byte of 8 bytes of mandatory fields. + PrintBytes(1); + PRINTBOOL("\t#", TbTable, isInterruptHandler); + PRINTBOOL(",", TbTable, isFuncNamePresent); + PRINTBOOL(",", TbTable, isAllocaUsed); + SPLIT; + PRINTGET("\t ", TbTable, OnConditionDirective); + PRINTBOOL(",", TbTable, isCRSaved); + PRINTBOOL(",", TbTable, isLRSaved); + OS << "\n"; + + // Print the 5th byte of 8 bytes of mandatory fields. + PrintBytes(1); + PRINTBOOL("\t#", TbTable, isBackChainStored); + PRINTBOOL(",", TbTable, isFixup); + PRINTGET(",", TbTable, NumOfFPRsSaved); + OS << "\n"; + + // Print the 6th byte of 8 bytes of mandatory fields. + PrintBytes(1); + PRINTBOOL("\t#", TbTable, hasExtensionTable); + PRINTBOOL(",", TbTable, hasVectorInfo); + PRINTGET(",", TbTable, NumOfGPRsSaved); + OS << "\n"; + + // Print the 7th byte of 8 bytes of mandatory fields. + PrintBytes(1); + PRINTGET("\t#", TbTable, NumberOfFixedParms); + OS << "\n"; + + // Print the 8th byte of 8 bytes of mandatory fields. + PrintBytes(1); + PRINTGET("\t#", TbTable, NumberOfFPParms); + PRINTBOOL(",", TbTable, hasParmsOnStack); + + PRINTOPTIONAL(ParmsType); + PRINTOPTIONAL(TraceBackTableOffset); + PRINTOPTIONAL(HandlerMask); + PRINTOPTIONAL(NumOfCtlAnchors); + + if (TbTable.getControlledStorageInfoDisp()) { + SmallVector Disp = + TbTable.getControlledStorageInfoDisp().value(); + for (unsigned I = 0; I < Disp.size(); ++I) { + OS << "\n"; + PrintBytes(4); + OS << "\t ControlledStorageInfoDisp[" << I << "] = " << Disp[I]; + // Print another 4 bytes for 64 bit. + if (Is64Bit) { + OS << "\n"; + PrintBytes(4); + } + } + } + + // If there is a name, print the function name and function name length. + if (TbTable.isFuncNamePresent()) { + uint16_t FunctionNameLen = TbTable.getFunctionName().value().size(); + + assert(FunctionNameLen > 0 && + "The length of the function name must be greater than zero."); + + OS << "\n"; + PrintBytes(2); + OS << "\t# FunctionNameLen = " << FunctionNameLen; + + uint16_t RemainingBytes = FunctionNameLen; + bool HasPrinted = false; + while (RemainingBytes > 0) { + OS << "\n"; + uint16_t PrintLen = RemainingBytes >= 4 ? 4 : RemainingBytes; + printRawData(Bytes.slice(Index, PrintLen), Address + Index, OS, STI); + Index += PrintLen; + RemainingBytes -= PrintLen; + + if (!HasPrinted) { + OS << "\t# FunctionName = " << TbTable.getFunctionName().value(); + HasPrinted = true; + } + } + } + + if (TbTable.isAllocaUsed()) { + OS << "\n"; + PrintBytes(1); + OS << format("\t# AllocaRegister = %u", + TbTable.getAllocaRegister().value()); + } + + if (TbTable.getVectorExt()) { + OS << "\n"; + TBVectorExt VecExt = TbTable.getVectorExt().value(); + // Print first byte of VectorExt. + PrintBytes(1); + PRINTGET("\t#", VecExt, NumberOfVRSaved); + PRINTBOOL(",", VecExt, isVRSavedOnStack); + PRINTBOOL(",", VecExt, hasVarArgs); + OS << "\n"; + + // Print the second byte of VectorExt. + PrintBytes(1); + PRINTGET("\t#", VecExt, NumberOfVectorParms); + PRINTBOOL(",", VecExt, hasVMXInstruction); + OS << "\n"; + + PrintBytes(4); + OS << "\t# VectorParmsInfoString = " << VecExt.getVectorParmsInfo(); + + // There are two bytes of padding after vector info. + OS << "\n"; + PrintBytes(2); + OS << "\t# Padding"; + } + + if (TbTable.getExtensionTable()) { + OS << "\n"; + PrintBytes(1); + ExtendedTBTableFlag Flag = + static_cast(TbTable.getExtensionTable().value()); + OS << "\t# ExtensionTable = " << getExtendedTBTableFlagString(Flag); + } + + if (TbTable.getEhInfoDisp()) { + // There are 4 bytes alignment before eh info displacement. + if (Index % 4) { + OS << "\n"; + PrintBytes(4 - Index % 4); + OS << "\t# Alignment padding for except info displacement"; + } + OS << "\n"; + PrintBytes(4); + OS << "\t# Except info displacement"; + // Print another 4 bytes for 64 bit. The displacement (address) is 8 bytes + // in 64 bit object files. + if (Is64Bit) { + OS << "\n"; + PrintBytes(4); + } + } + + if (End == Address + Index) { + OS << "\n"; + return; + } + + // Print all padding. + OS << "\n"; + Size = End - Address; + uint64_t Remaining = Size - Index; + if (Remaining >= 8) { + while (Remaining > 0 && Bytes[Size - Remaining] == 0) + --Remaining; + if (Remaining == 0) { + OS << "\t\t...\n"; + return; + } + } + + uint16_t AlignmentLen = 4 - Index % 4; + printRawData(Bytes.slice(Index, AlignmentLen), Address + Index, OS, STI); + OS << "\t# Paddings\n"; + Index += AlignmentLen; + while (Index < End - Address) { + printRawData(Bytes.slice(Index, 4), Address + Index, OS, STI); + OS << "\t# Padding\n"; + Index += 4; + } +} +#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 @@ -12,9 +12,11 @@ #include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Object/Archive.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" +#include "llvm/Support/FormattedStream.h" namespace llvm { class StringRef; @@ -55,6 +57,7 @@ extern bool SectionContents; extern bool ShowRawInsn; extern bool SymbolDescription; +extern bool TracebackTable; extern bool SymbolTable; extern std::string TripleName; extern bool UnwindInfo; @@ -156,8 +159,11 @@ unsigned Index); SymbolInfoTy createSymbolInfo(const object::ObjectFile &Obj, const object::SymbolRef &Symbol); +unsigned getInstStartColumn(const MCSubtargetInfo &STI); +void printRawData(llvm::ArrayRef Bytes, uint64_t Address, + llvm::formatted_raw_ostream &OS, + llvm::MCSubtargetInfo const &STI); } // namespace objdump } // end namespace llvm - #endif 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 @@ -50,7 +50,6 @@ #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Object/Archive.h" @@ -189,6 +188,7 @@ bool objdump::Disassemble; bool objdump::DisassembleAll; bool objdump::SymbolDescription; +bool objdump::TracebackTable; static std::vector DisassembleSymbols; static bool DisassembleZeroes; static std::vector DisassemblerOptions; @@ -435,14 +435,36 @@ return false; } -namespace { - /// Get the column at which we want to start printing the instruction /// disassembly, taking into account anything which appears to the left of it. -unsigned getInstStartColumn(const MCSubtargetInfo &STI) { +unsigned objdump::getInstStartColumn(const MCSubtargetInfo &STI) { return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; } +static void AlignToInstStartColumn(size_t Start, const MCSubtargetInfo &STI, + raw_ostream &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 = getInstStartColumn(STI); + unsigned Column = OS.tell() - Start; + OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); +} + +void objdump::printRawData(ArrayRef Bytes, uint64_t Address, + formatted_raw_ostream &OS, + MCSubtargetInfo const &STI) { + size_t Start = OS.tell(); + if (LeadingAddr) + OS << format("%8" PRIx64 ":", Address); + if (ShowRawInsn) { + OS << ' '; + dumpBytes(Bytes, OS); + } + AlignToInstStartColumn(Start, STI, OS); +} + +namespace { + static bool isAArch64Elf(const ObjectFile &Obj) { const auto *Elf = dyn_cast(&Obj); return Elf && Elf->getEMachine() == ELF::EM_AARCH64; @@ -477,15 +499,6 @@ OS << Name << "\t" << Val; } -static void AlignToInstStartColumn(size_t Start, const MCSubtargetInfo &STI, - raw_ostream &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 = getInstStartColumn(STI); - unsigned Column = OS.tell() - Start; - OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); -} - class PrettyPrinter { public: virtual ~PrettyPrinter() = default; @@ -499,15 +512,7 @@ SP->printSourceLine(OS, Address, ObjectFilename, LVP); LVP.printBetweenInsts(OS, false); - size_t Start = OS.tell(); - if (LeadingAddr) - OS << format("%8" PRIx64 ":", Address.Address); - if (ShowRawInsn) { - OS << ' '; - dumpBytes(Bytes, OS); - } - - AlignToInstStartColumn(Start, STI, OS); + printRawData(Bytes, Address.Address, OS, STI); if (MI) { // See MCInstPrinter::printInst. On targets where a PC relative immediate @@ -794,7 +799,7 @@ return AArch64PrettyPrinterInst; } } -} +} // namespace static uint8_t getElfSymbolType(const ObjectFile &Obj, const SymbolRef &Sym) { assert(Obj.isELF()); @@ -1078,7 +1083,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(); @@ -1099,7 +1104,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); @@ -1724,6 +1729,12 @@ } bool DumpARMELFData = false; + bool DumpTracebackTableForXCOFFFunction = + Obj.isXCOFF() && Section.isText() && TracebackTable && + Symbols[SI - 1].XCOFFSymInfo.StorageMappingClass && + (Symbols[SI - 1].XCOFFSymInfo.StorageMappingClass.value() == + XCOFF::XMC_PR); + formatted_raw_ostream FOS(outs()); std::unordered_map AllLabels; @@ -1776,6 +1787,15 @@ } } + if (DumpTracebackTableForXCOFFFunction && + doesXCOFFTracebackTableBegin(Bytes.slice(Index, 4))) { + dumpTracebackTable(Bytes.slice(Index), + SectionAddr + Index + VMAAdjustment, FOS, End, + *STI, &Obj); + Index = End; + continue; + } + // Print local label if there's any. auto Iter1 = BBAddrMapLabels.find(SectionAddr + Index); if (Iter1 != BBAddrMapLabels.end()) { @@ -3005,6 +3025,7 @@ Disassemble = InputArgs.hasArg(OBJDUMP_disassemble); DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all); SymbolDescription = InputArgs.hasArg(OBJDUMP_symbol_description); + TracebackTable = InputArgs.hasArg(OBJDUMP_traceback_table); DisassembleSymbols = commaSeparatedValues(InputArgs, OBJDUMP_disassemble_symbols_EQ); DisassembleZeroes = InputArgs.hasArg(OBJDUMP_disassemble_zeroes); diff --git a/llvm/unittests/Object/XCOFFObjectFileTest.cpp b/llvm/unittests/Object/XCOFFObjectFileTest.cpp --- a/llvm/unittests/Object/XCOFFObjectFileTest.cpp +++ b/llvm/unittests/Object/XCOFFObjectFileTest.cpp @@ -158,10 +158,11 @@ } const uint8_t TBTableData[] = { - 0x00, 0x00, 0x2A, 0x60, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc4, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00, - 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, - 0x6c, 0x1f, 0x02, 0x05, 0xf0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; + 0x00, 0x00, 0x2A, 0x60, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, + 0x05, 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, + 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x1f, 0x02, 0x05, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIControlledStorageInfoDisp) { uint64_t Size = sizeof(TBTableData); @@ -175,12 +176,12 @@ ASSERT_TRUE(TT.getControlledStorageInfoDisp()); - SmallVector Disp = TT.getControlledStorageInfoDisp().value(); + SmallVector Disp = TT.getControlledStorageInfoDisp().value(); ASSERT_EQ(Disp.size(), 2UL); EXPECT_EQ(Disp[0], 0x05050000u); EXPECT_EQ(Disp[1], 0x06060000u); - EXPECT_EQ(Size, 45u); + EXPECT_EQ(Size, 47u); } TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIAllocaRegister) { @@ -224,15 +225,16 @@ ASSERT_TRUE(TT.getExtensionTable()); EXPECT_EQ(*TT.getExtensionTable(), ExtendedTBTableFlag::TB_SSP_CANARY); - EXPECT_EQ(Size, 45u); + EXPECT_EQ(Size, 47u); } TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIHasVectorInfo1) { const uint8_t TBTableData[] = { - 0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc5, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00, - 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, - 0x6c, 0x11, 0x07, 0x90, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; + 0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, + 0x05, 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, + 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x11, 0x07, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; uint64_t Size = sizeof(TBTableData); Expected TTOrErr = XCOFFTracebackTable::create(TBTableData, Size); @@ -257,7 +259,7 @@ ASSERT_TRUE(TT.getExtensionTable()); EXPECT_EQ(*TT.getExtensionTable(), ExtendedTBTableFlag::TB_SSP_CANARY); - EXPECT_EQ(Size, 44u); + EXPECT_EQ(Size, 46u); } TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtMandatory) { @@ -386,15 +388,15 @@ } TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtExtLongTBTable) { - uint64_t Size = 44; + uint64_t Size = 46; Expected TTOrErr = XCOFFTracebackTable::create(TBTableData, Size); EXPECT_THAT_ERROR( TTOrErr.takeError(), FailedWithMessage( - "unexpected end of data at offset 0x2c while reading [0x2c, 0x2d)")); - EXPECT_EQ(Size, 44u); + "unexpected end of data at offset 0x2e while reading [0x2e, 0x2f)")); + EXPECT_EQ(Size, 46u); } TEST(XCOFFObjectFileTest, XCOFFGetCsectAuxRef32) { @@ -528,11 +530,12 @@ TEST(XCOFFObjectFileTest, XCOFFTracebackTableErrorAtParameterTypeWithVecInfo) { const uint8_t TBTableData[] = { - 0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc0, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00, - 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, - 0x6c, 0x11, 0x07, 0x90, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; - uint64_t Size = 44; + 0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc0, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, + 0x05, 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, + 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x11, 0x07, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; + uint64_t Size = 46; Expected TTOrErr = XCOFFTracebackTable::create(TBTableData, Size); @@ -544,11 +547,12 @@ TEST(XCOFFObjectFileTest, XCOFFTracebackTableErrorAtVecParameterType) { const uint8_t TBTableData[] = { - 0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00, - 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, - 0x6c, 0x11, 0x07, 0x90, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00}; - uint64_t Size = 44; + 0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, + 0x05, 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, + 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x11, 0x07, 0x90, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; + uint64_t Size = 46; Expected TTOrErr = XCOFFTracebackTable::create(TBTableData, Size);