| //===-- XCOFFDump.cpp - XCOFF-specific dumper -----------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file implements the XCOFF-specific dumper for llvm-objdump. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "XCOFFDump.h" |
| |
| #include "llvm-objdump.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Demangle/Demangle.h" |
| #include "llvm/MC/MCInstPrinter.h" |
| #include "llvm/MC/MCSubtargetInfo.h" |
| #include "llvm/Support/Endian.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/FormattedStream.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <algorithm> |
| |
| using namespace llvm; |
| using namespace llvm::object; |
| using namespace llvm::XCOFF; |
| using namespace llvm::support; |
| |
| namespace { |
| class XCOFFDumper : public objdump::Dumper { |
| enum PrintStyle { Hex, Number }; |
| const XCOFFObjectFile &Obj; |
| unsigned Width; |
| |
| public: |
| XCOFFDumper(const object::XCOFFObjectFile &O) : Dumper(O), Obj(O) {} |
| |
| private: |
| void printPrivateHeaders() override; |
| void printFileHeader(); |
| void printAuxiliaryHeader(); |
| void printLoaderSectionHeader(); |
| void printAuxiliaryHeader(const XCOFFAuxiliaryHeader32 *AuxHeader); |
| void printAuxiliaryHeader(const XCOFFAuxiliaryHeader64 *AuxHeader); |
| template <typename AuxHeaderMemberType, typename XCOFFAuxiliaryHeader> |
| void printAuxMemberHelper(PrintStyle Style, const char *MemberName, |
| const AuxHeaderMemberType &Member, |
| const XCOFFAuxiliaryHeader *AuxHeader, |
| uint16_t AuxSize, uint16_t &PartialFieldOffset, |
| const char *&PartialFieldName); |
| template <typename XCOFFAuxiliaryHeader> |
| void checkAndPrintAuxHeaderParseError(const char *PartialFieldName, |
| uint16_t PartialFieldOffset, |
| uint16_t AuxSize, |
| XCOFFAuxiliaryHeader &AuxHeader); |
| |
| void printBinary(StringRef Name, ArrayRef<uint8_t> Data); |
| void printHex(StringRef Name, uint64_t Value); |
| void printNumber(StringRef Name, uint64_t Value); |
| FormattedString formatName(StringRef Name); |
| void printStrHex(StringRef Name, StringRef Str, uint64_t Value); |
| }; |
| |
| void XCOFFDumper::printPrivateHeaders() { |
| printFileHeader(); |
| printAuxiliaryHeader(); |
| printLoaderSectionHeader(); |
| } |
| |
| FormattedString XCOFFDumper::formatName(StringRef Name) { |
| return FormattedString(Name, Width, FormattedString::JustifyLeft); |
| } |
| |
| void XCOFFDumper::printHex(StringRef Name, uint64_t Value) { |
| outs() << formatName(Name) << format_hex(Value, 0) << "\n"; |
| } |
| |
| void XCOFFDumper::printNumber(StringRef Name, uint64_t Value) { |
| outs() << formatName(Name) << format_decimal(Value, 0) << "\n"; |
| } |
| |
| void XCOFFDumper::printStrHex(StringRef Name, StringRef Str, uint64_t Value) { |
| outs() << formatName(Name) << Str << " (" << format_decimal(Value, 0) |
| << ")\n"; |
| } |
| |
| void XCOFFDumper::printBinary(StringRef Name, ArrayRef<uint8_t> Data) { |
| unsigned OrgWidth = Width; |
| Width = 0; |
| outs() << formatName(Name) << " (" << format_bytes(Data) << ")\n"; |
| Width = OrgWidth; |
| } |
| |
| void XCOFFDumper::printAuxiliaryHeader() { |
| Width = 36; |
| if (Obj.is64Bit()) |
| printAuxiliaryHeader(Obj.auxiliaryHeader64()); |
| else |
| printAuxiliaryHeader(Obj.auxiliaryHeader32()); |
| } |
| |
| template <typename AuxHeaderMemberType, typename XCOFFAuxiliaryHeader> |
| void XCOFFDumper::printAuxMemberHelper(PrintStyle Style, const char *MemberName, |
| const AuxHeaderMemberType &Member, |
| const XCOFFAuxiliaryHeader *AuxHeader, |
| uint16_t AuxSize, |
| uint16_t &PartialFieldOffset, |
| const char *&PartialFieldName) { |
| ptrdiff_t Offset = reinterpret_cast<const char *>(&Member) - |
| reinterpret_cast<const char *>(AuxHeader); |
| if (Offset + sizeof(Member) <= AuxSize) { |
| if (Style == Hex) |
| printHex(MemberName, Member); |
| else |
| printNumber(MemberName, Member); |
| } else if (Offset < AuxSize) { |
| PartialFieldOffset = Offset; |
| PartialFieldName = MemberName; |
| } |
| } |
| |
| template <typename XCOFFAuxiliaryHeader> |
| void XCOFFDumper::checkAndPrintAuxHeaderParseError( |
| const char *PartialFieldName, uint16_t PartialFieldOffset, uint16_t AuxSize, |
| XCOFFAuxiliaryHeader &AuxHeader) { |
| if (PartialFieldOffset < AuxSize) { |
| std::string Buf; |
| raw_string_ostream OS(Buf); |
| OS.flush(); |
| OS << FormattedString("Raw data", 0, FormattedString::JustifyLeft) << " (" |
| << format_bytes( |
| ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&AuxHeader) + |
| PartialFieldOffset, |
| AuxSize - PartialFieldOffset)) |
| << ")\n"; |
| reportUniqueWarning(Twine("only partial field for ") + PartialFieldName + |
| " at offset (" + Twine(PartialFieldOffset) + ")\n" + |
| OS.str()); |
| } else if (sizeof(AuxHeader) < AuxSize) { |
| printBinary( |
| "Extra raw data", |
| ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&AuxHeader) + |
| sizeof(AuxHeader), |
| AuxSize - sizeof(AuxHeader))); |
| } |
| } |
| |
| void XCOFFDumper::printAuxiliaryHeader( |
| const XCOFFAuxiliaryHeader32 *AuxHeader) { |
| if (AuxHeader == nullptr) |
| return; |
| outs() << "\n---Auxiliary Header:\n"; |
| uint16_t AuxSize = Obj.getOptionalHeaderSize(); |
| uint16_t PartialFieldOffset = AuxSize; |
| const char *PartialFieldName = nullptr; |
| |
| auto PrintAuxMember = [&](PrintStyle Style, const char *MemberName, |
| auto &Member) { |
| printAuxMemberHelper(Style, MemberName, Member, AuxHeader, AuxSize, |
| PartialFieldOffset, PartialFieldName); |
| }; |
| |
| PrintAuxMember(Hex, "Magic:", AuxHeader->AuxMagic); |
| PrintAuxMember(Hex, "Version:", AuxHeader->Version); |
| PrintAuxMember(Hex, "Size of .text section:", AuxHeader->TextSize); |
| PrintAuxMember(Hex, "Size of .data section:", AuxHeader->InitDataSize); |
| PrintAuxMember(Hex, "Size of .bss section:", AuxHeader->BssDataSize); |
| PrintAuxMember(Hex, "Entry point address:", AuxHeader->EntryPointAddr); |
| PrintAuxMember(Hex, ".text section start address:", AuxHeader->TextStartAddr); |
| PrintAuxMember(Hex, ".data section start address:", AuxHeader->DataStartAddr); |
| PrintAuxMember(Hex, "TOC anchor address:", AuxHeader->TOCAnchorAddr); |
| PrintAuxMember( |
| Number, "Section number of entryPoint:", AuxHeader->SecNumOfEntryPoint); |
| PrintAuxMember(Number, "Section number of .text:", AuxHeader->SecNumOfText); |
| PrintAuxMember(Number, "Section number of .data:", AuxHeader->SecNumOfData); |
| PrintAuxMember(Number, "Section number of TOC:", AuxHeader->SecNumOfTOC); |
| PrintAuxMember(Number, |
| "Section number of loader data:", AuxHeader->SecNumOfLoader); |
| PrintAuxMember(Number, "Section number of .bss:", AuxHeader->SecNumOfBSS); |
| PrintAuxMember(Hex, "Maxium alignment of .text:", AuxHeader->MaxAlignOfText); |
| PrintAuxMember(Hex, "Maxium alignment of .data:", AuxHeader->MaxAlignOfData); |
| PrintAuxMember(Hex, "Module type:", AuxHeader->ModuleType); |
| PrintAuxMember(Hex, "CPU type of objects:", AuxHeader->CpuFlag); |
| PrintAuxMember(Hex, "Maximum stack size:", AuxHeader->MaxStackSize); |
| PrintAuxMember(Hex, "Maximum data size:", AuxHeader->MaxDataSize); |
| PrintAuxMember(Hex, "Reserved for debugger:", AuxHeader->ReservedForDebugger); |
| PrintAuxMember(Hex, "Text page size:", AuxHeader->TextPageSize); |
| PrintAuxMember(Hex, "Data page size:", AuxHeader->DataPageSize); |
| PrintAuxMember(Hex, "Stack page size:", AuxHeader->StackPageSize); |
| if (offsetof(XCOFFAuxiliaryHeader32, FlagAndTDataAlignment) + |
| sizeof(XCOFFAuxiliaryHeader32::FlagAndTDataAlignment) <= |
| AuxSize) { |
| printHex("Flag:", AuxHeader->getFlag()); |
| printHex("Alignment of thread-local storage:", |
| AuxHeader->getTDataAlignment()); |
| } |
| |
| PrintAuxMember(Number, |
| "Section number for .tdata:", AuxHeader->SecNumOfTData); |
| PrintAuxMember(Number, "Section number for .tbss:", AuxHeader->SecNumOfTBSS); |
| |
| checkAndPrintAuxHeaderParseError(PartialFieldName, PartialFieldOffset, |
| AuxSize, *AuxHeader); |
| } |
| |
| void XCOFFDumper::printAuxiliaryHeader( |
| const XCOFFAuxiliaryHeader64 *AuxHeader) { |
| if (AuxHeader == nullptr) |
| return; |
| uint16_t AuxSize = Obj.getOptionalHeaderSize(); |
| outs() << "\n---Auxiliary Header:\n"; |
| uint16_t PartialFieldOffset = AuxSize; |
| const char *PartialFieldName = nullptr; |
| |
| auto PrintAuxMember = [&](PrintStyle Style, const char *MemberName, |
| auto &Member) { |
| printAuxMemberHelper(Style, MemberName, Member, AuxHeader, AuxSize, |
| PartialFieldOffset, PartialFieldName); |
| }; |
| |
| PrintAuxMember(Hex, "Magic:", AuxHeader->AuxMagic); |
| PrintAuxMember(Hex, "Version:", AuxHeader->Version); |
| PrintAuxMember(Hex, "Reserved for debugger:", AuxHeader->ReservedForDebugger); |
| PrintAuxMember(Hex, ".text section start address:", AuxHeader->TextStartAddr); |
| PrintAuxMember(Hex, ".data section start address:", AuxHeader->DataStartAddr); |
| PrintAuxMember(Hex, "TOC anchor address:", AuxHeader->TOCAnchorAddr); |
| PrintAuxMember( |
| Number, "Section number of entryPoint:", AuxHeader->SecNumOfEntryPoint); |
| PrintAuxMember(Number, "Section number of .text:", AuxHeader->SecNumOfText); |
| PrintAuxMember(Number, "Section number of .data:", AuxHeader->SecNumOfData); |
| PrintAuxMember(Number, "Section number of TOC:", AuxHeader->SecNumOfTOC); |
| PrintAuxMember(Number, |
| "Section number of loader data:", AuxHeader->SecNumOfLoader); |
| PrintAuxMember(Number, "Section number of .bss:", AuxHeader->SecNumOfBSS); |
| PrintAuxMember(Hex, "Maxium alignment of .text:", AuxHeader->MaxAlignOfText); |
| PrintAuxMember(Hex, "Maxium alignment of .data:", AuxHeader->MaxAlignOfData); |
| PrintAuxMember(Hex, "Module type:", AuxHeader->ModuleType); |
| PrintAuxMember(Hex, "CPU type of objects:", AuxHeader->CpuFlag); |
| PrintAuxMember(Hex, "Text page size:", AuxHeader->TextPageSize); |
| PrintAuxMember(Hex, "Data page size:", AuxHeader->DataPageSize); |
| PrintAuxMember(Hex, "Stack page size:", AuxHeader->StackPageSize); |
| if (offsetof(XCOFFAuxiliaryHeader64, FlagAndTDataAlignment) + |
| sizeof(XCOFFAuxiliaryHeader64::FlagAndTDataAlignment) <= |
| AuxSize) { |
| printHex("Flag:", AuxHeader->getFlag()); |
| printHex("Alignment of thread-local storage:", |
| AuxHeader->getTDataAlignment()); |
| } |
| PrintAuxMember(Hex, "Size of .text section:", AuxHeader->TextSize); |
| PrintAuxMember(Hex, "Size of .data section:", AuxHeader->InitDataSize); |
| PrintAuxMember(Hex, "Size of .bss section:", AuxHeader->BssDataSize); |
| PrintAuxMember(Hex, "Entry point address:", AuxHeader->EntryPointAddr); |
| PrintAuxMember(Hex, "Maximum stack size:", AuxHeader->MaxStackSize); |
| PrintAuxMember(Hex, "Maximum data size:", AuxHeader->MaxDataSize); |
| PrintAuxMember(Number, |
| "Section number for .tdata:", AuxHeader->SecNumOfTData); |
| PrintAuxMember(Number, "Section number for .tbss:", AuxHeader->SecNumOfTBSS); |
| PrintAuxMember(Hex, "Additional flags 64-bit XCOFF:", AuxHeader->XCOFF64Flag); |
| |
| checkAndPrintAuxHeaderParseError(PartialFieldName, PartialFieldOffset, |
| AuxSize, *AuxHeader); |
| } |
| |
| void XCOFFDumper::printLoaderSectionHeader() { |
| Expected<uintptr_t> LoaderSectionAddrOrError = |
| Obj.getSectionFileOffsetToRawData(XCOFF::STYP_LOADER); |
| if (!LoaderSectionAddrOrError) { |
| reportUniqueWarning(LoaderSectionAddrOrError.takeError()); |
| return; |
| } |
| uintptr_t LoaderSectionAddr = LoaderSectionAddrOrError.get(); |
| |
| if (LoaderSectionAddr == 0) |
| return; |
| |
| auto PrintLoadSecHeaderCommon = [&](const auto *LDHeader) { |
| printNumber("Version:", LDHeader->Version); |
| printNumber("NumberOfSymbolEntries:", LDHeader->NumberOfSymTabEnt); |
| printNumber("NumberOfRelocationEntries:", LDHeader->NumberOfRelTabEnt); |
| printNumber("LengthOfImportFileIDStringTable:", |
| LDHeader->LengthOfImpidStrTbl); |
| printNumber("NumberOfImportFileIDs:", LDHeader->NumberOfImpid); |
| printHex("OffsetToImportFileIDs:", LDHeader->OffsetToImpid); |
| printNumber("LengthOfStringTable:", LDHeader->LengthOfStrTbl); |
| printHex("OffsetToStringTable:", LDHeader->OffsetToStrTbl); |
| }; |
| |
| Width = 35; |
| outs() << "\n---Loader Section Header:\n"; |
| if (Obj.is64Bit()) { |
| const LoaderSectionHeader64 *LoaderSec64 = |
| reinterpret_cast<const LoaderSectionHeader64 *>(LoaderSectionAddr); |
| PrintLoadSecHeaderCommon(LoaderSec64); |
| printHex("OffsetToSymbolTable", LoaderSec64->OffsetToSymTbl); |
| printHex("OffsetToRelocationEntries", LoaderSec64->OffsetToRelEnt); |
| } else { |
| const LoaderSectionHeader32 *LoaderSec32 = |
| reinterpret_cast<const LoaderSectionHeader32 *>(LoaderSectionAddr); |
| PrintLoadSecHeaderCommon(LoaderSec32); |
| } |
| } |
| |
| void XCOFFDumper::printFileHeader() { |
| Width = 20; |
| outs() << "\n---File Header:\n"; |
| printHex("Magic:", Obj.getMagic()); |
| printNumber("NumberOfSections:", Obj.getNumberOfSections()); |
| |
| int32_t Timestamp = Obj.getTimeStamp(); |
| if (Timestamp > 0) { |
| // This handling of the timestamp 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[20] = {}; |
| |
| size_t BytesFormatted = std::strftime(FormattedTime, sizeof(FormattedTime), |
| "%F %T", std::gmtime(&TimeDate)); |
| assert(BytesFormatted && "The size of the buffer FormattedTime is less " |
| "than the size of the date/time string."); |
| (void)BytesFormatted; |
| printStrHex("Timestamp:", FormattedTime, Timestamp); |
| } else { |
| // Negative timestamp values are reserved for future use. |
| printStrHex("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()) { |
| printHex("SymbolTableOffset:", Obj.getSymbolTableOffset64()); |
| printNumber("SymbolTableEntries:", Obj.getNumberOfSymbolTableEntries64()); |
| } else { |
| printHex("SymbolTableOffset:", Obj.getSymbolTableOffset32()); |
| int32_t SymTabEntries = Obj.getRawNumberOfSymbolTableEntries32(); |
| if (SymTabEntries >= 0) |
| printNumber("SymbolTableEntries:", SymTabEntries); |
| else |
| printStrHex("SymbolTableEntries:", "Reserved Value", SymTabEntries); |
| } |
| |
| printHex("OptionalHeaderSize:", Obj.getOptionalHeaderSize()); |
| printHex("Flags:", Obj.getFlags()); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<objdump::Dumper> |
| objdump::createXCOFFDumper(const object::XCOFFObjectFile &Obj) { |
| return std::make_unique<XCOFFDumper>(Obj); |
| } |
| |
| Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile &Obj, |
| const RelocationRef &Rel, |
| bool SymbolDescription, |
| SmallVectorImpl<char> &Result) { |
| symbol_iterator SymI = Rel.getSymbol(); |
| if (SymI == Obj.symbol_end()) |
| return make_error<GenericBinaryError>( |
| "invalid symbol reference in relocation entry", |
| object_error::parse_failed); |
| |
| Expected<StringRef> SymNameOrErr = SymI->getName(); |
| if (!SymNameOrErr) |
| return SymNameOrErr.takeError(); |
| |
| std::string SymName = |
| Demangle ? demangle(*SymNameOrErr) : SymNameOrErr->str(); |
| if (SymbolDescription) |
| SymName = getXCOFFSymbolDescription(createSymbolInfo(Obj, *SymI), SymName); |
| |
| Result.append(SymName.begin(), SymName.end()); |
| return Error::success(); |
| } |
| |
| std::optional<XCOFF::StorageMappingClass> |
| objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile &Obj, |
| const SymbolRef &Sym) { |
| const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); |
| |
| if (!SymRef.isCsectSymbol()) |
| return std::nullopt; |
| |
| auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); |
| if (!CsectAuxEntOrErr) |
| return std::nullopt; |
| |
| return CsectAuxEntOrErr.get().getStorageMappingClass(); |
| } |
| |
| std::optional<object::SymbolRef> |
| objdump::getXCOFFSymbolContainingSymbolRef(const XCOFFObjectFile &Obj, |
| const SymbolRef &Sym) { |
| const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); |
| if (!SymRef.isCsectSymbol()) |
| return std::nullopt; |
| |
| Expected<XCOFFCsectAuxRef> CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); |
| if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel()) |
| return std::nullopt; |
| uint32_t Idx = |
| static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength()); |
| DataRefImpl DRI; |
| DRI.p = Obj.getSymbolByIndex(Idx); |
| return SymbolRef(DRI, &Obj); |
| } |
| |
| bool objdump::isLabel(const XCOFFObjectFile &Obj, const SymbolRef &Sym) { |
| const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); |
| if (!SymRef.isCsectSymbol()) |
| return false; |
| |
| auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); |
| if (!CsectAuxEntOrErr) |
| return false; |
| |
| return CsectAuxEntOrErr.get().isLabel(); |
| } |
| |
| std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo, |
| StringRef SymbolName) { |
| assert(SymbolInfo.isXCOFF() && "Must be a XCOFFSymInfo."); |
| |
| std::string Result; |
| // Dummy symbols have no symbol index. |
| if (SymbolInfo.XCOFFSymInfo.Index) |
| Result = |
| ("(idx: " + Twine(*SymbolInfo.XCOFFSymInfo.Index) + ") " + SymbolName) |
| .str(); |
| else |
| Result.append(SymbolName.begin(), SymbolName.end()); |
| |
| if (SymbolInfo.XCOFFSymInfo.StorageMappingClass && |
| !SymbolInfo.XCOFFSymInfo.IsLabel) { |
| const XCOFF::StorageMappingClass Smc = |
| *SymbolInfo.XCOFFSymInfo.StorageMappingClass; |
| Result.append(("[" + XCOFF::getMappingClassString(Smc) + "]").str()); |
| } |
| |
| return Result; |
| } |
| |
| #define PRINTBOOL(Prefix, Obj, Field) \ |
| OS << Prefix << " " << ((Obj.Field()) ? "+" : "-") << #Field |
| |
| #define PRINTGET(Prefix, Obj, Field) \ |
| OS << Prefix << " " << #Field << " = " \ |
| << static_cast<unsigned>(Obj.get##Field()) |
| |
| #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(); \ |
| } |
| |
| void objdump::dumpTracebackTable(ArrayRef<uint8_t> Bytes, uint64_t Address, |
| formatted_raw_ostream &OS, uint64_t End, |
| const MCSubtargetInfo &STI, |
| const XCOFFObjectFile *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 start\n"; |
| Index += 4; |
| |
| uint64_t Size = End - Address; |
| bool Is64Bit = Obj->is64Bit(); |
| |
| // XCOFFTracebackTable::create modifies the size parameter, so ensure Size |
| // isn't changed. |
| uint64_t SizeCopy = End - Address; |
| Expected<XCOFFTracebackTable> TTOrErr = |
| XCOFFTracebackTable::create(Bytes.data() + Index, SizeCopy, Is64Bit); |
| |
| if (!TTOrErr) { |
| std::string WarningMsgStr; |
| raw_string_ostream WarningStream(WarningMsgStr); |
| WarningStream << "failure parsing traceback table with address: 0x" |
| << utohexstr(Address) + "\n>>> " |
| << toString(TTOrErr.takeError()) |
| << "\n>>> Raw traceback table data is:\n"; |
| |
| uint64_t LastNonZero = Index; |
| for (uint64_t I = Index; I < Size; I += 4) |
| if (support::endian::read32be(Bytes.slice(I, 4).data()) != 0) |
| LastNonZero = I + 4 > Size ? Size : I + 4; |
| |
| if (Size - LastNonZero <= 4) |
| LastNonZero = Size; |
| |
| formatted_raw_ostream FOS(WarningStream); |
| while (Index < LastNonZero) { |
| printRawData(Bytes.slice(Index, 4), Address + Index, FOS, STI); |
| Index += 4; |
| WarningStream << '\n'; |
| } |
| |
| // Print all remaining zeroes as ... |
| if (Size - LastNonZero >= 8) |
| WarningStream << "\t\t...\n"; |
| |
| reportWarning(WarningMsgStr, Obj->getFileName()); |
| return; |
| } |
| |
| auto PrintBytes = [&](uint64_t N) { |
| printRawData(Bytes.slice(Index, N), Address + Index, OS, STI); |
| Index += N; |
| }; |
| |
| XCOFFTracebackTable TbTable = *TTOrErr; |
| // Print the first of the 8 bytes of mandatory fields. |
| PrintBytes(1); |
| OS << format("\t# Version = %i", TbTable.getVersion()) << '\n'; |
| |
| // Print the second of the 8 bytes of mandatory fields. |
| PrintBytes(1); |
| TracebackTable::LanguageID LangId = |
| static_cast<TracebackTable::LanguageID>(TbTable.getLanguageID()); |
| OS << "\t# Language = " << getNameForTracebackTableLanguageId(LangId) << '\n'; |
| |
| auto Split = [&]() { |
| OS << '\n'; |
| OS.indent(TabStop); |
| }; |
| |
| // Print the third of the 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 of the 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 of the 8 bytes of mandatory fields. |
| PrintBytes(1); |
| PRINTBOOL("\t#", TbTable, isBackChainStored); |
| PRINTBOOL(",", TbTable, isFixup); |
| PRINTGET(",", TbTable, NumOfFPRsSaved); |
| OS << '\n'; |
| |
| // Print the 6th of the 8 bytes of mandatory fields. |
| PrintBytes(1); |
| PRINTBOOL("\t#", TbTable, hasExtensionTable); |
| PRINTBOOL(",", TbTable, hasVectorInfo); |
| PRINTGET(",", TbTable, NumOfGPRsSaved); |
| OS << '\n'; |
| |
| // Print the 7th of the 8 bytes of mandatory fields. |
| PrintBytes(1); |
| PRINTGET("\t#", TbTable, NumberOfFixedParms); |
| OS << '\n'; |
| |
| // Print the 8th of the 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<uint32_t, 8> Disp = *TbTable.getControlledStorageInfoDisp(); |
| for (unsigned I = 0; I < Disp.size(); ++I) { |
| OS << '\n'; |
| PrintBytes(4); |
| OS << "\t" << (I ? " " : "#") << " ControlledStorageInfoDisp[" << I |
| << "] = " << Disp[I]; |
| } |
| } |
| |
| // If there is a name, print the function name and function name length. |
| if (TbTable.isFuncNamePresent()) { |
| uint16_t FunctionNameLen = TbTable.getFunctionName()->size(); |
| if (FunctionNameLen == 0) { |
| OS << '\n'; |
| reportWarning( |
| "the length of the function name must be greater than zero if the " |
| "isFuncNamePresent bit is set in the traceback table", |
| Obj->getFileName()); |
| return; |
| } |
| |
| 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(); |
| HasPrinted = true; |
| } |
| } |
| } |
| |
| if (TbTable.isAllocaUsed()) { |
| OS << '\n'; |
| PrintBytes(1); |
| OS << format("\t# AllocaRegister = %u", *TbTable.getAllocaRegister()); |
| } |
| |
| if (TbTable.getVectorExt()) { |
| OS << '\n'; |
| TBVectorExt VecExt = *TbTable.getVectorExt(); |
| // 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<ExtendedTBTableFlag>(*TbTable.getExtensionTable()); |
| 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 eh info displacement"; |
| } |
| OS << '\n'; |
| // The size of the displacement (address) is 4 bytes in 32-bit object files, |
| // and 8 bytes in 64-bit object files. |
| PrintBytes(4); |
| OS << "\t# EH info displacement"; |
| if (Is64Bit) { |
| OS << '\n'; |
| PrintBytes(4); |
| } |
| } |
| |
| OS << '\n'; |
| if (End == Address + Index) |
| return; |
| |
| Size = End - Address; |
| |
| const char *LineSuffix = "\t# Padding\n"; |
| auto IsWordZero = [&](uint64_t WordPos) { |
| if (WordPos >= Size) |
| return false; |
| uint64_t LineLength = std::min(4 - WordPos % 4, Size - WordPos); |
| return std::all_of(Bytes.begin() + WordPos, |
| Bytes.begin() + WordPos + LineLength, |
| [](uint8_t Byte) { return Byte == 0; }); |
| }; |
| |
| bool AreWordsZero[] = {IsWordZero(Index), IsWordZero(alignTo(Index, 4) + 4), |
| IsWordZero(alignTo(Index, 4) + 8)}; |
| bool ShouldPrintLine = true; |
| while (true) { |
| // Determine the length of the line (4, except for the first line, which |
| // will be just enough to align to the word boundary, and the last line, |
| // which will be the remainder of the data). |
| uint64_t LineLength = std::min(4 - Index % 4, Size - Index); |
| if (ShouldPrintLine) { |
| // Print the line. |
| printRawData(Bytes.slice(Index, LineLength), Address + Index, OS, STI); |
| OS << LineSuffix; |
| LineSuffix = "\n"; |
| } |
| |
| Index += LineLength; |
| if (Index == Size) |
| return; |
| |
| // For 3 or more consecutive lines of zeros, skip all but the first one, and |
| // replace them with "...". |
| if (AreWordsZero[0] && AreWordsZero[1] && AreWordsZero[2]) { |
| if (ShouldPrintLine) |
| OS << std::string(8, ' ') << "...\n"; |
| ShouldPrintLine = false; |
| } else if (!AreWordsZero[1]) { |
| // We have reached the end of a skipped block of zeros. |
| ShouldPrintLine = true; |
| } |
| AreWordsZero[0] = AreWordsZero[1]; |
| AreWordsZero[1] = AreWordsZero[2]; |
| AreWordsZero[2] = IsWordZero(Index + 8); |
| } |
| } |
| #undef PRINTBOOL |
| #undef PRINTGET |
| #undef PRINTOPTIONAL |