forked from OSchip/llvm-project
633 lines
22 KiB
C++
633 lines
22 KiB
C++
//===- 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 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;
|
|
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)) {
|
|
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::NeededLibraries = Args.hasArg(OPT_needed_libs);
|
|
opts::Notes = Args.hasArg(OPT_notes);
|
|
opts::PrettyPrint = Args.hasArg(OPT_pretty_print);
|
|
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()
|
|
: 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();
|
|
|
|
Dumper->printFileSummary(FileStr, Obj, opts::InputFilenames, A);
|
|
|
|
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::CGProfile)
|
|
Dumper->printCGProfile();
|
|
}
|
|
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)));
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
std::unique_ptr<ScopedPrinter> Writer = createWriter();
|
|
|
|
for (const std::string &I : opts::InputFilenames)
|
|
dumpInput(I, *Writer.get());
|
|
|
|
if (opts::CodeViewMergedTypes) {
|
|
if (opts::CodeViewEnableGHash)
|
|
dumpCodeViewMergedTypes(*Writer.get(), CVTypes.GlobalIDTable.records(),
|
|
CVTypes.GlobalTypeTable.records());
|
|
else
|
|
dumpCodeViewMergedTypes(*Writer.get(), CVTypes.IDTable.records(),
|
|
CVTypes.TypeTable.records());
|
|
}
|
|
|
|
return 0;
|
|
}
|