| //===- llvm-readobj.cpp - Dump contents of an Object File -----------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This is a tool similar to readelf, except it works on multiple object file | |
| // formats. The main purpose of this tool is to provide detailed output suitable | |
| // for FileCheck. | |
| // | |
| // Flags should be similar to readelf where supported, but the output format | |
| // does not need to be identical. The point is to not make users learn yet | |
| // another set of flags. | |
| // | |
| // Output should be specialized for each format where appropriate. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm-readobj.h" | |
| #include "ObjDumper.h" | |
| #include "WindowsResourceDumper.h" | |
| #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" | |
| #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Object/Archive.h" | |
| #include "llvm/Object/COFFImportFile.h" | |
| #include "llvm/Object/ELFObjectFile.h" | |
| #include "llvm/Object/MachOUniversal.h" | |
| #include "llvm/Object/ObjectFile.h" | |
| #include "llvm/Object/Wasm.h" | |
| #include "llvm/Object/WindowsResource.h" | |
| #include "llvm/Object/XCOFFObjectFile.h" | |
| #include "llvm/Option/Arg.h" | |
| #include "llvm/Option/ArgList.h" | |
| #include "llvm/Option/Option.h" | |
| #include "llvm/Support/Casting.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/DataTypes.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/Driver.h" | |
| #include "llvm/Support/Errc.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/Support/Path.h" | |
| #include "llvm/Support/ScopedPrinter.h" | |
| #include "llvm/Support/WithColor.h" | |
| using namespace llvm; | |
| using namespace llvm::object; | |
| namespace { | |
| using namespace llvm::opt; // for HelpHidden in Opts.inc | |
| enum ID { | |
| OPT_INVALID = 0, // This is not an option ID. | |
| #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), | |
| #include "Opts.inc" | |
| #undef OPTION | |
| }; | |
| #define OPTTABLE_CODE | |
| #include "Opts.inc" | |
| class ReadobjOptTable : public opt::OptTable { | |
| public: | |
| ReadobjOptTable() : opt::OptTable(optionTables()) { | |
| setGroupedShortOptions(true); | |
| } | |
| }; | |
| enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; | |
| enum SortSymbolKeyTy { | |
| NAME = 0, | |
| TYPE = 1, | |
| UNKNOWN = 100, | |
| // TODO: add ADDRESS, SIZE as needed. | |
| }; | |
| } // namespace | |
| namespace opts { | |
| static bool Addrsig; | |
| static bool All; | |
| static bool ArchSpecificInfo; | |
| static bool BBAddrMap; | |
| static bool PrettyPGOAnalysisMap; | |
| bool ExpandRelocs; | |
| static bool CallGraphInfo; | |
| static bool CGProfile; | |
| static bool Decompress; | |
| bool Demangle; | |
| static bool DependentLibraries; | |
| static bool DynRelocs; | |
| static bool DynamicSymbols; | |
| static bool ExtraSymInfo; | |
| static bool FileHeaders; | |
| static bool Headers; | |
| static std::vector<std::string> HexDump; | |
| static bool PrettyPrint; | |
| static bool PrintStackMap; | |
| static bool PrintStackSizes; | |
| static bool Relocations; | |
| bool SectionData; | |
| static bool SectionDetails; | |
| static bool SectionHeaders; | |
| bool SectionRelocations; | |
| bool SectionSymbols; | |
| static std::vector<std::string> StringDump; | |
| static bool StringTable; | |
| static bool Symbols; | |
| static bool UnwindInfo; | |
| bool UnwindShowWODPool; | |
| static cl::boolOrDefault SectionMapping; | |
| static SmallVector<SortSymbolKeyTy> SortKeys; | |
| // ELF specific options. | |
| static bool DynamicTable; | |
| static bool ELFLinkerOptions; | |
| static bool GnuHashTable; | |
| static bool HashSymbols; | |
| static bool HashTable; | |
| static bool HashHistogram; | |
| static bool Memtag; | |
| static bool NeededLibraries; | |
| static bool Notes; | |
| static bool Offloading; | |
| static bool ProgramHeaders; | |
| static bool SectionGroups; | |
| static std::vector<std::string> SFrame; | |
| static bool VersionInfo; | |
| // Mach-O specific options. | |
| static bool MachODataInCode; | |
| static bool MachODysymtab; | |
| static bool MachOIndirectSymbols; | |
| static bool MachOLinkerOptions; | |
| static bool MachOSegment; | |
| static bool MachOVersionMin; | |
| static bool MachOTargetTriple; | |
| // PE/COFF specific options. | |
| static bool CodeView; | |
| static bool CodeViewEnableGHash; | |
| static bool CodeViewMergedTypes; | |
| bool CodeViewSubsectionBytes; | |
| static bool COFFBaseRelocs; | |
| static bool COFFPseudoRelocs; | |
| static bool COFFDebugDirectory; | |
| static bool COFFDirectives; | |
| static bool COFFExports; | |
| static bool COFFImports; | |
| static bool COFFLoadConfig; | |
| static bool COFFResources; | |
| static bool COFFTLSDirectory; | |
| // XCOFF specific options. | |
| static bool XCOFFAuxiliaryHeader; | |
| static bool XCOFFLoaderSectionHeader; | |
| static bool XCOFFLoaderSectionSymbol; | |
| static bool XCOFFLoaderSectionRelocation; | |
| static bool XCOFFExceptionSection; | |
| OutputStyleTy Output = OutputStyleTy::LLVM; | |
| static std::vector<std::string> InputFilenames; | |
| } // namespace opts | |
| static StringRef ToolName; | |
| namespace llvm { | |
| [[noreturn]] static void error(Twine Msg) { | |
| // Flush the standard output to print the error at a | |
| // proper place. | |
| fouts().flush(); | |
| WithColor::error(errs(), ToolName) << Msg << "\n"; | |
| exit(1); | |
| } | |
| [[noreturn]] void reportError(Error Err, StringRef Input) { | |
| assert(Err); | |
| if (Input == "-") | |
| Input = "<stdin>"; | |
| handleAllErrors(createFileError(Input, std::move(Err)), | |
| [&](const ErrorInfoBase &EI) { error(EI.message()); }); | |
| llvm_unreachable("error() call should never return"); | |
| } | |
| void reportWarning(Error Err, StringRef Input) { | |
| assert(Err); | |
| if (Input == "-") | |
| Input = "<stdin>"; | |
| // Flush the standard output to print the warning at a | |
| // proper place. | |
| fouts().flush(); | |
| handleAllErrors( | |
| createFileError(Input, std::move(Err)), [&](const ErrorInfoBase &EI) { | |
| WithColor::warning(errs(), ToolName) << EI.message() << "\n"; | |
| }); | |
| } | |
| } // namespace llvm | |
| static void parseOptions(const opt::InputArgList &Args) { | |
| opts::Addrsig = Args.hasArg(OPT_addrsig); | |
| opts::All = Args.hasArg(OPT_all); | |
| opts::ArchSpecificInfo = Args.hasArg(OPT_arch_specific); | |
| opts::BBAddrMap = Args.hasArg(OPT_bb_addr_map); | |
| opts::PrettyPGOAnalysisMap = Args.hasArg(OPT_pretty_pgo_analysis_map); | |
| if (opts::PrettyPGOAnalysisMap && !opts::BBAddrMap) | |
| WithColor::warning(errs(), ToolName) | |
| << "--bb-addr-map must be enabled for --pretty-pgo-analysis-map to " | |
| "have an effect\n"; | |
| opts::CallGraphInfo = Args.hasArg(OPT_call_graph_info); | |
| opts::CGProfile = Args.hasArg(OPT_cg_profile); | |
| opts::Decompress = Args.hasArg(OPT_decompress); | |
| opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); | |
| opts::DependentLibraries = Args.hasArg(OPT_dependent_libraries); | |
| opts::DynRelocs = Args.hasArg(OPT_dyn_relocations); | |
| opts::DynamicSymbols = Args.hasArg(OPT_dyn_syms); | |
| opts::ExpandRelocs = Args.hasArg(OPT_expand_relocs); | |
| opts::ExtraSymInfo = Args.hasArg(OPT_extra_sym_info); | |
| opts::FileHeaders = Args.hasArg(OPT_file_header); | |
| opts::Headers = Args.hasArg(OPT_headers); | |
| opts::HexDump = Args.getAllArgValues(OPT_hex_dump_EQ); | |
| opts::Relocations = Args.hasArg(OPT_relocs); | |
| opts::SectionData = Args.hasArg(OPT_section_data); | |
| opts::SectionDetails = Args.hasArg(OPT_section_details); | |
| opts::SectionHeaders = Args.hasArg(OPT_section_headers); | |
| opts::SectionRelocations = Args.hasArg(OPT_section_relocations); | |
| opts::SectionSymbols = Args.hasArg(OPT_section_symbols); | |
| if (Args.hasArg(OPT_section_mapping)) | |
| opts::SectionMapping = cl::boolOrDefault::BOU_TRUE; | |
| else if (Args.hasArg(OPT_section_mapping_EQ_false)) | |
| opts::SectionMapping = cl::boolOrDefault::BOU_FALSE; | |
| else | |
| opts::SectionMapping = cl::boolOrDefault::BOU_UNSET; | |
| opts::PrintStackSizes = Args.hasArg(OPT_stack_sizes); | |
| opts::PrintStackMap = Args.hasArg(OPT_stackmap); | |
| opts::StringDump = Args.getAllArgValues(OPT_string_dump_EQ); | |
| opts::StringTable = Args.hasArg(OPT_string_table); | |
| opts::Symbols = Args.hasArg(OPT_symbols); | |
| opts::UnwindInfo = Args.hasArg(OPT_unwind); | |
| opts::UnwindShowWODPool = Args.hasArg(OPT_unwind_show_wod_pool); | |
| // ELF specific options. | |
| opts::DynamicTable = Args.hasArg(OPT_dynamic_table); | |
| opts::ELFLinkerOptions = Args.hasArg(OPT_elf_linker_options); | |
| if (Arg *A = Args.getLastArg(OPT_elf_output_style_EQ)) { | |
| std::string OutputStyleChoice = A->getValue(); | |
| opts::Output = StringSwitch<opts::OutputStyleTy>(OutputStyleChoice) | |
| .Case("LLVM", opts::OutputStyleTy::LLVM) | |
| .Case("GNU", opts::OutputStyleTy::GNU) | |
| .Case("JSON", opts::OutputStyleTy::JSON) | |
| .Default(opts::OutputStyleTy::UNKNOWN); | |
| if (opts::Output == opts::OutputStyleTy::UNKNOWN) { | |
| error("--elf-output-style value should be either 'LLVM', 'GNU', or " | |
| "'JSON', but was '" + | |
| OutputStyleChoice + "'"); | |
| } | |
| } | |
| opts::GnuHashTable = Args.hasArg(OPT_gnu_hash_table); | |
| opts::HashSymbols = Args.hasArg(OPT_hash_symbols); | |
| opts::HashTable = Args.hasArg(OPT_hash_table); | |
| opts::HashHistogram = Args.hasArg(OPT_histogram); | |
| opts::Memtag = Args.hasArg(OPT_memtag); | |
| opts::NeededLibraries = Args.hasArg(OPT_needed_libs); | |
| opts::Notes = Args.hasArg(OPT_notes); | |
| opts::Offloading = Args.hasArg(OPT_offloading); | |
| opts::PrettyPrint = Args.hasArg(OPT_pretty_print); | |
| opts::ProgramHeaders = Args.hasArg(OPT_program_headers); | |
| opts::SectionGroups = Args.hasArg(OPT_section_groups); | |
| opts::SFrame = Args.getAllArgValues(OPT_sframe_EQ); | |
| if (Arg *A = Args.getLastArg(OPT_sort_symbols_EQ)) { | |
| for (StringRef KeyStr : llvm::split(A->getValue(), ",")) { | |
| SortSymbolKeyTy KeyType = StringSwitch<SortSymbolKeyTy>(KeyStr) | |
| .Case("name", SortSymbolKeyTy::NAME) | |
| .Case("type", SortSymbolKeyTy::TYPE) | |
| .Default(SortSymbolKeyTy::UNKNOWN); | |
| if (KeyType == SortSymbolKeyTy::UNKNOWN) | |
| error("--sort-symbols value should be 'name' or 'type', but was '" + | |
| Twine(KeyStr) + "'"); | |
| opts::SortKeys.push_back(KeyType); | |
| } | |
| } | |
| opts::VersionInfo = Args.hasArg(OPT_version_info); | |
| // Mach-O specific options. | |
| opts::MachODataInCode = Args.hasArg(OPT_macho_data_in_code); | |
| opts::MachODysymtab = Args.hasArg(OPT_macho_dysymtab); | |
| opts::MachOIndirectSymbols = Args.hasArg(OPT_macho_indirect_symbols); | |
| opts::MachOLinkerOptions = Args.hasArg(OPT_macho_linker_options); | |
| opts::MachOSegment = Args.hasArg(OPT_macho_segment); | |
| opts::MachOVersionMin = Args.hasArg(OPT_macho_version_min); | |
| opts::MachOTargetTriple = Args.hasArg(OPT_macho_target_triple); | |
| // PE/COFF specific options. | |
| opts::CodeView = Args.hasArg(OPT_codeview); | |
| opts::CodeViewEnableGHash = Args.hasArg(OPT_codeview_ghash); | |
| opts::CodeViewMergedTypes = Args.hasArg(OPT_codeview_merged_types); | |
| opts::CodeViewSubsectionBytes = Args.hasArg(OPT_codeview_subsection_bytes); | |
| opts::COFFBaseRelocs = Args.hasArg(OPT_coff_basereloc); | |
| opts::COFFPseudoRelocs = Args.hasArg(OPT_coff_pseudoreloc); | |
| opts::COFFDebugDirectory = Args.hasArg(OPT_coff_debug_directory); | |
| opts::COFFDirectives = Args.hasArg(OPT_coff_directives); | |
| opts::COFFExports = Args.hasArg(OPT_coff_exports); | |
| opts::COFFImports = Args.hasArg(OPT_coff_imports); | |
| opts::COFFLoadConfig = Args.hasArg(OPT_coff_load_config); | |
| opts::COFFResources = Args.hasArg(OPT_coff_resources); | |
| opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory); | |
| // XCOFF specific options. | |
| opts::XCOFFAuxiliaryHeader = Args.hasArg(OPT_auxiliary_header); | |
| opts::XCOFFLoaderSectionHeader = Args.hasArg(OPT_loader_section_header); | |
| opts::XCOFFLoaderSectionSymbol = Args.hasArg(OPT_loader_section_symbols); | |
| opts::XCOFFLoaderSectionRelocation = | |
| Args.hasArg(OPT_loader_section_relocations); | |
| opts::XCOFFExceptionSection = Args.hasArg(OPT_exception_section); | |
| opts::InputFilenames = Args.getAllArgValues(OPT_INPUT); | |
| } | |
| namespace { | |
| struct ReadObjTypeTableBuilder { | |
| ReadObjTypeTableBuilder() | |
| : IDTable(Allocator), TypeTable(Allocator), GlobalIDTable(Allocator), | |
| GlobalTypeTable(Allocator) {} | |
| llvm::BumpPtrAllocator Allocator; | |
| llvm::codeview::MergingTypeTableBuilder IDTable; | |
| llvm::codeview::MergingTypeTableBuilder TypeTable; | |
| llvm::codeview::GlobalTypeTableBuilder GlobalIDTable; | |
| llvm::codeview::GlobalTypeTableBuilder GlobalTypeTable; | |
| std::vector<OwningBinary<Binary>> Binaries; | |
| }; | |
| } // namespace | |
| static ReadObjTypeTableBuilder CVTypes; | |
| /// Creates an format-specific object file dumper. | |
| static Expected<std::unique_ptr<ObjDumper>> | |
| createDumper(const ObjectFile &Obj, ScopedPrinter &Writer) { | |
| if (const COFFObjectFile *COFFObj = dyn_cast<COFFObjectFile>(&Obj)) | |
| return createCOFFDumper(*COFFObj, Writer); | |
| if (const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) | |
| return createELFDumper(*ELFObj, Writer); | |
| if (const MachOObjectFile *MachOObj = dyn_cast<MachOObjectFile>(&Obj)) | |
| return createMachODumper(*MachOObj, Writer); | |
| if (const WasmObjectFile *WasmObj = dyn_cast<WasmObjectFile>(&Obj)) | |
| return createWasmDumper(*WasmObj, Writer); | |
| if (const XCOFFObjectFile *XObj = dyn_cast<XCOFFObjectFile>(&Obj)) | |
| return createXCOFFDumper(*XObj, Writer); | |
| return createStringError(errc::invalid_argument, | |
| "unsupported object file format"); | |
| } | |
| /// Dumps the specified object file. | |
| static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, | |
| const Archive *A = nullptr) { | |
| std::string FileStr = | |
| A ? Twine(A->getFileName() + "(" + Obj.getFileName() + ")").str() | |
| : Obj.getFileName().str(); | |
| std::string ContentErrString; | |
| if (Error ContentErr = Obj.initContent()) | |
| ContentErrString = "unable to continue dumping, the file is corrupt: " + | |
| toString(std::move(ContentErr)); | |
| ObjDumper *Dumper; | |
| std::optional<SymbolComparator> SymComp; | |
| Expected<std::unique_ptr<ObjDumper>> DumperOrErr = createDumper(Obj, Writer); | |
| if (!DumperOrErr) | |
| reportError(DumperOrErr.takeError(), FileStr); | |
| Dumper = (*DumperOrErr).get(); | |
| if (!opts::SortKeys.empty()) { | |
| if (Dumper->canCompareSymbols()) { | |
| SymComp = SymbolComparator(); | |
| for (SortSymbolKeyTy Key : opts::SortKeys) { | |
| switch (Key) { | |
| case NAME: | |
| SymComp->addPredicate([Dumper](SymbolRef LHS, SymbolRef RHS) { | |
| return Dumper->compareSymbolsByName(LHS, RHS); | |
| }); | |
| break; | |
| case TYPE: | |
| SymComp->addPredicate([Dumper](SymbolRef LHS, SymbolRef RHS) { | |
| return Dumper->compareSymbolsByType(LHS, RHS); | |
| }); | |
| break; | |
| case UNKNOWN: | |
| llvm_unreachable("Unsupported sort key"); | |
| } | |
| } | |
| } else { | |
| reportWarning(createStringError( | |
| errc::invalid_argument, | |
| "--sort-symbols is not supported yet for this format"), | |
| FileStr); | |
| } | |
| } | |
| Dumper->printFileSummary(FileStr, Obj, opts::InputFilenames, A); | |
| if (opts::FileHeaders) | |
| Dumper->printFileHeaders(); | |
| // Auxiliary header in XOCFF is right after the file header, so print the data | |
| // here. | |
| if (Obj.isXCOFF() && opts::XCOFFAuxiliaryHeader) | |
| Dumper->printAuxiliaryHeader(); | |
| // This is only used for ELF currently. In some cases, when an object is | |
| // corrupt (e.g. truncated), we can't dump anything except the file header. | |
| if (!ContentErrString.empty()) | |
| reportError(createError(ContentErrString), FileStr); | |
| if (opts::SectionDetails || opts::SectionHeaders) { | |
| if (opts::Output == opts::GNU && opts::SectionDetails) | |
| Dumper->printSectionDetails(); | |
| else | |
| Dumper->printSectionHeaders(); | |
| } | |
| if (opts::HashSymbols) | |
| Dumper->printHashSymbols(); | |
| if (opts::ProgramHeaders || | |
| opts::SectionMapping == cl::boolOrDefault::BOU_TRUE) | |
| Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping); | |
| if (opts::DynamicTable) | |
| Dumper->printDynamicTable(); | |
| if (opts::NeededLibraries) | |
| Dumper->printNeededLibraries(); | |
| if (opts::Relocations) | |
| Dumper->printRelocations(); | |
| if (opts::DynRelocs) | |
| Dumper->printDynamicRelocations(); | |
| if (opts::UnwindInfo) | |
| Dumper->printUnwindInfo(); | |
| if (opts::Symbols || opts::DynamicSymbols) | |
| Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols, | |
| opts::ExtraSymInfo, SymComp); | |
| if (!opts::StringDump.empty()) | |
| Dumper->printSectionsAsString(Obj, opts::StringDump, opts::Decompress); | |
| if (!opts::HexDump.empty()) | |
| Dumper->printSectionsAsHex(Obj, opts::HexDump, opts::Decompress); | |
| if (opts::HashTable) | |
| Dumper->printHashTable(); | |
| if (opts::GnuHashTable) | |
| Dumper->printGnuHashTable(); | |
| if (opts::VersionInfo) | |
| Dumper->printVersionInfo(); | |
| if (opts::Offloading) | |
| Dumper->printOffloading(Obj); | |
| if (opts::StringTable) | |
| Dumper->printStringTable(); | |
| if (Obj.isELF()) { | |
| if (opts::DependentLibraries) | |
| Dumper->printDependentLibs(); | |
| if (opts::ELFLinkerOptions) | |
| Dumper->printELFLinkerOptions(); | |
| if (opts::ArchSpecificInfo) | |
| Dumper->printArchSpecificInfo(); | |
| if (opts::SectionGroups) | |
| Dumper->printGroupSections(); | |
| if (opts::HashHistogram) | |
| Dumper->printHashHistograms(); | |
| if (opts::CGProfile) | |
| Dumper->printCGProfile(); | |
| if (opts::CallGraphInfo) | |
| Dumper->printCallGraphInfo(); | |
| if (opts::BBAddrMap) | |
| Dumper->printBBAddrMaps(opts::PrettyPGOAnalysisMap); | |
| if (opts::Addrsig) | |
| Dumper->printAddrsig(); | |
| if (opts::Notes) | |
| Dumper->printNotes(); | |
| if (opts::Memtag) | |
| Dumper->printMemtag(); | |
| if (!opts::SFrame.empty()) | |
| Dumper->printSectionsAsSFrame(opts::SFrame); | |
| } | |
| if (Obj.isCOFF()) { | |
| if (opts::COFFImports) | |
| Dumper->printCOFFImports(); | |
| if (opts::COFFExports) | |
| Dumper->printCOFFExports(); | |
| if (opts::COFFDirectives) | |
| Dumper->printCOFFDirectives(); | |
| if (opts::COFFBaseRelocs) | |
| Dumper->printCOFFBaseReloc(); | |
| if (opts::COFFPseudoRelocs) | |
| Dumper->printCOFFPseudoReloc(); | |
| if (opts::COFFDebugDirectory) | |
| Dumper->printCOFFDebugDirectory(); | |
| if (opts::COFFTLSDirectory) | |
| Dumper->printCOFFTLSDirectory(); | |
| if (opts::COFFResources) | |
| Dumper->printCOFFResources(); | |
| if (opts::COFFLoadConfig) | |
| Dumper->printCOFFLoadConfig(); | |
| if (opts::CGProfile) | |
| Dumper->printCGProfile(); | |
| if (opts::Addrsig) | |
| Dumper->printAddrsig(); | |
| if (opts::CodeView) | |
| Dumper->printCodeViewDebugInfo(); | |
| if (opts::CodeViewMergedTypes) | |
| Dumper->mergeCodeViewTypes(CVTypes.IDTable, CVTypes.TypeTable, | |
| CVTypes.GlobalIDTable, CVTypes.GlobalTypeTable, | |
| opts::CodeViewEnableGHash); | |
| } | |
| if (Obj.isMachO()) { | |
| if (opts::MachODataInCode) | |
| Dumper->printMachODataInCode(); | |
| if (opts::MachOIndirectSymbols) | |
| Dumper->printMachOIndirectSymbols(); | |
| if (opts::MachOLinkerOptions) | |
| Dumper->printMachOLinkerOptions(); | |
| if (opts::MachOSegment) | |
| Dumper->printMachOSegment(); | |
| if (opts::MachOVersionMin) | |
| Dumper->printMachOVersionMin(); | |
| if (opts::MachOTargetTriple) | |
| Dumper->printMachOTargetTriple(); | |
| if (opts::MachODysymtab) | |
| Dumper->printMachODysymtab(); | |
| if (opts::CGProfile) | |
| Dumper->printCGProfile(); | |
| } | |
| if (Obj.isXCOFF()) { | |
| if (opts::XCOFFLoaderSectionHeader || opts::XCOFFLoaderSectionSymbol || | |
| opts::XCOFFLoaderSectionRelocation) | |
| Dumper->printLoaderSection(opts::XCOFFLoaderSectionHeader, | |
| opts::XCOFFLoaderSectionSymbol, | |
| opts::XCOFFLoaderSectionRelocation); | |
| if (opts::XCOFFExceptionSection) | |
| Dumper->printExceptionSection(); | |
| } | |
| if (opts::PrintStackMap) | |
| Dumper->printStackMap(); | |
| if (opts::PrintStackSizes) | |
| Dumper->printStackSizes(); | |
| } | |
| /// Dumps each object file in \a Arc; | |
| static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) { | |
| Error Err = Error::success(); | |
| for (auto &Child : Arc->children(Err)) { | |
| Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); | |
| if (!ChildOrErr) { | |
| if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) | |
| reportError(std::move(E), Arc->getFileName()); | |
| continue; | |
| } | |
| Binary *Bin = ChildOrErr->get(); | |
| if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin)) | |
| dumpObject(*Obj, Writer, Arc); | |
| else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(Bin)) | |
| dumpCOFFImportFile(Imp, Writer); | |
| else | |
| reportWarning(createStringError(errc::invalid_argument, | |
| Bin->getFileName() + | |
| " has an unsupported file type"), | |
| Arc->getFileName()); | |
| } | |
| if (Err) | |
| reportError(std::move(Err), Arc->getFileName()); | |
| } | |
| /// Dumps each object file in \a MachO Universal Binary; | |
| static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary, | |
| ScopedPrinter &Writer) { | |
| for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) { | |
| Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile(); | |
| if (ObjOrErr) | |
| dumpObject(*ObjOrErr.get(), Writer); | |
| else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) | |
| reportError(ObjOrErr.takeError(), UBinary->getFileName()); | |
| else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive()) | |
| dumpArchive(&*AOrErr.get(), Writer); | |
| } | |
| } | |
| /// Dumps \a COFF file; | |
| static void dumpCOFFObject(COFFObjectFile *Obj, ScopedPrinter &Writer) { | |
| dumpObject(*Obj, Writer); | |
| // Dump a hybrid object when available. | |
| MemoryBufferRef HybridView; | |
| std::unique_ptr<MemoryBuffer> HybridViewBuf; | |
| if (std::optional<MemoryBufferRef> HybridSec = Obj->findHybridObjectSection()) | |
| HybridView = *HybridSec; | |
| else if ((HybridViewBuf = Obj->getHybridObjectView())) | |
| HybridView = HybridViewBuf->getMemBufferRef(); | |
| else | |
| return; | |
| Expected<std::unique_ptr<COFFObjectFile>> HybridObjOrErr = | |
| COFFObjectFile::create(HybridView); | |
| if (!HybridObjOrErr) | |
| reportError(HybridObjOrErr.takeError(), Obj->getFileName().str()); | |
| DictScope D(Writer, "HybridObject"); | |
| dumpObject(**HybridObjOrErr, Writer); | |
| } | |
| /// Dumps \a WinRes, Windows Resource (.res) file; | |
| static void dumpWindowsResourceFile(WindowsResource *WinRes, | |
| ScopedPrinter &Printer) { | |
| WindowsRes::Dumper Dumper(WinRes, Printer); | |
| if (auto Err = Dumper.printData()) | |
| reportError(std::move(Err), WinRes->getFileName()); | |
| } | |
| /// Opens \a File and dumps it. | |
| static void dumpInput(StringRef File, ScopedPrinter &Writer) { | |
| ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = | |
| MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false, | |
| /*RequiresNullTerminator=*/false); | |
| if (std::error_code EC = FileOrErr.getError()) | |
| return reportError(errorCodeToError(EC), File); | |
| std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get(); | |
| file_magic Type = identify_magic(Buffer->getBuffer()); | |
| if (Type == file_magic::bitcode) { | |
| reportWarning(createStringError(errc::invalid_argument, | |
| "bitcode files are not supported"), | |
| File); | |
| return; | |
| } | |
| Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary( | |
| Buffer->getMemBufferRef(), /*Context=*/nullptr, /*InitContent=*/false); | |
| if (!BinaryOrErr) | |
| reportError(BinaryOrErr.takeError(), File); | |
| std::unique_ptr<Binary> Bin = std::move(*BinaryOrErr); | |
| if (Archive *Arc = dyn_cast<Archive>(Bin.get())) | |
| dumpArchive(Arc, Writer); | |
| else if (MachOUniversalBinary *UBinary = | |
| dyn_cast<MachOUniversalBinary>(Bin.get())) | |
| dumpMachOUniversalBinary(UBinary, Writer); | |
| else if (COFFObjectFile *Obj = dyn_cast<COFFObjectFile>(Bin.get())) | |
| dumpCOFFObject(Obj, Writer); | |
| else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin.get())) | |
| dumpObject(*Obj, Writer); | |
| else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Bin.get())) | |
| dumpCOFFImportFile(Import, Writer); | |
| else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Bin.get())) | |
| dumpWindowsResourceFile(WinRes, Writer); | |
| else | |
| llvm_unreachable("unrecognized file type"); | |
| CVTypes.Binaries.push_back( | |
| OwningBinary<Binary>(std::move(Bin), std::move(Buffer))); | |
| } | |
| std::unique_ptr<ScopedPrinter> createWriter() { | |
| if (opts::Output == opts::JSON) | |
| return std::make_unique<JSONScopedPrinter>( | |
| fouts(), opts::PrettyPrint ? 2 : 0, std::make_unique<ListScope>()); | |
| return std::make_unique<ScopedPrinter>(fouts()); | |
| } | |
| int llvm_readobj_main(int argc, char **argv, const llvm::ToolContext &) { | |
| BumpPtrAllocator A; | |
| StringSaver Saver(A); | |
| ReadobjOptTable Tbl; | |
| ToolName = argv[0]; | |
| opt::InputArgList Args = | |
| Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { | |
| error(Msg); | |
| exit(1); | |
| }); | |
| if (Args.hasArg(OPT_help)) { | |
| Tbl.printHelp( | |
| outs(), | |
| (Twine(ToolName) + " [options] <input object files>").str().c_str(), | |
| "LLVM Object Reader"); | |
| // TODO Replace this with OptTable API once it adds extrahelp support. | |
| outs() << "\nPass @FILE as argument to read options from FILE.\n"; | |
| return 0; | |
| } | |
| if (Args.hasArg(OPT_version)) { | |
| cl::PrintVersionMessage(); | |
| return 0; | |
| } | |
| if (sys::path::stem(argv[0]).contains("readelf")) | |
| opts::Output = opts::GNU; | |
| parseOptions(Args); | |
| // Default to print error if no filename is specified. | |
| if (opts::InputFilenames.empty()) { | |
| error("no input files specified"); | |
| } | |
| if (opts::All) { | |
| opts::FileHeaders = true; | |
| opts::XCOFFAuxiliaryHeader = true; | |
| opts::ProgramHeaders = true; | |
| opts::SectionHeaders = true; | |
| opts::Symbols = true; | |
| opts::Relocations = true; | |
| opts::DynamicTable = true; | |
| opts::Notes = true; | |
| opts::VersionInfo = true; | |
| opts::Offloading = true; | |
| opts::UnwindInfo = true; | |
| opts::SectionGroups = true; | |
| opts::HashHistogram = true; | |
| if (opts::Output == opts::LLVM) { | |
| opts::Addrsig = true; | |
| opts::PrintStackSizes = true; | |
| } | |
| opts::Memtag = true; | |
| } | |
| if (opts::Headers) { | |
| opts::FileHeaders = true; | |
| opts::XCOFFAuxiliaryHeader = true; | |
| opts::ProgramHeaders = true; | |
| opts::SectionHeaders = true; | |
| } | |
| std::unique_ptr<ScopedPrinter> Writer = createWriter(); | |
| for (const std::string &I : opts::InputFilenames) | |
| dumpInput(I, *Writer); | |
| if (opts::CodeViewMergedTypes) { | |
| if (opts::CodeViewEnableGHash) | |
| dumpCodeViewMergedTypes(*Writer, CVTypes.GlobalIDTable.records(), | |
| CVTypes.GlobalTypeTable.records()); | |
| else | |
| dumpCodeViewMergedTypes(*Writer, CVTypes.IDTable.records(), | |
| CVTypes.TypeTable.records()); | |
| } | |
| return 0; | |
| } |