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/Endian.h"
22#include "llvm/Support/Format.h"
23#include "llvm/Support/FormattedStream.h"
24#include "llvm/Support/raw_ostream.h"
25#include <algorithm>
26
27using namespace llvm;
28using namespace llvm::object;
29using namespace llvm::XCOFF;
30using namespace llvm::support;
31
32namespace {
33class XCOFFDumper : public objdump::Dumper {
34 enum PrintStyle { Hex, Number };
35 const XCOFFObjectFile &Obj;
36 unsigned Width;
37
38public:
39 XCOFFDumper(const object::XCOFFObjectFile &O) : Dumper(O), Obj(O) {}
40
41private:
42 void printPrivateHeaders() override;
43 void printFileHeader();
44 void printAuxiliaryHeader();
45 void printLoaderSectionHeader();
46 void printAuxiliaryHeader(const XCOFFAuxiliaryHeader32 *AuxHeader);
47 void printAuxiliaryHeader(const XCOFFAuxiliaryHeader64 *AuxHeader);
48 template <typename AuxHeaderMemberType, typename XCOFFAuxiliaryHeader>
49 void printAuxMemberHelper(PrintStyle Style, const char *MemberName,
50 const AuxHeaderMemberType &Member,
51 const XCOFFAuxiliaryHeader *AuxHeader,
52 uint16_t AuxSize, uint16_t &PartialFieldOffset,
53 const char *&PartialFieldName);
54 template <typename XCOFFAuxiliaryHeader>
55 void checkAndPrintAuxHeaderParseError(const char *PartialFieldName,
56 uint16_t PartialFieldOffset,
57 uint16_t AuxSize,
58 XCOFFAuxiliaryHeader &AuxHeader);
59
60 void printBinary(StringRef Name, ArrayRef<uint8_t> Data);
61 void printHex(StringRef Name, uint64_t Value);
62 void printNumber(StringRef Name, uint64_t Value);
63 FormattedString formatName(StringRef Name);
64 void printStrHex(StringRef Name, StringRef Str, uint64_t Value);
65};
66
67void XCOFFDumper::printPrivateHeaders() {
68 printFileHeader();
69 printAuxiliaryHeader();
70 printLoaderSectionHeader();
71}
72
73FormattedString XCOFFDumper::formatName(StringRef Name) {
74 return FormattedString(Name, Width, FormattedString::JustifyLeft);
75}
76
77void XCOFFDumper::printHex(StringRef Name, uint64_t Value) {
78 outs() << formatName(Name) << format_hex(N: Value, Width: 0) << "\n";
79}
80
81void XCOFFDumper::printNumber(StringRef Name, uint64_t Value) {
82 outs() << formatName(Name) << format_decimal(N: Value, Width: 0) << "\n";
83}
84
85void XCOFFDumper::printStrHex(StringRef Name, StringRef Str, uint64_t Value) {
86 outs() << formatName(Name) << Str << " (" << format_decimal(N: Value, Width: 0)
87 << ")\n";
88}
89
90void XCOFFDumper::printBinary(StringRef Name, ArrayRef<uint8_t> Data) {
91 unsigned OrgWidth = Width;
92 Width = 0;
93 outs() << formatName(Name) << " (" << format_bytes(Bytes: Data) << ")\n";
94 Width = OrgWidth;
95}
96
97void XCOFFDumper::printAuxiliaryHeader() {
98 Width = 36;
99 if (Obj.is64Bit())
100 printAuxiliaryHeader(AuxHeader: Obj.auxiliaryHeader64());
101 else
102 printAuxiliaryHeader(AuxHeader: Obj.auxiliaryHeader32());
103}
104
105template <typename AuxHeaderMemberType, typename XCOFFAuxiliaryHeader>
106void XCOFFDumper::printAuxMemberHelper(PrintStyle Style, const char *MemberName,
107 const AuxHeaderMemberType &Member,
108 const XCOFFAuxiliaryHeader *AuxHeader,
109 uint16_t AuxSize,
110 uint16_t &PartialFieldOffset,
111 const char *&PartialFieldName) {
112 ptrdiff_t Offset = reinterpret_cast<const char *>(&Member) -
113 reinterpret_cast<const char *>(AuxHeader);
114 if (Offset + sizeof(Member) <= AuxSize) {
115 if (Style == Hex)
116 printHex(Name: MemberName, Value: Member);
117 else
118 printNumber(Name: MemberName, Value: Member);
119 } else if (Offset < AuxSize) {
120 PartialFieldOffset = Offset;
121 PartialFieldName = MemberName;
122 }
123}
124
125template <typename XCOFFAuxiliaryHeader>
126void XCOFFDumper::checkAndPrintAuxHeaderParseError(
127 const char *PartialFieldName, uint16_t PartialFieldOffset, uint16_t AuxSize,
128 XCOFFAuxiliaryHeader &AuxHeader) {
129 if (PartialFieldOffset < AuxSize) {
130 std::string Buf;
131 raw_string_ostream OS(Buf);
132 OS << FormattedString("Raw data", 0, FormattedString::JustifyLeft) << " ("
133 << format_bytes(
134 Bytes: ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&AuxHeader) +
135 PartialFieldOffset,
136 AuxSize - PartialFieldOffset))
137 << ")\n";
138 reportUniqueWarning(Msg: Twine("only partial field for ") + PartialFieldName +
139 " at offset (" + Twine(PartialFieldOffset) + ")\n" +
140 OS.str());
141 } else if (sizeof(AuxHeader) < AuxSize) {
142 printBinary(
143 Name: "Extra raw data",
144 Data: ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&AuxHeader) +
145 sizeof(AuxHeader),
146 AuxSize - sizeof(AuxHeader)));
147 }
148}
149
150void XCOFFDumper::printAuxiliaryHeader(
151 const XCOFFAuxiliaryHeader32 *AuxHeader) {
152 if (AuxHeader == nullptr)
153 return;
154 outs() << "\n---Auxiliary Header:\n";
155 uint16_t AuxSize = Obj.getOptionalHeaderSize();
156 uint16_t PartialFieldOffset = AuxSize;
157 const char *PartialFieldName = nullptr;
158
159 auto PrintAuxMember = [&](PrintStyle Style, const char *MemberName,
160 auto &Member) {
161 printAuxMemberHelper(Style, MemberName, Member, AuxHeader, AuxSize,
162 PartialFieldOffset, PartialFieldName);
163 };
164
165 PrintAuxMember(Hex, "Magic:", AuxHeader->AuxMagic);
166 PrintAuxMember(Hex, "Version:", AuxHeader->Version);
167 PrintAuxMember(Hex, "Size of .text section:", AuxHeader->TextSize);
168 PrintAuxMember(Hex, "Size of .data section:", AuxHeader->InitDataSize);
169 PrintAuxMember(Hex, "Size of .bss section:", AuxHeader->BssDataSize);
170 PrintAuxMember(Hex, "Entry point address:", AuxHeader->EntryPointAddr);
171 PrintAuxMember(Hex, ".text section start address:", AuxHeader->TextStartAddr);
172 PrintAuxMember(Hex, ".data section start address:", AuxHeader->DataStartAddr);
173 PrintAuxMember(Hex, "TOC anchor address:", AuxHeader->TOCAnchorAddr);
174 PrintAuxMember(
175 Number, "Section number of entryPoint:", AuxHeader->SecNumOfEntryPoint);
176 PrintAuxMember(Number, "Section number of .text:", AuxHeader->SecNumOfText);
177 PrintAuxMember(Number, "Section number of .data:", AuxHeader->SecNumOfData);
178 PrintAuxMember(Number, "Section number of TOC:", AuxHeader->SecNumOfTOC);
179 PrintAuxMember(Number,
180 "Section number of loader data:", AuxHeader->SecNumOfLoader);
181 PrintAuxMember(Number, "Section number of .bss:", AuxHeader->SecNumOfBSS);
182 PrintAuxMember(Hex, "Maxium alignment of .text:", AuxHeader->MaxAlignOfText);
183 PrintAuxMember(Hex, "Maxium alignment of .data:", AuxHeader->MaxAlignOfData);
184 PrintAuxMember(Hex, "Module type:", AuxHeader->ModuleType);
185 PrintAuxMember(Hex, "CPU type of objects:", AuxHeader->CpuFlag);
186 PrintAuxMember(Hex, "Maximum stack size:", AuxHeader->MaxStackSize);
187 PrintAuxMember(Hex, "Maximum data size:", AuxHeader->MaxDataSize);
188 PrintAuxMember(Hex, "Reserved for debugger:", AuxHeader->ReservedForDebugger);
189 PrintAuxMember(Hex, "Text page size:", AuxHeader->TextPageSize);
190 PrintAuxMember(Hex, "Data page size:", AuxHeader->DataPageSize);
191 PrintAuxMember(Hex, "Stack page size:", AuxHeader->StackPageSize);
192 if (offsetof(XCOFFAuxiliaryHeader32, FlagAndTDataAlignment) +
193 sizeof(XCOFFAuxiliaryHeader32::FlagAndTDataAlignment) <=
194 AuxSize) {
195 printHex(Name: "Flag:", Value: AuxHeader->getFlag());
196 printHex(Name: "Alignment of thread-local storage:",
197 Value: AuxHeader->getTDataAlignment());
198 }
199
200 PrintAuxMember(Number,
201 "Section number for .tdata:", AuxHeader->SecNumOfTData);
202 PrintAuxMember(Number, "Section number for .tbss:", AuxHeader->SecNumOfTBSS);
203
204 checkAndPrintAuxHeaderParseError(PartialFieldName, PartialFieldOffset,
205 AuxSize, AuxHeader: *AuxHeader);
206}
207
208void XCOFFDumper::printAuxiliaryHeader(
209 const XCOFFAuxiliaryHeader64 *AuxHeader) {
210 if (AuxHeader == nullptr)
211 return;
212 uint16_t AuxSize = Obj.getOptionalHeaderSize();
213 outs() << "\n---Auxiliary Header:\n";
214 uint16_t PartialFieldOffset = AuxSize;
215 const char *PartialFieldName = nullptr;
216
217 auto PrintAuxMember = [&](PrintStyle Style, const char *MemberName,
218 auto &Member) {
219 printAuxMemberHelper(Style, MemberName, Member, AuxHeader, AuxSize,
220 PartialFieldOffset, PartialFieldName);
221 };
222
223 PrintAuxMember(Hex, "Magic:", AuxHeader->AuxMagic);
224 PrintAuxMember(Hex, "Version:", AuxHeader->Version);
225 PrintAuxMember(Hex, "Reserved for debugger:", AuxHeader->ReservedForDebugger);
226 PrintAuxMember(Hex, ".text section start address:", AuxHeader->TextStartAddr);
227 PrintAuxMember(Hex, ".data section start address:", AuxHeader->DataStartAddr);
228 PrintAuxMember(Hex, "TOC anchor address:", AuxHeader->TOCAnchorAddr);
229 PrintAuxMember(
230 Number, "Section number of entryPoint:", AuxHeader->SecNumOfEntryPoint);
231 PrintAuxMember(Number, "Section number of .text:", AuxHeader->SecNumOfText);
232 PrintAuxMember(Number, "Section number of .data:", AuxHeader->SecNumOfData);
233 PrintAuxMember(Number, "Section number of TOC:", AuxHeader->SecNumOfTOC);
234 PrintAuxMember(Number,
235 "Section number of loader data:", AuxHeader->SecNumOfLoader);
236 PrintAuxMember(Number, "Section number of .bss:", AuxHeader->SecNumOfBSS);
237 PrintAuxMember(Hex, "Maxium alignment of .text:", AuxHeader->MaxAlignOfText);
238 PrintAuxMember(Hex, "Maxium alignment of .data:", AuxHeader->MaxAlignOfData);
239 PrintAuxMember(Hex, "Module type:", AuxHeader->ModuleType);
240 PrintAuxMember(Hex, "CPU type of objects:", AuxHeader->CpuFlag);
241 PrintAuxMember(Hex, "Text page size:", AuxHeader->TextPageSize);
242 PrintAuxMember(Hex, "Data page size:", AuxHeader->DataPageSize);
243 PrintAuxMember(Hex, "Stack page size:", AuxHeader->StackPageSize);
244 if (offsetof(XCOFFAuxiliaryHeader64, FlagAndTDataAlignment) +
245 sizeof(XCOFFAuxiliaryHeader64::FlagAndTDataAlignment) <=
246 AuxSize) {
247 printHex(Name: "Flag:", Value: AuxHeader->getFlag());
248 printHex(Name: "Alignment of thread-local storage:",
249 Value: AuxHeader->getTDataAlignment());
250 }
251 PrintAuxMember(Hex, "Size of .text section:", AuxHeader->TextSize);
252 PrintAuxMember(Hex, "Size of .data section:", AuxHeader->InitDataSize);
253 PrintAuxMember(Hex, "Size of .bss section:", AuxHeader->BssDataSize);
254 PrintAuxMember(Hex, "Entry point address:", AuxHeader->EntryPointAddr);
255 PrintAuxMember(Hex, "Maximum stack size:", AuxHeader->MaxStackSize);
256 PrintAuxMember(Hex, "Maximum data size:", AuxHeader->MaxDataSize);
257 PrintAuxMember(Number,
258 "Section number for .tdata:", AuxHeader->SecNumOfTData);
259 PrintAuxMember(Number, "Section number for .tbss:", AuxHeader->SecNumOfTBSS);
260 PrintAuxMember(Hex, "Additional flags 64-bit XCOFF:", AuxHeader->XCOFF64Flag);
261
262 checkAndPrintAuxHeaderParseError(PartialFieldName, PartialFieldOffset,
263 AuxSize, AuxHeader: *AuxHeader);
264}
265
266void XCOFFDumper::printLoaderSectionHeader() {
267 Expected<uintptr_t> LoaderSectionAddrOrError =
268 Obj.getSectionFileOffsetToRawData(SectType: XCOFF::STYP_LOADER);
269 if (!LoaderSectionAddrOrError) {
270 reportUniqueWarning(Err: LoaderSectionAddrOrError.takeError());
271 return;
272 }
273 uintptr_t LoaderSectionAddr = LoaderSectionAddrOrError.get();
274
275 if (LoaderSectionAddr == 0)
276 return;
277
278 auto PrintLoadSecHeaderCommon = [&](const auto *LDHeader) {
279 printNumber(Name: "Version:", Value: LDHeader->Version);
280 printNumber(Name: "NumberOfSymbolEntries:", Value: LDHeader->NumberOfSymTabEnt);
281 printNumber(Name: "NumberOfRelocationEntries:", Value: LDHeader->NumberOfRelTabEnt);
282 printNumber(Name: "LengthOfImportFileIDStringTable:",
283 Value: LDHeader->LengthOfImpidStrTbl);
284 printNumber(Name: "NumberOfImportFileIDs:", Value: LDHeader->NumberOfImpid);
285 printHex(Name: "OffsetToImportFileIDs:", Value: LDHeader->OffsetToImpid);
286 printNumber(Name: "LengthOfStringTable:", Value: LDHeader->LengthOfStrTbl);
287 printHex(Name: "OffsetToStringTable:", Value: LDHeader->OffsetToStrTbl);
288 };
289
290 Width = 35;
291 outs() << "\n---Loader Section Header:\n";
292 if (Obj.is64Bit()) {
293 const LoaderSectionHeader64 *LoaderSec64 =
294 reinterpret_cast<const LoaderSectionHeader64 *>(LoaderSectionAddr);
295 PrintLoadSecHeaderCommon(LoaderSec64);
296 printHex(Name: "OffsetToSymbolTable", Value: LoaderSec64->OffsetToSymTbl);
297 printHex(Name: "OffsetToRelocationEntries", Value: LoaderSec64->OffsetToRelEnt);
298 } else {
299 const LoaderSectionHeader32 *LoaderSec32 =
300 reinterpret_cast<const LoaderSectionHeader32 *>(LoaderSectionAddr);
301 PrintLoadSecHeaderCommon(LoaderSec32);
302 }
303}
304
305void XCOFFDumper::printFileHeader() {
306 Width = 20;
307 outs() << "\n---File Header:\n";
308 printHex(Name: "Magic:", Value: Obj.getMagic());
309 printNumber(Name: "NumberOfSections:", Value: Obj.getNumberOfSections());
310
311 int32_t Timestamp = Obj.getTimeStamp();
312 if (Timestamp > 0) {
313 // This handling of the timestamp assumes that the host system's time_t is
314 // compatible with AIX time_t. If a platform is not compatible, the lit
315 // tests will let us know.
316 time_t TimeDate = Timestamp;
317
318 char FormattedTime[20] = {};
319
320 size_t BytesFormatted = std::strftime(s: FormattedTime, maxsize: sizeof(FormattedTime),
321 format: "%F %T", tp: std::gmtime(timer: &TimeDate));
322 assert(BytesFormatted && "The size of the buffer FormattedTime is less "
323 "than the size of the date/time string.");
324 (void)BytesFormatted;
325 printStrHex(Name: "Timestamp:", Str: FormattedTime, Value: Timestamp);
326 } else {
327 // Negative timestamp values are reserved for future use.
328 printStrHex(Name: "Timestamp:", Str: Timestamp == 0 ? "None" : "Reserved Value",
329 Value: Timestamp);
330 }
331
332 // The number of symbol table entries is an unsigned value in 64-bit objects
333 // and a signed value (with negative values being 'reserved') in 32-bit
334 // objects.
335 if (Obj.is64Bit()) {
336 printHex(Name: "SymbolTableOffset:", Value: Obj.getSymbolTableOffset64());
337 printNumber(Name: "SymbolTableEntries:", Value: Obj.getNumberOfSymbolTableEntries64());
338 } else {
339 printHex(Name: "SymbolTableOffset:", Value: Obj.getSymbolTableOffset32());
340 int32_t SymTabEntries = Obj.getRawNumberOfSymbolTableEntries32();
341 if (SymTabEntries >= 0)
342 printNumber(Name: "SymbolTableEntries:", Value: SymTabEntries);
343 else
344 printStrHex(Name: "SymbolTableEntries:", Str: "Reserved Value", Value: SymTabEntries);
345 }
346
347 printHex(Name: "OptionalHeaderSize:", Value: Obj.getOptionalHeaderSize());
348 printHex(Name: "Flags:", Value: Obj.getFlags());
349}
350
351} // namespace
352
353std::unique_ptr<objdump::Dumper>
354objdump::createXCOFFDumper(const object::XCOFFObjectFile &Obj) {
355 return std::make_unique<XCOFFDumper>(args: Obj);
356}
357
358Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile &Obj,
359 const RelocationRef &Rel,
360 bool SymbolDescription,
361 SmallVectorImpl<char> &Result) {
362 symbol_iterator SymI = Rel.getSymbol();
363 if (SymI == Obj.symbol_end())
364 return make_error<GenericBinaryError>(
365 Args: "invalid symbol reference in relocation entry",
366 Args: object_error::parse_failed);
367
368 Expected<StringRef> SymNameOrErr = SymI->getName();
369 if (!SymNameOrErr)
370 return SymNameOrErr.takeError();
371
372 std::string SymName =
373 Demangle ? demangle(MangledName: *SymNameOrErr) : SymNameOrErr->str();
374 if (SymbolDescription)
375 SymName = getXCOFFSymbolDescription(SymbolInfo: createSymbolInfo(Obj, Symbol: *SymI), SymbolName: SymName);
376
377 Result.append(in_start: SymName.begin(), in_end: SymName.end());
378 return Error::success();
379}
380
381std::optional<XCOFF::StorageMappingClass>
382objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile &Obj,
383 const SymbolRef &Sym) {
384 const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Ref: Sym.getRawDataRefImpl());
385
386 if (!SymRef.isCsectSymbol())
387 return std::nullopt;
388
389 auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
390 if (!CsectAuxEntOrErr)
391 return std::nullopt;
392
393 return CsectAuxEntOrErr.get().getStorageMappingClass();
394}
395
396std::optional<object::SymbolRef>
397objdump::getXCOFFSymbolContainingSymbolRef(const XCOFFObjectFile &Obj,
398 const SymbolRef &Sym) {
399 const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Ref: Sym.getRawDataRefImpl());
400 if (!SymRef.isCsectSymbol())
401 return std::nullopt;
402
403 Expected<XCOFFCsectAuxRef> CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
404 if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel())
405 return std::nullopt;
406 uint32_t Idx =
407 static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength());
408 DataRefImpl DRI;
409 DRI.p = Obj.getSymbolByIndex(Idx);
410 return SymbolRef(DRI, &Obj);
411}
412
413bool objdump::isLabel(const XCOFFObjectFile &Obj, const SymbolRef &Sym) {
414 const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Ref: Sym.getRawDataRefImpl());
415 if (!SymRef.isCsectSymbol())
416 return false;
417
418 auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
419 if (!CsectAuxEntOrErr)
420 return false;
421
422 return CsectAuxEntOrErr.get().isLabel();
423}
424
425std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
426 StringRef SymbolName) {
427 assert(SymbolInfo.isXCOFF() && "Must be a XCOFFSymInfo.");
428
429 std::string Result;
430 // Dummy symbols have no symbol index.
431 if (SymbolInfo.XCOFFSymInfo.Index)
432 Result =
433 ("(idx: " + Twine(*SymbolInfo.XCOFFSymInfo.Index) + ") " + SymbolName)
434 .str();
435 else
436 Result.append(first: SymbolName.begin(), last: SymbolName.end());
437
438 if (SymbolInfo.XCOFFSymInfo.StorageMappingClass &&
439 !SymbolInfo.XCOFFSymInfo.IsLabel) {
440 const XCOFF::StorageMappingClass Smc =
441 *SymbolInfo.XCOFFSymInfo.StorageMappingClass;
442 Result.append(str: ("[" + XCOFF::getMappingClassString(SMC: Smc) + "]").str());
443 }
444
445 return Result;
446}
447
448#define PRINTBOOL(Prefix, Obj, Field) \
449 OS << Prefix << " " << ((Obj.Field()) ? "+" : "-") << #Field
450
451#define PRINTGET(Prefix, Obj, Field) \
452 OS << Prefix << " " << #Field << " = " \
453 << static_cast<unsigned>(Obj.get##Field())
454
455#define PRINTOPTIONAL(Field) \
456 if (TbTable.get##Field()) { \
457 OS << '\n'; \
458 printRawData(Bytes.slice(Index, 4), Address + Index, OS, STI); \
459 Index += 4; \
460 OS << "\t# " << #Field << " = " << *TbTable.get##Field(); \
461 }
462
463void objdump::dumpTracebackTable(ArrayRef<uint8_t> Bytes, uint64_t Address,
464 formatted_raw_ostream &OS, uint64_t End,
465 const MCSubtargetInfo &STI,
466 const XCOFFObjectFile *Obj) {
467 uint64_t Index = 0;
468 unsigned TabStop = getInstStartColumn(STI) - 1;
469 // Print traceback table boundary.
470 printRawData(Bytes: Bytes.slice(N: Index, M: 4), Address, OS, STI);
471 OS << "\t# Traceback table start\n";
472 Index += 4;
473
474 uint64_t Size = End - Address;
475 bool Is64Bit = Obj->is64Bit();
476
477 // XCOFFTracebackTable::create modifies the size parameter, so ensure Size
478 // isn't changed.
479 uint64_t SizeCopy = End - Address;
480 Expected<XCOFFTracebackTable> TTOrErr =
481 XCOFFTracebackTable::create(Ptr: Bytes.data() + Index, Size&: SizeCopy, Is64Bits: Is64Bit);
482
483 if (!TTOrErr) {
484 std::string WarningMsgStr;
485 raw_string_ostream WarningStream(WarningMsgStr);
486 WarningStream << "failure parsing traceback table with address: 0x"
487 << utohexstr(X: Address) + "\n>>> "
488 << toString(E: TTOrErr.takeError())
489 << "\n>>> Raw traceback table data is:\n";
490
491 uint64_t LastNonZero = Index;
492 for (uint64_t I = Index; I < Size; I += 4)
493 if (support::endian::read32be(P: Bytes.slice(N: I, M: 4).data()) != 0)
494 LastNonZero = I + 4 > Size ? Size : I + 4;
495
496 if (Size - LastNonZero <= 4)
497 LastNonZero = Size;
498
499 formatted_raw_ostream FOS(WarningStream);
500 while (Index < LastNonZero) {
501 printRawData(Bytes: Bytes.slice(N: Index, M: 4), Address: Address + Index, OS&: FOS, STI);
502 Index += 4;
503 WarningStream << '\n';
504 }
505
506 // Print all remaining zeroes as ...
507 if (Size - LastNonZero >= 8)
508 WarningStream << "\t\t...\n";
509
510 reportWarning(Message: WarningMsgStr, File: Obj->getFileName());
511 return;
512 }
513
514 auto PrintBytes = [&](uint64_t N) {
515 printRawData(Bytes: Bytes.slice(N: Index, M: N), Address: Address + Index, OS, STI);
516 Index += N;
517 };
518
519 XCOFFTracebackTable TbTable = *TTOrErr;
520 // Print the first of the 8 bytes of mandatory fields.
521 PrintBytes(1);
522 OS << format(Fmt: "\t# Version = %i", Vals: TbTable.getVersion()) << '\n';
523
524 // Print the second of the 8 bytes of mandatory fields.
525 PrintBytes(1);
526 TracebackTable::LanguageID LangId =
527 static_cast<TracebackTable::LanguageID>(TbTable.getLanguageID());
528 OS << "\t# Language = " << getNameForTracebackTableLanguageId(LangId) << '\n';
529
530 auto Split = [&]() {
531 OS << '\n';
532 OS.indent(NumSpaces: TabStop);
533 };
534
535 // Print the third of the 8 bytes of mandatory fields.
536 PrintBytes(1);
537 PRINTBOOL("\t#", TbTable, isGlobalLinkage);
538 PRINTBOOL(",", TbTable, isOutOfLineEpilogOrPrologue);
539 Split();
540 PRINTBOOL("\t ", TbTable, hasTraceBackTableOffset);
541 PRINTBOOL(",", TbTable, isInternalProcedure);
542 Split();
543 PRINTBOOL("\t ", TbTable, hasControlledStorage);
544 PRINTBOOL(",", TbTable, isTOCless);
545 Split();
546 PRINTBOOL("\t ", TbTable, isFloatingPointPresent);
547 Split();
548 PRINTBOOL("\t ", TbTable, isFloatingPointOperationLogOrAbortEnabled);
549 OS << '\n';
550
551 // Print the 4th of the 8 bytes of mandatory fields.
552 PrintBytes(1);
553 PRINTBOOL("\t#", TbTable, isInterruptHandler);
554 PRINTBOOL(",", TbTable, isFuncNamePresent);
555 PRINTBOOL(",", TbTable, isAllocaUsed);
556 Split();
557 PRINTGET("\t ", TbTable, OnConditionDirective);
558 PRINTBOOL(",", TbTable, isCRSaved);
559 PRINTBOOL(",", TbTable, isLRSaved);
560 OS << '\n';
561
562 // Print the 5th of the 8 bytes of mandatory fields.
563 PrintBytes(1);
564 PRINTBOOL("\t#", TbTable, isBackChainStored);
565 PRINTBOOL(",", TbTable, isFixup);
566 PRINTGET(",", TbTable, NumOfFPRsSaved);
567 OS << '\n';
568
569 // Print the 6th of the 8 bytes of mandatory fields.
570 PrintBytes(1);
571 PRINTBOOL("\t#", TbTable, hasExtensionTable);
572 PRINTBOOL(",", TbTable, hasVectorInfo);
573 PRINTGET(",", TbTable, NumOfGPRsSaved);
574 OS << '\n';
575
576 // Print the 7th of the 8 bytes of mandatory fields.
577 PrintBytes(1);
578 PRINTGET("\t#", TbTable, NumberOfFixedParms);
579 OS << '\n';
580
581 // Print the 8th of the 8 bytes of mandatory fields.
582 PrintBytes(1);
583 PRINTGET("\t#", TbTable, NumberOfFPParms);
584 PRINTBOOL(",", TbTable, hasParmsOnStack);
585
586 PRINTOPTIONAL(ParmsType);
587 PRINTOPTIONAL(TraceBackTableOffset);
588 PRINTOPTIONAL(HandlerMask);
589 PRINTOPTIONAL(NumOfCtlAnchors);
590
591 if (TbTable.getControlledStorageInfoDisp()) {
592 SmallVector<uint32_t, 8> Disp = *TbTable.getControlledStorageInfoDisp();
593 for (unsigned I = 0; I < Disp.size(); ++I) {
594 OS << '\n';
595 PrintBytes(4);
596 OS << "\t" << (I ? " " : "#") << " ControlledStorageInfoDisp[" << I
597 << "] = " << Disp[I];
598 }
599 }
600
601 // If there is a name, print the function name and function name length.
602 if (TbTable.isFuncNamePresent()) {
603 uint16_t FunctionNameLen = TbTable.getFunctionName()->size();
604 if (FunctionNameLen == 0) {
605 OS << '\n';
606 reportWarning(
607 Message: "the length of the function name must be greater than zero if the "
608 "isFuncNamePresent bit is set in the traceback table",
609 File: Obj->getFileName());
610 return;
611 }
612
613 OS << '\n';
614 PrintBytes(2);
615 OS << "\t# FunctionNameLen = " << FunctionNameLen;
616
617 uint16_t RemainingBytes = FunctionNameLen;
618 bool HasPrinted = false;
619 while (RemainingBytes > 0) {
620 OS << '\n';
621 uint16_t PrintLen = RemainingBytes >= 4 ? 4 : RemainingBytes;
622 printRawData(Bytes: Bytes.slice(N: Index, M: PrintLen), Address: Address + Index, OS, STI);
623 Index += PrintLen;
624 RemainingBytes -= PrintLen;
625
626 if (!HasPrinted) {
627 OS << "\t# FunctionName = " << *TbTable.getFunctionName();
628 HasPrinted = true;
629 }
630 }
631 }
632
633 if (TbTable.isAllocaUsed()) {
634 OS << '\n';
635 PrintBytes(1);
636 OS << format(Fmt: "\t# AllocaRegister = %u", Vals: *TbTable.getAllocaRegister());
637 }
638
639 if (TbTable.getVectorExt()) {
640 OS << '\n';
641 TBVectorExt VecExt = *TbTable.getVectorExt();
642 // Print first byte of VectorExt.
643 PrintBytes(1);
644 PRINTGET("\t#", VecExt, NumberOfVRSaved);
645 PRINTBOOL(",", VecExt, isVRSavedOnStack);
646 PRINTBOOL(",", VecExt, hasVarArgs);
647 OS << '\n';
648
649 // Print the second byte of VectorExt.
650 PrintBytes(1);
651 PRINTGET("\t#", VecExt, NumberOfVectorParms);
652 PRINTBOOL(",", VecExt, hasVMXInstruction);
653 OS << '\n';
654
655 PrintBytes(4);
656 OS << "\t# VectorParmsInfoString = " << VecExt.getVectorParmsInfo();
657
658 // There are two bytes of padding after vector info.
659 OS << '\n';
660 PrintBytes(2);
661 OS << "\t# Padding";
662 }
663
664 if (TbTable.getExtensionTable()) {
665 OS << '\n';
666 PrintBytes(1);
667 ExtendedTBTableFlag Flag =
668 static_cast<ExtendedTBTableFlag>(*TbTable.getExtensionTable());
669 OS << "\t# ExtensionTable = " << getExtendedTBTableFlagString(Flag);
670 }
671
672 if (TbTable.getEhInfoDisp()) {
673 // There are 4 bytes alignment before eh info displacement.
674 if (Index % 4) {
675 OS << '\n';
676 PrintBytes(4 - Index % 4);
677 OS << "\t# Alignment padding for eh info displacement";
678 }
679 OS << '\n';
680 // The size of the displacement (address) is 4 bytes in 32-bit object files,
681 // and 8 bytes in 64-bit object files.
682 PrintBytes(4);
683 OS << "\t# EH info displacement";
684 if (Is64Bit) {
685 OS << '\n';
686 PrintBytes(4);
687 }
688 }
689
690 OS << '\n';
691 if (End == Address + Index)
692 return;
693
694 Size = End - Address;
695
696 const char *LineSuffix = "\t# Padding\n";
697 auto IsWordZero = [&](uint64_t WordPos) {
698 if (WordPos >= Size)
699 return false;
700 uint64_t LineLength = std::min(a: 4 - WordPos % 4, b: Size - WordPos);
701 return std::all_of(first: Bytes.begin() + WordPos,
702 last: Bytes.begin() + WordPos + LineLength,
703 pred: [](uint8_t Byte) { return Byte == 0; });
704 };
705
706 bool AreWordsZero[] = {IsWordZero(Index), IsWordZero(alignTo(Value: Index, Align: 4) + 4),
707 IsWordZero(alignTo(Value: Index, Align: 4) + 8)};
708 bool ShouldPrintLine = true;
709 while (true) {
710 // Determine the length of the line (4, except for the first line, which
711 // will be just enough to align to the word boundary, and the last line,
712 // which will be the remainder of the data).
713 uint64_t LineLength = std::min(a: 4 - Index % 4, b: Size - Index);
714 if (ShouldPrintLine) {
715 // Print the line.
716 printRawData(Bytes: Bytes.slice(N: Index, M: LineLength), Address: Address + Index, OS, STI);
717 OS << LineSuffix;
718 LineSuffix = "\n";
719 }
720
721 Index += LineLength;
722 if (Index == Size)
723 return;
724
725 // For 3 or more consecutive lines of zeros, skip all but the first one, and
726 // replace them with "...".
727 if (AreWordsZero[0] && AreWordsZero[1] && AreWordsZero[2]) {
728 if (ShouldPrintLine)
729 OS << std::string(8, ' ') << "...\n";
730 ShouldPrintLine = false;
731 } else if (!AreWordsZero[1]) {
732 // We have reached the end of a skipped block of zeros.
733 ShouldPrintLine = true;
734 }
735 AreWordsZero[0] = AreWordsZero[1];
736 AreWordsZero[1] = AreWordsZero[2];
737 AreWordsZero[2] = IsWordZero(Index + 8);
738 }
739}
740#undef PRINTBOOL
741#undef PRINTGET
742#undef PRINTOPTIONAL
743