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 
26 using namespace llvm;
27 using namespace llvm::object;
28 using namespace llvm::XCOFF;
29 using namespace llvm::support;
30 
31 namespace {
32 class XCOFFDumper : public objdump::Dumper {
33 public:
XCOFFDumper(const object::XCOFFObjectFile & O)34   XCOFFDumper(const object::XCOFFObjectFile &O) : Dumper(O) {}
printPrivateHeaders()35   void printPrivateHeaders() override {}
36 };
37 } // namespace
38 
39 std::unique_ptr<objdump::Dumper>
createXCOFFDumper(const object::XCOFFObjectFile & Obj)40 objdump::createXCOFFDumper(const object::XCOFFObjectFile &Obj) {
41   return std::make_unique<XCOFFDumper>(Obj);
42 }
43 
getXCOFFRelocationValueString(const XCOFFObjectFile & Obj,const RelocationRef & Rel,bool SymbolDescription,SmallVectorImpl<char> & Result)44 Error 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 
67 std::optional<XCOFF::StorageMappingClass>
getXCOFFSymbolCsectSMC(const XCOFFObjectFile & Obj,const SymbolRef & Sym)68 objdump::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 
82 std::optional<object::SymbolRef>
getXCOFFSymbolContainingSymbolRef(const XCOFFObjectFile & Obj,const SymbolRef & Sym)83 objdump::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 
isLabel(const XCOFFObjectFile & Obj,const SymbolRef & Sym)99 bool 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 
getXCOFFSymbolDescription(const SymbolInfoTy & SymbolInfo,StringRef SymbolName)111 std::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 
dumpTracebackTable(ArrayRef<uint8_t> Bytes,uint64_t Address,formatted_raw_ostream & OS,uint64_t End,const MCSubtargetInfo & STI,const XCOFFObjectFile * Obj)149 void 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