1//===-- COFFDump.cpp - COFF-specific dumper ---------------------*- C++ -*-===//
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 COFF-specific dumper for llvm-objdump.
11/// It outputs the Win64 EH data structures as plain text.
12/// The encoding of the unwind codes is described in MSDN:
13/// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64
14///
15//===----------------------------------------------------------------------===//
16
17#include "COFFDump.h"
18
19#include "llvm-objdump.h"
20#include "llvm/Demangle/Demangle.h"
21#include "llvm/Object/COFF.h"
22#include "llvm/Object/COFFImportFile.h"
23#include "llvm/Object/ObjectFile.h"
24#include "llvm/Support/Format.h"
25#include "llvm/Support/ScopedPrinter.h"
26#include "llvm/Support/Win64EH.h"
27#include "llvm/Support/WithColor.h"
28#include "llvm/Support/raw_ostream.h"
29
30using namespace llvm;
31using namespace llvm::objdump;
32using namespace llvm::object;
33using namespace llvm::Win64EH;
34
35namespace {
36class COFFDumper : public Dumper {
37public:
38 explicit COFFDumper(const llvm::object::COFFObjectFile &O)
39 : Dumper(O), Obj(O) {
40 Is64 = !Obj.getPE32Header();
41 }
42
43 template <class PEHeader> void printPEHeader(const PEHeader &Hdr) const;
44 void printPrivateHeaders() override;
45
46private:
47 template <typename T> FormattedNumber formatAddr(T V) const {
48 return format_hex_no_prefix(V, Is64 ? 16 : 8);
49 }
50
51 uint32_t getBaseOfData(const void *Hdr) const {
52 return Is64 ? 0 : static_cast<const pe32_header *>(Hdr)->BaseOfData;
53 }
54
55 const llvm::object::COFFObjectFile &Obj;
56 bool Is64;
57};
58} // namespace
59
60std::unique_ptr<Dumper>
61objdump::createCOFFDumper(const object::COFFObjectFile &Obj) {
62 return std::make_unique<COFFDumper>(args: Obj);
63}
64
65constexpr EnumStringDef<uint16_t> PEHeaderMagicDefs[] = {
66 {.Names: {"PE32"}, .Value: uint16_t(COFF::PE32Header::PE32)},
67 {.Names: {"PE32+"}, .Value: uint16_t(COFF::PE32Header::PE32_PLUS)},
68};
69constexpr auto PEHeaderMagic = BUILD_ENUM_STRINGS(PEHeaderMagicDefs);
70
71constexpr EnumStringDef<COFF::WindowsSubsystem> PEWindowsSubsystemDefs[] = {
72 {.Names: {"unspecified"}, .Value: COFF::IMAGE_SUBSYSTEM_UNKNOWN},
73 {.Names: {"NT native"}, .Value: COFF::IMAGE_SUBSYSTEM_NATIVE},
74 {.Names: {"Windows GUI"}, .Value: COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI},
75 {.Names: {"Windows CUI"}, .Value: COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI},
76 {.Names: {"POSIX CUI"}, .Value: COFF::IMAGE_SUBSYSTEM_POSIX_CUI},
77 {.Names: {"Wince CUI"}, .Value: COFF::IMAGE_SUBSYSTEM_WINDOWS_CE_GUI},
78 {.Names: {"EFI application"}, .Value: COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION},
79 {.Names: {"EFI boot service driver"},
80 .Value: COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER},
81 {.Names: {"EFI runtime driver"}, .Value: COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER},
82 {.Names: {"SAL runtime driver"}, .Value: COFF::IMAGE_SUBSYSTEM_EFI_ROM},
83 {.Names: {"XBOX"}, .Value: COFF::IMAGE_SUBSYSTEM_XBOX},
84};
85constexpr auto PEWindowsSubsystem = BUILD_ENUM_STRINGS(PEWindowsSubsystemDefs);
86
87template <typename T, typename TEnum>
88static void printOptionalEnumName(T Value, EnumStrings<TEnum> EnumValues) {
89 if (StringRef Name = EnumValues.toString(Value); !Name.empty())
90 outs() << "\t(" << Name << ')';
91}
92
93template <class PEHeader>
94void COFFDumper::printPEHeader(const PEHeader &Hdr) const {
95 auto print = [](const char *K, auto V, const char *Fmt = "%d\n") {
96 outs() << format(Fmt: "%-23s ", Vals: K) << format(Fmt, V);
97 };
98 auto printU16 = [&](const char *K, support::ulittle16_t V,
99 const char *Fmt = "%d\n") { print(K, uint16_t(V), Fmt); };
100 auto printU32 = [&](const char *K, support::ulittle32_t V,
101 const char *Fmt = "%d\n") { print(K, uint32_t(V), Fmt); };
102 auto printAddr = [=](const char *K, uint64_t V) {
103 outs() << format(Fmt: "%-23s ", Vals: K) << formatAddr(V) << '\n';
104 };
105
106 printU16("Magic", Hdr.Magic, "%04x");
107 printOptionalEnumName(Hdr.Magic, EnumStrings(PEHeaderMagic));
108 outs() << '\n';
109 print("MajorLinkerVersion", Hdr.MajorLinkerVersion);
110 print("MinorLinkerVersion", Hdr.MinorLinkerVersion);
111 printAddr("SizeOfCode", Hdr.SizeOfCode);
112 printAddr("SizeOfInitializedData", Hdr.SizeOfInitializedData);
113 printAddr("SizeOfUninitializedData", Hdr.SizeOfUninitializedData);
114 printAddr("AddressOfEntryPoint", Hdr.AddressOfEntryPoint);
115 printAddr("BaseOfCode", Hdr.BaseOfCode);
116 if (!Is64)
117 printAddr("BaseOfData", getBaseOfData(Hdr: &Hdr));
118 printAddr("ImageBase", Hdr.ImageBase);
119 printU32("SectionAlignment", Hdr.SectionAlignment, "%08x\n");
120 printU32("FileAlignment", Hdr.FileAlignment, "%08x\n");
121 printU16("MajorOSystemVersion", Hdr.MajorOperatingSystemVersion);
122 printU16("MinorOSystemVersion", Hdr.MinorOperatingSystemVersion);
123 printU16("MajorImageVersion", Hdr.MajorImageVersion);
124 printU16("MinorImageVersion", Hdr.MinorImageVersion);
125 printU16("MajorSubsystemVersion", Hdr.MajorSubsystemVersion);
126 printU16("MinorSubsystemVersion", Hdr.MinorSubsystemVersion);
127 printU32("Win32Version", Hdr.Win32VersionValue, "%08x\n");
128 printU32("SizeOfImage", Hdr.SizeOfImage, "%08x\n");
129 printU32("SizeOfHeaders", Hdr.SizeOfHeaders, "%08x\n");
130 printU32("CheckSum", Hdr.CheckSum, "%08x\n");
131 printU16("Subsystem", Hdr.Subsystem, "%08x");
132 printOptionalEnumName(Hdr.Subsystem, EnumStrings(PEWindowsSubsystem));
133 outs() << '\n';
134
135 printU16("DllCharacteristics", Hdr.DLLCharacteristics, "%08x\n");
136#define FLAG(Name) \
137 if (Hdr.DLLCharacteristics & COFF::IMAGE_DLL_CHARACTERISTICS_##Name) \
138 outs() << "\t\t\t\t\t" << #Name << '\n';
139 FLAG(HIGH_ENTROPY_VA);
140 FLAG(DYNAMIC_BASE);
141 FLAG(FORCE_INTEGRITY);
142 FLAG(NX_COMPAT);
143 FLAG(NO_ISOLATION);
144 FLAG(NO_SEH);
145 FLAG(NO_BIND);
146 FLAG(APPCONTAINER);
147 FLAG(WDM_DRIVER);
148 FLAG(GUARD_CF);
149 FLAG(TERMINAL_SERVER_AWARE);
150#undef FLAG
151
152 printAddr("SizeOfStackReserve", Hdr.SizeOfStackReserve);
153 printAddr("SizeOfStackCommit", Hdr.SizeOfStackCommit);
154 printAddr("SizeOfHeapReserve", Hdr.SizeOfHeapReserve);
155 printAddr("SizeOfHeapCommit", Hdr.SizeOfHeapCommit);
156 printU32("LoaderFlags", Hdr.LoaderFlags, "%08x\n");
157 printU32("NumberOfRvaAndSizes", Hdr.NumberOfRvaAndSize, "%08x\n");
158
159 static const char *DirName[COFF::NUM_DATA_DIRECTORIES + 1] = {
160 "Export Directory [.edata (or where ever we found it)]",
161 "Import Directory [parts of .idata]",
162 "Resource Directory [.rsrc]",
163 "Exception Directory [.pdata]",
164 "Security Directory",
165 "Base Relocation Directory [.reloc]",
166 "Debug Directory",
167 "Description Directory",
168 "Special Directory",
169 "Thread Storage Directory [.tls]",
170 "Load Configuration Directory",
171 "Bound Import Directory",
172 "Import Address Table Directory",
173 "Delay Import Directory",
174 "CLR Runtime Header",
175 "Reserved",
176 };
177 outs() << "\nThe Data Directory\n";
178 for (uint32_t I = 0; I != std::size(DirName); ++I) {
179 uint32_t Addr = 0, Size = 0;
180 if (const data_directory *Data = Obj.getDataDirectory(index: I)) {
181 Addr = Data->RelativeVirtualAddress;
182 Size = Data->Size;
183 }
184 outs() << format(Fmt: "Entry %x ", Vals: I) << formatAddr(V: Addr)
185 << format(Fmt: " %08x %s\n", Vals: Size, Vals: DirName[I]);
186 }
187}
188
189// Returns the name of the unwind code.
190static StringRef getUnwindCodeTypeName(uint8_t Code) {
191 switch(Code) {
192 default: llvm_unreachable("Invalid unwind code");
193 case UOP_PushNonVol: return "UOP_PushNonVol";
194 case UOP_AllocLarge: return "UOP_AllocLarge";
195 case UOP_AllocSmall: return "UOP_AllocSmall";
196 case UOP_SetFPReg: return "UOP_SetFPReg";
197 case UOP_SaveNonVol: return "UOP_SaveNonVol";
198 case UOP_SaveNonVolBig: return "UOP_SaveNonVolBig";
199 case UOP_Epilog: return "UOP_Epilog";
200 case UOP_SpareCode: return "UOP_SpareCode";
201 case UOP_SaveXMM128: return "UOP_SaveXMM128";
202 case UOP_SaveXMM128Big: return "UOP_SaveXMM128Big";
203 case UOP_PushMachFrame: return "UOP_PushMachFrame";
204 }
205}
206
207// Returns the name of a referenced register.
208static StringRef getUnwindRegisterName(uint8_t Reg) {
209 switch(Reg) {
210 default: llvm_unreachable("Invalid register");
211 case 0: return "RAX";
212 case 1: return "RCX";
213 case 2: return "RDX";
214 case 3: return "RBX";
215 case 4: return "RSP";
216 case 5: return "RBP";
217 case 6: return "RSI";
218 case 7: return "RDI";
219 case 8: return "R8";
220 case 9: return "R9";
221 case 10: return "R10";
222 case 11: return "R11";
223 case 12: return "R12";
224 case 13: return "R13";
225 case 14: return "R14";
226 case 15: return "R15";
227 }
228}
229
230// Calculates the number of array slots required for the unwind code.
231static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
232 switch (UnwindCode.getUnwindOp()) {
233 default: llvm_unreachable("Invalid unwind code");
234 case UOP_PushNonVol:
235 case UOP_AllocSmall:
236 case UOP_SetFPReg:
237 case UOP_PushMachFrame:
238 case UOP_Epilog:
239 return 1;
240 case UOP_SaveNonVol:
241 case UOP_SaveXMM128:
242 return 2;
243 case UOP_SaveNonVolBig:
244 case UOP_SaveXMM128Big:
245 case UOP_SpareCode:
246 return 3;
247 case UOP_AllocLarge:
248 return (UnwindCode.getOpInfo() == 0) ? 2 : 3;
249 }
250}
251
252// Prints one unwind code. Because an unwind code can occupy up to 3 slots in
253// the unwind codes array, this function requires that the correct number of
254// slots is provided.
255static void printUnwindCode(ArrayRef<UnwindCode> UCs, bool &SeenFirstEpilog) {
256 assert(UCs.size() >= getNumUsedSlots(UCs[0]));
257 outs() << format(Fmt: " 0x%02x: ", Vals: unsigned(UCs[0].u.CodeOffset))
258 << getUnwindCodeTypeName(Code: UCs[0].getUnwindOp());
259 switch (UCs[0].getUnwindOp()) {
260 case UOP_PushNonVol:
261 outs() << " " << getUnwindRegisterName(Reg: UCs[0].getOpInfo());
262 break;
263 case UOP_AllocLarge:
264 if (UCs[0].getOpInfo() == 0) {
265 outs() << " " << UCs[1].FrameOffset;
266 } else {
267 outs() << " " << UCs[1].FrameOffset
268 + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16);
269 }
270 break;
271 case UOP_AllocSmall:
272 outs() << " " << ((UCs[0].getOpInfo() + 1) * 8);
273 break;
274 case UOP_SetFPReg:
275 outs() << " ";
276 break;
277 case UOP_SaveNonVol:
278 outs() << " " << getUnwindRegisterName(Reg: UCs[0].getOpInfo())
279 << format(Fmt: " [0x%04x]", Vals: 8 * UCs[1].FrameOffset);
280 break;
281 case UOP_SaveNonVolBig:
282 outs() << " " << getUnwindRegisterName(Reg: UCs[0].getOpInfo())
283 << format(Fmt: " [0x%08x]", Vals: UCs[1].FrameOffset
284 + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16));
285 break;
286 case UOP_SaveXMM128:
287 outs() << " XMM" << static_cast<uint32_t>(UCs[0].getOpInfo())
288 << format(Fmt: " [0x%04x]", Vals: 16 * UCs[1].FrameOffset);
289 break;
290 case UOP_SaveXMM128Big:
291 outs() << " XMM" << UCs[0].getOpInfo()
292 << format(Fmt: " [0x%08x]", Vals: UCs[1].FrameOffset
293 + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16));
294 break;
295 case UOP_PushMachFrame:
296 outs() << " " << (UCs[0].getOpInfo() ? "w/o" : "w")
297 << " error code";
298 break;
299
300 case UOP_Epilog:
301 if (SeenFirstEpilog) {
302 uint32_t Offset = UCs[0].getEpilogOffset();
303 if (Offset == 0) {
304 outs() << " padding";
305 } else {
306 outs() << " offset=" << format(Fmt: "0x%X", Vals: Offset);
307 }
308 } else {
309 SeenFirstEpilog = true;
310 bool AtEnd = (UCs[0].getOpInfo() & 0x1) != 0;
311 uint32_t Length = UCs[0].u.CodeOffset;
312 outs() << " atend=" << (AtEnd ? "yes" : "no")
313 << ", length=" << format(Fmt: "0x%X", Vals: Length);
314 }
315 break;
316 }
317 outs() << "\n";
318}
319
320static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) {
321 bool SeenFirstEpilog = false;
322 for (const UnwindCode *I = UCs.begin(), *E = UCs.end(); I < E; ) {
323 unsigned UsedSlots = getNumUsedSlots(UnwindCode: *I);
324 if (UsedSlots > UCs.size()) {
325 outs() << "Unwind data corrupted: Encountered unwind op "
326 << getUnwindCodeTypeName(Code: (*I).getUnwindOp())
327 << " which requires " << UsedSlots
328 << " slots, but only " << UCs.size()
329 << " remaining in buffer";
330 return ;
331 }
332 printUnwindCode(UCs: ArrayRef(I, E), SeenFirstEpilog);
333 I += UsedSlots;
334 }
335}
336
337// Given a symbol sym this functions returns the address and section of it.
338static Error resolveSectionAndAddress(const COFFObjectFile *Obj,
339 const SymbolRef &Sym,
340 const coff_section *&ResolvedSection,
341 uint64_t &ResolvedAddr) {
342 Expected<uint64_t> ResolvedAddrOrErr = Sym.getAddress();
343 if (!ResolvedAddrOrErr)
344 return ResolvedAddrOrErr.takeError();
345 ResolvedAddr = *ResolvedAddrOrErr;
346 Expected<section_iterator> Iter = Sym.getSection();
347 if (!Iter)
348 return Iter.takeError();
349 ResolvedSection = Obj->getCOFFSection(Section: **Iter);
350 return Error::success();
351}
352
353// Given a vector of relocations for a section and an offset into this section
354// the function returns the symbol used for the relocation at the offset.
355static Error resolveSymbol(const std::vector<RelocationRef> &Rels,
356 uint64_t Offset, SymbolRef &Sym) {
357 for (auto &R : Rels) {
358 uint64_t Ofs = R.getOffset();
359 if (Ofs == Offset) {
360 Sym = *R.getSymbol();
361 return Error::success();
362 }
363 }
364 return make_error<BinaryError>();
365}
366
367// Given a vector of relocations for a section and an offset into this section
368// the function resolves the symbol used for the relocation at the offset and
369// returns the section content and the address inside the content pointed to
370// by the symbol.
371static Error
372getSectionContents(const COFFObjectFile *Obj,
373 const std::vector<RelocationRef> &Rels, uint64_t Offset,
374 ArrayRef<uint8_t> &Contents, uint64_t &Addr) {
375 SymbolRef Sym;
376 if (Error E = resolveSymbol(Rels, Offset, Sym))
377 return E;
378 const coff_section *Section;
379 if (Error E = resolveSectionAndAddress(Obj, Sym, ResolvedSection&: Section, ResolvedAddr&: Addr))
380 return E;
381 return Obj->getSectionContents(Sec: Section, Res&: Contents);
382}
383
384// Given a vector of relocations for a section and an offset into this section
385// the function returns the name of the symbol used for the relocation at the
386// offset.
387static Error resolveSymbolName(const std::vector<RelocationRef> &Rels,
388 uint64_t Offset, StringRef &Name) {
389 SymbolRef Sym;
390 if (Error EC = resolveSymbol(Rels, Offset, Sym))
391 return EC;
392 Expected<StringRef> NameOrErr = Sym.getName();
393 if (!NameOrErr)
394 return NameOrErr.takeError();
395 Name = *NameOrErr;
396 return Error::success();
397}
398
399static void printCOFFSymbolAddress(raw_ostream &Out,
400 const std::vector<RelocationRef> &Rels,
401 uint64_t Offset, uint32_t Disp) {
402 StringRef Sym;
403 if (!resolveSymbolName(Rels, Offset, Name&: Sym)) {
404 Out << Sym;
405 if (Disp > 0)
406 Out << format(Fmt: " + 0x%04x", Vals: Disp);
407 } else {
408 Out << format(Fmt: "0x%04x", Vals: Disp);
409 }
410}
411
412static void
413printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) {
414 if (Count == 0)
415 return;
416
417 uintptr_t IntPtr = 0;
418 if (Error E = Obj->getVaPtr(VA: TableVA, Res&: IntPtr))
419 reportError(E: std::move(E), FileName: Obj->getFileName());
420
421 const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr;
422 outs() << "SEH Table:";
423 for (int I = 0; I < Count; ++I)
424 outs() << format(Fmt: " 0x%x", Vals: P[I] + Obj->getPE32Header()->ImageBase);
425 outs() << "\n\n";
426}
427
428template <typename T>
429static void printTLSDirectoryT(const coff_tls_directory<T> *TLSDir) {
430 size_t FormatWidth = sizeof(T) * 2;
431 outs() << "TLS directory:"
432 << "\n StartAddressOfRawData: "
433 << format_hex(TLSDir->StartAddressOfRawData, FormatWidth)
434 << "\n EndAddressOfRawData: "
435 << format_hex(TLSDir->EndAddressOfRawData, FormatWidth)
436 << "\n AddressOfIndex: "
437 << format_hex(TLSDir->AddressOfIndex, FormatWidth)
438 << "\n AddressOfCallBacks: "
439 << format_hex(TLSDir->AddressOfCallBacks, FormatWidth)
440 << "\n SizeOfZeroFill: "
441 << TLSDir->SizeOfZeroFill
442 << "\n Characteristics: "
443 << TLSDir->Characteristics
444 << "\n Alignment: "
445 << TLSDir->getAlignment()
446 << "\n\n";
447}
448
449static void printTLSDirectory(const COFFObjectFile *Obj) {
450 const pe32_header *PE32Header = Obj->getPE32Header();
451 const pe32plus_header *PE32PlusHeader = Obj->getPE32PlusHeader();
452
453 // Skip if it's not executable.
454 if (!PE32Header && !PE32PlusHeader)
455 return;
456
457 if (PE32Header) {
458 if (auto *TLSDir = Obj->getTLSDirectory32())
459 printTLSDirectoryT(TLSDir);
460 } else {
461 if (auto *TLSDir = Obj->getTLSDirectory64())
462 printTLSDirectoryT(TLSDir);
463 }
464
465 outs() << "\n";
466}
467
468static void printLoadConfiguration(const COFFObjectFile *Obj) {
469 // Skip if it's not executable.
470 if (!Obj->getPE32Header())
471 return;
472
473 // Currently only x86 is supported
474 if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_I386)
475 return;
476
477 auto *LoadConf = Obj->getLoadConfig32();
478 if (!LoadConf)
479 return;
480
481 outs() << "Load configuration:"
482 << "\n Timestamp: " << LoadConf->TimeDateStamp
483 << "\n Major Version: " << LoadConf->MajorVersion
484 << "\n Minor Version: " << LoadConf->MinorVersion
485 << "\n GlobalFlags Clear: " << LoadConf->GlobalFlagsClear
486 << "\n GlobalFlags Set: " << LoadConf->GlobalFlagsSet
487 << "\n Critical Section Default Timeout: " << LoadConf->CriticalSectionDefaultTimeout
488 << "\n Decommit Free Block Threshold: " << LoadConf->DeCommitFreeBlockThreshold
489 << "\n Decommit Total Free Threshold: " << LoadConf->DeCommitTotalFreeThreshold
490 << "\n Lock Prefix Table: " << LoadConf->LockPrefixTable
491 << "\n Maximum Allocation Size: " << LoadConf->MaximumAllocationSize
492 << "\n Virtual Memory Threshold: " << LoadConf->VirtualMemoryThreshold
493 << "\n Process Affinity Mask: " << LoadConf->ProcessAffinityMask
494 << "\n Process Heap Flags: " << LoadConf->ProcessHeapFlags
495 << "\n CSD Version: " << LoadConf->CSDVersion
496 << "\n Security Cookie: " << LoadConf->SecurityCookie
497 << "\n SEH Table: " << LoadConf->SEHandlerTable
498 << "\n SEH Count: " << LoadConf->SEHandlerCount
499 << "\n\n";
500 printSEHTable(Obj, TableVA: LoadConf->SEHandlerTable, Count: LoadConf->SEHandlerCount);
501 outs() << "\n";
502}
503
504// Prints import tables. The import table is a table containing the list of
505// DLL name and symbol names which will be linked by the loader.
506static void printImportTables(const COFFObjectFile *Obj) {
507 import_directory_iterator I = Obj->import_directory_begin();
508 import_directory_iterator E = Obj->import_directory_end();
509 if (I == E)
510 return;
511 outs() << "The Import Tables:\n";
512 for (const ImportDirectoryEntryRef &DirRef : Obj->import_directories()) {
513 const coff_import_directory_table_entry *Dir;
514 StringRef Name;
515 if (DirRef.getImportTableEntry(Result&: Dir)) return;
516 if (DirRef.getName(Result&: Name)) return;
517
518 outs() << format(Fmt: " lookup %08x time %08x fwd %08x name %08x addr %08x\n\n",
519 Vals: static_cast<uint32_t>(Dir->ImportLookupTableRVA),
520 Vals: static_cast<uint32_t>(Dir->TimeDateStamp),
521 Vals: static_cast<uint32_t>(Dir->ForwarderChain),
522 Vals: static_cast<uint32_t>(Dir->NameRVA),
523 Vals: static_cast<uint32_t>(Dir->ImportAddressTableRVA));
524 outs() << " DLL Name: " << Name << "\n";
525 outs() << " Hint/Ord Name\n";
526 for (const ImportedSymbolRef &Entry : DirRef.imported_symbols()) {
527 bool IsOrdinal;
528 if (Entry.isOrdinal(Result&: IsOrdinal))
529 return;
530 if (IsOrdinal) {
531 uint16_t Ordinal;
532 if (Entry.getOrdinal(Result&: Ordinal))
533 return;
534 outs() << format(Fmt: " % 6d\n", Vals: Ordinal);
535 continue;
536 }
537 uint32_t HintNameRVA;
538 if (Entry.getHintNameRVA(Result&: HintNameRVA))
539 return;
540 uint16_t Hint;
541 StringRef Name;
542 if (Obj->getHintName(Rva: HintNameRVA, Hint, Name))
543 return;
544 outs() << format(Fmt: " % 6d ", Vals: Hint) << Name << "\n";
545 }
546 outs() << "\n";
547 }
548}
549
550// Prints export tables. The export table is a table containing the list of
551// exported symbol from the DLL.
552static void printExportTable(const COFFObjectFile *Obj) {
553 export_directory_iterator I = Obj->export_directory_begin();
554 export_directory_iterator E = Obj->export_directory_end();
555 if (I == E)
556 return;
557 outs() << "Export Table:\n";
558 StringRef DllName;
559 uint32_t OrdinalBase;
560 if (I->getDllName(Result&: DllName))
561 return;
562 if (I->getOrdinalBase(Result&: OrdinalBase))
563 return;
564 outs() << " DLL name: " << DllName << "\n";
565 outs() << " Ordinal base: " << OrdinalBase << "\n";
566 outs() << " Ordinal RVA Name\n";
567 for (; I != E; I = ++I) {
568 uint32_t RVA;
569 if (I->getExportRVA(Result&: RVA))
570 return;
571 StringRef Name;
572 if (I->getSymbolName(Result&: Name))
573 continue;
574 if (!RVA && Name.empty())
575 continue;
576
577 uint32_t Ordinal;
578 if (I->getOrdinal(Result&: Ordinal))
579 return;
580 bool IsForwarder;
581 if (I->isForwarder(Result&: IsForwarder))
582 return;
583
584 if (IsForwarder) {
585 // Export table entries can be used to re-export symbols that
586 // this COFF file is imported from some DLLs. This is rare.
587 // In most cases IsForwarder is false.
588 outs() << format(Fmt: " %5d ", Vals: Ordinal);
589 } else {
590 outs() << format(Fmt: " %5d %# 8x", Vals: Ordinal, Vals: RVA);
591 }
592
593 if (!Name.empty())
594 outs() << " " << Name;
595 if (IsForwarder) {
596 StringRef S;
597 if (I->getForwardTo(Result&: S))
598 return;
599 outs() << " (forwarded to " << S << ")";
600 }
601 outs() << "\n";
602 }
603}
604
605// Given the COFF object file, this function returns the relocations for .pdata
606// and the pointer to "runtime function" structs.
607static bool getPDataSection(const COFFObjectFile *Obj,
608 std::vector<RelocationRef> &Rels,
609 const RuntimeFunction *&RFStart, int &NumRFs) {
610 for (const SectionRef &Section : Obj->sections()) {
611 StringRef Name = unwrapOrError(EO: Section.getName(), Args: Obj->getFileName());
612 if (Name != ".pdata")
613 continue;
614
615 const coff_section *Pdata = Obj->getCOFFSection(Section);
616 append_range(C&: Rels, R: Section.relocations());
617
618 // Sort relocations by address.
619 llvm::sort(C&: Rels, Comp: isRelocAddressLess);
620
621 ArrayRef<uint8_t> Contents;
622 if (Error E = Obj->getSectionContents(Sec: Pdata, Res&: Contents))
623 reportError(E: std::move(E), FileName: Obj->getFileName());
624
625 if (Contents.empty())
626 continue;
627
628 RFStart = reinterpret_cast<const RuntimeFunction *>(Contents.data());
629 NumRFs = Contents.size() / sizeof(RuntimeFunction);
630 return true;
631 }
632 return false;
633}
634
635Error objdump::getCOFFRelocationValueString(const COFFObjectFile *Obj,
636 const RelocationRef &Rel,
637 SmallVectorImpl<char> &Result) {
638 symbol_iterator SymI = Rel.getSymbol();
639 Expected<StringRef> SymNameOrErr = SymI->getName();
640 if (!SymNameOrErr)
641 return SymNameOrErr.takeError();
642 StringRef SymName = *SymNameOrErr;
643 Result.append(in_start: SymName.begin(), in_end: SymName.end());
644 return Error::success();
645}
646
647static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) {
648 // The casts to int are required in order to output the value as number.
649 // Without the casts the value would be interpreted as char data (which
650 // results in garbage output).
651 outs() << " Version: " << static_cast<int>(UI->getVersion()) << "\n";
652 outs() << " Flags: " << static_cast<int>(UI->getFlags());
653 if (UI->getFlags()) {
654 if (UI->getFlags() & UNW_ExceptionHandler)
655 outs() << " UNW_ExceptionHandler";
656 if (UI->getFlags() & UNW_TerminateHandler)
657 outs() << " UNW_TerminateHandler";
658 if (UI->getFlags() & UNW_ChainInfo)
659 outs() << " UNW_ChainInfo";
660 }
661 outs() << "\n";
662 outs() << " Size of prolog: " << static_cast<int>(UI->PrologSize) << "\n";
663 outs() << " Number of Codes: " << static_cast<int>(UI->NumCodes) << "\n";
664 // Maybe this should move to output of UOP_SetFPReg?
665 if (UI->getFrameRegister()) {
666 outs() << " Frame register: "
667 << getUnwindRegisterName(Reg: UI->getFrameRegister()) << "\n";
668 outs() << " Frame offset: " << 16 * UI->getFrameOffset() << "\n";
669 } else {
670 outs() << " No frame pointer used\n";
671 }
672 if (UI->getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
673 // FIXME: Output exception handler data
674 } else if (UI->getFlags() & UNW_ChainInfo) {
675 // FIXME: Output chained unwind info
676 }
677
678 if (UI->NumCodes)
679 outs() << " Unwind Codes:\n";
680
681 printAllUnwindCodes(UCs: ArrayRef(&UI->UnwindCodes[0], UI->NumCodes));
682
683 outs() << "\n";
684 outs().flush();
685}
686
687static void printDecodedWOD(const DecodedWOD &W) {
688 switch (W.Opcode) {
689 case WOD_PUSH:
690 outs() << "WOD_PUSH Reg=" << getRegisterNameV3(Reg: W.Register);
691 break;
692 case WOD_PUSH2:
693 outs() << "WOD_PUSH2 Reg1=" << getRegisterNameV3(Reg: W.Register)
694 << ", Reg2=" << getRegisterNameV3(Reg: W.Register2);
695 break;
696 case WOD_PUSH_CONSECUTIVE_2:
697 outs() << "WOD_PUSH_CONSECUTIVE_2 Reg=" << getRegisterNameV3(Reg: W.Register)
698 << " (+" << getRegisterNameV3(Reg: W.Register + 1) << ")";
699 break;
700 case WOD_ALLOC_SMALL:
701 outs() << format(Fmt: "WOD_ALLOC_SMALL Size=0x%X", Vals: W.Size);
702 break;
703 case WOD_ALLOC_LARGE:
704 outs() << format(Fmt: "WOD_ALLOC_LARGE Size=0x%X", Vals: W.Size);
705 break;
706 case WOD_ALLOC_HUGE:
707 outs() << format(Fmt: "WOD_ALLOC_HUGE Size=0x%X", Vals: W.Size);
708 break;
709 case WOD_SET_FPREG:
710 outs() << "WOD_SET_FPREG Reg=" << getRegisterNameV3(Reg: W.Register)
711 << format(Fmt: ", Offset=0x%X", Vals: W.Displacement);
712 break;
713 case WOD_SAVE_NONVOL:
714 outs() << "WOD_SAVE_NONVOL Reg=" << getRegisterNameV3(Reg: W.Register)
715 << format(Fmt: ", Disp=0x%X", Vals: W.Displacement);
716 break;
717 case WOD_SAVE_NONVOL_FAR:
718 outs() << "WOD_SAVE_NONVOL_FAR Reg=" << getRegisterNameV3(Reg: W.Register)
719 << format(Fmt: ", Disp=0x%X", Vals: W.Displacement);
720 break;
721 case WOD_SAVE_XMM128:
722 outs() << "WOD_SAVE_XMM128 Reg=XMM" << static_cast<unsigned>(W.Register)
723 << format(Fmt: ", Disp=0x%X", Vals: W.Displacement);
724 break;
725 case WOD_SAVE_XMM128_FAR:
726 outs() << "WOD_SAVE_XMM128_FAR Reg=XMM" << static_cast<unsigned>(W.Register)
727 << format(Fmt: ", Disp=0x%X", Vals: W.Displacement);
728 break;
729 case WOD_PUSH_CANONICAL_FRAME:
730 // TODO: When the Windows x64 Unwind V3 spec is finalized, replace this
731 // raw Type value with a descriptive name. Type values are defined by the
732 // OS (see the Windows SDK headers) but the set is not yet stable.
733 outs() << "WOD_PUSH_CANONICAL_FRAME Type=" << static_cast<unsigned>(W.Type);
734 break;
735 }
736}
737
738static void printWODSequence(ArrayRef<uint8_t> WODPool, unsigned PoolOffset,
739 ArrayRef<uint16_t> IpOffsets, unsigned Count,
740 StringRef Indent) {
741 unsigned CurrentOffset = PoolOffset;
742 for (unsigned I = 0; I < Count; ++I) {
743 Expected<DecodedWOD> WOrErr = decodeWOD(Pool: WODPool, Offset: CurrentOffset);
744 if (!WOrErr) {
745 WithColor::warning(OS&: errs()) << toString(E: WOrErr.takeError()) << "\n";
746 return;
747 }
748 const DecodedWOD &W = *WOrErr;
749 outs() << Indent
750 << format(Fmt: "[%u] IP +0x%04X: ", Vals: I,
751 Vals: I < IpOffsets.size() ? IpOffsets[I] : 0);
752 printDecodedWOD(W);
753 outs() << "\n";
754 CurrentOffset += W.ByteSize;
755 }
756}
757
758static void printWin64EHUnwindInfoV3(ArrayRef<uint8_t> Data) {
759 Expected<DecodedUnwindInfoV3> InfoOrErr = decodeUnwindInfoV3(Data);
760 if (!InfoOrErr) {
761 WithColor::warning(OS&: errs()) << toString(E: InfoOrErr.takeError()) << "\n";
762 return;
763 }
764 const DecodedUnwindInfoV3 &Info = *InfoOrErr;
765
766 outs() << " Version: " << static_cast<int>(Info.Version) << "\n";
767 outs() << format(Fmt: " Flags: 0x%X", Vals: static_cast<unsigned>(Info.Flags));
768 if (Info.Flags) {
769 if (Info.Flags & UNW_ExceptionHandler)
770 outs() << " UNW_ExceptionHandler";
771 if (Info.Flags & UNW_TerminateHandler)
772 outs() << " UNW_TerminateHandler";
773 if (Info.Flags & UNW_ChainInfo)
774 outs() << " UNW_ChainInfo";
775 if (Info.Flags & UNW_FlagLarge)
776 outs() << " UNW_FlagLarge";
777 }
778 outs() << "\n";
779 outs() << format(Fmt: " Size of prolog: 0x%X\n",
780 Vals: static_cast<unsigned>(Info.SizeOfProlog));
781 outs() << " PayloadWords: " << static_cast<int>(Info.PayloadWords) << "\n";
782 outs() << " NumberOfOps: " << static_cast<int>(Info.NumberOfOps) << "\n";
783 outs() << " NumberOfEpilogs: " << static_cast<int>(Info.NumberOfEpilogs)
784 << "\n";
785
786 // Validation: SizeOfProlog must be >= first (largest) prolog IP offset.
787 if (Info.NumberOfOps > 0 && Info.SizeOfProlog < Info.PrologIpOffsets[0]) {
788 WithColor::warning(OS&: errs())
789 << format(Fmt: "SizeOfProlog (%u) is smaller than first prolog IP offset "
790 "(%u)\n",
791 Vals: Info.SizeOfProlog, Vals: Info.PrologIpOffsets[0]);
792 }
793
794 // Per the V3 spec, Flags bit 4 (0x10) is reserved and must be zero. Warn
795 // (rather than error) so we stay forward-compatible if Microsoft later
796 // defines this bit.
797 if (Info.Flags & 0x10)
798 WithColor::warning(OS&: errs())
799 << "V3 unwind info has reserved Flags bit 4 set\n";
800
801 // Prolog ops
802 outs() << format(Fmt: " Prolog [%u ops]:\n", Vals: Info.NumberOfOps);
803 printWODSequence(WODPool: Info.WODPool, PoolOffset: 0, IpOffsets: ArrayRef(Info.PrologIpOffsets),
804 Count: Info.NumberOfOps, Indent: " ");
805
806 // Epilog descriptors
807 uint8_t BaseEpilogFlags = 0;
808 bool HaveBaseEpilog = false;
809 for (unsigned I = 0; I < Info.NumberOfEpilogs; ++I) {
810 const DecodedEpilogV3 &Epi = Info.Epilogs[I];
811
812 // Format the signed EpilogOffset as hex with explicit sign so negative
813 // tail-relative offsets remain readable (e.g. "-0x14" rather than
814 // "0xFFFFFFEC").
815 int32_t SignedOff = static_cast<int32_t>(Epi.EpilogOffset);
816 uint32_t AbsOff =
817 SignedOff < 0 ? static_cast<uint32_t>(-static_cast<int64_t>(SignedOff))
818 : static_cast<uint32_t>(SignedOff);
819 const char *Sign = SignedOff < 0 ? "-" : "+";
820
821 // Render Flags as "0xNN [name1 name2 ...]" so each set bit is visible.
822 std::string FlagsStr;
823 {
824 raw_string_ostream OSS(FlagsStr);
825 OSS << format(Fmt: "0x%02X", Vals: Epi.Flags);
826 if (Epi.Flags & EPILOG_PARENT_FRAGMENT_TRANSFER)
827 OSS << " EPILOG_PARENT_FRAGMENT_TRANSFER";
828 if (Epi.Flags & EPILOG_INFO_LARGE)
829 OSS << " EPILOG_INFO_LARGE";
830 }
831
832 if (Epi.NumberOfOps == 0) {
833 outs() << format(
834 Fmt: " Epilog [%u] (Flags=%s, Offset=%s0x%X) [inherited]:\n", Vals: I,
835 Vals: FlagsStr.c_str(), Vals: Sign, Vals: AbsOff);
836 if (I == 0) {
837 WithColor::warning(OS&: errs())
838 << "first epilog cannot inherit (NumberOfOps=0)\n";
839 } else {
840 // Per the V3 spec, Flags bits 0 and 1 are producer-replicated into an
841 // inherited descriptor, so they must match the base epilog. Warn if a
842 // non-compliant producer left them inconsistent.
843 if (HaveBaseEpilog && (Epi.Flags & 0x03) != (BaseEpilogFlags & 0x03))
844 WithColor::warning(OS&: errs())
845 << format(Fmt: "inherited epilog flags (0x%X) do not match base "
846 "epilog flags (0x%X)\n",
847 Vals: Epi.Flags & 0x03, Vals: BaseEpilogFlags & 0x03);
848 // Surface the values inherited from the base epilog so a
849 // reader can see what the unwinder will actually execute.
850 outs() << format(Fmt: " (inherits from base epilog: FirstOp=0x%X, "
851 "IpOfLast=+0x%X, %u ops)\n",
852 Vals: Epi.FirstOp,
853 Vals: static_cast<unsigned>(Epi.IpOffsetOfLastInstruction),
854 Vals: static_cast<unsigned>(Epi.IpOffsets.size()));
855 }
856 } else {
857 outs() << format(
858 Fmt: " Epilog [%u] (Flags=%s, Offset=%s0x%X, IpOfLast=+0x%X) "
859 "[%u ops, FirstOp=0x%X]:\n",
860 Vals: I, Vals: FlagsStr.c_str(), Vals: Sign, Vals: AbsOff,
861 Vals: static_cast<unsigned>(Epi.IpOffsetOfLastInstruction), Vals: Epi.NumberOfOps,
862 Vals: Epi.FirstOp);
863 printWODSequence(WODPool: Info.WODPool, PoolOffset: Epi.FirstOp, IpOffsets: ArrayRef(Epi.IpOffsets),
864 Count: Epi.NumberOfOps, Indent: " ");
865 // This is a full descriptor; it becomes the base that subsequent
866 // inherited descriptors replicate their flags from.
867 BaseEpilogFlags = Epi.Flags;
868 HaveBaseEpilog = true;
869 }
870 }
871
872 // Optionally dump the WOD pool with byte offsets. This is useful for
873 // understanding how WODs are shared between the prolog and epilogs but is
874 // normally redundant with the per-prolog / per-epilog decoded output, so
875 // it's gated behind --unwind-show-wod-pool.
876 if (UnwindShowWODPool) {
877 outs() << format(Fmt: " WOD pool [%zu bytes]:\n", Vals: Info.WODPool.size());
878 unsigned PoolOffset = 0;
879 while (PoolOffset < Info.WODPool.size()) {
880 // PayloadWords counts 2-byte words, so the pool may have a single
881 // trailing zero padding byte to round up to a word boundary. A bare
882 // 0x00 byte is never a valid 1-byte WOD (WOD_ALLOC_SMALL requires the
883 // low nibble to be 8), so treat a final zero byte as padding rather
884 // than trying to decode it.
885 if (PoolOffset + 1 == Info.WODPool.size() &&
886 Info.WODPool[PoolOffset] == 0) {
887 outs() << format(Fmt: " +0x%04X: (padding)\n", Vals: PoolOffset);
888 break;
889 }
890 Expected<DecodedWOD> WOrErr = decodeWOD(Pool: Info.WODPool, Offset: PoolOffset);
891 if (!WOrErr) {
892 WithColor::warning(OS&: errs()) << toString(E: WOrErr.takeError()) << "\n";
893 break;
894 }
895 const DecodedWOD &W = *WOrErr;
896 outs() << format(Fmt: " +0x%04X: ", Vals: PoolOffset);
897 printDecodedWOD(W);
898 outs() << "\n";
899 PoolOffset += W.ByteSize;
900 }
901 }
902
903 // Exception handler RVA or chained RUNTIME_FUNCTION sits immediately after
904 // the payload. Match the readobj output by surfacing it here when the
905 // corresponding flag is set. We don't have symbol-formatting context here
906 // (unlike readobj), so we print the raw RVA / fields.
907 if (Info.Flags & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
908 if (Info.PayloadSize + 4 <= Data.size()) {
909 uint32_t HandlerRVA = support::endian::read32le(P: &Data[Info.PayloadSize]);
910 outs() << format(Fmt: " Handler: 0x%X\n", Vals: HandlerRVA);
911 }
912 } else if (Info.Flags & UNW_ChainInfo) {
913 if (Info.PayloadSize + sizeof(RuntimeFunction) <= Data.size()) {
914 const auto *Chained =
915 reinterpret_cast<const RuntimeFunction *>(&Data[Info.PayloadSize]);
916 outs() << " Chained:\n"
917 << format(Fmt: " Start Address: 0x%X\n",
918 Vals: static_cast<uint32_t>(Chained->StartAddress))
919 << format(Fmt: " End Address: 0x%X\n",
920 Vals: static_cast<uint32_t>(Chained->EndAddress))
921 << format(Fmt: " Unwind Info Address: 0x%X\n",
922 Vals: static_cast<uint32_t>(Chained->UnwindInfoOffset));
923 }
924 }
925
926 outs() << "\n";
927 outs().flush();
928}
929
930/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
931/// pointing to an executable file.
932static void printRuntimeFunction(const COFFObjectFile *Obj,
933 const RuntimeFunction &RF) {
934 if (!RF.StartAddress)
935 return;
936 outs() << "Function Table:\n"
937 << format(Fmt: " Start Address: 0x%04x\n",
938 Vals: static_cast<uint32_t>(RF.StartAddress))
939 << format(Fmt: " End Address: 0x%04x\n",
940 Vals: static_cast<uint32_t>(RF.EndAddress))
941 << format(Fmt: " Unwind Info Address: 0x%04x\n",
942 Vals: static_cast<uint32_t>(RF.UnwindInfoOffset));
943 uintptr_t addr;
944 if (Obj->getRvaPtr(Rva: RF.UnwindInfoOffset, Res&: addr))
945 return;
946
947 // Check version before interpreting through V1/V2 struct.
948 const uint8_t *RawBytes = reinterpret_cast<const uint8_t *>(addr);
949 uint8_t Version = RawBytes[0] & 0x07;
950 if (Version == 3) {
951 // Estimate available size conservatively. V3 layout is a 4-byte header
952 // followed by PayloadWords*2 bytes of payload (the optional
953 // UNWIND_INFO_LARGE_V3 byte is part of the payload), plus possible
954 // handler/chain data.
955 //
956 // Clamp the slice we hand to the V3 decoder to the bytes actually
957 // available in the mapped file, so a malformed or truncated record
958 // cannot drive the decoder past the end of the underlying buffer.
959 StringRef FileData = Obj->getData();
960 const uint8_t *FileBegin =
961 reinterpret_cast<const uint8_t *>(FileData.data());
962 const uint8_t *FileEnd = FileBegin + FileData.size();
963 if (RawBytes < FileBegin || RawBytes >= FileEnd) {
964 WithColor::warning(OS&: errs())
965 << "unwind info pointer is outside of file bounds\n";
966 return;
967 }
968 size_t Remaining = static_cast<size_t>(FileEnd - RawBytes);
969 if (Remaining < 4) {
970 WithColor::warning(OS&: errs()) << "truncated UNWIND_INFO_V3 header\n";
971 return;
972 }
973 unsigned PayloadWords = RawBytes[2];
974 // When PayloadWords is odd there are 2 bytes of zero padding between
975 // the payload and the trailing handler/chain. Use the aligned size so the
976 // slice we hand the decoder reaches the handler/chain bytes.
977 size_t MinSize = alignTo(Value: 4 + static_cast<size_t>(PayloadWords) * 2, Align: 4);
978 // Include enough trailing bytes for the optional handler RVA (4 bytes)
979 // or chained RUNTIME_FUNCTION (12 bytes) that may follow the payload.
980 size_t ExtraBytes = 0;
981 uint8_t Flags = (RawBytes[0] >> 3) & 0x1F;
982 if (Flags & (UNW_ExceptionHandler | UNW_TerminateHandler))
983 ExtraBytes = 4;
984 else if (Flags & UNW_ChainInfo)
985 ExtraBytes = sizeof(RuntimeFunction);
986 size_t ClampedSize = std::min(a: MinSize + ExtraBytes, b: Remaining);
987 if (ClampedSize < MinSize)
988 WithColor::warning(OS&: errs()) << "truncated UNWIND_INFO_V3 payload\n";
989 printWin64EHUnwindInfoV3(Data: ArrayRef<uint8_t>(RawBytes, ClampedSize));
990 } else {
991 printWin64EHUnwindInfo(UI: reinterpret_cast<const Win64EH::UnwindInfo *>(addr));
992 }
993}
994
995/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
996/// pointing to an object file. Unlike executable, fields in RuntimeFunction
997/// struct are filled with zeros, but instead there are relocations pointing to
998/// them so that the linker will fill targets' RVAs to the fields at link
999/// time. This function interprets the relocations to find the data to be used
1000/// in the resulting executable.
1001static void printRuntimeFunctionRels(const COFFObjectFile *Obj,
1002 const RuntimeFunction &RF,
1003 uint64_t SectionOffset,
1004 const std::vector<RelocationRef> &Rels) {
1005 outs() << "Function Table:\n";
1006 outs() << " Start Address: ";
1007 printCOFFSymbolAddress(Out&: outs(), Rels,
1008 Offset: SectionOffset +
1009 /*offsetof(RuntimeFunction, StartAddress)*/ 0,
1010 Disp: RF.StartAddress);
1011 outs() << "\n";
1012
1013 outs() << " End Address: ";
1014 printCOFFSymbolAddress(Out&: outs(), Rels,
1015 Offset: SectionOffset +
1016 /*offsetof(RuntimeFunction, EndAddress)*/ 4,
1017 Disp: RF.EndAddress);
1018 outs() << "\n";
1019
1020 outs() << " Unwind Info Address: ";
1021 printCOFFSymbolAddress(Out&: outs(), Rels,
1022 Offset: SectionOffset +
1023 /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
1024 Disp: RF.UnwindInfoOffset);
1025 outs() << "\n";
1026
1027 ArrayRef<uint8_t> XContents;
1028 uint64_t UnwindInfoOffset = 0;
1029 if (Error E = getSectionContents(
1030 Obj, Rels,
1031 Offset: SectionOffset +
1032 /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
1033 Contents&: XContents, Addr&: UnwindInfoOffset))
1034 reportError(E: std::move(E), FileName: Obj->getFileName());
1035 if (XContents.empty())
1036 return;
1037
1038 UnwindInfoOffset += RF.UnwindInfoOffset;
1039 if (UnwindInfoOffset >= XContents.size())
1040 return;
1041
1042 // Check version before interpreting through V1/V2 struct.
1043 uint8_t Version = XContents[UnwindInfoOffset] & 0x07;
1044 if (Version == 3) {
1045 ArrayRef<uint8_t> RawData = XContents.slice(N: UnwindInfoOffset);
1046 printWin64EHUnwindInfoV3(Data: RawData);
1047 } else {
1048 auto *UI = reinterpret_cast<const Win64EH::UnwindInfo *>(XContents.data() +
1049 UnwindInfoOffset);
1050 printWin64EHUnwindInfo(UI);
1051 }
1052}
1053
1054void objdump::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
1055 if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) {
1056 WithColor::error(OS&: errs(), Prefix: "llvm-objdump")
1057 << "unsupported image machine type "
1058 "(currently only AMD64 is supported).\n";
1059 return;
1060 }
1061
1062 std::vector<RelocationRef> Rels;
1063 const RuntimeFunction *RFStart;
1064 int NumRFs;
1065 if (!getPDataSection(Obj, Rels, RFStart, NumRFs))
1066 return;
1067 ArrayRef<RuntimeFunction> RFs(RFStart, NumRFs);
1068
1069 bool IsExecutable = Rels.empty();
1070 if (IsExecutable) {
1071 for (const RuntimeFunction &RF : RFs)
1072 printRuntimeFunction(Obj, RF);
1073 return;
1074 }
1075
1076 for (const RuntimeFunction &RF : RFs) {
1077 uint64_t SectionOffset =
1078 std::distance(first: RFs.begin(), last: &RF) * sizeof(RuntimeFunction);
1079 printRuntimeFunctionRels(Obj, RF, SectionOffset, Rels);
1080 }
1081}
1082
1083void COFFDumper::printPrivateHeaders() {
1084 COFFDumper CD(Obj);
1085 const uint16_t Cha = Obj.getCharacteristics();
1086 outs() << "Characteristics 0x" << Twine::utohexstr(Val: Cha) << '\n';
1087#define FLAG(F, Name) \
1088 if (Cha & F) \
1089 outs() << '\t' << Name << '\n';
1090 FLAG(COFF::IMAGE_FILE_RELOCS_STRIPPED, "relocations stripped");
1091 FLAG(COFF::IMAGE_FILE_EXECUTABLE_IMAGE, "executable");
1092 FLAG(COFF::IMAGE_FILE_LINE_NUMS_STRIPPED, "line numbers stripped");
1093 FLAG(COFF::IMAGE_FILE_LOCAL_SYMS_STRIPPED, "symbols stripped");
1094 FLAG(COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE, "large address aware");
1095 FLAG(COFF::IMAGE_FILE_BYTES_REVERSED_LO, "little endian");
1096 FLAG(COFF::IMAGE_FILE_32BIT_MACHINE, "32 bit words");
1097 FLAG(COFF::IMAGE_FILE_DEBUG_STRIPPED, "debugging information removed");
1098 FLAG(COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP,
1099 "copy to swap file if on removable media");
1100 FLAG(COFF::IMAGE_FILE_NET_RUN_FROM_SWAP,
1101 "copy to swap file if on network media");
1102 FLAG(COFF::IMAGE_FILE_SYSTEM, "system file");
1103 FLAG(COFF::IMAGE_FILE_DLL, "DLL");
1104 FLAG(COFF::IMAGE_FILE_UP_SYSTEM_ONLY, "run only on uniprocessor machine");
1105 FLAG(COFF::IMAGE_FILE_BYTES_REVERSED_HI, "big endian");
1106#undef FLAG
1107
1108 // TODO Support PE_IMAGE_DEBUG_TYPE_REPRO.
1109 // Since ctime(3) returns a 26 character string of the form:
1110 // "Sun Sep 16 01:03:52 1973\n\0"
1111 // just print 24 characters.
1112 const time_t Timestamp = Obj.getTimeDateStamp();
1113 outs() << format(Fmt: "\nTime/Date %.24s\n", Vals: ctime(timer: &Timestamp));
1114
1115 if (const pe32_header *Hdr = Obj.getPE32Header())
1116 CD.printPEHeader<pe32_header>(Hdr: *Hdr);
1117 else if (const pe32plus_header *Hdr = Obj.getPE32PlusHeader())
1118 CD.printPEHeader<pe32plus_header>(Hdr: *Hdr);
1119
1120 printTLSDirectory(Obj: &Obj);
1121 printLoadConfiguration(Obj: &Obj);
1122 printImportTables(Obj: &Obj);
1123 printExportTable(Obj: &Obj);
1124}
1125
1126void objdump::printCOFFSymbolTable(const object::COFFImportFile &i) {
1127 unsigned Index = 0;
1128 bool IsCode = i.getCOFFImportHeader()->getType() == COFF::IMPORT_CODE;
1129
1130 for (const object::BasicSymbolRef &Sym : i.symbols()) {
1131 std::string Name;
1132 raw_string_ostream NS(Name);
1133
1134 cantFail(Err: Sym.printName(OS&: NS));
1135
1136 outs() << "[" << format(Fmt: "%2d", Vals: Index) << "]"
1137 << "(sec " << format(Fmt: "%2d", Vals: 0) << ")"
1138 << "(fl 0x00)" // Flag bits, which COFF doesn't have.
1139 << "(ty " << format(Fmt: "%3x", Vals: (IsCode && Index) ? 32 : 0) << ")"
1140 << "(scl " << format(Fmt: "%3x", Vals: 0) << ") "
1141 << "(nx " << 0 << ") "
1142 << "0x" << format(Fmt: "%08x", Vals: 0) << " " << Name << '\n';
1143
1144 ++Index;
1145 }
1146}
1147
1148void objdump::printCOFFSymbolTable(const COFFObjectFile &coff) {
1149 for (unsigned SI = 0, SE = coff.getNumberOfSymbols(); SI != SE; ++SI) {
1150 Expected<COFFSymbolRef> Symbol = coff.getSymbol(index: SI);
1151 if (!Symbol)
1152 reportError(E: Symbol.takeError(), FileName: coff.getFileName());
1153
1154 Expected<StringRef> NameOrErr = coff.getSymbolName(Symbol: *Symbol);
1155 if (!NameOrErr)
1156 reportError(E: NameOrErr.takeError(), FileName: coff.getFileName());
1157 StringRef Name = *NameOrErr;
1158
1159 outs() << "[" << format(Fmt: "%2d", Vals: SI) << "]"
1160 << "(sec " << format(Fmt: "%2d", Vals: int(Symbol->getSectionNumber())) << ")"
1161 << "(fl 0x00)" // Flag bits, which COFF doesn't have.
1162 << "(ty " << format(Fmt: "%3x", Vals: unsigned(Symbol->getType())) << ")"
1163 << "(scl " << format(Fmt: "%3x", Vals: unsigned(Symbol->getStorageClass()))
1164 << ") "
1165 << "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") "
1166 << "0x" << format(Fmt: "%08x", Vals: unsigned(Symbol->getValue())) << " "
1167 << Name;
1168 if (Demangle && Name.starts_with(Prefix: "?")) {
1169 int Status = -1;
1170 char *DemangledSymbol = microsoftDemangle(mangled_name: Name, n_read: nullptr, status: &Status);
1171
1172 if (Status == 0 && DemangledSymbol) {
1173 outs() << " (" << StringRef(DemangledSymbol) << ")";
1174 std::free(ptr: DemangledSymbol);
1175 } else {
1176 outs() << " (invalid mangled name)";
1177 }
1178 }
1179 outs() << "\n";
1180
1181 for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) {
1182 if (Symbol->isSectionDefinition()) {
1183 const coff_aux_section_definition *asd;
1184 if (Error E =
1185 coff.getAuxSymbol<coff_aux_section_definition>(index: SI + 1, Res&: asd))
1186 reportError(E: std::move(E), FileName: coff.getFileName());
1187
1188 int32_t AuxNumber = asd->getNumber(IsBigObj: Symbol->isBigObj());
1189
1190 outs() << "AUX "
1191 << format(Fmt: "scnlen 0x%x nreloc %d nlnno %d checksum 0x%x "
1192 , Vals: unsigned(asd->Length)
1193 , Vals: unsigned(asd->NumberOfRelocations)
1194 , Vals: unsigned(asd->NumberOfLinenumbers)
1195 , Vals: unsigned(asd->CheckSum))
1196 << format(Fmt: "assoc %d comdat %d\n"
1197 , Vals: unsigned(AuxNumber)
1198 , Vals: unsigned(asd->Selection));
1199 } else if (Symbol->isFileRecord()) {
1200 const char *FileName;
1201 if (Error E = coff.getAuxSymbol<char>(index: SI + 1, Res&: FileName))
1202 reportError(E: std::move(E), FileName: coff.getFileName());
1203
1204 StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() *
1205 coff.getSymbolTableEntrySize());
1206 outs() << "AUX " << Name.rtrim(Chars: StringRef("\0", 1)) << '\n';
1207
1208 SI = SI + Symbol->getNumberOfAuxSymbols();
1209 break;
1210 } else if (Symbol->isWeakExternal()) {
1211 const coff_aux_weak_external *awe;
1212 if (Error E = coff.getAuxSymbol<coff_aux_weak_external>(index: SI + 1, Res&: awe))
1213 reportError(E: std::move(E), FileName: coff.getFileName());
1214
1215 outs() << "AUX " << format(Fmt: "indx %d srch %d\n",
1216 Vals: static_cast<uint32_t>(awe->TagIndex),
1217 Vals: static_cast<uint32_t>(awe->Characteristics));
1218 } else {
1219 outs() << "AUX Unknown\n";
1220 }
1221 }
1222 }
1223}
1224