aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-objdump/llvm-objdump.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-objdump/llvm-objdump.cpp')
-rw-r--r--llvm/tools/llvm-objdump/llvm-objdump.cpp1411
1 files changed, 543 insertions, 868 deletions
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 3134f989603a..48ae92f734c7 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -19,18 +19,19 @@
#include "COFFDump.h"
#include "ELFDump.h"
#include "MachODump.h"
+#include "ObjdumpOptID.h"
+#include "SourcePrinter.h"
#include "WasmDump.h"
#include "XCOFFDump.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetOperations.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
-#include "llvm/CodeGen/FaultMaps.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/Demangle/Demangle.h"
@@ -50,12 +51,15 @@
#include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/FaultMapParser.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/Wasm.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/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
@@ -81,307 +85,143 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::objdump;
+using namespace llvm::opt;
-#define DEBUG_TYPE "objdump"
-
-static cl::OptionCategory ObjdumpCat("llvm-objdump Options");
-
-static cl::opt<uint64_t> AdjustVMA(
- "adjust-vma",
- cl::desc("Increase the displayed address by the specified offset"),
- cl::value_desc("offset"), cl::init(0), cl::cat(ObjdumpCat));
-
-static cl::opt<bool>
- AllHeaders("all-headers",
- cl::desc("Display all available header information"),
- cl::cat(ObjdumpCat));
-static cl::alias AllHeadersShort("x", cl::desc("Alias for --all-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(AllHeaders));
-
-static cl::opt<std::string>
- ArchName("arch-name",
- cl::desc("Target arch to disassemble for, "
- "see --version for available targets"),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool>
- objdump::ArchiveHeaders("archive-headers",
- cl::desc("Display archive header information"),
- cl::cat(ObjdumpCat));
-static cl::alias ArchiveHeadersShort("a",
- cl::desc("Alias for --archive-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(ArchiveHeaders));
-
-cl::opt<bool> objdump::Demangle("demangle", cl::desc("Demangle symbols names"),
- cl::init(false), cl::cat(ObjdumpCat));
-static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(Demangle));
-
-cl::opt<bool> objdump::Disassemble(
- "disassemble",
- cl::desc("Display assembler mnemonics for the machine instructions"),
- cl::cat(ObjdumpCat));
-static cl::alias DisassembleShort("d", cl::desc("Alias for --disassemble"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(Disassemble));
-
-cl::opt<bool> objdump::DisassembleAll(
- "disassemble-all",
- cl::desc("Display assembler mnemonics for the machine instructions"),
- cl::cat(ObjdumpCat));
-static cl::alias DisassembleAllShort("D",
- cl::desc("Alias for --disassemble-all"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DisassembleAll));
-
-cl::opt<bool> objdump::SymbolDescription(
- "symbol-description",
- cl::desc("Add symbol description for disassembly. This "
- "option is for XCOFF files only"),
- cl::init(false), cl::cat(ObjdumpCat));
-
-static cl::list<std::string>
- DisassembleSymbols("disassemble-symbols", cl::CommaSeparated,
- cl::desc("List of symbols to disassemble. "
- "Accept demangled names when --demangle is "
- "specified, otherwise accept mangled names"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> DisassembleZeroes(
- "disassemble-zeroes",
- cl::desc("Do not skip blocks of zeroes when disassembling"),
- cl::cat(ObjdumpCat));
-static cl::alias
- DisassembleZeroesShort("z", cl::desc("Alias for --disassemble-zeroes"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DisassembleZeroes));
-
-static cl::list<std::string>
- DisassemblerOptions("disassembler-options",
- cl::desc("Pass target specific disassembler options"),
- cl::value_desc("options"), cl::CommaSeparated,
- cl::cat(ObjdumpCat));
-static cl::alias
- DisassemblerOptionsShort("M", cl::desc("Alias for --disassembler-options"),
- cl::NotHidden, cl::Grouping, cl::Prefix,
- cl::CommaSeparated,
- cl::aliasopt(DisassemblerOptions));
-
-cl::opt<DIDumpType> objdump::DwarfDumpType(
- "dwarf", cl::init(DIDT_Null), cl::desc("Dump of dwarf debug sections:"),
- cl::values(clEnumValN(DIDT_DebugFrame, "frames", ".debug_frame")),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> DynamicRelocations(
- "dynamic-reloc",
- cl::desc("Display the dynamic relocation entries in the file"),
- cl::cat(ObjdumpCat));
-static cl::alias DynamicRelocationShort("R",
- cl::desc("Alias for --dynamic-reloc"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DynamicRelocations));
-
-static cl::opt<bool>
- FaultMapSection("fault-map-section",
- cl::desc("Display contents of faultmap section"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool>
- FileHeaders("file-headers",
- cl::desc("Display the contents of the overall file header"),
- cl::cat(ObjdumpCat));
-static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(FileHeaders));
-
-cl::opt<bool>
- objdump::SectionContents("full-contents",
- cl::desc("Display the content of each section"),
- cl::cat(ObjdumpCat));
-static cl::alias SectionContentsShort("s",
- cl::desc("Alias for --full-contents"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(SectionContents));
-
-static cl::list<std::string> InputFilenames(cl::Positional,
- cl::desc("<input object files>"),
- cl::ZeroOrMore,
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool>
- PrintLines("line-numbers",
- cl::desc("Display source line numbers with "
- "disassembly. Implies disassemble object"),
- cl::cat(ObjdumpCat));
-static cl::alias PrintLinesShort("l", cl::desc("Alias for --line-numbers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(PrintLines));
-
-static cl::opt<bool> MachOOpt("macho",
- cl::desc("Use MachO specific object file parser"),
- cl::cat(ObjdumpCat));
-static cl::alias MachOm("m", cl::desc("Alias for --macho"), cl::NotHidden,
- cl::Grouping, cl::aliasopt(MachOOpt));
-
-cl::opt<std::string> objdump::MCPU(
- "mcpu", cl::desc("Target a specific cpu type (--mcpu=help for details)"),
- cl::value_desc("cpu-name"), cl::init(""), cl::cat(ObjdumpCat));
-
-cl::list<std::string> objdump::MAttrs(
- "mattr", cl::CommaSeparated,
- cl::desc("Target specific attributes (--mattr=help for details)"),
- cl::value_desc("a1,+a2,-a3,..."), cl::cat(ObjdumpCat));
-
-cl::opt<bool> objdump::NoShowRawInsn(
- "no-show-raw-insn",
- cl::desc(
- "When disassembling instructions, do not print the instruction bytes."),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool> objdump::NoLeadingAddr("no-leading-addr",
- cl::desc("Print no leading address"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> RawClangAST(
- "raw-clang-ast",
- cl::desc("Dump the raw binary contents of the clang AST section"),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool>
- objdump::Relocations("reloc",
- cl::desc("Display the relocation entries in the file"),
- cl::cat(ObjdumpCat));
-static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(Relocations));
+namespace {
-cl::opt<bool>
- objdump::PrintImmHex("print-imm-hex",
- cl::desc("Use hex format for immediate values"),
- cl::cat(ObjdumpCat));
+class CommonOptTable : public opt::OptTable {
+public:
+ CommonOptTable(ArrayRef<Info> OptionInfos, const char *Usage,
+ const char *Description)
+ : OptTable(OptionInfos), Usage(Usage), Description(Description) {
+ setGroupedShortOptions(true);
+ }
-cl::opt<bool>
- objdump::PrivateHeaders("private-headers",
- cl::desc("Display format specific file headers"),
- cl::cat(ObjdumpCat));
-static cl::alias PrivateHeadersShort("p",
- cl::desc("Alias for --private-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(PrivateHeaders));
+ void printHelp(StringRef Argv0, bool ShowHidden = false) const {
+ Argv0 = sys::path::filename(Argv0);
+ opt::OptTable::printHelp(outs(), (Argv0 + Usage).str().c_str(), Description,
+ ShowHidden, ShowHidden);
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ }
-cl::list<std::string>
- objdump::FilterSections("section",
- cl::desc("Operate on the specified sections only. "
- "With --macho dump segment,section"),
- cl::cat(ObjdumpCat));
-static cl::alias FilterSectionsj("j", cl::desc("Alias for --section"),
- cl::NotHidden, cl::Grouping, cl::Prefix,
- cl::aliasopt(FilterSections));
+private:
+ const char *Usage;
+ const char *Description;
+};
-cl::opt<bool> objdump::SectionHeaders(
- "section-headers",
- cl::desc("Display summaries of the headers for each section."),
- cl::cat(ObjdumpCat));
-static cl::alias SectionHeadersShort("headers",
- cl::desc("Alias for --section-headers"),
- cl::NotHidden,
- cl::aliasopt(SectionHeaders));
-static cl::alias SectionHeadersShorter("h",
- cl::desc("Alias for --section-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(SectionHeaders));
+// ObjdumpOptID is in ObjdumpOptID.h
-static cl::opt<bool>
- ShowLMA("show-lma",
- cl::desc("Display LMA column when dumping ELF section headers"),
- cl::cat(ObjdumpCat));
+#define PREFIX(NAME, VALUE) const char *const OBJDUMP_##NAME[] = VALUE;
+#include "ObjdumpOpts.inc"
+#undef PREFIX
-static cl::opt<bool> PrintSource(
- "source",
- cl::desc(
- "Display source inlined with disassembly. Implies disassemble object"),
- cl::cat(ObjdumpCat));
-static cl::alias PrintSourceShort("S", cl::desc("Alias for --source"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(PrintSource));
+static constexpr opt::OptTable::Info ObjdumpInfoTable[] = {
+#define OBJDUMP_nullptr nullptr
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OBJDUMP_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, OBJDUMP_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OBJDUMP_##GROUP, \
+ OBJDUMP_##ALIAS, ALIASARGS, VALUES},
+#include "ObjdumpOpts.inc"
+#undef OPTION
+#undef OBJDUMP_nullptr
+};
-static cl::opt<uint64_t>
- StartAddress("start-address", cl::desc("Disassemble beginning at address"),
- cl::value_desc("address"), cl::init(0), cl::cat(ObjdumpCat));
-static cl::opt<uint64_t> StopAddress("stop-address",
- cl::desc("Stop disassembly at address"),
- cl::value_desc("address"),
- cl::init(UINT64_MAX), cl::cat(ObjdumpCat));
+class ObjdumpOptTable : public CommonOptTable {
+public:
+ ObjdumpOptTable()
+ : CommonOptTable(ObjdumpInfoTable, " [options] <input object files>",
+ "llvm object file dumper") {}
+};
-cl::opt<bool> objdump::SymbolTable("syms", cl::desc("Display the symbol table"),
- cl::cat(ObjdumpCat));
-static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(SymbolTable));
+enum OtoolOptID {
+ OTOOL_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OTOOL_##ID,
+#include "OtoolOpts.inc"
+#undef OPTION
+};
-static cl::opt<bool> SymbolizeOperands(
- "symbolize-operands",
- cl::desc("Symbolize instruction operands when disassembling"),
- cl::cat(ObjdumpCat));
+#define PREFIX(NAME, VALUE) const char *const OTOOL_##NAME[] = VALUE;
+#include "OtoolOpts.inc"
+#undef PREFIX
-static cl::opt<bool> DynamicSymbolTable(
- "dynamic-syms",
- cl::desc("Display the contents of the dynamic symbol table"),
- cl::cat(ObjdumpCat));
-static cl::alias DynamicSymbolTableShort("T",
- cl::desc("Alias for --dynamic-syms"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DynamicSymbolTable));
+static constexpr opt::OptTable::Info OtoolInfoTable[] = {
+#define OTOOL_nullptr nullptr
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OTOOL_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, OTOOL_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OTOOL_##GROUP, \
+ OTOOL_##ALIAS, ALIASARGS, VALUES},
+#include "OtoolOpts.inc"
+#undef OPTION
+#undef OTOOL_nullptr
+};
-cl::opt<std::string>
- objdump::TripleName("triple",
- cl::desc("Target triple to disassemble for, see "
- "--version for available targets"),
- cl::cat(ObjdumpCat));
+class OtoolOptTable : public CommonOptTable {
+public:
+ OtoolOptTable()
+ : CommonOptTable(OtoolInfoTable, " [option...] [file...]",
+ "Mach-O object file displaying tool") {}
+};
-cl::opt<bool> objdump::UnwindInfo("unwind-info",
- cl::desc("Display unwind information"),
- cl::cat(ObjdumpCat));
-static cl::alias UnwindInfoShort("u", cl::desc("Alias for --unwind-info"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(UnwindInfo));
+} // namespace
-static cl::opt<bool>
- Wide("wide", cl::desc("Ignored for compatibility with GNU objdump"),
- cl::cat(ObjdumpCat));
-static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide));
+#define DEBUG_TYPE "objdump"
-cl::opt<std::string> objdump::Prefix("prefix",
- cl::desc("Add prefix to absolute paths"),
- cl::cat(ObjdumpCat));
+static uint64_t AdjustVMA;
+static bool AllHeaders;
+static std::string ArchName;
+bool objdump::ArchiveHeaders;
+bool objdump::Demangle;
+bool objdump::Disassemble;
+bool objdump::DisassembleAll;
+bool objdump::SymbolDescription;
+static std::vector<std::string> DisassembleSymbols;
+static bool DisassembleZeroes;
+static std::vector<std::string> DisassemblerOptions;
+DIDumpType objdump::DwarfDumpType;
+static bool DynamicRelocations;
+static bool FaultMapSection;
+static bool FileHeaders;
+bool objdump::SectionContents;
+static std::vector<std::string> InputFilenames;
+bool objdump::PrintLines;
+static bool MachOOpt;
+std::string objdump::MCPU;
+std::vector<std::string> objdump::MAttrs;
+bool objdump::ShowRawInsn;
+bool objdump::LeadingAddr;
+static bool RawClangAST;
+bool objdump::Relocations;
+bool objdump::PrintImmHex;
+bool objdump::PrivateHeaders;
+std::vector<std::string> objdump::FilterSections;
+bool objdump::SectionHeaders;
+static bool ShowLMA;
+bool objdump::PrintSource;
-enum DebugVarsFormat {
- DVDisabled,
- DVUnicode,
- DVASCII,
-};
+static uint64_t StartAddress;
+static bool HasStartAddressFlag;
+static uint64_t StopAddress = UINT64_MAX;
+static bool HasStopAddressFlag;
-static cl::opt<DebugVarsFormat> DbgVariables(
- "debug-vars", cl::init(DVDisabled),
- cl::desc("Print the locations (in registers or memory) of "
- "source-level variables alongside disassembly"),
- cl::ValueOptional,
- cl::values(clEnumValN(DVUnicode, "", "unicode"),
- clEnumValN(DVUnicode, "unicode", "unicode"),
- clEnumValN(DVASCII, "ascii", "unicode")),
- cl::cat(ObjdumpCat));
+bool objdump::SymbolTable;
+static bool SymbolizeOperands;
+static bool DynamicSymbolTable;
+std::string objdump::TripleName;
+bool objdump::UnwindInfo;
+static bool Wide;
+std::string objdump::Prefix;
+uint32_t objdump::PrefixStrip;
-static cl::opt<int>
- DbgIndent("debug-vars-indent", cl::init(40),
- cl::desc("Distance to indent the source-level variable display, "
- "relative to the start of the disassembly"),
- cl::cat(ObjdumpCat));
+DebugVarsFormat objdump::DbgVariables = DVDisabled;
-static cl::extrahelp
- HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+int objdump::DbgIndent = 52;
static StringSet<> DisasmSymbolSet;
StringSet<> objdump::FoundSectionSet;
@@ -588,515 +428,7 @@ namespace {
/// Get the column at which we want to start printing the instruction
/// disassembly, taking into account anything which appears to the left of it.
unsigned getInstStartColumn(const MCSubtargetInfo &STI) {
- return NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
-}
-
-/// Stores a single expression representing the location of a source-level
-/// variable, along with the PC range for which that expression is valid.
-struct LiveVariable {
- DWARFLocationExpression LocExpr;
- const char *VarName;
- DWARFUnit *Unit;
- const DWARFDie FuncDie;
-
- LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName,
- DWARFUnit *Unit, const DWARFDie FuncDie)
- : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {}
-
- bool liveAtAddress(object::SectionedAddress Addr) {
- if (LocExpr.Range == None)
- return false;
- return LocExpr.Range->SectionIndex == Addr.SectionIndex &&
- LocExpr.Range->LowPC <= Addr.Address &&
- LocExpr.Range->HighPC > Addr.Address;
- }
-
- void print(raw_ostream &OS, const MCRegisterInfo &MRI) const {
- DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()},
- Unit->getContext().isLittleEndian(), 0);
- DWARFExpression Expression(Data, Unit->getAddressByteSize());
- Expression.printCompact(OS, MRI);
- }
-};
-
-/// Helper class for printing source variable locations alongside disassembly.
-class LiveVariablePrinter {
- // Information we want to track about one column in which we are printing a
- // variable live range.
- struct Column {
- unsigned VarIdx = NullVarIdx;
- bool LiveIn = false;
- bool LiveOut = false;
- bool MustDrawLabel = false;
-
- bool isActive() const { return VarIdx != NullVarIdx; }
-
- static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max();
- };
-
- // All live variables we know about in the object/image file.
- std::vector<LiveVariable> LiveVariables;
-
- // The columns we are currently drawing.
- IndexedMap<Column> ActiveCols;
-
- const MCRegisterInfo &MRI;
- const MCSubtargetInfo &STI;
-
- void addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
- uint64_t FuncLowPC, FuncHighPC, SectionIndex;
- FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex);
- const char *VarName = VarDie.getName(DINameKind::ShortName);
- DWARFUnit *U = VarDie.getDwarfUnit();
-
- Expected<DWARFLocationExpressionsVector> Locs =
- VarDie.getLocations(dwarf::DW_AT_location);
- if (!Locs) {
- // If the variable doesn't have any locations, just ignore it. We don't
- // report an error or warning here as that could be noisy on optimised
- // code.
- consumeError(Locs.takeError());
- return;
- }
-
- for (const DWARFLocationExpression &LocExpr : *Locs) {
- if (LocExpr.Range) {
- LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie);
- } else {
- // If the LocExpr does not have an associated range, it is valid for
- // the whole of the function.
- // TODO: technically it is not valid for any range covered by another
- // LocExpr, does that happen in reality?
- DWARFLocationExpression WholeFuncExpr{
- DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex),
- LocExpr.Expr};
- LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie);
- }
- }
- }
-
- void addFunction(DWARFDie D) {
- for (const DWARFDie &Child : D.children()) {
- if (Child.getTag() == dwarf::DW_TAG_variable ||
- Child.getTag() == dwarf::DW_TAG_formal_parameter)
- addVariable(D, Child);
- else
- addFunction(Child);
- }
- }
-
- // Get the column number (in characters) at which the first live variable
- // line should be printed.
- unsigned getIndentLevel() const {
- return DbgIndent + getInstStartColumn(STI);
- }
-
- // Indent to the first live-range column to the right of the currently
- // printed line, and return the index of that column.
- // TODO: formatted_raw_ostream uses "column" to mean a number of characters
- // since the last \n, and we use it to mean the number of slots in which we
- // put live variable lines. Pick a less overloaded word.
- unsigned moveToFirstVarColumn(formatted_raw_ostream &OS) {
- // Logical column number: column zero is the first column we print in, each
- // logical column is 2 physical columns wide.
- unsigned FirstUnprintedLogicalColumn =
- std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0);
- // Physical column number: the actual column number in characters, with
- // zero being the left-most side of the screen.
- unsigned FirstUnprintedPhysicalColumn =
- getIndentLevel() + FirstUnprintedLogicalColumn * 2;
-
- if (FirstUnprintedPhysicalColumn > OS.getColumn())
- OS.PadToColumn(FirstUnprintedPhysicalColumn);
-
- return FirstUnprintedLogicalColumn;
- }
-
- unsigned findFreeColumn() {
- for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx)
- if (!ActiveCols[ColIdx].isActive())
- return ColIdx;
-
- size_t OldSize = ActiveCols.size();
- ActiveCols.grow(std::max<size_t>(OldSize * 2, 1));
- return OldSize;
- }
-
-public:
- LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI)
- : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {}
-
- void dump() const {
- for (const LiveVariable &LV : LiveVariables) {
- dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": ";
- LV.print(dbgs(), MRI);
- dbgs() << "\n";
- }
- }
-
- void addCompileUnit(DWARFDie D) {
- if (D.getTag() == dwarf::DW_TAG_subprogram)
- addFunction(D);
- else
- for (const DWARFDie &Child : D.children())
- addFunction(Child);
- }
-
- /// Update to match the state of the instruction between ThisAddr and
- /// NextAddr. In the common case, any live range active at ThisAddr is
- /// live-in to the instruction, and any live range active at NextAddr is
- /// live-out of the instruction. If IncludeDefinedVars is false, then live
- /// ranges starting at NextAddr will be ignored.
- void update(object::SectionedAddress ThisAddr,
- object::SectionedAddress NextAddr, bool IncludeDefinedVars) {
- // First, check variables which have already been assigned a column, so
- // that we don't change their order.
- SmallSet<unsigned, 8> CheckedVarIdxs;
- for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
- if (!ActiveCols[ColIdx].isActive())
- continue;
- CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx);
- LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx];
- ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr);
- ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr);
- LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-"
- << NextAddr.Address << ", " << LV.VarName << ", Col "
- << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn
- << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n");
-
- if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
- ActiveCols[ColIdx].VarIdx = Column::NullVarIdx;
- }
-
- // Next, look for variables which don't already have a column, but which
- // are now live.
- if (IncludeDefinedVars) {
- for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End;
- ++VarIdx) {
- if (CheckedVarIdxs.count(VarIdx))
- continue;
- LiveVariable &LV = LiveVariables[VarIdx];
- bool LiveIn = LV.liveAtAddress(ThisAddr);
- bool LiveOut = LV.liveAtAddress(NextAddr);
- if (!LiveIn && !LiveOut)
- continue;
-
- unsigned ColIdx = findFreeColumn();
- LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-"
- << NextAddr.Address << ", " << LV.VarName << ", Col "
- << ColIdx << ": LiveIn=" << LiveIn
- << ", LiveOut=" << LiveOut << "\n");
- ActiveCols[ColIdx].VarIdx = VarIdx;
- ActiveCols[ColIdx].LiveIn = LiveIn;
- ActiveCols[ColIdx].LiveOut = LiveOut;
- ActiveCols[ColIdx].MustDrawLabel = true;
- }
- }
- }
-
- enum class LineChar {
- RangeStart,
- RangeMid,
- RangeEnd,
- LabelVert,
- LabelCornerNew,
- LabelCornerActive,
- LabelHoriz,
- };
- const char *getLineChar(LineChar C) const {
- bool IsASCII = DbgVariables == DVASCII;
- switch (C) {
- case LineChar::RangeStart:
- return IsASCII ? "^" : (const char *)u8"\u2548";
- case LineChar::RangeMid:
- return IsASCII ? "|" : (const char *)u8"\u2503";
- case LineChar::RangeEnd:
- return IsASCII ? "v" : (const char *)u8"\u253b";
- case LineChar::LabelVert:
- return IsASCII ? "|" : (const char *)u8"\u2502";
- case LineChar::LabelCornerNew:
- return IsASCII ? "/" : (const char *)u8"\u250c";
- case LineChar::LabelCornerActive:
- return IsASCII ? "|" : (const char *)u8"\u2520";
- case LineChar::LabelHoriz:
- return IsASCII ? "-" : (const char *)u8"\u2500";
- }
- llvm_unreachable("Unhandled LineChar enum");
- }
-
- /// Print live ranges to the right of an existing line. This assumes the
- /// line is not an instruction, so doesn't start or end any live ranges, so
- /// we only need to print active ranges or empty columns. If AfterInst is
- /// true, this is being printed after the last instruction fed to update(),
- /// otherwise this is being printed before it.
- void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst) {
- if (ActiveCols.size()) {
- unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
- for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
- ColIdx < End; ++ColIdx) {
- if (ActiveCols[ColIdx].isActive()) {
- if ((AfterInst && ActiveCols[ColIdx].LiveOut) ||
- (!AfterInst && ActiveCols[ColIdx].LiveIn))
- OS << getLineChar(LineChar::RangeMid);
- else if (!AfterInst && ActiveCols[ColIdx].LiveOut)
- OS << getLineChar(LineChar::LabelVert);
- else
- OS << " ";
- }
- OS << " ";
- }
- }
- OS << "\n";
- }
-
- /// Print any live variable range info needed to the right of a
- /// non-instruction line of disassembly. This is where we print the variable
- /// names and expressions, with thin line-drawing characters connecting them
- /// to the live range which starts at the next instruction. If MustPrint is
- /// true, we have to print at least one line (with the continuation of any
- /// already-active live ranges) because something has already been printed
- /// earlier on this line.
- void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) {
- bool PrintedSomething = false;
- for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
- if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
- // First we need to print the live range markers for any active
- // columns to the left of this one.
- OS.PadToColumn(getIndentLevel());
- for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) {
- if (ActiveCols[ColIdx2].isActive()) {
- if (ActiveCols[ColIdx2].MustDrawLabel &&
- !ActiveCols[ColIdx2].LiveIn)
- OS << getLineChar(LineChar::LabelVert) << " ";
- else
- OS << getLineChar(LineChar::RangeMid) << " ";
- } else
- OS << " ";
- }
-
- // Then print the variable name and location of the new live range,
- // with box drawing characters joining it to the live range line.
- OS << getLineChar(ActiveCols[ColIdx].LiveIn
- ? LineChar::LabelCornerActive
- : LineChar::LabelCornerNew)
- << getLineChar(LineChar::LabelHoriz) << " ";
- WithColor(OS, raw_ostream::GREEN)
- << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName;
- OS << " = ";
- {
- WithColor ExprColor(OS, raw_ostream::CYAN);
- LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI);
- }
-
- // If there are any columns to the right of the expression we just
- // printed, then continue their live range lines.
- unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
- for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size();
- ColIdx2 < End; ++ColIdx2) {
- if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn)
- OS << getLineChar(LineChar::RangeMid) << " ";
- else
- OS << " ";
- }
-
- OS << "\n";
- PrintedSomething = true;
- }
- }
-
- for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx)
- if (ActiveCols[ColIdx].isActive())
- ActiveCols[ColIdx].MustDrawLabel = false;
-
- // If we must print something (because we printed a line/column number),
- // but don't have any new variables to print, then print a line which
- // just continues any existing live ranges.
- if (MustPrint && !PrintedSomething)
- printAfterOtherLine(OS, false);
- }
-
- /// Print the live variable ranges to the right of a disassembled instruction.
- void printAfterInst(formatted_raw_ostream &OS) {
- if (!ActiveCols.size())
- return;
- unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
- for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
- ColIdx < End; ++ColIdx) {
- if (!ActiveCols[ColIdx].isActive())
- OS << " ";
- else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut)
- OS << getLineChar(LineChar::RangeMid) << " ";
- else if (ActiveCols[ColIdx].LiveOut)
- OS << getLineChar(LineChar::RangeStart) << " ";
- else if (ActiveCols[ColIdx].LiveIn)
- OS << getLineChar(LineChar::RangeEnd) << " ";
- else
- llvm_unreachable("var must be live in or out!");
- }
- }
-};
-
-class SourcePrinter {
-protected:
- DILineInfo OldLineInfo;
- const ObjectFile *Obj = nullptr;
- std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
- // File name to file contents of source.
- std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache;
- // Mark the line endings of the cached source.
- std::unordered_map<std::string, std::vector<StringRef>> LineCache;
- // Keep track of missing sources.
- StringSet<> MissingSources;
- // Only emit 'no debug info' warning once.
- bool WarnedNoDebugInfo;
-
-private:
- bool cacheSource(const DILineInfo& LineInfoFile);
-
- void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef Delimiter, LiveVariablePrinter &LVP);
-
- void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef ObjectFilename, StringRef Delimiter,
- LiveVariablePrinter &LVP);
-
-public:
- SourcePrinter() = default;
- SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch)
- : Obj(Obj), WarnedNoDebugInfo(false) {
- symbolize::LLVMSymbolizer::Options SymbolizerOpts;
- SymbolizerOpts.PrintFunctions =
- DILineInfoSpecifier::FunctionNameKind::LinkageName;
- SymbolizerOpts.Demangle = Demangle;
- SymbolizerOpts.DefaultArch = std::string(DefaultArch);
- Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
- }
- virtual ~SourcePrinter() = default;
- virtual void printSourceLine(formatted_raw_ostream &OS,
- object::SectionedAddress Address,
- StringRef ObjectFilename,
- LiveVariablePrinter &LVP,
- StringRef Delimiter = "; ");
-};
-
-bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
- std::unique_ptr<MemoryBuffer> Buffer;
- if (LineInfo.Source) {
- Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source);
- } else {
- auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName);
- if (!BufferOrError) {
- if (MissingSources.insert(LineInfo.FileName).second)
- reportWarning("failed to find source " + LineInfo.FileName,
- Obj->getFileName());
- return false;
- }
- Buffer = std::move(*BufferOrError);
- }
- // Chomp the file to get lines
- const char *BufferStart = Buffer->getBufferStart(),
- *BufferEnd = Buffer->getBufferEnd();
- std::vector<StringRef> &Lines = LineCache[LineInfo.FileName];
- const char *Start = BufferStart;
- for (const char *I = BufferStart; I != BufferEnd; ++I)
- if (*I == '\n') {
- Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r'));
- Start = I + 1;
- }
- if (Start < BufferEnd)
- Lines.emplace_back(Start, BufferEnd - Start);
- SourceCache[LineInfo.FileName] = std::move(Buffer);
- return true;
-}
-
-void SourcePrinter::printSourceLine(formatted_raw_ostream &OS,
- object::SectionedAddress Address,
- StringRef ObjectFilename,
- LiveVariablePrinter &LVP,
- StringRef Delimiter) {
- if (!Symbolizer)
- return;
-
- DILineInfo LineInfo = DILineInfo();
- auto ExpectedLineInfo = Symbolizer->symbolizeCode(*Obj, Address);
- std::string ErrorMessage;
- if (!ExpectedLineInfo)
- ErrorMessage = toString(ExpectedLineInfo.takeError());
- else
- LineInfo = *ExpectedLineInfo;
-
- if (LineInfo.FileName == DILineInfo::BadString) {
- if (!WarnedNoDebugInfo) {
- std::string Warning =
- "failed to parse debug information for " + ObjectFilename.str();
- if (!ErrorMessage.empty())
- Warning += ": " + ErrorMessage;
- reportWarning(Warning, ObjectFilename);
- WarnedNoDebugInfo = true;
- }
- }
-
- if (!Prefix.empty() && sys::path::is_absolute_gnu(LineInfo.FileName)) {
- SmallString<128> FilePath;
- sys::path::append(FilePath, Prefix, LineInfo.FileName);
-
- LineInfo.FileName = std::string(FilePath);
- }
-
- if (PrintLines)
- printLines(OS, LineInfo, Delimiter, LVP);
- if (PrintSource)
- printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP);
- OldLineInfo = LineInfo;
-}
-
-void SourcePrinter::printLines(formatted_raw_ostream &OS,
- const DILineInfo &LineInfo, StringRef Delimiter,
- LiveVariablePrinter &LVP) {
- bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString &&
- LineInfo.FunctionName != OldLineInfo.FunctionName;
- if (PrintFunctionName) {
- OS << Delimiter << LineInfo.FunctionName;
- // If demangling is successful, FunctionName will end with "()". Print it
- // only if demangling did not run or was unsuccessful.
- if (!StringRef(LineInfo.FunctionName).endswith("()"))
- OS << "()";
- OS << ":\n";
- }
- if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 &&
- (OldLineInfo.Line != LineInfo.Line ||
- OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) {
- OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line;
- LVP.printBetweenInsts(OS, true);
- }
-}
-
-void SourcePrinter::printSources(formatted_raw_ostream &OS,
- const DILineInfo &LineInfo,
- StringRef ObjectFilename, StringRef Delimiter,
- LiveVariablePrinter &LVP) {
- if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 ||
- (OldLineInfo.Line == LineInfo.Line &&
- OldLineInfo.FileName == LineInfo.FileName))
- return;
-
- if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
- if (!cacheSource(LineInfo))
- return;
- auto LineBuffer = LineCache.find(LineInfo.FileName);
- if (LineBuffer != LineCache.end()) {
- if (LineInfo.Line > LineBuffer->second.size()) {
- reportWarning(
- formatv(
- "debug info line number {0} exceeds the number of lines in {1}",
- LineInfo.Line, LineInfo.FileName),
- ObjectFilename);
- return;
- }
- // Vector begins at 0, line numbers are non-zero
- OS << Delimiter << LineBuffer->second[LineInfo.Line - 1];
- LVP.printBetweenInsts(OS, true);
- }
+ return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
}
static bool isAArch64Elf(const ObjectFile *Obj) {
@@ -1139,9 +471,9 @@ public:
LVP.printBetweenInsts(OS, false);
size_t Start = OS.tell();
- if (!NoLeadingAddr)
+ if (LeadingAddr)
OS << format("%8" PRIx64 ":", Address.Address);
- if (!NoShowRawInsn) {
+ if (ShowRawInsn) {
OS << ' ';
dumpBytes(Bytes, OS);
}
@@ -1172,9 +504,9 @@ public:
formatted_raw_ostream &OS) {
uint32_t opcode =
(Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0];
- if (!NoLeadingAddr)
+ if (LeadingAddr)
OS << format("%8" PRIx64 ":", Address);
- if (!NoShowRawInsn) {
+ if (ShowRawInsn) {
OS << "\t";
dumpBytes(Bytes.slice(0, 4), OS);
OS << format("\t%08" PRIx32, opcode);
@@ -1309,9 +641,9 @@ public:
LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
SP->printSourceLine(OS, Address, ObjectFilename, LVP);
- if (!NoLeadingAddr)
+ if (LeadingAddr)
OS << format("%8" PRId64 ":", Address.Address / 8);
- if (!NoShowRawInsn) {
+ if (ShowRawInsn) {
OS << "\t";
dumpBytes(Bytes, OS);
}
@@ -1404,6 +736,43 @@ addDynamicElfSymbols(const ObjectFile *Obj,
llvm_unreachable("Unsupported binary format");
}
+static Optional<SectionRef> getWasmCodeSection(const WasmObjectFile *Obj) {
+ for (auto SecI : Obj->sections()) {
+ const WasmSection &Section = Obj->getWasmSection(SecI);
+ if (Section.Type == wasm::WASM_SEC_CODE)
+ return SecI;
+ }
+ return None;
+}
+
+static void
+addMissingWasmCodeSymbols(const WasmObjectFile *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
+ Optional<SectionRef> Section = getWasmCodeSection(Obj);
+ if (!Section)
+ return;
+ SectionSymbolsTy &Symbols = AllSymbols[*Section];
+
+ std::set<uint64_t> SymbolAddresses;
+ for (const auto &Sym : Symbols)
+ SymbolAddresses.insert(Sym.Addr);
+
+ for (const wasm::WasmFunction &Function : Obj->functions()) {
+ uint64_t Address = Function.CodeSectionOffset;
+ // Only add fallback symbols for functions not already present in the symbol
+ // table.
+ if (SymbolAddresses.count(Address))
+ continue;
+ // This function has no symbol, so it should have no SymbolName.
+ assert(Function.SymbolName.empty());
+ // We use DebugName for the name, though it may be empty if there is no
+ // "name" custom section, or that section is missing a name for this
+ // function.
+ StringRef Name = Function.DebugName;
+ Symbols.emplace_back(Address, Name, ELF::STT_NOTYPE);
+ }
+}
+
static void addPltEntries(const ObjectFile *Obj,
std::map<SectionRef, SectionSymbolsTy> &AllSymbols,
StringSaver &Saver) {
@@ -1479,8 +848,7 @@ getRelocsMap(object::ObjectFile const &Obj) {
if (Relocated == Obj.section_end() || !checkSectionFilter(*Relocated).Keep)
continue;
std::vector<RelocationRef> &V = Ret[*Relocated];
- for (const RelocationRef &R : Sec.relocations())
- V.push_back(R);
+ append_range(V, Sec.relocations());
// Sort relocations by address.
llvm::stable_sort(V, isRelocAddressLess);
}
@@ -1641,6 +1009,63 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, const MCInstrAnalysis *MIA,
}
}
+// Create an MCSymbolizer for the target and add it to the MCDisassembler.
+// This is currently only used on AMDGPU, and assumes the format of the
+// void * argument passed to AMDGPU's createMCSymbolizer.
+static void addSymbolizer(
+ MCContext &Ctx, const Target *Target, StringRef TripleName,
+ MCDisassembler *DisAsm, uint64_t SectionAddr, ArrayRef<uint8_t> Bytes,
+ SectionSymbolsTy &Symbols,
+ std::vector<std::unique_ptr<std::string>> &SynthesizedLabelNames) {
+
+ std::unique_ptr<MCRelocationInfo> RelInfo(
+ Target->createMCRelocationInfo(TripleName, Ctx));
+ if (!RelInfo)
+ return;
+ std::unique_ptr<MCSymbolizer> Symbolizer(Target->createMCSymbolizer(
+ TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
+ MCSymbolizer *SymbolizerPtr = &*Symbolizer;
+ DisAsm->setSymbolizer(std::move(Symbolizer));
+
+ if (!SymbolizeOperands)
+ return;
+
+ // Synthesize labels referenced by branch instructions by
+ // disassembling, discarding the output, and collecting the referenced
+ // addresses from the symbolizer.
+ for (size_t Index = 0; Index != Bytes.size();) {
+ MCInst Inst;
+ uint64_t Size;
+ DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), SectionAddr + Index,
+ nulls());
+ if (Size == 0)
+ Size = 1;
+ Index += Size;
+ }
+ ArrayRef<uint64_t> LabelAddrsRef = SymbolizerPtr->getReferencedAddresses();
+ // Copy and sort to remove duplicates.
+ std::vector<uint64_t> LabelAddrs;
+ LabelAddrs.insert(LabelAddrs.end(), LabelAddrsRef.begin(),
+ LabelAddrsRef.end());
+ llvm::sort(LabelAddrs);
+ LabelAddrs.resize(std::unique(LabelAddrs.begin(), LabelAddrs.end()) -
+ LabelAddrs.begin());
+ // Add the labels.
+ for (unsigned LabelNum = 0; LabelNum != LabelAddrs.size(); ++LabelNum) {
+ auto Name = std::make_unique<std::string>();
+ *Name = (Twine("L") + Twine(LabelNum)).str();
+ SynthesizedLabelNames.push_back(std::move(Name));
+ Symbols.push_back(SymbolInfoTy(
+ LabelAddrs[LabelNum], *SynthesizedLabelNames.back(), ELF::STT_NOTYPE));
+ }
+ llvm::stable_sort(Symbols);
+ // Recreate the symbolizer with the new symbols list.
+ RelInfo.reset(Target->createMCRelocationInfo(TripleName, Ctx));
+ Symbolizer.reset(Target->createMCSymbolizer(
+ TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
+ DisAsm->setSymbolizer(std::move(Symbolizer));
+}
+
static StringRef getSegmentName(const MachOObjectFile *MachO,
const SectionRef &Section) {
if (MachO) {
@@ -1651,6 +1076,29 @@ static StringRef getSegmentName(const MachOObjectFile *MachO,
return "";
}
+static void emitPostInstructionInfo(formatted_raw_ostream &FOS,
+ const MCAsmInfo &MAI,
+ const MCSubtargetInfo &STI,
+ StringRef Comments,
+ LiveVariablePrinter &LVP) {
+ do {
+ if (!Comments.empty()) {
+ // Emit a line of comments.
+ StringRef Comment;
+ std::tie(Comment, Comments) = Comments.split('\n');
+ // MAI.getCommentColumn() assumes that instructions are printed at the
+ // position of 8, while getInstStartColumn() returns the actual position.
+ unsigned CommentColumn =
+ MAI.getCommentColumn() - 8 + getInstStartColumn(STI);
+ FOS.PadToColumn(CommentColumn);
+ FOS << MAI.getCommentString() << ' ' << Comment;
+ }
+ LVP.printAfterInst(FOS);
+ FOS << '\n';
+ } while (!Comments.empty());
+ FOS.flush();
+}
+
static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
MCContext &Ctx, MCDisassembler *PrimaryDisAsm,
MCDisassembler *SecondaryDisAsm,
@@ -1688,10 +1136,15 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (Obj->isELF() && getElfSymbolType(Obj, Symbol) == ELF::STT_SECTION)
continue;
- // Don't ask a Mach-O STAB symbol for its section unless you know that
- // STAB symbol's section field refers to a valid section index. Otherwise
- // the symbol may error trying to load a section that does not exist.
if (MachO) {
+ // __mh_(execute|dylib|dylinker|bundle|preload|object)_header are special
+ // symbols that support MachO header introspection. They do not bind to
+ // code locations and are irrelevant for disassembly.
+ if (NameOrErr->startswith("__mh_") && NameOrErr->endswith("_header"))
+ continue;
+ // Don't ask a Mach-O STAB symbol for its section unless you know that
+ // STAB symbol's section field refers to a valid section index. Otherwise
+ // the symbol may error trying to load a section that does not exist.
DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
uint8_t NType = (MachO->is64Bit() ?
MachO->getSymbol64TableEntry(SymDRI).n_type:
@@ -1710,6 +1163,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (AllSymbols.empty() && Obj->isELF())
addDynamicElfSymbols(Obj, AllSymbols);
+ if (Obj->isWasm())
+ addMissingWasmCodeSymbols(cast<WasmObjectFile>(Obj), AllSymbols);
+
BumpPtrAllocator A;
StringSaver Saver(A);
addPltEntries(Obj, AllSymbols, Saver);
@@ -1803,16 +1259,14 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
llvm::sort(MappingSymbols);
+ ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(
+ unwrapOrError(Section.getContents(), Obj->getFileName()));
+
+ std::vector<std::unique_ptr<std::string>> SynthesizedLabelNames;
if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
// AMDGPU disassembler uses symbolizer for printing labels
- std::unique_ptr<MCRelocationInfo> RelInfo(
- TheTarget->createMCRelocationInfo(TripleName, Ctx));
- if (RelInfo) {
- std::unique_ptr<MCSymbolizer> Symbolizer(
- TheTarget->createMCSymbolizer(
- TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
- DisAsm->setSymbolizer(std::move(Symbolizer));
- }
+ addSymbolizer(Ctx, TheTarget, TripleName, DisAsm, SectionAddr, Bytes,
+ Symbols, SynthesizedLabelNames);
}
StringRef SegmentName = getSegmentName(MachO, Section);
@@ -1828,9 +1282,6 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
SmallString<40> Comments;
raw_svector_ostream CommentStream(Comments);
- ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(
- unwrapOrError(Section.getContents(), Obj->getFileName()));
-
uint64_t VMAAdjustment = 0;
if (shouldAdjustVA(Section))
VMAAdjustment = AdjustVMA;
@@ -1877,7 +1328,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
}
outs() << '\n';
- if (!NoLeadingAddr)
+ if (LeadingAddr)
outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ",
SectionAddr + Start + VMAAdjustment);
if (Obj->isXCOFF() && SymbolDescription) {
@@ -2008,18 +1459,22 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
LVP.update({Index, Section.getIndex()},
{Index + Size, Section.getIndex()}, Index + Size != End);
+ IP->setCommentStream(CommentStream);
+
PIP.printInst(
*IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size),
{SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS,
"", *STI, &SP, Obj->getFileName(), &Rels, LVP);
- FOS << CommentStream.str();
- Comments.clear();
+
+ IP->setCommentStream(llvm::nulls());
// If disassembly has failed, avoid analysing invalid/incomplete
// instruction information. Otherwise, try to resolve the target
// address (jump target or memory operand address) and print it on the
// right of the instruction.
if (Disassembled && MIA) {
+ // Branch targets are printed just after the instructions.
+ llvm::raw_ostream *TargetOS = &FOS;
uint64_t Target;
bool PrintTarget =
MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target);
@@ -2030,8 +1485,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
Target = *MaybeTarget;
PrintTarget = true;
// Do not print real address when symbolizing.
- if (!SymbolizeOperands)
- FOS << " # " << Twine::utohexstr(Target);
+ if (!SymbolizeOperands) {
+ // Memory operand addresses are printed as comments.
+ TargetOS = &CommentStream;
+ *TargetOS << "0x" << Twine::utohexstr(Target);
+ }
}
if (PrintTarget) {
// In a relocatable object, the target's section must reside in
@@ -2090,28 +1548,34 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (Demangle)
TargetName = demangle(TargetName);
- FOS << " <";
+ *TargetOS << " <";
if (!Disp) {
// Always Print the binary symbol precisely corresponding to
// the target address.
- FOS << TargetName;
+ *TargetOS << TargetName;
} else if (!LabelAvailable) {
// Always Print the binary symbol plus an offset if there's no
// local label corresponding to the target address.
- FOS << TargetName << "+0x" << Twine::utohexstr(Disp);
+ *TargetOS << TargetName << "+0x" << Twine::utohexstr(Disp);
} else {
- FOS << AllLabels[Target];
+ *TargetOS << AllLabels[Target];
}
- FOS << ">";
+ *TargetOS << ">";
} else if (LabelAvailable) {
- FOS << " <" << AllLabels[Target] << ">";
+ *TargetOS << " <" << AllLabels[Target] << ">";
}
+ // By convention, each record in the comment stream should be
+ // terminated.
+ if (TargetOS == &CommentStream)
+ *TargetOS << "\n";
}
}
}
- LVP.printAfterInst(FOS);
- FOS << "\n";
+ assert(Ctx.getAsmInfo());
+ emitPostInstructionInfo(FOS, *Ctx.getAsmInfo(), *STI,
+ CommentStream.str(), LVP);
+ Comments.clear();
// Hexagon does this in pretty printer
if (Obj->getArch() != Triple::hexagon) {
@@ -2193,10 +1657,11 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (!MII)
reportError(Obj->getFileName(),
"no instruction info for target " + TripleName);
- MCObjectFileInfo MOFI;
- MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI);
+ MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get());
// FIXME: for now initialize MCObjectFileInfo with default values
- MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx);
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
+ Ctx.setObjectFileInfo(MOFI.get());
std::unique_ptr<MCDisassembler> DisAsm(
TheTarget->createMCDisassembler(*STI, Ctx));
@@ -2273,7 +1738,7 @@ void objdump::printRelocations(const ObjectFile *Obj) {
for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) {
StringRef SecName = unwrapOrError(P.first.getName(), Obj->getFileName());
- outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n";
+ outs() << "\nRELOCATION RECORDS FOR [" << SecName << "]:\n";
uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8);
uint32_t TypePadding = 24;
outs() << left_justify("OFFSET", OffsetPadding) << " "
@@ -2296,7 +1761,6 @@ void objdump::printRelocations(const ObjectFile *Obj) {
<< "\n";
}
}
- outs() << "\n";
}
}
@@ -2356,16 +1820,13 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) {
size_t NameWidth = getMaxSectionNameWidth(Obj);
size_t AddressWidth = 2 * Obj->getBytesInAddress();
bool HasLMAColumn = shouldDisplayLMA(Obj);
+ outs() << "\nSections:\n";
if (HasLMAColumn)
- outs() << "Sections:\n"
- "Idx "
- << left_justify("Name", NameWidth) << " Size "
+ outs() << "Idx " << left_justify("Name", NameWidth) << " Size "
<< left_justify("VMA", AddressWidth) << " "
<< left_justify("LMA", AddressWidth) << " Type\n";
else
- outs() << "Sections:\n"
- "Idx "
- << left_justify("Name", NameWidth) << " Size "
+ outs() << "Idx " << left_justify("Name", NameWidth) << " Size "
<< left_justify("VMA", AddressWidth) << " Type\n";
uint64_t Idx;
@@ -2379,9 +1840,11 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) {
std::string Type = Section.isText() ? "TEXT" : "";
if (Section.isData())
- Type += Type.empty() ? "DATA" : " DATA";
+ Type += Type.empty() ? "DATA" : ", DATA";
if (Section.isBSS())
- Type += Type.empty() ? "BSS" : " BSS";
+ Type += Type.empty() ? "BSS" : ", BSS";
+ if (Section.isDebugSection())
+ Type += Type.empty() ? "DEBUG" : ", DEBUG";
if (HasLMAColumn)
outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth,
@@ -2394,7 +1857,6 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) {
Name.str().c_str(), Size)
<< format_hex_no_prefix(VMA, AddressWidth) << " " << Type << "\n";
}
- outs() << "\n";
}
void objdump::printSectionContents(const ObjectFile *Obj) {
@@ -2450,7 +1912,7 @@ void objdump::printSectionContents(const ObjectFile *Obj) {
void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
StringRef ArchitectureName, bool DumpDynamic) {
if (O->isCOFF() && !DumpDynamic) {
- outs() << "SYMBOL TABLE:\n";
+ outs() << "\nSYMBOL TABLE:\n";
printCOFFSymbolTable(cast<const COFFObjectFile>(O));
return;
}
@@ -2458,13 +1920,13 @@ void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
const StringRef FileName = O->getFileName();
if (!DumpDynamic) {
- outs() << "SYMBOL TABLE:\n";
+ outs() << "\nSYMBOL TABLE:\n";
for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I)
printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic);
return;
}
- outs() << "DYNAMIC SYMBOL TABLE:\n";
+ outs() << "\nDYNAMIC SYMBOL TABLE:\n";
if (!O->isELF()) {
reportWarning(
"this operation is not currently supported for this file format",
@@ -2736,7 +2198,7 @@ static void printFileHeaders(const ObjectFile *O) {
StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
outs() << "start address: "
- << "0x" << format(Fmt.data(), Address) << "\n\n";
+ << "0x" << format(Fmt.data(), Address) << "\n";
}
static void printArchiveChild(StringRef Filename, const Archive::Child &C) {
@@ -2809,11 +2271,11 @@ static void checkForInvalidStartStopAddress(ObjectFile *Obj,
return;
}
- if (StartAddress.getNumOccurrences() == 0)
+ if (!HasStartAddressFlag)
reportWarning("no section has address less than 0x" +
Twine::utohexstr(Stop) + " specified by --stop-address",
Obj->getFileName());
- else if (StopAddress.getNumOccurrences() == 0)
+ else if (!HasStopAddressFlag)
reportWarning("no section has address greater than or equal to 0x" +
Twine::utohexstr(Start) + " specified by --start-address",
Obj->getFileName());
@@ -2833,10 +2295,10 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
outs() << A->getFileName() << "(" << O->getFileName() << ")";
else
outs() << O->getFileName();
- outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n\n";
+ outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n";
}
- if (StartAddress.getNumOccurrences() || StopAddress.getNumOccurrences())
+ if (HasStartAddressFlag || HasStopAddressFlag)
checkForInvalidStartStopAddress(O, StartAddress, StopAddress);
// Note: the order here matches GNU objdump for compatability.
@@ -2956,33 +2418,246 @@ static void dumpInput(StringRef file) {
reportError(errorCodeToError(object_error::invalid_file_type), file);
}
+template <typename T>
+static void parseIntArg(const llvm::opt::InputArgList &InputArgs, int ID,
+ T &Value) {
+ if (const opt::Arg *A = InputArgs.getLastArg(ID)) {
+ StringRef V(A->getValue());
+ if (!llvm::to_integer(V, Value, 0)) {
+ reportCmdLineError(A->getSpelling() +
+ ": expected a non-negative integer, but got '" + V +
+ "'");
+ }
+ }
+}
+
+static std::vector<std::string>
+commaSeparatedValues(const llvm::opt::InputArgList &InputArgs, int ID) {
+ std::vector<std::string> Values;
+ for (StringRef Value : InputArgs.getAllArgValues(ID)) {
+ llvm::SmallVector<StringRef, 2> SplitValues;
+ llvm::SplitString(Value, SplitValues, ",");
+ for (StringRef SplitValue : SplitValues)
+ Values.push_back(SplitValue.str());
+ }
+ return Values;
+}
+
+static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) {
+ MachOOpt = true;
+ FullLeadingAddr = true;
+ PrintImmHex = true;
+
+ ArchName = InputArgs.getLastArgValue(OTOOL_arch).str();
+ LinkOptHints = InputArgs.hasArg(OTOOL_C);
+ if (InputArgs.hasArg(OTOOL_d))
+ FilterSections.push_back("__DATA,__data");
+ DylibId = InputArgs.hasArg(OTOOL_D);
+ UniversalHeaders = InputArgs.hasArg(OTOOL_f);
+ DataInCode = InputArgs.hasArg(OTOOL_G);
+ FirstPrivateHeader = InputArgs.hasArg(OTOOL_h);
+ IndirectSymbols = InputArgs.hasArg(OTOOL_I);
+ ShowRawInsn = InputArgs.hasArg(OTOOL_j);
+ PrivateHeaders = InputArgs.hasArg(OTOOL_l);
+ DylibsUsed = InputArgs.hasArg(OTOOL_L);
+ MCPU = InputArgs.getLastArgValue(OTOOL_mcpu_EQ).str();
+ ObjcMetaData = InputArgs.hasArg(OTOOL_o);
+ DisSymName = InputArgs.getLastArgValue(OTOOL_p).str();
+ InfoPlist = InputArgs.hasArg(OTOOL_P);
+ Relocations = InputArgs.hasArg(OTOOL_r);
+ if (const Arg *A = InputArgs.getLastArg(OTOOL_s)) {
+ auto Filter = (A->getValue(0) + StringRef(",") + A->getValue(1)).str();
+ FilterSections.push_back(Filter);
+ }
+ if (InputArgs.hasArg(OTOOL_t))
+ FilterSections.push_back("__TEXT,__text");
+ Verbose = InputArgs.hasArg(OTOOL_v) || InputArgs.hasArg(OTOOL_V) ||
+ InputArgs.hasArg(OTOOL_o);
+ SymbolicOperands = InputArgs.hasArg(OTOOL_V);
+ if (InputArgs.hasArg(OTOOL_x))
+ FilterSections.push_back(",__text");
+ LeadingAddr = LeadingHeaders = !InputArgs.hasArg(OTOOL_X);
+
+ InputFilenames = InputArgs.getAllArgValues(OTOOL_INPUT);
+ if (InputFilenames.empty())
+ reportCmdLineError("no input file");
+
+ for (const Arg *A : InputArgs) {
+ const Option &O = A->getOption();
+ if (O.getGroup().isValid() && O.getGroup().getID() == OTOOL_grp_obsolete) {
+ reportCmdLineWarning(O.getPrefixedName() +
+ " is obsolete and not implemented");
+ }
+ }
+}
+
+static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
+ parseIntArg(InputArgs, OBJDUMP_adjust_vma_EQ, AdjustVMA);
+ AllHeaders = InputArgs.hasArg(OBJDUMP_all_headers);
+ ArchName = InputArgs.getLastArgValue(OBJDUMP_arch_name_EQ).str();
+ ArchiveHeaders = InputArgs.hasArg(OBJDUMP_archive_headers);
+ Demangle = InputArgs.hasArg(OBJDUMP_demangle);
+ Disassemble = InputArgs.hasArg(OBJDUMP_disassemble);
+ DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all);
+ SymbolDescription = InputArgs.hasArg(OBJDUMP_symbol_description);
+ DisassembleSymbols =
+ commaSeparatedValues(InputArgs, OBJDUMP_disassemble_symbols_EQ);
+ DisassembleZeroes = InputArgs.hasArg(OBJDUMP_disassemble_zeroes);
+ if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_dwarf_EQ)) {
+ DwarfDumpType =
+ StringSwitch<DIDumpType>(A->getValue()).Case("frames", DIDT_DebugFrame);
+ }
+ DynamicRelocations = InputArgs.hasArg(OBJDUMP_dynamic_reloc);
+ FaultMapSection = InputArgs.hasArg(OBJDUMP_fault_map_section);
+ FileHeaders = InputArgs.hasArg(OBJDUMP_file_headers);
+ SectionContents = InputArgs.hasArg(OBJDUMP_full_contents);
+ PrintLines = InputArgs.hasArg(OBJDUMP_line_numbers);
+ InputFilenames = InputArgs.getAllArgValues(OBJDUMP_INPUT);
+ MachOOpt = InputArgs.hasArg(OBJDUMP_macho);
+ MCPU = InputArgs.getLastArgValue(OBJDUMP_mcpu_EQ).str();
+ MAttrs = commaSeparatedValues(InputArgs, OBJDUMP_mattr_EQ);
+ ShowRawInsn = !InputArgs.hasArg(OBJDUMP_no_show_raw_insn);
+ LeadingAddr = !InputArgs.hasArg(OBJDUMP_no_leading_addr);
+ RawClangAST = InputArgs.hasArg(OBJDUMP_raw_clang_ast);
+ Relocations = InputArgs.hasArg(OBJDUMP_reloc);
+ PrintImmHex =
+ InputArgs.hasFlag(OBJDUMP_print_imm_hex, OBJDUMP_no_print_imm_hex, false);
+ PrivateHeaders = InputArgs.hasArg(OBJDUMP_private_headers);
+ FilterSections = InputArgs.getAllArgValues(OBJDUMP_section_EQ);
+ SectionHeaders = InputArgs.hasArg(OBJDUMP_section_headers);
+ ShowLMA = InputArgs.hasArg(OBJDUMP_show_lma);
+ PrintSource = InputArgs.hasArg(OBJDUMP_source);
+ parseIntArg(InputArgs, OBJDUMP_start_address_EQ, StartAddress);
+ HasStartAddressFlag = InputArgs.hasArg(OBJDUMP_start_address_EQ);
+ parseIntArg(InputArgs, OBJDUMP_stop_address_EQ, StopAddress);
+ HasStopAddressFlag = InputArgs.hasArg(OBJDUMP_stop_address_EQ);
+ SymbolTable = InputArgs.hasArg(OBJDUMP_syms);
+ SymbolizeOperands = InputArgs.hasArg(OBJDUMP_symbolize_operands);
+ DynamicSymbolTable = InputArgs.hasArg(OBJDUMP_dynamic_syms);
+ TripleName = InputArgs.getLastArgValue(OBJDUMP_triple_EQ).str();
+ UnwindInfo = InputArgs.hasArg(OBJDUMP_unwind_info);
+ Wide = InputArgs.hasArg(OBJDUMP_wide);
+ Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str();
+ parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip);
+ if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) {
+ DbgVariables = StringSwitch<DebugVarsFormat>(A->getValue())
+ .Case("ascii", DVASCII)
+ .Case("unicode", DVUnicode);
+ }
+ parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent);
+
+ parseMachOOptions(InputArgs);
+
+ // Parse -M (--disassembler-options) and deprecated
+ // --x86-asm-syntax={att,intel}.
+ //
+ // Note, for x86, the asm dialect (AssemblerDialect) is initialized when the
+ // MCAsmInfo is constructed. MCInstPrinter::applyTargetSpecificCLOption is
+ // called too late. For now we have to use the internal cl::opt option.
+ const char *AsmSyntax = nullptr;
+ for (const auto *A : InputArgs.filtered(OBJDUMP_disassembler_options_EQ,
+ OBJDUMP_x86_asm_syntax_att,
+ OBJDUMP_x86_asm_syntax_intel)) {
+ switch (A->getOption().getID()) {
+ case OBJDUMP_x86_asm_syntax_att:
+ AsmSyntax = "--x86-asm-syntax=att";
+ continue;
+ case OBJDUMP_x86_asm_syntax_intel:
+ AsmSyntax = "--x86-asm-syntax=intel";
+ continue;
+ }
+
+ SmallVector<StringRef, 2> Values;
+ llvm::SplitString(A->getValue(), Values, ",");
+ for (StringRef V : Values) {
+ if (V == "att")
+ AsmSyntax = "--x86-asm-syntax=att";
+ else if (V == "intel")
+ AsmSyntax = "--x86-asm-syntax=intel";
+ else
+ DisassemblerOptions.push_back(V.str());
+ }
+ }
+ if (AsmSyntax) {
+ const char *Argv[] = {"llvm-objdump", AsmSyntax};
+ llvm::cl::ParseCommandLineOptions(2, Argv);
+ }
+
+ // objdump defaults to a.out if no filenames specified.
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+}
+
int main(int argc, char **argv) {
using namespace llvm;
InitLLVM X(argc, argv);
- const cl::OptionCategory *OptionFilters[] = {&ObjdumpCat, &MachOCat};
- cl::HideUnrelatedOptions(OptionFilters);
+
+ ToolName = argv[0];
+ std::unique_ptr<CommonOptTable> T;
+ OptSpecifier Unknown, HelpFlag, HelpHiddenFlag, VersionFlag;
+
+ StringRef Stem = sys::path::stem(ToolName);
+ auto Is = [=](StringRef Tool) {
+ // We need to recognize the following filenames:
+ //
+ // llvm-objdump -> objdump
+ // llvm-otool-10.exe -> otool
+ // powerpc64-unknown-freebsd13-objdump -> objdump
+ auto I = Stem.rfind_insensitive(Tool);
+ return I != StringRef::npos &&
+ (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
+ };
+ if (Is("otool")) {
+ T = std::make_unique<OtoolOptTable>();
+ Unknown = OTOOL_UNKNOWN;
+ HelpFlag = OTOOL_help;
+ HelpHiddenFlag = OTOOL_help_hidden;
+ VersionFlag = OTOOL_version;
+ } else {
+ T = std::make_unique<ObjdumpOptTable>();
+ Unknown = OBJDUMP_UNKNOWN;
+ HelpFlag = OBJDUMP_help;
+ HelpHiddenFlag = OBJDUMP_help_hidden;
+ VersionFlag = OBJDUMP_version;
+ }
+
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ opt::InputArgList InputArgs =
+ T->parseArgs(argc, argv, Unknown, Saver,
+ [&](StringRef Msg) { reportCmdLineError(Msg); });
+
+ if (InputArgs.size() == 0 || InputArgs.hasArg(HelpFlag)) {
+ T->printHelp(ToolName);
+ return 0;
+ }
+ if (InputArgs.hasArg(HelpHiddenFlag)) {
+ T->printHelp(ToolName, /*show_hidden=*/true);
+ return 0;
+ }
// Initialize targets and assembly printers/parsers.
InitializeAllTargetInfos();
InitializeAllTargetMCs();
InitializeAllDisassemblers();
- // Register the target printer for --version.
- cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+ if (InputArgs.hasArg(VersionFlag)) {
+ cl::PrintVersionMessage();
+ if (!Is("otool")) {
+ outs() << '\n';
+ TargetRegistry::printRegisteredTargetsForVersion(outs());
+ }
+ return 0;
+ }
- cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n", nullptr,
- /*EnvVar=*/nullptr,
- /*LongOptionsUseDoubleDash=*/true);
+ if (Is("otool"))
+ parseOtoolOptions(InputArgs);
+ else
+ parseObjdumpOptions(InputArgs);
if (StartAddress >= StopAddress)
reportCmdLineError("start address should be less than stop address");
- ToolName = argv[0];
-
- // Defaults to a.out if no filenames specified.
- if (InputFilenames.empty())
- InputFilenames.push_back("a.out");
-
// Removes trailing separators from prefix.
while (!Prefix.empty() && sys::path::is_separator(Prefix.back()))
Prefix.pop_back();
@@ -3001,10 +2676,10 @@ int main(int argc, char **argv) {
!DynamicSymbolTable && !UnwindInfo && !FaultMapSection &&
!(MachOOpt &&
(Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie ||
- FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind ||
- LinkOptHints || ObjcMetaData || Rebase || UniversalHeaders ||
- WeakBind || !FilterSections.empty()))) {
- cl::PrintHelpMessage();
+ FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist ||
+ LazyBind || LinkOptHints || ObjcMetaData || Rebase || Rpaths ||
+ UniversalHeaders || WeakBind || !FilterSections.empty()))) {
+ T->printHelp(ToolName);
return 2;
}