1//===-- XCOFFDump.cpp - XCOFF-specific dumper -----------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file implements the XCOFF-specific dumper for llvm-objdump.
11///
12//===----------------------------------------------------------------------===//
13
14#include "XCOFFDump.h"
15
16#include "llvm-objdump.h"
17#include "llvm/ADT/StringExtras.h"
18#include "llvm/Demangle/Demangle.h"
19#include "llvm/MC/MCInstPrinter.h"
20#include "llvm/MC/MCSubtargetInfo.h"
21#include "llvm/Support/Casting.h"
22#include "llvm/Support/Endian.h"
23#include "llvm/Support/FormattedStream.h"
24#include <algorithm>
25
26using namespace llvm;
27using namespace llvm::object;
28using namespace llvm::XCOFF;
29using namespace llvm::support;
30
31namespace {
32class XCOFFDumper : public objdump::Dumper {
33public:
34  XCOFFDumper(const object::XCOFFObjectFile &O) : Dumper(O) {}
35  void printPrivateHeaders() override {}
36};
37} // namespace
38
39std::unique_ptr<objdump::Dumper>
40objdump::createXCOFFDumper(const object::XCOFFObjectFile &Obj) {
41  return std::make_unique<XCOFFDumper>(Obj);
42}
43
44Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile &Obj,
45                                             const RelocationRef &Rel,
46                                             bool SymbolDescription,
47                                             SmallVectorImpl<char> &Result) {
48  symbol_iterator SymI = Rel.getSymbol();
49  if (SymI == Obj.symbol_end())
50    return make_error<GenericBinaryError>(
51        "invalid symbol reference in relocation entry",
52        object_error::parse_failed);
53
54  Expected<StringRef> SymNameOrErr = SymI->getName();
55  if (!SymNameOrErr)
56    return SymNameOrErr.takeError();
57
58  std::string SymName =
59      Demangle ? demangle(*SymNameOrErr) : SymNameOrErr->str();
60  if (SymbolDescription)
61    SymName = getXCOFFSymbolDescription(createSymbolInfo(Obj, *SymI), SymName);
62
63  Result.append(SymName.begin(), SymName.end());
64  return Error::success();
65}
66
67std::optional<XCOFF::StorageMappingClass>
68objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile &Obj,
69                                const SymbolRef &Sym) {
70  const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl());
71
72  if (!SymRef.isCsectSymbol())
73    return std::nullopt;
74
75  auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
76  if (!CsectAuxEntOrErr)
77    return std::nullopt;
78
79  return CsectAuxEntOrErr.get().getStorageMappingClass();
80}
81
82std::optional<object::SymbolRef>
83objdump::getXCOFFSymbolContainingSymbolRef(const XCOFFObjectFile &Obj,
84                                           const SymbolRef &Sym) {
85  const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl());
86  if (!SymRef.isCsectSymbol())
87    return std::nullopt;
88
89  Expected<XCOFFCsectAuxRef> CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
90  if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel())
91    return std::nullopt;
92  uint32_t Idx =
93      static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength());
94  DataRefImpl DRI;
95  DRI.p = Obj.getSymbolByIndex(Idx);
96  return SymbolRef(DRI, &Obj);
97}
98
99bool objdump::isLabel(const XCOFFObjectFile &Obj, const SymbolRef &Sym) {
100  const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl());
101  if (!SymRef.isCsectSymbol())
102    return false;
103
104  auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
105  if (!CsectAuxEntOrErr)
106    return false;
107
108  return CsectAuxEntOrErr.get().isLabel();
109}
110
111std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
112                                               StringRef SymbolName) {
113  assert(SymbolInfo.isXCOFF() && "Must be a XCOFFSymInfo.");
114
115  std::string Result;
116  // Dummy symbols have no symbol index.
117  if (SymbolInfo.XCOFFSymInfo.Index)
118    Result =
119        ("(idx: " + Twine(*SymbolInfo.XCOFFSymInfo.Index) + ") " + SymbolName)
120            .str();
121  else
122    Result.append(SymbolName.begin(), SymbolName.end());
123
124  if (SymbolInfo.XCOFFSymInfo.StorageMappingClass &&
125      !SymbolInfo.XCOFFSymInfo.IsLabel) {
126    const XCOFF::StorageMappingClass Smc =
127        *SymbolInfo.XCOFFSymInfo.StorageMappingClass;
128    Result.append(("[" + XCOFF::getMappingClassString(Smc) + "]").str());
129  }
130
131  return Result;
132}
133
134#define PRINTBOOL(Prefix, Obj, Field)                                          \
135  OS << Prefix << " " << ((Obj.Field()) ? "+" : "-") << #Field
136
137#define PRINTGET(Prefix, Obj, Field)                                           \
138  OS << Prefix << " " << #Field << " = "                                       \
139     << static_cast<unsigned>(Obj.get##Field())
140
141#define PRINTOPTIONAL(Field)                                                   \
142  if (TbTable.get##Field()) {                                                  \
143    OS << '\n';                                                                \
144    printRawData(Bytes.slice(Index, 4), Address + Index, OS, STI);             \
145    Index += 4;                                                                \
146    OS << "\t# " << #Field << " = " << *TbTable.get##Field();                  \
147  }
148
149void objdump::dumpTracebackTable(ArrayRef<uint8_t> Bytes, uint64_t Address,
150                                 formatted_raw_ostream &OS, uint64_t End,
151                                 const MCSubtargetInfo &STI,
152                                 const XCOFFObjectFile *Obj) {
153  uint64_t Index = 0;
154  unsigned TabStop = getInstStartColumn(STI) - 1;
155  // Print traceback table boundary.
156  printRawData(Bytes.slice(Index, 4), Address, OS, STI);
157  OS << "\t# Traceback table start\n";
158  Index += 4;
159
160  uint64_t Size = End - Address;
161  bool Is64Bit = Obj->is64Bit();
162
163  // XCOFFTracebackTable::create modifies the size parameter, so ensure Size
164  // isn't changed.
165  uint64_t SizeCopy = End - Address;
166  Expected<XCOFFTracebackTable> TTOrErr =
167      XCOFFTracebackTable::create(Bytes.data() + Index, SizeCopy, Is64Bit);
168
169  if (!TTOrErr) {
170    std::string WarningMsgStr;
171    raw_string_ostream WarningStream(WarningMsgStr);
172    WarningStream << "failure parsing traceback table with address: 0x"
173                  << utohexstr(Address) + "\n>>> "
174                  << toString(TTOrErr.takeError())
175                  << "\n>>> Raw traceback table data is:\n";
176
177    uint64_t LastNonZero = Index;
178    for (uint64_t I = Index; I < Size; I += 4)
179      if (support::endian::read32be(Bytes.slice(I, 4).data()) != 0)
180        LastNonZero = I + 4 > Size ? Size : I + 4;
181
182    if (Size - LastNonZero <= 4)
183      LastNonZero = Size;
184
185    formatted_raw_ostream FOS(WarningStream);
186    while (Index < LastNonZero) {
187      printRawData(Bytes.slice(Index, 4), Address + Index, FOS, STI);
188      Index += 4;
189      WarningStream << '\n';
190    }
191
192    // Print all remaining zeroes as ...
193    if (Size - LastNonZero >= 8)
194      WarningStream << "\t\t...\n";
195
196    reportWarning(WarningMsgStr, Obj->getFileName());
197    return;
198  }
199
200  auto PrintBytes = [&](uint64_t N) {
201    printRawData(Bytes.slice(Index, N), Address + Index, OS, STI);
202    Index += N;
203  };
204
205  XCOFFTracebackTable TbTable = *TTOrErr;
206  // Print the first of the 8 bytes of mandatory fields.
207  PrintBytes(1);
208  OS << format("\t# Version = %i", TbTable.getVersion()) << '\n';
209
210  // Print the second of the 8 bytes of mandatory fields.
211  PrintBytes(1);
212  TracebackTable::LanguageID LangId =
213      static_cast<TracebackTable::LanguageID>(TbTable.getLanguageID());
214  OS << "\t# Language = " << getNameForTracebackTableLanguageId(LangId) << '\n';
215
216  auto Split = [&]() {
217    OS << '\n';
218    OS.indent(TabStop);
219  };
220
221  // Print the third of the 8 bytes of mandatory fields.
222  PrintBytes(1);
223  PRINTBOOL("\t#", TbTable, isGlobalLinkage);
224  PRINTBOOL(",", TbTable, isOutOfLineEpilogOrPrologue);
225  Split();
226  PRINTBOOL("\t ", TbTable, hasTraceBackTableOffset);
227  PRINTBOOL(",", TbTable, isInternalProcedure);
228  Split();
229  PRINTBOOL("\t ", TbTable, hasControlledStorage);
230  PRINTBOOL(",", TbTable, isTOCless);
231  Split();
232  PRINTBOOL("\t ", TbTable, isFloatingPointPresent);
233  Split();
234  PRINTBOOL("\t ", TbTable, isFloatingPointOperationLogOrAbortEnabled);
235  OS << '\n';
236
237  // Print the 4th of the 8 bytes of mandatory fields.
238  PrintBytes(1);
239  PRINTBOOL("\t#", TbTable, isInterruptHandler);
240  PRINTBOOL(",", TbTable, isFuncNamePresent);
241  PRINTBOOL(",", TbTable, isAllocaUsed);
242  Split();
243  PRINTGET("\t ", TbTable, OnConditionDirective);
244  PRINTBOOL(",", TbTable, isCRSaved);
245  PRINTBOOL(",", TbTable, isLRSaved);
246  OS << '\n';
247
248  // Print the 5th of the 8 bytes of mandatory fields.
249  PrintBytes(1);
250  PRINTBOOL("\t#", TbTable, isBackChainStored);
251  PRINTBOOL(",", TbTable, isFixup);
252  PRINTGET(",", TbTable, NumOfFPRsSaved);
253  OS << '\n';
254
255  // Print the 6th of the 8 bytes of mandatory fields.
256  PrintBytes(1);
257  PRINTBOOL("\t#", TbTable, hasExtensionTable);
258  PRINTBOOL(",", TbTable, hasVectorInfo);
259  PRINTGET(",", TbTable, NumOfGPRsSaved);
260  OS << '\n';
261
262  // Print the 7th of the 8 bytes of mandatory fields.
263  PrintBytes(1);
264  PRINTGET("\t#", TbTable, NumberOfFixedParms);
265  OS << '\n';
266
267  // Print the 8th of the 8 bytes of mandatory fields.
268  PrintBytes(1);
269  PRINTGET("\t#", TbTable, NumberOfFPParms);
270  PRINTBOOL(",", TbTable, hasParmsOnStack);
271
272  PRINTOPTIONAL(ParmsType);
273  PRINTOPTIONAL(TraceBackTableOffset);
274  PRINTOPTIONAL(HandlerMask);
275  PRINTOPTIONAL(NumOfCtlAnchors);
276
277  if (TbTable.getControlledStorageInfoDisp()) {
278    SmallVector<uint32_t, 8> Disp = *TbTable.getControlledStorageInfoDisp();
279    for (unsigned I = 0; I < Disp.size(); ++I) {
280      OS << '\n';
281      PrintBytes(4);
282      OS << "\t" << (I ? " " : "#") << " ControlledStorageInfoDisp[" << I
283         << "] = " << Disp[I];
284    }
285  }
286
287  // If there is a name, print the function name and function name length.
288  if (TbTable.isFuncNamePresent()) {
289    uint16_t FunctionNameLen = TbTable.getFunctionName()->size();
290    if (FunctionNameLen == 0) {
291      OS << '\n';
292      reportWarning(
293          "the length of the function name must be greater than zero if the "
294          "isFuncNamePresent bit is set in the traceback table",
295          Obj->getFileName());
296      return;
297    }
298
299    OS << '\n';
300    PrintBytes(2);
301    OS << "\t# FunctionNameLen = " << FunctionNameLen;
302
303    uint16_t RemainingBytes = FunctionNameLen;
304    bool HasPrinted = false;
305    while (RemainingBytes > 0) {
306      OS << '\n';
307      uint16_t PrintLen = RemainingBytes >= 4 ? 4 : RemainingBytes;
308      printRawData(Bytes.slice(Index, PrintLen), Address + Index, OS, STI);
309      Index += PrintLen;
310      RemainingBytes -= PrintLen;
311
312      if (!HasPrinted) {
313        OS << "\t# FunctionName = " << *TbTable.getFunctionName();
314        HasPrinted = true;
315      }
316    }
317  }
318
319  if (TbTable.isAllocaUsed()) {
320    OS << '\n';
321    PrintBytes(1);
322    OS << format("\t# AllocaRegister = %u", *TbTable.getAllocaRegister());
323  }
324
325  if (TbTable.getVectorExt()) {
326    OS << '\n';
327    TBVectorExt VecExt = *TbTable.getVectorExt();
328    // Print first byte of VectorExt.
329    PrintBytes(1);
330    PRINTGET("\t#", VecExt, NumberOfVRSaved);
331    PRINTBOOL(",", VecExt, isVRSavedOnStack);
332    PRINTBOOL(",", VecExt, hasVarArgs);
333    OS << '\n';
334
335    // Print the second byte of VectorExt.
336    PrintBytes(1);
337    PRINTGET("\t#", VecExt, NumberOfVectorParms);
338    PRINTBOOL(",", VecExt, hasVMXInstruction);
339    OS << '\n';
340
341    PrintBytes(4);
342    OS << "\t# VectorParmsInfoString = " << VecExt.getVectorParmsInfo();
343
344    // There are two bytes of padding after vector info.
345    OS << '\n';
346    PrintBytes(2);
347    OS << "\t# Padding";
348  }
349
350  if (TbTable.getExtensionTable()) {
351    OS << '\n';
352    PrintBytes(1);
353    ExtendedTBTableFlag Flag =
354        static_cast<ExtendedTBTableFlag>(*TbTable.getExtensionTable());
355    OS << "\t# ExtensionTable = " << getExtendedTBTableFlagString(Flag);
356  }
357
358  if (TbTable.getEhInfoDisp()) {
359    // There are 4 bytes alignment before eh info displacement.
360    if (Index % 4) {
361      OS << '\n';
362      PrintBytes(4 - Index % 4);
363      OS << "\t# Alignment padding for eh info displacement";
364    }
365    OS << '\n';
366    // The size of the displacement (address) is 4 bytes in 32-bit object files,
367    // and 8 bytes in 64-bit object files.
368    PrintBytes(4);
369    OS << "\t# EH info displacement";
370    if (Is64Bit) {
371      OS << '\n';
372      PrintBytes(4);
373    }
374  }
375
376  OS << '\n';
377  if (End == Address + Index)
378    return;
379
380  Size = End - Address;
381
382  const char *LineSuffix = "\t# Padding\n";
383  auto IsWordZero = [&](uint64_t WordPos) {
384    if (WordPos >= Size)
385      return false;
386    uint64_t LineLength = std::min(4 - WordPos % 4, Size - WordPos);
387    return std::all_of(Bytes.begin() + WordPos,
388                       Bytes.begin() + WordPos + LineLength,
389                       [](uint8_t Byte) { return Byte == 0; });
390  };
391
392  bool AreWordsZero[] = {IsWordZero(Index), IsWordZero(alignTo(Index, 4) + 4),
393                         IsWordZero(alignTo(Index, 4) + 8)};
394  bool ShouldPrintLine = true;
395  while (true) {
396    // Determine the length of the line (4, except for the first line, which
397    // will be just enough to align to the word boundary, and the last line,
398    // which will be the remainder of the data).
399    uint64_t LineLength = std::min(4 - Index % 4, Size - Index);
400    if (ShouldPrintLine) {
401      // Print the line.
402      printRawData(Bytes.slice(Index, LineLength), Address + Index, OS, STI);
403      OS << LineSuffix;
404      LineSuffix = "\n";
405    }
406
407    Index += LineLength;
408    if (Index == Size)
409      return;
410
411    // For 3 or more consecutive lines of zeros, skip all but the first one, and
412    // replace them with "...".
413    if (AreWordsZero[0] && AreWordsZero[1] && AreWordsZero[2]) {
414      if (ShouldPrintLine)
415        OS << std::string(8, ' ') << "...\n";
416      ShouldPrintLine = false;
417    } else if (!AreWordsZero[1]) {
418      // We have reached the end of a skipped block of zeros.
419      ShouldPrintLine = true;
420    }
421    AreWordsZero[0] = AreWordsZero[1];
422    AreWordsZero[1] = AreWordsZero[2];
423    AreWordsZero[2] = IsWordZero(Index + 8);
424  }
425}
426#undef PRINTBOOL
427#undef PRINTGET
428#undef PRINTOPTIONAL
429