diff --git a/llvm/docs/CommandGuide/llvm-readobj.rst b/llvm/docs/CommandGuide/llvm-readobj.rst --- a/llvm/docs/CommandGuide/llvm-readobj.rst +++ b/llvm/docs/CommandGuide/llvm-readobj.rst @@ -330,6 +330,10 @@ Display XCOFF Auxiliary header. +.. option:: --exception-section + + Display XCOFF exception section entries. + EXIT STATUS ----------- 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 @@ -220,6 +220,35 @@ support::big32_t OffsetToRelEnt; }; +template struct ExceptionSectionEntry { + union { + support::ubig32_t SymbolIdx; + AddressType TrapInstAddr; + }; + uint8_t LangId; + uint8_t Reason; + + uint32_t getSymbolIndex() const { + assert(Reason == 0 && "Get symbol table index of the function only when " + "the e_reason field is 0."); + return SymbolIdx; + } + + uint64_t getTrapInstAddr() const { + assert(Reason != 0 && "Zero is not a valid trap exception reason code."); + return TrapInstAddr; + } + uint8_t getLangID() const { return LangId; } + uint8_t getReason() const { return Reason; } +}; + +typedef ExceptionSectionEntry ExceptionSectionEntry32; +typedef ExceptionSectionEntry ExceptionSectionEntry64; + +// Explicit extern template declarations. +extern template struct ExceptionSectionEntry; +extern template struct ExceptionSectionEntry; + struct XCOFFStringTable { uint32_t Size; const char *Data; @@ -464,6 +493,11 @@ uintptr_t getEndOfSymbolTableAddress() const; Expected getLoaderSectionAddress() const; + DataRefImpl getSectionByType(XCOFF::SectionTypeFlags SectType) const; + uint64_t getSectionFileOffsetToRawData(DataRefImpl Sec) const; + Expected + getSectionFileOffsetToRawData(XCOFF::SectionTypeFlags SectType) const; + // This returns a pointer to the start of the storage for the name field of // the 32-bit or 64-bit SectionHeader struct. This string is *not* necessarily // null-terminated. @@ -618,6 +652,10 @@ // Loader section related interfaces. Expected getImportFileTable() const; + // Exception-related interface. + template + Expected> getExceptionEntries() const; + // This function returns string table entry. Expected getStringTableEntry(uint32_t Offset) 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 @@ -86,6 +86,9 @@ return (Info & XR_BIASED_LENGTH_MASK) + 1; } +template struct ExceptionSectionEntry; +template struct ExceptionSectionEntry; + uintptr_t XCOFFObjectFile::getAdvancedSymbolEntryAddress(uintptr_t CurrentAddress, uint32_t Distance) { @@ -392,6 +395,13 @@ return Result; } +uint64_t XCOFFObjectFile::getSectionFileOffsetToRawData(DataRefImpl Sec) const { + if (is64Bit()) + return toSection64(Sec)->FileOffsetToRawData; + + return toSection32(Sec)->FileOffsetToRawData; +} + Expected XCOFFObjectFile::getLoaderSectionAddress() const { uint64_t OffsetToLoaderSection = 0; uint64_t SizeOfLoaderSection = 0; @@ -429,6 +439,53 @@ return LoderSectionStart; } +Expected XCOFFObjectFile::getSectionFileOffsetToRawData( + XCOFF::SectionTypeFlags SectType) const { + DataRefImpl DRI = getSectionByType(SectType); + + if (DRI.p == 0) // No section is not an error. + return 0; + + uint64_t SectionOffset = getSectionFileOffsetToRawData(DRI); + uint64_t SizeOfSection = getSectionSize(DRI); + + uintptr_t SectionStart = reinterpret_cast(base() + SectionOffset); + if (Error E = Binary::checkOffset(Data, SectionStart, SizeOfSection)) { + SmallString<32> UnknownType; + Twine(("") + .toVector(UnknownType); + const char *SectionName = UnknownType.c_str(); + + switch (SectType) { +#define ECASE(Value, String) \ + case XCOFF::Value: \ + SectionName = String; \ + break + + ECASE(STYP_PAD, "pad"); + ECASE(STYP_DWARF, "dwarf"); + ECASE(STYP_TEXT, "text"); + ECASE(STYP_DATA, "data"); + ECASE(STYP_BSS, "bss"); + ECASE(STYP_EXCEPT, "expect"); + ECASE(STYP_INFO, "info"); + ECASE(STYP_TDATA, "tdata"); + ECASE(STYP_TBSS, "tbss"); + ECASE(STYP_LOADER, "loader"); + ECASE(STYP_DEBUG, "debug"); + ECASE(STYP_TYPCHK, "typchk"); + ECASE(STYP_OVRFLO, "ovrflo"); +#undef ECASE + } + return createError(toString(std::move(E)) + ": " + SectionName + + " section with offset 0x" + + Twine::utohexstr(SectionOffset) + " and size 0x" + + Twine::utohexstr(SizeOfSection) + + " goes past the end of the file"); + } + return SectionStart; +} + bool XCOFFObjectFile::isSectionCompressed(DataRefImpl Sec) const { return false; } @@ -738,6 +795,22 @@ return DRI; } +DataRefImpl +XCOFFObjectFile::getSectionByType(XCOFF::SectionTypeFlags SectType) const { + DataRefImpl DRI; + auto GetSectionAddr = [&](const auto &Sections) { + for (const auto &Sec : Sections) + if (Sec.getSectionType() == SectType) + return reinterpret_cast(&Sec); + return 0ul; + }; + if (is64Bit()) + DRI.p = GetSectionAddr(sections64()); + else + DRI.p = GetSectionAddr(sections32()); + return DRI; +} + Expected XCOFFObjectFile::getSymbolSectionName(XCOFFSymbolRef SymEntPtr) const { const int16_t SectionNum = SymEntPtr.getSectionNumber(); @@ -960,6 +1033,31 @@ return ArrayRef(StartReloc, StartReloc + NumRelocEntries); } +template +Expected> XCOFFObjectFile::getExceptionEntries() const { + assert(is64Bit() && sizeof(ExceptEnt) == sizeof(ExceptionSectionEntry64) || + !is64Bit() && sizeof(ExceptEnt) == sizeof(ExceptionSectionEntry32)); + + Expected ExceptionSectOrErr = + getSectionFileOffsetToRawData(XCOFF::STYP_EXCEPT); + if (!ExceptionSectOrErr) + return ExceptionSectOrErr.takeError(); + + DataRefImpl DRI = getSectionByType(XCOFF::STYP_EXCEPT); + if (DRI.p == 0) + return ArrayRef(); + + ExceptEnt *ExceptEntStart = + reinterpret_cast(*ExceptionSectOrErr); + return ArrayRef( + ExceptEntStart, ExceptEntStart + getSectionSize(DRI) / sizeof(ExceptEnt)); +} + +template Expected> +XCOFFObjectFile::getExceptionEntries() const; +template Expected> +XCOFFObjectFile::getExceptionEntries() const; + Expected XCOFFObjectFile::parseStringTable(const XCOFFObjectFile *Obj, uint64_t Offset) { // If there is a string table, then the buffer must contain at least 4 bytes diff --git a/llvm/test/tools/llvm-readobj/XCOFF/exception-section.test b/llvm/test/tools/llvm-readobj/XCOFF/exception-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/XCOFF/exception-section.test @@ -0,0 +1,55 @@ +## Test the --exception-section option. + +# RUN: yaml2obj --docnum=1 %s -o %t_xcoff32.o +# RUN: yaml2obj --docnum=2 %s -o %t_xcoff64.o +# RUN: llvm-readobj --exception-section %t_xcoff32.o |\ +# RUN: FileCheck %s --check-prefixes=CHECK +# RUN: llvm-readobj --exception-section %t_xcoff64.o |\ +# RUN: FileCheck %s --check-prefixes=CHECK + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .except + Flags: [ STYP_EXCEPT ] + SectionData: "000000000000000000340003" +## ^------- -SymbolIndex=0 +## ^- -LangID=0 +## ^- -Reason=0 +## ^------- -Trap Instr Addr=0x34 +## ^- -LangID=0 +## ^- -Reason=3 +Symbols: + - Name: .bar + Section: .text + +--- !XCOFF +FileHeader: + MagicNumber: 0x1F7 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .except + Flags: [ STYP_EXCEPT ] + SectionData: "0000000000000000000000000000000000340003" +## ^--------------- -SymbolIndex=0 +## ^- -LangID=0 +## ^- -Reason=0 +## ^-------------- -Trap Instr Addr=0x34 +## ^- -LangID=0 +## ^- -Reason=3 +Symbols: + - Name: .bar + Section: .text + +# CHECK: Exception section { +# CHECK-NEXT: Symbol: .bar (0) +# CHECK-NEXT: LangID: 0 +# CHECK-NEXT: Reason: 0 +# CHECK-NEXT: Trap Instr Addr: 0x34 +# CHECK-NEXT: LangID: 0 +# CHECK-NEXT: Reason: 3 +# CHECK-NEXT: } diff --git a/llvm/test/tools/llvm-readobj/XCOFF/invalid-exception-section.test b/llvm/test/tools/llvm-readobj/XCOFF/invalid-exception-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/XCOFF/invalid-exception-section.test @@ -0,0 +1,38 @@ +## Test decoding an invalid exception section and symbol index. + +# RUN: yaml2obj --docnum=1 %s -o %t_invalid_size.o +# RUN: yaml2obj --docnum=2 %s -o %t_invalid_sym.o +# RUN: llvm-readobj --exception-section %t_invalid_size.o 2>&1 |\ +# RUN: FileCheck -DFILE=%t_invalid_size.o %s --check-prefixes=CHECK-WARN-SIZE +# RUN: llvm-readobj --exception-section %t_invalid_sym.o 2>&1 |\ +# RUN: FileCheck -DFILE=%t_invalid_sym.o %s --check-prefixes=CHECK-WARN-SYM + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .except + Size: 1000 + Flags: [ STYP_EXCEPT ] + SectionData: "000000000000" +Symbols: + - Name: .bar + Section: .text + +--- !XCOFF +FileHeader: + MagicNumber: 0x1F7 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .except + Flags: [ STYP_EXCEPT ] + SectionData: "00000004000000000000" +Symbols: + - Name: .bar + Section: .text + +# CHECK-WARN-SIZE: warning: '[[FILE]]': The end of the file was unexpectedly encountered: expect section with offset 0x64 and size 0x3e8 goes past the end of the file +# CHECK-WARN-SYM: warning: '[[FILE]]': symbol index 4 exceeds symbol count 1 diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -157,6 +157,7 @@ // Only implement for XCOFF virtual void printAuxiliaryHeader() {} + virtual void printExceptionSection() {} // Only implemented for MachO. virtual void printMachODataInCode() { } diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td --- a/llvm/tools/llvm-readobj/Opts.td +++ b/llvm/tools/llvm-readobj/Opts.td @@ -88,6 +88,7 @@ // XCOFF specific options. def grp_xcoff : OptionGroup<"kind">, HelpText<"OPTIONS (XCOFF specific)">; def auxiliary_header : FF<"auxiliary-header" , "Display the auxiliary header">, Group; +def exception_section : FF<"exception-section" , "Display the exception section entries">, Group; def help : FF<"help", "Display this help">; def version : FF<"version", "Display the version">; diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp --- a/llvm/tools/llvm-readobj/XCOFFDumper.cpp +++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -39,6 +39,7 @@ void printStackMap() const override; void printNeededLibraries() override; void printStringTable() override; + void printExceptionSection() override; ScopedPrinter &getScopedPrinter() const { return W; } @@ -46,6 +47,9 @@ template void printSectionHeaders(ArrayRef Sections); template void printGenericSectionHeader(T &Sec) const; template void printOverflowSectionHeader(T &Sec) const; + template + void printExceptionSectionEntry(const T &ExceptionSectEnt) const; + template void printExceptionSectionEntries() const; template const T *getAuxEntPtr(uintptr_t AuxAddress); void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr); void printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef); @@ -129,6 +133,47 @@ printSectionHeaders(Obj.sections32()); } +template +void XCOFFDumper::printExceptionSectionEntry(const T &ExceptionSectEnt) const { + if (ExceptionSectEnt.getReason()) + W.printHex("Trap Instr Addr", ExceptionSectEnt.getTrapInstAddr()); + else { + uint32_t SymIdx = ExceptionSectEnt.getSymbolIndex(); + Expected ErrOrSymbolName = Obj.getSymbolNameByIndex(SymIdx); + if (Error E = ErrOrSymbolName.takeError()) { + reportUniqueWarning(std::move(E)); + return; + } + StringRef SymName = *ErrOrSymbolName; + + W.printNumber("Symbol", SymName, SymIdx); + } + W.printNumber("LangID", ExceptionSectEnt.getLangID()); + W.printNumber("Reason", ExceptionSectEnt.getReason()); +} + +template void XCOFFDumper::printExceptionSectionEntries() const { + Expected> ExceptSectEntsOrErr = Obj.getExceptionEntries(); + if (Error E = ExceptSectEntsOrErr.takeError()) { + reportUniqueWarning(std::move(E)); + return; + } + ArrayRef ExceptSectEnts = *ExceptSectEntsOrErr; + + DictScope DS(W, "Exception section"); + if (ExceptSectEnts.empty()) + return; + for (auto &Ent : ExceptSectEnts) + printExceptionSectionEntry(Ent); +} + +void XCOFFDumper::printExceptionSection() { + if (Obj.is64Bit()) + printExceptionSectionEntries(); + else + printExceptionSectionEntries(); +} + void XCOFFDumper::printRelocations() { if (Obj.is64Bit()) printRelocations(Obj.sections64()); diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -162,6 +162,7 @@ // XCOFF specific options. static bool XCOFFAuxiliaryHeader; +static bool XCOFFExceptionSection; OutputStyleTy Output = OutputStyleTy::LLVM; static std::vector InputFilenames; @@ -302,6 +303,7 @@ // XCOFF specific options. opts::XCOFFAuxiliaryHeader = Args.hasArg(OPT_auxiliary_header); + opts::XCOFFExceptionSection = Args.hasArg(OPT_exception_section); opts::InputFilenames = Args.getAllArgValues(OPT_INPUT); } @@ -395,6 +397,8 @@ if (opts::FileHeaders) Dumper->printFileHeaders(); + // Auxiliary header in XOCFF is right after the file header, so print the data + // here. if (Obj.isXCOFF() && opts::XCOFFAuxiliaryHeader) Dumper->printAuxiliaryHeader(); @@ -502,6 +506,10 @@ if (opts::CGProfile) Dumper->printCGProfile(); } + + if (Obj.isXCOFF() && opts::XCOFFExceptionSection) + Dumper->printExceptionSection(); + if (opts::PrintStackMap) Dumper->printStackMap(); if (opts::PrintStackSizes)