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>(args: 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 Args: "invalid symbol reference in relocation entry",
52 Args: 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(MangledName: *SymNameOrErr) : SymNameOrErr->str();
60 if (SymbolDescription)
61 SymName = getXCOFFSymbolDescription(SymbolInfo: createSymbolInfo(Obj, Symbol: *SymI), SymbolName: SymName);
62
63 Result.append(in_start: SymName.begin(), in_end: 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(Ref: 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(Ref: 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(Ref: 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(first: SymbolName.begin(), last: SymbolName.end());
123
124 if (SymbolInfo.XCOFFSymInfo.StorageMappingClass &&
125 !SymbolInfo.XCOFFSymInfo.IsLabel) {
126 const XCOFF::StorageMappingClass Smc =
127 *SymbolInfo.XCOFFSymInfo.StorageMappingClass;
128 Result.append(str: ("[" + XCOFF::getMappingClassString(SMC: 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: Bytes.slice(N: Index, M: 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(Ptr: Bytes.data() + Index, Size&: SizeCopy, Is64Bits: 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(X: Address) + "\n>>> "
174 << toString(E: 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(P: Bytes.slice(N: I, M: 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: Bytes.slice(N: Index, M: 4), Address: Address + Index, OS&: 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(Message: WarningMsgStr, File: Obj->getFileName());
197 return;
198 }
199
200 auto PrintBytes = [&](uint64_t N) {
201 printRawData(Bytes: Bytes.slice(N: Index, M: N), Address: 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(Fmt: "\t# Version = %i", Vals: 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(NumSpaces: 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 Message: "the length of the function name must be greater than zero if the "
294 "isFuncNamePresent bit is set in the traceback table",
295 File: 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: Bytes.slice(N: Index, M: PrintLen), Address: 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(Fmt: "\t# AllocaRegister = %u", Vals: *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(a: 4 - WordPos % 4, b: Size - WordPos);
387 return std::all_of(first: Bytes.begin() + WordPos,
388 last: Bytes.begin() + WordPos + LineLength,
389 pred: [](uint8_t Byte) { return Byte == 0; });
390 };
391
392 bool AreWordsZero[] = {IsWordZero(Index), IsWordZero(alignTo(Value: Index, Align: 4) + 4),
393 IsWordZero(alignTo(Value: Index, Align: 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(a: 4 - Index % 4, b: Size - Index);
400 if (ShouldPrintLine) {
401 // Print the line.
402 printRawData(Bytes: Bytes.slice(N: Index, M: LineLength), Address: 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