1 | //===- llvm-readobj.cpp - Dump contents of an Object File -----------------===// |
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 | // This is a tool similar to readelf, except it works on multiple object file |
10 | // formats. The main purpose of this tool is to provide detailed output suitable |
11 | // for FileCheck. |
12 | // |
13 | // Flags should be similar to readelf where supported, but the output format |
14 | // does not need to be identical. The point is to not make users learn yet |
15 | // another set of flags. |
16 | // |
17 | // Output should be specialized for each format where appropriate. |
18 | // |
19 | //===----------------------------------------------------------------------===// |
20 | |
21 | #include "llvm-readobj.h" |
22 | #include "ObjDumper.h" |
23 | #include "WindowsResourceDumper.h" |
24 | #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" |
25 | #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" |
26 | #include "llvm/MC/TargetRegistry.h" |
27 | #include "llvm/Object/Archive.h" |
28 | #include "llvm/Object/COFFImportFile.h" |
29 | #include "llvm/Object/ELFObjectFile.h" |
30 | #include "llvm/Object/MachOUniversal.h" |
31 | #include "llvm/Object/ObjectFile.h" |
32 | #include "llvm/Object/Wasm.h" |
33 | #include "llvm/Object/WindowsResource.h" |
34 | #include "llvm/Object/XCOFFObjectFile.h" |
35 | #include "llvm/Option/Arg.h" |
36 | #include "llvm/Option/ArgList.h" |
37 | #include "llvm/Option/Option.h" |
38 | #include "llvm/Support/Casting.h" |
39 | #include "llvm/Support/CommandLine.h" |
40 | #include "llvm/Support/DataTypes.h" |
41 | #include "llvm/Support/Debug.h" |
42 | #include "llvm/Support/Errc.h" |
43 | #include "llvm/Support/FileSystem.h" |
44 | #include "llvm/Support/FormatVariadic.h" |
45 | #include "llvm/Support/LLVMDriver.h" |
46 | #include "llvm/Support/Path.h" |
47 | #include "llvm/Support/ScopedPrinter.h" |
48 | #include "llvm/Support/WithColor.h" |
49 | |
50 | using namespace llvm; |
51 | using namespace llvm::object; |
52 | |
53 | namespace { |
54 | using namespace llvm::opt; // for HelpHidden in Opts.inc |
55 | enum ID { |
56 | OPT_INVALID = 0, // This is not an option ID. |
57 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
58 | #include "Opts.inc" |
59 | #undef OPTION |
60 | }; |
61 | |
62 | #define OPTTABLE_STR_TABLE_CODE |
63 | #include "Opts.inc" |
64 | #undef OPTTABLE_STR_TABLE_CODE |
65 | |
66 | #define OPTTABLE_PREFIXES_TABLE_CODE |
67 | #include "Opts.inc" |
68 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
69 | |
70 | static constexpr opt::OptTable::Info InfoTable[] = { |
71 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
72 | #include "Opts.inc" |
73 | #undef OPTION |
74 | }; |
75 | |
76 | class ReadobjOptTable : public opt::GenericOptTable { |
77 | public: |
78 | ReadobjOptTable() |
79 | : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) { |
80 | setGroupedShortOptions(true); |
81 | } |
82 | }; |
83 | |
84 | enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; |
85 | |
86 | enum SortSymbolKeyTy { |
87 | NAME = 0, |
88 | TYPE = 1, |
89 | UNKNOWN = 100, |
90 | // TODO: add ADDRESS, SIZE as needed. |
91 | }; |
92 | |
93 | } // namespace |
94 | |
95 | namespace opts { |
96 | static bool Addrsig; |
97 | static bool All; |
98 | static bool ArchSpecificInfo; |
99 | static bool BBAddrMap; |
100 | static bool PrettyPGOAnalysisMap; |
101 | bool ExpandRelocs; |
102 | static bool CGProfile; |
103 | static bool Decompress; |
104 | bool Demangle; |
105 | static bool DependentLibraries; |
106 | static bool DynRelocs; |
107 | static bool DynamicSymbols; |
108 | static bool ; |
109 | static bool ; |
110 | static bool ; |
111 | static std::vector<std::string> HexDump; |
112 | static bool PrettyPrint; |
113 | static bool PrintStackMap; |
114 | static bool PrintStackSizes; |
115 | static bool Relocations; |
116 | bool SectionData; |
117 | static bool SectionDetails; |
118 | static bool ; |
119 | bool SectionRelocations; |
120 | bool SectionSymbols; |
121 | static std::vector<std::string> StringDump; |
122 | static bool StringTable; |
123 | static bool Symbols; |
124 | static bool UnwindInfo; |
125 | static cl::boolOrDefault SectionMapping; |
126 | static SmallVector<SortSymbolKeyTy> SortKeys; |
127 | |
128 | // ELF specific options. |
129 | static bool DynamicTable; |
130 | static bool ELFLinkerOptions; |
131 | static bool GnuHashTable; |
132 | static bool HashSymbols; |
133 | static bool HashTable; |
134 | static bool HashHistogram; |
135 | static bool Memtag; |
136 | static bool NeededLibraries; |
137 | static bool Notes; |
138 | static bool ; |
139 | static bool SectionGroups; |
140 | static bool VersionInfo; |
141 | |
142 | // Mach-O specific options. |
143 | static bool MachODataInCode; |
144 | static bool MachODysymtab; |
145 | static bool MachOIndirectSymbols; |
146 | static bool MachOLinkerOptions; |
147 | static bool MachOSegment; |
148 | static bool MachOVersionMin; |
149 | |
150 | // PE/COFF specific options. |
151 | static bool CodeView; |
152 | static bool CodeViewEnableGHash; |
153 | static bool CodeViewMergedTypes; |
154 | bool CodeViewSubsectionBytes; |
155 | static bool COFFBaseRelocs; |
156 | static bool COFFDebugDirectory; |
157 | static bool COFFDirectives; |
158 | static bool COFFExports; |
159 | static bool COFFImports; |
160 | static bool COFFLoadConfig; |
161 | static bool COFFResources; |
162 | static bool COFFTLSDirectory; |
163 | |
164 | // XCOFF specific options. |
165 | static bool ; |
166 | static bool ; |
167 | static bool XCOFFLoaderSectionSymbol; |
168 | static bool XCOFFLoaderSectionRelocation; |
169 | static bool XCOFFExceptionSection; |
170 | |
171 | OutputStyleTy Output = OutputStyleTy::LLVM; |
172 | static std::vector<std::string> InputFilenames; |
173 | } // namespace opts |
174 | |
175 | static StringRef ToolName; |
176 | |
177 | namespace llvm { |
178 | |
179 | [[noreturn]] static void error(Twine Msg) { |
180 | // Flush the standard output to print the error at a |
181 | // proper place. |
182 | fouts().flush(); |
183 | WithColor::error(OS&: errs(), Prefix: ToolName) << Msg << "\n" ; |
184 | exit(status: 1); |
185 | } |
186 | |
187 | [[noreturn]] void reportError(Error Err, StringRef Input) { |
188 | assert(Err); |
189 | if (Input == "-" ) |
190 | Input = "<stdin>" ; |
191 | handleAllErrors(E: createFileError(F: Input, E: std::move(Err)), |
192 | Handlers: [&](const ErrorInfoBase &EI) { error(Msg: EI.message()); }); |
193 | llvm_unreachable("error() call should never return" ); |
194 | } |
195 | |
196 | void reportWarning(Error Err, StringRef Input) { |
197 | assert(Err); |
198 | if (Input == "-" ) |
199 | Input = "<stdin>" ; |
200 | |
201 | // Flush the standard output to print the warning at a |
202 | // proper place. |
203 | fouts().flush(); |
204 | handleAllErrors( |
205 | E: createFileError(F: Input, E: std::move(Err)), Handlers: [&](const ErrorInfoBase &EI) { |
206 | WithColor::warning(OS&: errs(), Prefix: ToolName) << EI.message() << "\n" ; |
207 | }); |
208 | } |
209 | |
210 | } // namespace llvm |
211 | |
212 | static void parseOptions(const opt::InputArgList &Args) { |
213 | opts::Addrsig = Args.hasArg(Ids: OPT_addrsig); |
214 | opts::All = Args.hasArg(Ids: OPT_all); |
215 | opts::ArchSpecificInfo = Args.hasArg(Ids: OPT_arch_specific); |
216 | opts::BBAddrMap = Args.hasArg(Ids: OPT_bb_addr_map); |
217 | opts::PrettyPGOAnalysisMap = Args.hasArg(Ids: OPT_pretty_pgo_analysis_map); |
218 | if (opts::PrettyPGOAnalysisMap && !opts::BBAddrMap) |
219 | WithColor::warning(OS&: errs(), Prefix: ToolName) |
220 | << "--bb-addr-map must be enabled for --pretty-pgo-analysis-map to " |
221 | "have an effect\n" ; |
222 | opts::CGProfile = Args.hasArg(Ids: OPT_cg_profile); |
223 | opts::Decompress = Args.hasArg(Ids: OPT_decompress); |
224 | opts::Demangle = Args.hasFlag(Pos: OPT_demangle, Neg: OPT_no_demangle, Default: false); |
225 | opts::DependentLibraries = Args.hasArg(Ids: OPT_dependent_libraries); |
226 | opts::DynRelocs = Args.hasArg(Ids: OPT_dyn_relocations); |
227 | opts::DynamicSymbols = Args.hasArg(Ids: OPT_dyn_syms); |
228 | opts::ExpandRelocs = Args.hasArg(Ids: OPT_expand_relocs); |
229 | opts::ExtraSymInfo = Args.hasArg(Ids: OPT_extra_sym_info); |
230 | opts::FileHeaders = Args.hasArg(Ids: OPT_file_header); |
231 | opts::Headers = Args.hasArg(Ids: OPT_headers); |
232 | opts::HexDump = Args.getAllArgValues(Id: OPT_hex_dump_EQ); |
233 | opts::Relocations = Args.hasArg(Ids: OPT_relocs); |
234 | opts::SectionData = Args.hasArg(Ids: OPT_section_data); |
235 | opts::SectionDetails = Args.hasArg(Ids: OPT_section_details); |
236 | opts::SectionHeaders = Args.hasArg(Ids: OPT_section_headers); |
237 | opts::SectionRelocations = Args.hasArg(Ids: OPT_section_relocations); |
238 | opts::SectionSymbols = Args.hasArg(Ids: OPT_section_symbols); |
239 | if (Args.hasArg(Ids: OPT_section_mapping)) |
240 | opts::SectionMapping = cl::BOU_TRUE; |
241 | else if (Args.hasArg(Ids: OPT_section_mapping_EQ_false)) |
242 | opts::SectionMapping = cl::BOU_FALSE; |
243 | else |
244 | opts::SectionMapping = cl::BOU_UNSET; |
245 | opts::PrintStackSizes = Args.hasArg(Ids: OPT_stack_sizes); |
246 | opts::PrintStackMap = Args.hasArg(Ids: OPT_stackmap); |
247 | opts::StringDump = Args.getAllArgValues(Id: OPT_string_dump_EQ); |
248 | opts::StringTable = Args.hasArg(Ids: OPT_string_table); |
249 | opts::Symbols = Args.hasArg(Ids: OPT_symbols); |
250 | opts::UnwindInfo = Args.hasArg(Ids: OPT_unwind); |
251 | |
252 | // ELF specific options. |
253 | opts::DynamicTable = Args.hasArg(Ids: OPT_dynamic_table); |
254 | opts::ELFLinkerOptions = Args.hasArg(Ids: OPT_elf_linker_options); |
255 | if (Arg *A = Args.getLastArg(Ids: OPT_elf_output_style_EQ)) { |
256 | std::string OutputStyleChoice = A->getValue(); |
257 | opts::Output = StringSwitch<opts::OutputStyleTy>(OutputStyleChoice) |
258 | .Case(S: "LLVM" , Value: opts::OutputStyleTy::LLVM) |
259 | .Case(S: "GNU" , Value: opts::OutputStyleTy::GNU) |
260 | .Case(S: "JSON" , Value: opts::OutputStyleTy::JSON) |
261 | .Default(Value: opts::OutputStyleTy::UNKNOWN); |
262 | if (opts::Output == opts::OutputStyleTy::UNKNOWN) { |
263 | error(Msg: "--elf-output-style value should be either 'LLVM', 'GNU', or " |
264 | "'JSON', but was '" + |
265 | OutputStyleChoice + "'" ); |
266 | } |
267 | } |
268 | opts::GnuHashTable = Args.hasArg(Ids: OPT_gnu_hash_table); |
269 | opts::HashSymbols = Args.hasArg(Ids: OPT_hash_symbols); |
270 | opts::HashTable = Args.hasArg(Ids: OPT_hash_table); |
271 | opts::HashHistogram = Args.hasArg(Ids: OPT_histogram); |
272 | opts::Memtag = Args.hasArg(Ids: OPT_memtag); |
273 | opts::NeededLibraries = Args.hasArg(Ids: OPT_needed_libs); |
274 | opts::Notes = Args.hasArg(Ids: OPT_notes); |
275 | opts::PrettyPrint = Args.hasArg(Ids: OPT_pretty_print); |
276 | opts::ProgramHeaders = Args.hasArg(Ids: OPT_program_headers); |
277 | opts::SectionGroups = Args.hasArg(Ids: OPT_section_groups); |
278 | if (Arg *A = Args.getLastArg(Ids: OPT_sort_symbols_EQ)) { |
279 | for (StringRef KeyStr : llvm::split(Str: A->getValue(), Separator: "," )) { |
280 | SortSymbolKeyTy KeyType = StringSwitch<SortSymbolKeyTy>(KeyStr) |
281 | .Case(S: "name" , Value: SortSymbolKeyTy::NAME) |
282 | .Case(S: "type" , Value: SortSymbolKeyTy::TYPE) |
283 | .Default(Value: SortSymbolKeyTy::UNKNOWN); |
284 | if (KeyType == SortSymbolKeyTy::UNKNOWN) |
285 | error(Msg: "--sort-symbols value should be 'name' or 'type', but was '" + |
286 | Twine(KeyStr) + "'" ); |
287 | opts::SortKeys.push_back(Elt: KeyType); |
288 | } |
289 | } |
290 | opts::VersionInfo = Args.hasArg(Ids: OPT_version_info); |
291 | |
292 | // Mach-O specific options. |
293 | opts::MachODataInCode = Args.hasArg(Ids: OPT_macho_data_in_code); |
294 | opts::MachODysymtab = Args.hasArg(Ids: OPT_macho_dysymtab); |
295 | opts::MachOIndirectSymbols = Args.hasArg(Ids: OPT_macho_indirect_symbols); |
296 | opts::MachOLinkerOptions = Args.hasArg(Ids: OPT_macho_linker_options); |
297 | opts::MachOSegment = Args.hasArg(Ids: OPT_macho_segment); |
298 | opts::MachOVersionMin = Args.hasArg(Ids: OPT_macho_version_min); |
299 | |
300 | // PE/COFF specific options. |
301 | opts::CodeView = Args.hasArg(Ids: OPT_codeview); |
302 | opts::CodeViewEnableGHash = Args.hasArg(Ids: OPT_codeview_ghash); |
303 | opts::CodeViewMergedTypes = Args.hasArg(Ids: OPT_codeview_merged_types); |
304 | opts::CodeViewSubsectionBytes = Args.hasArg(Ids: OPT_codeview_subsection_bytes); |
305 | opts::COFFBaseRelocs = Args.hasArg(Ids: OPT_coff_basereloc); |
306 | opts::COFFDebugDirectory = Args.hasArg(Ids: OPT_coff_debug_directory); |
307 | opts::COFFDirectives = Args.hasArg(Ids: OPT_coff_directives); |
308 | opts::COFFExports = Args.hasArg(Ids: OPT_coff_exports); |
309 | opts::COFFImports = Args.hasArg(Ids: OPT_coff_imports); |
310 | opts::COFFLoadConfig = Args.hasArg(Ids: OPT_coff_load_config); |
311 | opts::COFFResources = Args.hasArg(Ids: OPT_coff_resources); |
312 | opts::COFFTLSDirectory = Args.hasArg(Ids: OPT_coff_tls_directory); |
313 | |
314 | // XCOFF specific options. |
315 | opts::XCOFFAuxiliaryHeader = Args.hasArg(Ids: OPT_auxiliary_header); |
316 | opts::XCOFFLoaderSectionHeader = Args.hasArg(Ids: OPT_loader_section_header); |
317 | opts::XCOFFLoaderSectionSymbol = Args.hasArg(Ids: OPT_loader_section_symbols); |
318 | opts::XCOFFLoaderSectionRelocation = |
319 | Args.hasArg(Ids: OPT_loader_section_relocations); |
320 | opts::XCOFFExceptionSection = Args.hasArg(Ids: OPT_exception_section); |
321 | |
322 | opts::InputFilenames = Args.getAllArgValues(Id: OPT_INPUT); |
323 | } |
324 | |
325 | namespace { |
326 | struct ReadObjTypeTableBuilder { |
327 | ReadObjTypeTableBuilder() |
328 | : IDTable(Allocator), TypeTable(Allocator), GlobalIDTable(Allocator), |
329 | GlobalTypeTable(Allocator) {} |
330 | |
331 | llvm::BumpPtrAllocator Allocator; |
332 | llvm::codeview::MergingTypeTableBuilder IDTable; |
333 | llvm::codeview::MergingTypeTableBuilder TypeTable; |
334 | llvm::codeview::GlobalTypeTableBuilder GlobalIDTable; |
335 | llvm::codeview::GlobalTypeTableBuilder GlobalTypeTable; |
336 | std::vector<OwningBinary<Binary>> Binaries; |
337 | }; |
338 | } // namespace |
339 | static ReadObjTypeTableBuilder CVTypes; |
340 | |
341 | /// Creates an format-specific object file dumper. |
342 | static Expected<std::unique_ptr<ObjDumper>> |
343 | createDumper(const ObjectFile &Obj, ScopedPrinter &Writer) { |
344 | if (const COFFObjectFile *COFFObj = dyn_cast<COFFObjectFile>(Val: &Obj)) |
345 | return createCOFFDumper(Obj: *COFFObj, Writer); |
346 | |
347 | if (const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(Val: &Obj)) |
348 | return createELFDumper(Obj: *ELFObj, Writer); |
349 | |
350 | if (const MachOObjectFile *MachOObj = dyn_cast<MachOObjectFile>(Val: &Obj)) |
351 | return createMachODumper(Obj: *MachOObj, Writer); |
352 | |
353 | if (const WasmObjectFile *WasmObj = dyn_cast<WasmObjectFile>(Val: &Obj)) |
354 | return createWasmDumper(Obj: *WasmObj, Writer); |
355 | |
356 | if (const XCOFFObjectFile *XObj = dyn_cast<XCOFFObjectFile>(Val: &Obj)) |
357 | return createXCOFFDumper(Obj: *XObj, Writer); |
358 | |
359 | return createStringError(EC: errc::invalid_argument, |
360 | S: "unsupported object file format" ); |
361 | } |
362 | |
363 | /// Dumps the specified object file. |
364 | static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, |
365 | const Archive *A = nullptr) { |
366 | std::string FileStr = |
367 | A ? Twine(A->getFileName() + "(" + Obj.getFileName() + ")" ).str() |
368 | : Obj.getFileName().str(); |
369 | |
370 | std::string ContentErrString; |
371 | if (Error ContentErr = Obj.initContent()) |
372 | ContentErrString = "unable to continue dumping, the file is corrupt: " + |
373 | toString(E: std::move(ContentErr)); |
374 | |
375 | ObjDumper *Dumper; |
376 | std::optional<SymbolComparator> SymComp; |
377 | Expected<std::unique_ptr<ObjDumper>> DumperOrErr = createDumper(Obj, Writer); |
378 | if (!DumperOrErr) |
379 | reportError(Err: DumperOrErr.takeError(), Input: FileStr); |
380 | Dumper = (*DumperOrErr).get(); |
381 | |
382 | if (!opts::SortKeys.empty()) { |
383 | if (Dumper->canCompareSymbols()) { |
384 | SymComp = SymbolComparator(); |
385 | for (SortSymbolKeyTy Key : opts::SortKeys) { |
386 | switch (Key) { |
387 | case NAME: |
388 | SymComp->addPredicate(Pred: [Dumper](SymbolRef LHS, SymbolRef RHS) { |
389 | return Dumper->compareSymbolsByName(LHS, RHS); |
390 | }); |
391 | break; |
392 | case TYPE: |
393 | SymComp->addPredicate(Pred: [Dumper](SymbolRef LHS, SymbolRef RHS) { |
394 | return Dumper->compareSymbolsByType(LHS, RHS); |
395 | }); |
396 | break; |
397 | case UNKNOWN: |
398 | llvm_unreachable("Unsupported sort key" ); |
399 | } |
400 | } |
401 | |
402 | } else { |
403 | reportWarning(Err: createStringError( |
404 | EC: errc::invalid_argument, |
405 | S: "--sort-symbols is not supported yet for this format" ), |
406 | Input: FileStr); |
407 | } |
408 | } |
409 | Dumper->printFileSummary(FileStr, Obj, InputFilenames: opts::InputFilenames, A); |
410 | |
411 | if (opts::FileHeaders) |
412 | Dumper->printFileHeaders(); |
413 | |
414 | // Auxiliary header in XOCFF is right after the file header, so print the data |
415 | // here. |
416 | if (Obj.isXCOFF() && opts::XCOFFAuxiliaryHeader) |
417 | Dumper->printAuxiliaryHeader(); |
418 | |
419 | // This is only used for ELF currently. In some cases, when an object is |
420 | // corrupt (e.g. truncated), we can't dump anything except the file header. |
421 | if (!ContentErrString.empty()) |
422 | reportError(Err: createError(Err: ContentErrString), Input: FileStr); |
423 | |
424 | if (opts::SectionDetails || opts::SectionHeaders) { |
425 | if (opts::Output == opts::GNU && opts::SectionDetails) |
426 | Dumper->printSectionDetails(); |
427 | else |
428 | Dumper->printSectionHeaders(); |
429 | } |
430 | |
431 | if (opts::HashSymbols) |
432 | Dumper->printHashSymbols(); |
433 | if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) |
434 | Dumper->printProgramHeaders(PrintProgramHeaders: opts::ProgramHeaders, PrintSectionMapping: opts::SectionMapping); |
435 | if (opts::DynamicTable) |
436 | Dumper->printDynamicTable(); |
437 | if (opts::NeededLibraries) |
438 | Dumper->printNeededLibraries(); |
439 | if (opts::Relocations) |
440 | Dumper->printRelocations(); |
441 | if (opts::DynRelocs) |
442 | Dumper->printDynamicRelocations(); |
443 | if (opts::UnwindInfo) |
444 | Dumper->printUnwindInfo(); |
445 | if (opts::Symbols || opts::DynamicSymbols) |
446 | Dumper->printSymbols(PrintSymbols: opts::Symbols, PrintDynamicSymbols: opts::DynamicSymbols, |
447 | ExtraSymInfo: opts::ExtraSymInfo, SymComp); |
448 | if (!opts::StringDump.empty()) |
449 | Dumper->printSectionsAsString(Obj, Sections: opts::StringDump, Decompress: opts::Decompress); |
450 | if (!opts::HexDump.empty()) |
451 | Dumper->printSectionsAsHex(Obj, Sections: opts::HexDump, Decompress: opts::Decompress); |
452 | if (opts::HashTable) |
453 | Dumper->printHashTable(); |
454 | if (opts::GnuHashTable) |
455 | Dumper->printGnuHashTable(); |
456 | if (opts::VersionInfo) |
457 | Dumper->printVersionInfo(); |
458 | if (opts::StringTable) |
459 | Dumper->printStringTable(); |
460 | if (Obj.isELF()) { |
461 | if (opts::DependentLibraries) |
462 | Dumper->printDependentLibs(); |
463 | if (opts::ELFLinkerOptions) |
464 | Dumper->printELFLinkerOptions(); |
465 | if (opts::ArchSpecificInfo) |
466 | Dumper->printArchSpecificInfo(); |
467 | if (opts::SectionGroups) |
468 | Dumper->printGroupSections(); |
469 | if (opts::HashHistogram) |
470 | Dumper->printHashHistograms(); |
471 | if (opts::CGProfile) |
472 | Dumper->printCGProfile(); |
473 | if (opts::BBAddrMap) |
474 | Dumper->printBBAddrMaps(PrettyPGOAnalysis: opts::PrettyPGOAnalysisMap); |
475 | if (opts::Addrsig) |
476 | Dumper->printAddrsig(); |
477 | if (opts::Notes) |
478 | Dumper->printNotes(); |
479 | if (opts::Memtag) |
480 | Dumper->printMemtag(); |
481 | } |
482 | if (Obj.isCOFF()) { |
483 | if (opts::COFFImports) |
484 | Dumper->printCOFFImports(); |
485 | if (opts::COFFExports) |
486 | Dumper->printCOFFExports(); |
487 | if (opts::COFFDirectives) |
488 | Dumper->printCOFFDirectives(); |
489 | if (opts::COFFBaseRelocs) |
490 | Dumper->printCOFFBaseReloc(); |
491 | if (opts::COFFDebugDirectory) |
492 | Dumper->printCOFFDebugDirectory(); |
493 | if (opts::COFFTLSDirectory) |
494 | Dumper->printCOFFTLSDirectory(); |
495 | if (opts::COFFResources) |
496 | Dumper->printCOFFResources(); |
497 | if (opts::COFFLoadConfig) |
498 | Dumper->printCOFFLoadConfig(); |
499 | if (opts::CGProfile) |
500 | Dumper->printCGProfile(); |
501 | if (opts::Addrsig) |
502 | Dumper->printAddrsig(); |
503 | if (opts::CodeView) |
504 | Dumper->printCodeViewDebugInfo(); |
505 | if (opts::CodeViewMergedTypes) |
506 | Dumper->mergeCodeViewTypes(CVIDs&: CVTypes.IDTable, CVTypes&: CVTypes.TypeTable, |
507 | GlobalCVIDs&: CVTypes.GlobalIDTable, GlobalCVTypes&: CVTypes.GlobalTypeTable, |
508 | GHash: opts::CodeViewEnableGHash); |
509 | } |
510 | if (Obj.isMachO()) { |
511 | if (opts::MachODataInCode) |
512 | Dumper->printMachODataInCode(); |
513 | if (opts::MachOIndirectSymbols) |
514 | Dumper->printMachOIndirectSymbols(); |
515 | if (opts::MachOLinkerOptions) |
516 | Dumper->printMachOLinkerOptions(); |
517 | if (opts::MachOSegment) |
518 | Dumper->printMachOSegment(); |
519 | if (opts::MachOVersionMin) |
520 | Dumper->printMachOVersionMin(); |
521 | if (opts::MachODysymtab) |
522 | Dumper->printMachODysymtab(); |
523 | if (opts::CGProfile) |
524 | Dumper->printCGProfile(); |
525 | } |
526 | |
527 | if (Obj.isXCOFF()) { |
528 | if (opts::XCOFFLoaderSectionHeader || opts::XCOFFLoaderSectionSymbol || |
529 | opts::XCOFFLoaderSectionRelocation) |
530 | Dumper->printLoaderSection(PrintHeader: opts::XCOFFLoaderSectionHeader, |
531 | PrintSymbols: opts::XCOFFLoaderSectionSymbol, |
532 | PrintRelocations: opts::XCOFFLoaderSectionRelocation); |
533 | |
534 | if (opts::XCOFFExceptionSection) |
535 | Dumper->printExceptionSection(); |
536 | } |
537 | |
538 | if (opts::PrintStackMap) |
539 | Dumper->printStackMap(); |
540 | if (opts::PrintStackSizes) |
541 | Dumper->printStackSizes(); |
542 | } |
543 | |
544 | /// Dumps each object file in \a Arc; |
545 | static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) { |
546 | Error Err = Error::success(); |
547 | for (auto &Child : Arc->children(Err)) { |
548 | Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); |
549 | if (!ChildOrErr) { |
550 | if (auto E = isNotObjectErrorInvalidFileType(Err: ChildOrErr.takeError())) |
551 | reportError(Err: std::move(E), Input: Arc->getFileName()); |
552 | continue; |
553 | } |
554 | |
555 | Binary *Bin = ChildOrErr->get(); |
556 | if (ObjectFile *Obj = dyn_cast<ObjectFile>(Val: Bin)) |
557 | dumpObject(Obj&: *Obj, Writer, A: Arc); |
558 | else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(Val: Bin)) |
559 | dumpCOFFImportFile(File: Imp, Writer); |
560 | else |
561 | reportWarning(Err: createStringError(EC: errc::invalid_argument, |
562 | S: Bin->getFileName() + |
563 | " has an unsupported file type" ), |
564 | Input: Arc->getFileName()); |
565 | } |
566 | if (Err) |
567 | reportError(Err: std::move(Err), Input: Arc->getFileName()); |
568 | } |
569 | |
570 | /// Dumps each object file in \a MachO Universal Binary; |
571 | static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary, |
572 | ScopedPrinter &Writer) { |
573 | for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) { |
574 | Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile(); |
575 | if (ObjOrErr) |
576 | dumpObject(Obj&: *ObjOrErr.get(), Writer); |
577 | else if (auto E = isNotObjectErrorInvalidFileType(Err: ObjOrErr.takeError())) |
578 | reportError(Err: ObjOrErr.takeError(), Input: UBinary->getFileName()); |
579 | else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive()) |
580 | dumpArchive(Arc: &*AOrErr.get(), Writer); |
581 | } |
582 | } |
583 | |
584 | /// Dumps \a COFF file; |
585 | static void dumpCOFFObject(COFFObjectFile *Obj, ScopedPrinter &Writer) { |
586 | dumpObject(Obj&: *Obj, Writer); |
587 | |
588 | // Dump a hybrid object when available. |
589 | std::unique_ptr<MemoryBuffer> HybridView = Obj->getHybridObjectView(); |
590 | if (!HybridView) |
591 | return; |
592 | Expected<std::unique_ptr<COFFObjectFile>> HybridObjOrErr = |
593 | COFFObjectFile::create(Object: *HybridView); |
594 | if (!HybridObjOrErr) |
595 | reportError(Err: HybridObjOrErr.takeError(), Input: Obj->getFileName().str()); |
596 | DictScope D(Writer, "HybridObject" ); |
597 | dumpObject(Obj&: **HybridObjOrErr, Writer); |
598 | } |
599 | |
600 | /// Dumps \a WinRes, Windows Resource (.res) file; |
601 | static void dumpWindowsResourceFile(WindowsResource *WinRes, |
602 | ScopedPrinter &Printer) { |
603 | WindowsRes::Dumper Dumper(WinRes, Printer); |
604 | if (auto Err = Dumper.printData()) |
605 | reportError(Err: std::move(Err), Input: WinRes->getFileName()); |
606 | } |
607 | |
608 | |
609 | /// Opens \a File and dumps it. |
610 | static void dumpInput(StringRef File, ScopedPrinter &Writer) { |
611 | ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = |
612 | MemoryBuffer::getFileOrSTDIN(Filename: File, /*IsText=*/false, |
613 | /*RequiresNullTerminator=*/false); |
614 | if (std::error_code EC = FileOrErr.getError()) |
615 | return reportError(Err: errorCodeToError(EC), Input: File); |
616 | |
617 | std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get(); |
618 | file_magic Type = identify_magic(magic: Buffer->getBuffer()); |
619 | if (Type == file_magic::bitcode) { |
620 | reportWarning(Err: createStringError(EC: errc::invalid_argument, |
621 | S: "bitcode files are not supported" ), |
622 | Input: File); |
623 | return; |
624 | } |
625 | |
626 | Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary( |
627 | Source: Buffer->getMemBufferRef(), /*Context=*/nullptr, /*InitContent=*/false); |
628 | if (!BinaryOrErr) |
629 | reportError(Err: BinaryOrErr.takeError(), Input: File); |
630 | |
631 | std::unique_ptr<Binary> Bin = std::move(*BinaryOrErr); |
632 | if (Archive *Arc = dyn_cast<Archive>(Val: Bin.get())) |
633 | dumpArchive(Arc, Writer); |
634 | else if (MachOUniversalBinary *UBinary = |
635 | dyn_cast<MachOUniversalBinary>(Val: Bin.get())) |
636 | dumpMachOUniversalBinary(UBinary, Writer); |
637 | else if (COFFObjectFile *Obj = dyn_cast<COFFObjectFile>(Val: Bin.get())) |
638 | dumpCOFFObject(Obj, Writer); |
639 | else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Val: Bin.get())) |
640 | dumpObject(Obj&: *Obj, Writer); |
641 | else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Val: Bin.get())) |
642 | dumpCOFFImportFile(File: Import, Writer); |
643 | else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Val: Bin.get())) |
644 | dumpWindowsResourceFile(WinRes, Printer&: Writer); |
645 | else |
646 | llvm_unreachable("unrecognized file type" ); |
647 | |
648 | CVTypes.Binaries.push_back( |
649 | x: OwningBinary<Binary>(std::move(Bin), std::move(Buffer))); |
650 | } |
651 | |
652 | std::unique_ptr<ScopedPrinter> createWriter() { |
653 | if (opts::Output == opts::JSON) |
654 | return std::make_unique<JSONScopedPrinter>( |
655 | args&: fouts(), args: opts::PrettyPrint ? 2 : 0, args: std::make_unique<ListScope>()); |
656 | return std::make_unique<ScopedPrinter>(args&: fouts()); |
657 | } |
658 | |
659 | int llvm_readobj_main(int argc, char **argv, const llvm::ToolContext &) { |
660 | BumpPtrAllocator A; |
661 | StringSaver Saver(A); |
662 | ReadobjOptTable Tbl; |
663 | ToolName = argv[0]; |
664 | opt::InputArgList Args = |
665 | Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, ErrorFn: [&](StringRef Msg) { |
666 | error(Msg); |
667 | exit(status: 1); |
668 | }); |
669 | if (Args.hasArg(Ids: OPT_help)) { |
670 | Tbl.printHelp( |
671 | OS&: outs(), |
672 | Usage: (Twine(ToolName) + " [options] <input object files>" ).str().c_str(), |
673 | Title: "LLVM Object Reader" ); |
674 | // TODO Replace this with OptTable API once it adds extrahelp support. |
675 | outs() << "\nPass @FILE as argument to read options from FILE.\n" ; |
676 | return 0; |
677 | } |
678 | if (Args.hasArg(Ids: OPT_version)) { |
679 | cl::PrintVersionMessage(); |
680 | return 0; |
681 | } |
682 | |
683 | if (sys::path::stem(path: argv[0]).contains(Other: "readelf" )) |
684 | opts::Output = opts::GNU; |
685 | parseOptions(Args); |
686 | |
687 | // Default to print error if no filename is specified. |
688 | if (opts::InputFilenames.empty()) { |
689 | error(Msg: "no input files specified" ); |
690 | } |
691 | |
692 | if (opts::All) { |
693 | opts::FileHeaders = true; |
694 | opts::XCOFFAuxiliaryHeader = true; |
695 | opts::ProgramHeaders = true; |
696 | opts::SectionHeaders = true; |
697 | opts::Symbols = true; |
698 | opts::Relocations = true; |
699 | opts::DynamicTable = true; |
700 | opts::Notes = true; |
701 | opts::VersionInfo = true; |
702 | opts::UnwindInfo = true; |
703 | opts::SectionGroups = true; |
704 | opts::HashHistogram = true; |
705 | if (opts::Output == opts::LLVM) { |
706 | opts::Addrsig = true; |
707 | opts::PrintStackSizes = true; |
708 | } |
709 | opts::Memtag = true; |
710 | } |
711 | |
712 | if (opts::Headers) { |
713 | opts::FileHeaders = true; |
714 | opts::XCOFFAuxiliaryHeader = true; |
715 | opts::ProgramHeaders = true; |
716 | opts::SectionHeaders = true; |
717 | } |
718 | |
719 | std::unique_ptr<ScopedPrinter> Writer = createWriter(); |
720 | |
721 | for (const std::string &I : opts::InputFilenames) |
722 | dumpInput(File: I, Writer&: *Writer); |
723 | |
724 | if (opts::CodeViewMergedTypes) { |
725 | if (opts::CodeViewEnableGHash) |
726 | dumpCodeViewMergedTypes(Writer&: *Writer, IpiRecords: CVTypes.GlobalIDTable.records(), |
727 | TpiRecords: CVTypes.GlobalTypeTable.records()); |
728 | else |
729 | dumpCodeViewMergedTypes(Writer&: *Writer, IpiRecords: CVTypes.IDTable.records(), |
730 | TpiRecords: CVTypes.TypeTable.records()); |
731 | } |
732 | |
733 | return 0; |
734 | } |
735 | |