| //===- 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/Errc.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include "llvm/Support/InitLLVM.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(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ |
| HELPTEXT, METAVAR, VALUES) \ |
| OPT_##ID, |
| #include "Opts.inc" |
| #undef OPTION |
| }; |
| |
| #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; |
| #include "Opts.inc" |
| #undef PREFIX |
| |
| const opt::OptTable::Info InfoTable[] = { |
| #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ |
| HELPTEXT, METAVAR, VALUES) \ |
| { \ |
| PREFIX, NAME, HELPTEXT, \ |
| METAVAR, OPT_##ID, opt::Option::KIND##Class, \ |
| PARAM, FLAGS, OPT_##GROUP, \ |
| OPT_##ALIAS, ALIASARGS, VALUES}, |
| #include "Opts.inc" |
| #undef OPTION |
| }; |
| |
| class ReadobjOptTable : public opt::OptTable { |
| public: |
| ReadobjOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } |
| }; |
| |
| enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; |
| } // namespace |
| |
| namespace opts { |
| static bool Addrsig; |
| static bool All; |
| static bool ArchSpecificInfo; |
| static bool BBAddrMap; |
| bool ExpandRelocs; |
| static bool CGProfile; |
| bool Demangle; |
| static bool DependentLibraries; |
| static bool DynRelocs; |
| static bool DynamicSymbols; |
| static bool FileHeaders; |
| static bool Headers; |
| static std::vector<std::string> HexDump; |
| 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; |
| static cl::boolOrDefault SectionMapping; |
| |
| // ELF specific options. |
| static bool DynamicTable; |
| static bool ELFLinkerOptions; |
| static bool GnuHashTable; |
| static bool HashSymbols; |
| static bool HashTable; |
| static bool HashHistogram; |
| static bool NeededLibraries; |
| static bool Notes; |
| static bool ProgramHeaders; |
| bool RawRelr; |
| static bool SectionGroups; |
| 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; |
| |
| // PE/COFF specific options. |
| static bool CodeView; |
| static bool CodeViewEnableGHash; |
| static bool CodeViewMergedTypes; |
| bool CodeViewSubsectionBytes; |
| static bool COFFBaseRelocs; |
| 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; |
| |
| 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::CGProfile = Args.hasArg(OPT_cg_profile); |
| 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::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::BOU_TRUE; |
| else if (Args.hasArg(OPT_section_mapping_EQ_false)) |
| opts::SectionMapping = cl::BOU_FALSE; |
| else |
| opts::SectionMapping = cl::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); |
| |
| // 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)) { |
| StringRef V(A->getValue()); |
| if (V == "LLVM") |
| opts::Output = opts::OutputStyleTy::LLVM; |
| else if (V == "GNU") |
| opts::Output = opts::OutputStyleTy::GNU; |
| else |
| error("--elf-output-style value should be either 'LLVM' or 'GNU'"); |
| } |
| 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::NeededLibraries = Args.hasArg(OPT_needed_libs); |
| opts::Notes = Args.hasArg(OPT_notes); |
| opts::ProgramHeaders = Args.hasArg(OPT_program_headers); |
| opts::RawRelr = Args.hasArg(OPT_raw_relr); |
| opts::SectionGroups = Args.hasArg(OPT_section_groups); |
| 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); |
| |
| // 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::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::InputFilenames = Args.getAllArgValues(OPT_INPUT); |
| } |
| |
| namespace { |
| struct ReadObjTypeTableBuilder { |
| ReadObjTypeTableBuilder() |
| : Allocator(), 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; |
| Expected<std::unique_ptr<ObjDumper>> DumperOrErr = createDumper(Obj, Writer); |
| if (!DumperOrErr) |
| reportError(DumperOrErr.takeError(), FileStr); |
| Dumper = (*DumperOrErr).get(); |
| |
| if (opts::Output == opts::LLVM || opts::InputFilenames.size() > 1 || A) { |
| Writer.startLine() << "\n"; |
| Writer.printString("File", FileStr); |
| } |
| if (opts::Output == opts::LLVM) { |
| Writer.printString("Format", Obj.getFileFormatName()); |
| Writer.printString("Arch", Triple::getArchTypeName(Obj.getArch())); |
| Writer.printString( |
| "AddressSize", |
| std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress()))); |
| Dumper->printLoadName(); |
| } |
| |
| if (opts::FileHeaders) |
| Dumper->printFileHeaders(); |
| |
| 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::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); |
| if (!opts::StringDump.empty()) |
| Dumper->printSectionsAsString(Obj, opts::StringDump); |
| if (!opts::HexDump.empty()) |
| Dumper->printSectionsAsHex(Obj, opts::HexDump); |
| if (opts::HashTable) |
| Dumper->printHashTable(); |
| if (opts::GnuHashTable) |
| Dumper->printGnuHashTable(); |
| if (opts::VersionInfo) |
| Dumper->printVersionInfo(); |
| 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::BBAddrMap) |
| Dumper->printBBAddrMaps(); |
| if (opts::Addrsig) |
| Dumper->printAddrsig(); |
| if (opts::Notes) |
| Dumper->printNotes(); |
| } |
| 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::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::MachODysymtab) |
| Dumper->printMachODysymtab(); |
| } |
| 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 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 (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))); |
| } |
| |
| int main(int argc, char *argv[]) { |
| InitLLVM X(argc, argv); |
| 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::UnwindInfo = true; |
| opts::SectionGroups = true; |
| opts::HashHistogram = true; |
| if (opts::Output == opts::LLVM) { |
| opts::Addrsig = true; |
| opts::PrintStackSizes = true; |
| } |
| } |
| |
| if (opts::Headers) { |
| opts::FileHeaders = true; |
| opts::XCOFFAuxiliaryHeader = true; |
| opts::ProgramHeaders = true; |
| opts::SectionHeaders = true; |
| } |
| |
| ScopedPrinter Writer(fouts()); |
| 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; |
| } |