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