mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 17:46:04 +00:00

This presents misleading and confusing output. If you have a function defined at the beginning of an XCOFF object file, and you have a function call to an external function, the function call disassembles as a branch to the local function. That is, `void f() { f(); g();}` disassembles as >00000000 <.f>: 0: 7c 08 02 a6 mflr 0 4: 94 21 ff c0 stwu 1, -64(1) 8: 90 01 00 48 stw 0, 72(1) c: 4b ff ff f5 bl 0x0 <.f> 10: 4b ff ff f1 bl 0x0 <.f> With this PR, the second call will display: `10: 4b ff ff f1 bl 0x0 <.g> ` Using -r can help, but you still get the confusing output: >10: 4b ff ff f1 bl 0x0 <.f> 00000010: R_RBR .g
429 lines
14 KiB
C++
429 lines
14 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/Casting.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/FormattedStream.h"
|
|
#include <algorithm>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::object;
|
|
using namespace llvm::XCOFF;
|
|
using namespace llvm::support;
|
|
|
|
namespace {
|
|
class XCOFFDumper : public objdump::Dumper {
|
|
public:
|
|
XCOFFDumper(const object::XCOFFObjectFile &O) : Dumper(O) {}
|
|
void printPrivateHeaders() override {}
|
|
};
|
|
} // 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
|