diff options
Diffstat (limited to 'llvm/tools/llvm-objdump/llvm-objdump.cpp')
| -rw-r--r-- | llvm/tools/llvm-objdump/llvm-objdump.cpp | 1411 |
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; } |
