//===-- XCOFFDumper.cpp - XCOFF dumping utility -----------------*- 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 // //===----------------------------------------------------------------------===// // // This file implements an XCOFF specific dumper for llvm-readobj. // //===----------------------------------------------------------------------===// #include "Error.h" #include "ObjDumper.h" #include "llvm-readobj.h" #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Support/ScopedPrinter.h" using namespace llvm; using namespace object; namespace { class XCOFFDumper : public ObjDumper { enum { SymbolTypeMask = 0x07, SymbolAlignmentMask = 0xF8, SymbolAlignmentBitOffset = 3 }; public: XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer) : ObjDumper(Writer), Obj(Obj) {} void printFileHeaders() override; void printSectionHeaders() override; void printRelocations() override; void printSymbols() override; void printDynamicSymbols() override; void printUnwindInfo() override; void printStackMap() const override; void printNeededLibraries() override; private: template void printSectionHeaders(ArrayRef Sections); void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr); void printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr); void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr); void printSymbol(const SymbolRef &); // Least significant 3 bits are reserved. static constexpr unsigned SectionFlagsReservedMask = 0x7; const XCOFFObjectFile &Obj; }; } // anonymous namespace void XCOFFDumper::printFileHeaders() { DictScope DS(W, "FileHeader"); W.printHex("Magic", Obj.getMagic()); W.printNumber("NumberOfSections", Obj.getNumberOfSections()); // Negative timestamp values are reserved for future use. int32_t TimeStamp = Obj.getTimeStamp(); if (TimeStamp > 0) { // This handling of the time stamp assumes that the host system's time_t is // compatible with AIX time_t. If a platform is not compatible, the lit // tests will let us know. time_t TimeDate = TimeStamp; char FormattedTime[21] = {}; size_t BytesWritten = strftime(FormattedTime, 21, "%Y-%m-%dT%H:%M:%SZ", gmtime(&TimeDate)); if (BytesWritten) W.printHex("TimeStamp", FormattedTime, TimeStamp); else W.printHex("Timestamp", TimeStamp); } else { W.printHex("TimeStamp", TimeStamp == 0 ? "None" : "Reserved Value", TimeStamp); } // The number of symbol table entries is an unsigned value in 64-bit objects // and a signed value (with negative values being 'reserved') in 32-bit // objects. if (Obj.is64Bit()) { W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset64()); W.printNumber("SymbolTableEntries", Obj.getNumberOfSymbolTableEntries64()); } else { W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset32()); int32_t SymTabEntries = Obj.getRawNumberOfSymbolTableEntries32(); if (SymTabEntries >= 0) W.printNumber("SymbolTableEntries", SymTabEntries); else W.printHex("SymbolTableEntries", "Reserved Value", SymTabEntries); } W.printHex("OptionalHeaderSize", Obj.getOptionalHeaderSize()); W.printHex("Flags", Obj.getFlags()); // TODO FIXME Add support for the auxiliary header (if any) once // XCOFFObjectFile has the necessary support. } void XCOFFDumper::printSectionHeaders() { if (Obj.is64Bit()) printSectionHeaders(Obj.sections64()); else printSectionHeaders(Obj.sections32()); } void XCOFFDumper::printRelocations() { llvm_unreachable("Unimplemented functionality for XCOFFDumper"); } static const EnumEntry FileStringType[] = { #define ECase(X) \ { #X, XCOFF::X } ECase(XFT_FN), ECase(XFT_CT), ECase(XFT_CV), ECase(XFT_CD) #undef ECase }; void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) { if (Obj.is64Bit()) report_fatal_error( "Printing for File Auxiliary Entry in 64-bit is unimplemented."); StringRef FileName = unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr)); DictScope SymDs(W, "File Auxiliary Entry"); W.printNumber("Index", Obj.getSymbolIndex(reinterpret_cast(AuxEntPtr))); W.printString("Name", FileName); W.printEnum("Type", static_cast(AuxEntPtr->Type), makeArrayRef(FileStringType)); } static const EnumEntry CsectStorageMappingClass[] = { #define ECase(X) \ { #X, XCOFF::X } ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), ECase(XMC_GL), ECase(XMC_XO), ECase(XMC_SV), ECase(XMC_SV64), ECase(XMC_SV3264), ECase(XMC_TI), ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0), ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), ECase(XMC_UA), ECase(XMC_BS), ECase(XMC_UC), ECase(XMC_TL), ECase(XMC_TE) #undef ECase }; static const EnumEntry CsectSymbolTypeClass[] = { #define ECase(X) \ { #X, XCOFF::X } ECase(XTY_ER), ECase(XTY_SD), ECase(XTY_LD), ECase(XTY_CM) #undef ECase }; void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) { assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); DictScope SymDs(W, "CSECT Auxiliary Entry"); W.printNumber("Index", Obj.getSymbolIndex(reinterpret_cast(AuxEntPtr))); if ((AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask) == XCOFF::XTY_LD) W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength); else W.printNumber("SectionLen", AuxEntPtr->SectionOrLength); W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex); W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum); // Print out symbol alignment and type. W.printNumber("SymbolAlignmentLog2", (AuxEntPtr->SymbolAlignmentAndType & SymbolAlignmentMask) >> SymbolAlignmentBitOffset); W.printEnum("SymbolType", AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask, makeArrayRef(CsectSymbolTypeClass)); W.printEnum("StorageMappingClass", static_cast(AuxEntPtr->StorageMappingClass), makeArrayRef(CsectStorageMappingClass)); W.printHex("StabInfoIndex", AuxEntPtr->StabInfoIndex); W.printHex("StabSectNum", AuxEntPtr->StabSectNum); } void XCOFFDumper::printSectAuxEntForStat( const XCOFFSectAuxEntForStat *AuxEntPtr) { assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); DictScope SymDs(W, "Sect Auxiliary Entry For Stat"); W.printNumber("Index", Obj.getSymbolIndex(reinterpret_cast(AuxEntPtr))); W.printNumber("SectionLength", AuxEntPtr->SectionLength); // Unlike the corresponding fields in the section header, NumberOfRelocEnt // and NumberOfLineNum do not handle values greater than 65535. W.printNumber("NumberOfRelocEnt", AuxEntPtr->NumberOfRelocEnt); W.printNumber("NumberOfLineNum", AuxEntPtr->NumberOfLineNum); } static const EnumEntry SymStorageClass[] = { #define ECase(X) \ { #X, XCOFF::X } ECase(C_NULL), ECase(C_AUTO), ECase(C_EXT), ECase(C_STAT), ECase(C_REG), ECase(C_EXTDEF), ECase(C_LABEL), ECase(C_ULABEL), ECase(C_MOS), ECase(C_ARG), ECase(C_STRTAG), ECase(C_MOU), ECase(C_UNTAG), ECase(C_TPDEF), ECase(C_USTATIC), ECase(C_ENTAG), ECase(C_MOE), ECase(C_REGPARM), ECase(C_FIELD), ECase(C_BLOCK), ECase(C_FCN), ECase(C_EOS), ECase(C_FILE), ECase(C_LINE), ECase(C_ALIAS), ECase(C_HIDDEN), ECase(C_HIDEXT), ECase(C_BINCL), ECase(C_EINCL), ECase(C_INFO), ECase(C_WEAKEXT), ECase(C_DWARF), ECase(C_GSYM), ECase(C_LSYM), ECase(C_PSYM), ECase(C_RSYM), ECase(C_RPSYM), ECase(C_STSYM), ECase(C_TCSYM), ECase(C_BCOMM), ECase(C_ECOML), ECase(C_ECOMM), ECase(C_DECL), ECase(C_ENTRY), ECase(C_FUN), ECase(C_BSTAT), ECase(C_ESTAT), ECase(C_GTLS), ECase(C_STTLS), ECase(C_EFCN) #undef ECase }; static StringRef GetSymbolValueName(XCOFF::StorageClass SC) { switch (SC) { case XCOFF::C_EXT: case XCOFF::C_WEAKEXT: case XCOFF::C_HIDEXT: case XCOFF::C_STAT: return "Value (RelocatableAddress)"; case XCOFF::C_FILE: return "Value (SymbolTableIndex)"; case XCOFF::C_FCN: case XCOFF::C_BLOCK: case XCOFF::C_FUN: case XCOFF::C_STSYM: case XCOFF::C_BINCL: case XCOFF::C_EINCL: case XCOFF::C_INFO: case XCOFF::C_BSTAT: case XCOFF::C_LSYM: case XCOFF::C_PSYM: case XCOFF::C_RPSYM: case XCOFF::C_RSYM: case XCOFF::C_ECOML: case XCOFF::C_DWARF: assert(false && "This StorageClass for the symbol is not yet implemented."); return ""; default: return "Value"; } } static const EnumEntry CFileLangIdClass[] = { #define ECase(X) \ { #X, XCOFF::X } ECase(TB_C), ECase(TB_CPLUSPLUS) #undef ECase }; static const EnumEntry CFileCpuIdClass[] = { #define ECase(X) \ { #X, XCOFF::X } ECase(TCPU_PPC64), ECase(TCPU_COM), ECase(TCPU_970) #undef ECase }; void XCOFFDumper::printSymbol(const SymbolRef &S) { if (Obj.is64Bit()) report_fatal_error("64-bit support is unimplemented."); DataRefImpl SymbolDRI = S.getRawDataRefImpl(); const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI); XCOFFSymbolRef XCOFFSymRef(SymbolDRI, &Obj); uint8_t NumberOfAuxEntries = XCOFFSymRef.getNumberOfAuxEntries(); DictScope SymDs(W, "Symbol"); StringRef SymbolName = unwrapOrError(Obj.getFileName(), Obj.getSymbolName(SymbolDRI)); W.printNumber("Index", Obj.getSymbolIndex(reinterpret_cast(SymbolEntPtr))); W.printString("Name", SymbolName); W.printHex(GetSymbolValueName(SymbolEntPtr->StorageClass), SymbolEntPtr->Value); StringRef SectionName = unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntPtr)); W.printString("Section", SectionName); if (XCOFFSymRef.getStorageClass() == XCOFF::C_FILE) { W.printEnum("Source Language ID", SymbolEntPtr->CFileLanguageIdAndTypeId.LanguageId, makeArrayRef(CFileLangIdClass)); W.printEnum("CPU Version ID", SymbolEntPtr->CFileLanguageIdAndTypeId.CpuTypeId, makeArrayRef(CFileCpuIdClass)); } else W.printHex("Type", SymbolEntPtr->SymbolType); W.printEnum("StorageClass", static_cast(SymbolEntPtr->StorageClass), makeArrayRef(SymStorageClass)); W.printNumber("NumberOfAuxEntries", SymbolEntPtr->NumberOfAuxEntries); if (NumberOfAuxEntries == 0) return; switch (XCOFFSymRef.getStorageClass()) { case XCOFF::C_FILE: // If the symbol is C_FILE and has auxiliary entries... for (int i = 1; i <= NumberOfAuxEntries; i++) { const XCOFFFileAuxEnt *FileAuxEntPtr = reinterpret_cast(SymbolEntPtr + i); #ifndef NDEBUG Obj.checkSymbolEntryPointer(reinterpret_cast(FileAuxEntPtr)); #endif printFileAuxEnt(FileAuxEntPtr); } break; case XCOFF::C_EXT: case XCOFF::C_WEAKEXT: case XCOFF::C_HIDEXT: // If the symbol is for a function, and it has more than 1 auxiliary entry, // then one of them must be function auxiliary entry which we do not // support yet. if (XCOFFSymRef.isFunction() && NumberOfAuxEntries >= 2) report_fatal_error("Function auxiliary entry printing is unimplemented."); // If there is more than 1 auxiliary entry, instead of printing out // error information, print out the raw Auxiliary entry from 1st till // the last - 1. The last one must be a CSECT Auxiliary Entry. for (int i = 1; i < NumberOfAuxEntries; i++) { W.startLine() << "!Unexpected raw auxiliary entry data:\n"; W.startLine() << format_bytes( ArrayRef(reinterpret_cast(SymbolEntPtr + i), XCOFF::SymbolTableEntrySize)); } // The symbol's last auxiliary entry is a CSECT Auxiliary Entry. printCsectAuxEnt32(XCOFFSymRef.getXCOFFCsectAuxEnt32()); break; case XCOFF::C_STAT: if (NumberOfAuxEntries > 1) report_fatal_error( "C_STAT symbol should not have more than 1 auxiliary entry."); const XCOFFSectAuxEntForStat *StatAuxEntPtr; StatAuxEntPtr = reinterpret_cast(SymbolEntPtr + 1); #ifndef NDEBUG Obj.checkSymbolEntryPointer(reinterpret_cast(StatAuxEntPtr)); #endif printSectAuxEntForStat(StatAuxEntPtr); break; case XCOFF::C_DWARF: case XCOFF::C_BLOCK: case XCOFF::C_FCN: report_fatal_error("Symbol table entry printing for this storage class " "type is unimplemented."); break; default: for (int i = 1; i <= NumberOfAuxEntries; i++) { W.startLine() << "!Unexpected raw auxiliary entry data:\n"; W.startLine() << format_bytes( ArrayRef(reinterpret_cast(SymbolEntPtr + i), XCOFF::SymbolTableEntrySize)); } break; } } void XCOFFDumper::printSymbols() { ListScope Group(W, "Symbols"); for (const SymbolRef &S : Obj.symbols()) printSymbol(S); } void XCOFFDumper::printDynamicSymbols() { llvm_unreachable("Unimplemented functionality for XCOFFDumper"); } void XCOFFDumper::printUnwindInfo() { llvm_unreachable("Unimplemented functionality for XCOFFDumper"); } void XCOFFDumper::printStackMap() const { llvm_unreachable("Unimplemented functionality for XCOFFDumper"); } void XCOFFDumper::printNeededLibraries() { llvm_unreachable("Unimplemented functionality for XCOFFDumper"); } static const EnumEntry SectionTypeFlagsNames[] = { #define ECase(X) \ { #X, XCOFF::X } ECase(STYP_PAD), ECase(STYP_DWARF), ECase(STYP_TEXT), ECase(STYP_DATA), ECase(STYP_BSS), ECase(STYP_EXCEPT), ECase(STYP_INFO), ECase(STYP_TDATA), ECase(STYP_TBSS), ECase(STYP_LOADER), ECase(STYP_DEBUG), ECase(STYP_TYPCHK), ECase(STYP_OVRFLO) #undef ECase }; template void XCOFFDumper::printSectionHeaders(ArrayRef Sections) { ListScope Group(W, "Sections"); uint16_t Index = 1; for (const T &Sec : Sections) { DictScope SecDS(W, "Section"); W.printNumber("Index", Index++); W.printString("Name", Sec.getName()); W.printHex("PhysicalAddress", Sec.PhysicalAddress); W.printHex("VirtualAddress", Sec.VirtualAddress); W.printHex("Size", Sec.SectionSize); W.printHex("RawDataOffset", Sec.FileOffsetToRawData); W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo); W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo); // TODO Need to add overflow handling when NumberOfX == _OVERFLOW_MARKER // in 32-bit object files. W.printNumber("NumberOfRelocations", Sec.NumberOfRelocations); W.printNumber("NumberOfLineNumbers", Sec.NumberOfLineNumbers); // The most significant 16-bits represent the DWARF section subtype. For // now we just dump the section type flags. uint16_t Flags = Sec.Flags & 0xffffu; if (Flags & SectionFlagsReservedMask) W.printHex("Flags", "Reserved", Flags); else W.printEnum("Type", Flags, makeArrayRef(SectionTypeFlagsNames)); } if (opts::SectionRelocations) report_fatal_error("Dumping section relocations is unimplemented"); if (opts::SectionSymbols) report_fatal_error("Dumping symbols is unimplemented"); if (opts::SectionData) report_fatal_error("Dumping section data is unimplemented"); } namespace llvm { std::error_code createXCOFFDumper(const object::ObjectFile *Obj, ScopedPrinter &Writer, std::unique_ptr &Result) { const XCOFFObjectFile *XObj = dyn_cast(Obj); if (!XObj) return readobj_error::unsupported_obj_file_format; Result.reset(new XCOFFDumper(*XObj, Writer)); return readobj_error::success; } } // namespace llvm