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