mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 12:56:08 +00:00

[llvm-objdump] Print out xcoff load section of xcoff object file with option private-headers
744 lines
28 KiB
C++
744 lines
28 KiB
C++
//===-- 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
|