X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=tools%2Fllvm-objdump%2Fllvm-objdump.cpp;h=7bec062070a07905b5df7656ee5e5c05db54e340;hb=d94d5bbbf91589aafbf9c902f3839d3a51db6484;hp=4804f596b24e7e6f77416156cd731bb95f7613b8;hpb=41ab14b725c8f2bb3e54553d0d7d96ff184786b1;p=oota-llvm.git diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 4804f596b24..7bec062070a 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -11,187 +11,314 @@ // dumps out a plethora of information about an object file depending on the // flags. // +// The flags and output of this program should be near identical to those of +// binutils objdump. +// //===----------------------------------------------------------------------===// -#include "MCFunction.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/Triple.h" +#include "llvm-objdump.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Triple.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" -#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCInstrAnalysis.h" #include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCRelocationInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/Host.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/MemoryObject.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/system_error.h" -#include "llvm/Target/TargetRegistry.h" -#include "llvm/Target/TargetSelect.h" #include +#include #include +#include + using namespace llvm; using namespace object; -namespace { - cl::list - InputFilenames(cl::Positional, cl::desc(""), - cl::ZeroOrMore); +static cl::list +InputFilenames(cl::Positional, cl::desc(""),cl::ZeroOrMore); - cl::opt - Disassemble("disassemble", - cl::desc("Display assembler mnemonics for the machine instructions")); - cl::alias - Disassembled("d", cl::desc("Alias for --disassemble"), - cl::aliasopt(Disassemble)); +cl::opt +llvm::Disassemble("disassemble", + cl::desc("Display assembler mnemonics for the machine instructions")); +static cl::alias +Disassembled("d", cl::desc("Alias for --disassemble"), + cl::aliasopt(Disassemble)); - cl::opt - CFG("cfg", cl::desc("Create a CFG for every symbol in the object file and" - "write it to a graphviz file")); +cl::opt +llvm::Relocations("r", cl::desc("Display the relocation entries in the file")); - cl::opt - TripleName("triple", cl::desc("Target triple to disassemble for, " - "see -version for available targets")); +cl::opt +llvm::SectionContents("s", cl::desc("Display the content of each section")); - cl::opt - ArchName("arch", cl::desc("Target arch to disassemble for, " - "see -version for available targets")); +cl::opt +llvm::SymbolTable("t", cl::desc("Display the symbol table")); - StringRef ToolName; +cl::opt +llvm::ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols")); - bool error(error_code ec) { - if (!ec) return false; +cl::opt +llvm::Rebase("rebase", cl::desc("Display mach-o rebasing info")); - outs() << ToolName << ": error reading file: " << ec.message() << ".\n"; - outs().flush(); - return true; - } +cl::opt +llvm::Bind("bind", cl::desc("Display mach-o binding info")); + +cl::opt +llvm::LazyBind("lazy-bind", cl::desc("Display mach-o lazy binding info")); + +cl::opt +llvm::WeakBind("weak-bind", cl::desc("Display mach-o weak binding info")); + +static cl::opt +MachOOpt("macho", cl::desc("Use MachO specific object file parser")); +static cl::alias +MachOm("m", cl::desc("Alias for --macho"), cl::aliasopt(MachOOpt)); + +cl::opt +llvm::TripleName("triple", cl::desc("Target triple to disassemble for, " + "see -version for available targets")); + +cl::opt +llvm::MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), + cl::init("")); + +cl::opt +llvm::ArchName("arch-name", cl::desc("Target arch to disassemble for, " + "see -version for available targets")); + +cl::opt +llvm::SectionHeaders("section-headers", cl::desc("Display summaries of the " + "headers for each section.")); +static cl::alias +SectionHeadersShort("headers", cl::desc("Alias for --section-headers"), + cl::aliasopt(SectionHeaders)); +static cl::alias +SectionHeadersShorter("h", cl::desc("Alias for --section-headers"), + cl::aliasopt(SectionHeaders)); + +cl::list +llvm::MAttrs("mattr", + cl::CommaSeparated, + cl::desc("Target specific attributes"), + cl::value_desc("a1,+a2,-a3,...")); + +cl::opt +llvm::NoShowRawInsn("no-show-raw-insn", cl::desc("When disassembling " + "instructions, do not print " + "the instruction bytes.")); + +cl::opt +llvm::UnwindInfo("unwind-info", cl::desc("Display unwind information")); + +static cl::alias +UnwindInfoShort("u", cl::desc("Alias for --unwind-info"), + cl::aliasopt(UnwindInfo)); + +cl::opt +llvm::PrivateHeaders("private-headers", + cl::desc("Display format specific file headers")); + +static cl::alias +PrivateHeadersShort("p", cl::desc("Alias for --private-headers"), + cl::aliasopt(PrivateHeaders)); + +static StringRef ToolName; +static int ReturnValue = EXIT_SUCCESS; + +bool llvm::error(std::error_code EC) { + if (!EC) + return false; + + outs() << ToolName << ": error reading file: " << EC.message() << ".\n"; + outs().flush(); + ReturnValue = EXIT_FAILURE; + return true; } -static const Target *GetTarget(const ObjectFile *Obj = NULL) { +static const Target *getTarget(const ObjectFile *Obj = nullptr) { // Figure out the target triple. - llvm::Triple TT("unknown-unknown-unknown"); + llvm::Triple TheTriple("unknown-unknown-unknown"); if (TripleName.empty()) { - if (Obj) - TT.setArch(Triple::ArchType(Obj->getArch())); + if (Obj) { + TheTriple.setArch(Triple::ArchType(Obj->getArch())); + // TheTriple defaults to ELF, and COFF doesn't have an environment: + // the best we can do here is indicate that it is mach-o. + if (Obj->isMachO()) + TheTriple.setObjectFormat(Triple::MachO); + + if (Obj->isCOFF()) { + const auto COFFObj = dyn_cast(Obj); + if (COFFObj->getArch() == Triple::thumb) + TheTriple.setTriple("thumbv7-windows"); + } + } } else - TT.setTriple(Triple::normalize(TripleName)); - - if (!ArchName.empty()) - TT.setArchName(ArchName); - - TripleName = TT.str(); + TheTriple.setTriple(Triple::normalize(TripleName)); // Get the target specific parser. std::string Error; - const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); - if (TheTarget) - return TheTarget; + const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple, + Error); + if (!TheTarget) { + errs() << ToolName << ": " << Error; + return nullptr; + } - errs() << ToolName << ": error: unable to get target for '" << TripleName - << "', see --version and --triple.\n"; - return 0; + // Update the triple name and return the found target. + TripleName = TheTriple.getTriple(); + return TheTarget; } -namespace { -class StringRefMemoryObject : public MemoryObject { -private: - StringRef Bytes; -public: - StringRefMemoryObject(StringRef bytes) : Bytes(bytes) {} - - uint64_t getBase() const { return 0; } - uint64_t getExtent() const { return Bytes.size(); } - - int readByte(uint64_t Addr, uint8_t *Byte) const { - if (Addr >= getExtent()) - return -1; - *Byte = Bytes[Addr]; - return 0; +void llvm::DumpBytes(ArrayRef bytes) { + static const char hex_rep[] = "0123456789abcdef"; + SmallString<64> output; + + for (char i: bytes) { + output.push_back(hex_rep[(i & 0xF0) >> 4]); + output.push_back(hex_rep[i & 0xF]); + output.push_back(' '); } -}; + + outs() << output.c_str(); +} + +bool llvm::RelocAddressLess(RelocationRef a, RelocationRef b) { + uint64_t a_addr, b_addr; + if (error(a.getOffset(a_addr))) return false; + if (error(b.getOffset(b_addr))) return false; + return a_addr < b_addr; } -static void DumpBytes(StringRef bytes) { - static char hex_rep[] = "0123456789abcdef"; - // FIXME: The real way to do this is to figure out the longest instruction - // and align to that size before printing. I'll fix this when I get - // around to outputting relocations. - // 15 is the longest x86 instruction - // 3 is for the hex rep of a byte + a space. - // 1 is for the null terminator. - enum { OutputSize = (15 * 3) + 1 }; - char output[OutputSize]; - - assert(bytes.size() <= 15 - && "DumpBytes only supports instructions of up to 15 bytes"); - memset(output, ' ', sizeof(output)); - unsigned index = 0; - for (StringRef::iterator i = bytes.begin(), - e = bytes.end(); i != e; ++i) { - output[index] = hex_rep[(*i & 0xF0) >> 4]; - output[index + 1] = hex_rep[*i & 0xF]; - index += 3; +static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { + const Target *TheTarget = getTarget(Obj); + // getTarget() will have already issued a diagnostic if necessary, so + // just bail here if it failed. + if (!TheTarget) + return; + + // Package up features to be passed to target/subtarget + std::string FeaturesStr; + if (MAttrs.size()) { + SubtargetFeatures Features; + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); } - output[sizeof(output) - 1] = 0; - outs() << output; -} + std::unique_ptr MRI( + TheTarget->createMCRegInfo(TripleName)); + if (!MRI) { + errs() << "error: no register info for target " << TripleName << "\n"; + return; + } -static void DisassembleInput(const StringRef &Filename) { - OwningPtr Buff; + // Set up disassembler. + std::unique_ptr AsmInfo( + TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!AsmInfo) { + errs() << "error: no assembly info for target " << TripleName << "\n"; + return; + } - if (error_code ec = MemoryBuffer::getFileOrSTDIN(Filename, Buff)) { - errs() << ToolName << ": " << Filename << ": " << ec.message() << "\n"; + std::unique_ptr STI( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); + if (!STI) { + errs() << "error: no subtarget info for target " << TripleName << "\n"; return; } - OwningPtr Obj(ObjectFile::createObjectFile(Buff.take())); + std::unique_ptr MII(TheTarget->createMCInstrInfo()); + if (!MII) { + errs() << "error: no instruction info for target " << TripleName << "\n"; + return; + } - const Target *TheTarget = GetTarget(Obj.get()); - if (!TheTarget) { - // GetTarget prints out stuff. + std::unique_ptr MOFI(new MCObjectFileInfo); + MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get()); + + std::unique_ptr DisAsm( + TheTarget->createMCDisassembler(*STI, Ctx)); + + if (!DisAsm) { + errs() << "error: no disassembler for target " << TripleName << "\n"; return; } - const MCInstrInfo *InstrInfo = TheTarget->createMCInstrInfo(); - OwningPtr - InstrAnalysis(TheTarget->createMCInstrAnalysis(InstrInfo)); - outs() << '\n'; - outs() << Filename - << ":\tfile format " << Obj->getFileFormatName() << "\n\n"; - - error_code ec; - for (ObjectFile::section_iterator i = Obj->begin_sections(), - e = Obj->end_sections(); - i != e; i.increment(ec)) { - if (error(ec)) break; - bool text; - if (error(i->isText(text))) break; - if (!text) continue; + std::unique_ptr MIA( + TheTarget->createMCInstrAnalysis(MII.get())); + + int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); + std::unique_ptr IP(TheTarget->createMCInstPrinter( + AsmPrinterVariant, *AsmInfo, *MII, *MRI, *STI)); + if (!IP) { + errs() << "error: no instruction printer for target " << TripleName + << '\n'; + return; + } + + StringRef Fmt = Obj->getBytesInAddress() > 4 ? "\t\t%016" PRIx64 ": " : + "\t\t\t%08" PRIx64 ": "; + + // Create a mapping, RelocSecs = SectionRelocMap[S], where sections + // in RelocSecs contain the relocations for section S. + std::error_code EC; + std::map> SectionRelocMap; + for (const SectionRef &Section : Obj->sections()) { + section_iterator Sec2 = Section.getRelocatedSection(); + if (Sec2 != Obj->section_end()) + SectionRelocMap[*Sec2].push_back(Section); + } + + for (const SectionRef &Section : Obj->sections()) { + if (!Section.isText() || Section.isVirtual()) + continue; + + uint64_t SectionAddr = Section.getAddress(); + uint64_t SectSize = Section.getSize(); + if (!SectSize) + continue; // Make a list of all the symbols in this section. - std::vector > Symbols; - for (ObjectFile::symbol_iterator si = Obj->begin_symbols(), - se = Obj->end_symbols(); - si != se; si.increment(ec)) { - bool contains; - if (!error(i->containsSymbol(*si, contains)) && contains) { + std::vector> Symbols; + for (const SymbolRef &Symbol : Obj->symbols()) { + if (Section.containsSymbol(Symbol)) { uint64_t Address; - if (error(si->getAddress(Address))) break; + if (error(Symbol.getAddress(Address))) + break; + if (Address == UnknownAddressOrSize) + continue; + Address -= SectionAddr; + if (Address >= SectSize) + continue; + StringRef Name; - if (error(si->getName(Name))) break; + if (error(Symbol.getName(Name))) + break; Symbols.push_back(std::make_pair(Address, Name)); } } @@ -199,173 +326,532 @@ static void DisassembleInput(const StringRef &Filename) { // Sort the symbols by address, just in case they didn't come in that way. array_pod_sort(Symbols.begin(), Symbols.end()); + // Make a list of all the relocations for this section. + std::vector Rels; + if (InlineRelocs) { + for (const SectionRef &RelocSec : SectionRelocMap[Section]) { + for (const RelocationRef &Reloc : RelocSec.relocations()) { + Rels.push_back(Reloc); + } + } + } + + // Sort relocations by address. + std::sort(Rels.begin(), Rels.end(), RelocAddressLess); + + StringRef SegmentName = ""; + if (const MachOObjectFile *MachO = dyn_cast(Obj)) { + DataRefImpl DR = Section.getRawDataRefImpl(); + SegmentName = MachO->getSectionFinalSegmentName(DR); + } StringRef name; - if (error(i->getName(name))) break; - outs() << "Disassembly of section " << name << ':'; + if (error(Section.getName(name))) + break; + outs() << "Disassembly of section "; + if (!SegmentName.empty()) + outs() << SegmentName << ","; + outs() << name << ':'; // If the section has no symbols just insert a dummy one and disassemble // the whole section. if (Symbols.empty()) Symbols.push_back(std::make_pair(0, name)); - // Set up disassembler. - OwningPtr AsmInfo(TheTarget->createMCAsmInfo(TripleName)); - if (!AsmInfo) { - errs() << "error: no assembly info for target " << TripleName << "\n"; - return; - } + SmallString<40> Comments; + raw_svector_ostream CommentStream(Comments); - OwningPtr DisAsm(TheTarget->createMCDisassembler()); - if (!DisAsm) { - errs() << "error: no disassembler for target " << TripleName << "\n"; - return; - } + StringRef BytesStr; + if (error(Section.getContents(BytesStr))) + break; + ArrayRef Bytes(reinterpret_cast(BytesStr.data()), + BytesStr.size()); - int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); - OwningPtr IP(TheTarget->createMCInstPrinter( - AsmPrinterVariant, *AsmInfo)); - if (!IP) { - errs() << "error: no instruction printer for target " << TripleName << '\n'; - return; - } - - StringRef Bytes; - if (error(i->getContents(Bytes))) break; - StringRefMemoryObject memoryObject(Bytes); uint64_t Size; uint64_t Index; - uint64_t SectSize; - if (error(i->getSize(SectSize))) break; + std::vector::const_iterator rel_cur = Rels.begin(); + std::vector::const_iterator rel_end = Rels.end(); // Disassemble symbol by symbol. for (unsigned si = 0, se = Symbols.size(); si != se; ++si) { + uint64_t Start = Symbols[si].first; - uint64_t End = si == se-1 ? SectSize : Symbols[si + 1].first - 1; + // The end is either the section end or the beginning of the next symbol. + uint64_t End = (si == se - 1) ? SectSize : Symbols[si + 1].first; + // If this symbol has the same address as the next symbol, then skip it. + if (Start == End) + continue; + outs() << '\n' << Symbols[si].second << ":\n"; #ifndef NDEBUG - raw_ostream &DebugOut = DebugFlag ? dbgs() : nulls(); + raw_ostream &DebugOut = DebugFlag ? dbgs() : nulls(); #else - raw_ostream &DebugOut = nulls(); + raw_ostream &DebugOut = nulls(); #endif - if (!CFG) { - for (Index = Start; Index < End; Index += Size) { - MCInst Inst; - if (DisAsm->getInstruction(Inst, Size, memoryObject, Index, - DebugOut)) { - uint64_t addr; - if (error(i->getAddress(addr))) break; - outs() << format("%8x:\t", addr + Index); - DumpBytes(StringRef(Bytes.data() + Index, Size)); - IP->printInst(&Inst, outs()); - outs() << "\n"; - } else { - errs() << ToolName << ": warning: invalid instruction encoding\n"; - if (Size == 0) - Size = 1; // skip illegible bytes + for (Index = Start; Index < End; Index += Size) { + MCInst Inst; + + if (DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), + SectionAddr + Index, DebugOut, + CommentStream)) { + outs() << format("%8" PRIx64 ":", SectionAddr + Index); + if (!NoShowRawInsn) { + outs() << "\t"; + DumpBytes(ArrayRef(Bytes.data() + Index, Size)); } + IP->printInst(&Inst, outs(), ""); + outs() << CommentStream.str(); + Comments.clear(); + outs() << "\n"; + } else { + errs() << ToolName << ": warning: invalid instruction encoding\n"; + if (Size == 0) + Size = 1; // skip illegible bytes } - } else { - // Create CFG and use it for disassembly. - MCFunction f = - MCFunction::createFunctionFromMC(Symbols[si].second, DisAsm.get(), - memoryObject, Start, End, - InstrAnalysis.get(), DebugOut); - - for (MCFunction::iterator fi = f.begin(), fe = f.end(); fi != fe; ++fi){ - bool hasPreds = false; - // Only print blocks that have predecessors. - // FIXME: Slow. - for (MCFunction::iterator pi = f.begin(), pe = f.end(); pi != pe; - ++pi) - if (pi->second.contains(&fi->second)) { - hasPreds = true; - break; - } - - // Data block. - if (!hasPreds && fi != f.begin()) { - uint64_t End = llvm::next(fi) == fe ? SectSize : - llvm::next(fi)->first; - uint64_t addr; - if (error(i->getAddress(addr))) break; - outs() << "# " << End-fi->first << " bytes of data:\n"; - for (unsigned pos = fi->first; pos != End; ++pos) { - outs() << format("%8x:\t", addr + pos); - DumpBytes(StringRef(Bytes.data() + pos, 1)); - outs() << format("\t.byte 0x%02x\n", (uint8_t)Bytes[pos]); - } - continue; - } + // Print relocation for instruction. + while (rel_cur != rel_end) { + bool hidden = false; + uint64_t addr; + SmallString<16> name; + SmallString<32> val; - if (fi->second.contains(&fi->second)) - outs() << "# Loop begin:\n"; - - for (unsigned ii = 0, ie = fi->second.getInsts().size(); ii != ie; - ++ii) { - uint64_t addr; - if (error(i->getAddress(addr))) break; - const MCDecodedInst &Inst = fi->second.getInsts()[ii]; - outs() << format("%8x:\t", addr + Inst.Address); - DumpBytes(StringRef(Bytes.data() + Inst.Address, Inst.Size)); - // Simple loops. - if (fi->second.contains(&fi->second)) - outs() << '\t'; - IP->printInst(&Inst.Inst, outs()); - outs() << '\n'; - } - } + // If this relocation is hidden, skip it. + if (error(rel_cur->getHidden(hidden))) goto skip_print_rel; + if (hidden) goto skip_print_rel; - // Start a new dot file. - std::string Error; - raw_fd_ostream Out((f.getName().str() + ".dot").c_str(), Error); - if (!Error.empty()) { - errs() << ToolName << ": warning: " << Error << '\n'; - continue; - } + if (error(rel_cur->getOffset(addr))) goto skip_print_rel; + // Stop when rel_cur's address is past the current instruction. + if (addr >= Index + Size) break; + if (error(rel_cur->getTypeName(name))) goto skip_print_rel; + if (error(rel_cur->getValueString(val))) goto skip_print_rel; - Out << "digraph " << f.getName() << " {\n"; - Out << "graph [ rankdir = \"LR\" ];\n"; - for (MCFunction::iterator i = f.begin(), e = f.end(); i != e; ++i) { - bool hasPreds = false; - // Only print blocks that have predecessors. - // FIXME: Slow. - for (MCFunction::iterator pi = f.begin(), pe = f.end(); pi != pe; - ++pi) - if (pi->second.contains(&i->second)) { - hasPreds = true; - break; - } - - if (!hasPreds && i != f.begin()) - continue; - - Out << '"' << (uintptr_t)&i->second << "\" [ label=\""; - // Print instructions. - for (unsigned ii = 0, ie = i->second.getInsts().size(); ii != ie; - ++ii) { - // Escape special chars and print the instruction in mnemonic form. - std::string Str; - raw_string_ostream OS(Str); - IP->printInst(&i->second.getInsts()[ii].Inst, OS); - Out << DOT::EscapeString(OS.str()) << '|'; - } - Out << "\" shape=\"record\" ];\n"; + outs() << format(Fmt.data(), SectionAddr + addr) << name + << "\t" << val << "\n"; - // Add edges. - for (MCBasicBlock::succ_iterator si = i->second.succ_begin(), - se = i->second.succ_end(); si != se; ++si) - Out << (uintptr_t)&i->second << ":o -> " << (uintptr_t)*si <<":a\n"; + skip_print_rel: + ++rel_cur; } - Out << "}\n"; } } } } +void llvm::PrintRelocations(const ObjectFile *Obj) { + StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : + "%08" PRIx64; + // Regular objdump doesn't print relocations in non-relocatable object + // files. + if (!Obj->isRelocatableObject()) + return; + + for (const SectionRef &Section : Obj->sections()) { + if (Section.relocation_begin() == Section.relocation_end()) + continue; + StringRef secname; + if (error(Section.getName(secname))) + continue; + outs() << "RELOCATION RECORDS FOR [" << secname << "]:\n"; + for (const RelocationRef &Reloc : Section.relocations()) { + bool hidden; + uint64_t address; + SmallString<32> relocname; + SmallString<32> valuestr; + if (error(Reloc.getHidden(hidden))) + continue; + if (hidden) + continue; + if (error(Reloc.getTypeName(relocname))) + continue; + if (error(Reloc.getOffset(address))) + continue; + if (error(Reloc.getValueString(valuestr))) + continue; + outs() << format(Fmt.data(), address) << " " << relocname << " " + << valuestr << "\n"; + } + outs() << "\n"; + } +} + +void llvm::PrintSectionHeaders(const ObjectFile *Obj) { + outs() << "Sections:\n" + "Idx Name Size Address Type\n"; + unsigned i = 0; + for (const SectionRef &Section : Obj->sections()) { + StringRef Name; + if (error(Section.getName(Name))) + return; + uint64_t Address = Section.getAddress(); + uint64_t Size = Section.getSize(); + bool Text = Section.isText(); + bool Data = Section.isData(); + bool BSS = Section.isBSS(); + std::string Type = (std::string(Text ? "TEXT " : "") + + (Data ? "DATA " : "") + (BSS ? "BSS" : "")); + outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %s\n", i, + Name.str().c_str(), Size, Address, Type.c_str()); + ++i; + } +} + +void llvm::PrintSectionContents(const ObjectFile *Obj) { + std::error_code EC; + for (const SectionRef &Section : Obj->sections()) { + StringRef Name; + StringRef Contents; + if (error(Section.getName(Name))) + continue; + uint64_t BaseAddr = Section.getAddress(); + uint64_t Size = Section.getSize(); + if (!Size) + continue; + + outs() << "Contents of section " << Name << ":\n"; + if (Section.isBSS()) { + outs() << format("\n", + BaseAddr, BaseAddr + Size); + continue; + } + + if (error(Section.getContents(Contents))) + continue; + + // Dump out the content as hex and printable ascii characters. + for (std::size_t addr = 0, end = Contents.size(); addr < end; addr += 16) { + outs() << format(" %04" PRIx64 " ", BaseAddr + addr); + // Dump line of hex. + for (std::size_t i = 0; i < 16; ++i) { + if (i != 0 && i % 4 == 0) + outs() << ' '; + if (addr + i < end) + outs() << hexdigit((Contents[addr + i] >> 4) & 0xF, true) + << hexdigit(Contents[addr + i] & 0xF, true); + else + outs() << " "; + } + // Print ascii. + outs() << " "; + for (std::size_t i = 0; i < 16 && addr + i < end; ++i) { + if (std::isprint(static_cast(Contents[addr + i]) & 0xFF)) + outs() << Contents[addr + i]; + else + outs() << "."; + } + outs() << "\n"; + } + } +} + +static void PrintCOFFSymbolTable(const COFFObjectFile *coff) { + for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) { + ErrorOr Symbol = coff->getSymbol(SI); + StringRef Name; + if (error(Symbol.getError())) + return; + + if (error(coff->getSymbolName(*Symbol, Name))) + return; + + outs() << "[" << format("%2d", SI) << "]" + << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")" + << "(fl 0x00)" // Flag bits, which COFF doesn't have. + << "(ty " << format("%3x", unsigned(Symbol->getType())) << ")" + << "(scl " << format("%3x", unsigned(Symbol->getStorageClass())) << ") " + << "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") " + << "0x" << format("%08x", unsigned(Symbol->getValue())) << " " + << Name << "\n"; + + for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) { + if (Symbol->isSectionDefinition()) { + const coff_aux_section_definition *asd; + if (error(coff->getAuxSymbol(SI + 1, asd))) + return; + + int32_t AuxNumber = asd->getNumber(Symbol->isBigObj()); + + outs() << "AUX " + << format("scnlen 0x%x nreloc %d nlnno %d checksum 0x%x " + , unsigned(asd->Length) + , unsigned(asd->NumberOfRelocations) + , unsigned(asd->NumberOfLinenumbers) + , unsigned(asd->CheckSum)) + << format("assoc %d comdat %d\n" + , unsigned(AuxNumber) + , unsigned(asd->Selection)); + } else if (Symbol->isFileRecord()) { + const char *FileName; + if (error(coff->getAuxSymbol(SI + 1, FileName))) + return; + + StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() * + coff->getSymbolTableEntrySize()); + outs() << "AUX " << Name.rtrim(StringRef("\0", 1)) << '\n'; + + SI = SI + Symbol->getNumberOfAuxSymbols(); + break; + } else { + outs() << "AUX Unknown\n"; + } + } + } +} + +void llvm::PrintSymbolTable(const ObjectFile *o) { + outs() << "SYMBOL TABLE:\n"; + + if (const COFFObjectFile *coff = dyn_cast(o)) { + PrintCOFFSymbolTable(coff); + return; + } + for (const SymbolRef &Symbol : o->symbols()) { + StringRef Name; + uint64_t Address; + SymbolRef::Type Type; + uint64_t Size; + uint32_t Flags = Symbol.getFlags(); + section_iterator Section = o->section_end(); + if (error(Symbol.getName(Name))) + continue; + if (error(Symbol.getAddress(Address))) + continue; + if (error(Symbol.getType(Type))) + continue; + if (error(Symbol.getSize(Size))) + continue; + if (error(Symbol.getSection(Section))) + continue; + + bool Global = Flags & SymbolRef::SF_Global; + bool Weak = Flags & SymbolRef::SF_Weak; + bool Absolute = Flags & SymbolRef::SF_Absolute; + bool Common = Flags & SymbolRef::SF_Common; + + if (Common) { + uint32_t Alignment; + if (error(Symbol.getAlignment(Alignment))) + Alignment = 0; + Address = Size; + Size = Alignment; + } + if (Address == UnknownAddressOrSize) + Address = 0; + if (Size == UnknownAddressOrSize) + Size = 0; + char GlobLoc = ' '; + if (Type != SymbolRef::ST_Unknown) + GlobLoc = Global ? 'g' : 'l'; + char Debug = (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File) + ? 'd' : ' '; + char FileFunc = ' '; + if (Type == SymbolRef::ST_File) + FileFunc = 'f'; + else if (Type == SymbolRef::ST_Function) + FileFunc = 'F'; + + const char *Fmt = o->getBytesInAddress() > 4 ? "%016" PRIx64 : + "%08" PRIx64; + + outs() << format(Fmt, Address) << " " + << GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' ' + << (Weak ? 'w' : ' ') // Weak? + << ' ' // Constructor. Not supported yet. + << ' ' // Warning. Not supported yet. + << ' ' // Indirect reference to another symbol. + << Debug // Debugging (d) or dynamic (D) symbol. + << FileFunc // Name of function (F), file (f) or object (O). + << ' '; + if (Absolute) { + outs() << "*ABS*"; + } else if (Common) { + outs() << "*COM*"; + } else if (Section == o->section_end()) { + outs() << "*UND*"; + } else { + if (const MachOObjectFile *MachO = + dyn_cast(o)) { + DataRefImpl DR = Section->getRawDataRefImpl(); + StringRef SegmentName = MachO->getSectionFinalSegmentName(DR); + outs() << SegmentName << ","; + } + StringRef SectionName; + if (error(Section->getName(SectionName))) + SectionName = ""; + outs() << SectionName; + } + outs() << '\t' + << format("%08" PRIx64 " ", Size) + << Name + << '\n'; + } +} + +static void PrintUnwindInfo(const ObjectFile *o) { + outs() << "Unwind info:\n\n"; + + if (const COFFObjectFile *coff = dyn_cast(o)) { + printCOFFUnwindInfo(coff); + } else if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOUnwindInfo(MachO); + else { + // TODO: Extract DWARF dump tool to objdump. + errs() << "This operation is only currently supported " + "for COFF and MachO object files.\n"; + return; + } +} + +void llvm::printExportsTrie(const ObjectFile *o) { + outs() << "Exports trie:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOExportsTrie(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +void llvm::printRebaseTable(const ObjectFile *o) { + outs() << "Rebase table:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachORebaseTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +void llvm::printBindTable(const ObjectFile *o) { + outs() << "Bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +void llvm::printLazyBindTable(const ObjectFile *o) { + outs() << "Lazy bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOLazyBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +void llvm::printWeakBindTable(const ObjectFile *o) { + outs() << "Weak bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOWeakBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +static void printPrivateFileHeader(const ObjectFile *o) { + if (o->isELF()) { + printELFFileHeader(o); + } else if (o->isCOFF()) { + printCOFFFileHeader(o); + } else if (o->isMachO()) { + printMachOFileHeader(o); + } +} + +static void DumpObject(const ObjectFile *o) { + outs() << '\n'; + outs() << o->getFileName() + << ":\tfile format " << o->getFileFormatName() << "\n\n"; + + if (Disassemble) + DisassembleObject(o, Relocations); + if (Relocations && !Disassemble) + PrintRelocations(o); + if (SectionHeaders) + PrintSectionHeaders(o); + if (SectionContents) + PrintSectionContents(o); + if (SymbolTable) + PrintSymbolTable(o); + if (UnwindInfo) + PrintUnwindInfo(o); + if (PrivateHeaders) + printPrivateFileHeader(o); + if (ExportsTrie) + printExportsTrie(o); + if (Rebase) + printRebaseTable(o); + if (Bind) + printBindTable(o); + if (LazyBind) + printLazyBindTable(o); + if (WeakBind) + printWeakBindTable(o); +} + +/// @brief Dump each object file in \a a; +static void DumpArchive(const Archive *a) { + for (Archive::child_iterator i = a->child_begin(), e = a->child_end(); i != e; + ++i) { + ErrorOr> ChildOrErr = i->getAsBinary(); + if (std::error_code EC = ChildOrErr.getError()) { + // Ignore non-object files. + if (EC != object_error::invalid_file_type) + errs() << ToolName << ": '" << a->getFileName() << "': " << EC.message() + << ".\n"; + continue; + } + if (ObjectFile *o = dyn_cast(&*ChildOrErr.get())) + DumpObject(o); + else + errs() << ToolName << ": '" << a->getFileName() << "': " + << "Unrecognized file type.\n"; + } +} + +/// @brief Open file and figure out how to dump it. +static void DumpInput(StringRef file) { + // If file isn't stdin, check that it exists. + if (file != "-" && !sys::fs::exists(file)) { + errs() << ToolName << ": '" << file << "': " << "No such file\n"; + return; + } + + // If we are using the Mach-O specific object file parser, then let it parse + // the file and process the command line options. So the -arch flags can + // be used to select specific slices, etc. + if (MachOOpt) { + ParseInputMachO(file); + return; + } + + // Attempt to open the binary. + ErrorOr> BinaryOrErr = createBinary(file); + if (std::error_code EC = BinaryOrErr.getError()) { + errs() << ToolName << ": '" << file << "': " << EC.message() << ".\n"; + return; + } + Binary &Binary = *BinaryOrErr.get().getBinary(); + + if (Archive *a = dyn_cast(&Binary)) + DumpArchive(a); + else if (ObjectFile *o = dyn_cast(&Binary)) + DumpObject(o); + else + errs() << ToolName << ": '" << file << "': " << "Unrecognized file type.\n"; +} + int main(int argc, char **argv) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(); @@ -378,6 +864,9 @@ int main(int argc, char **argv) { llvm::InitializeAllAsmParsers(); llvm::InitializeAllDisassemblers(); + // Register the target printer for --version. + cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n"); TripleName = Triple::normalize(TripleName); @@ -387,15 +876,33 @@ int main(int argc, char **argv) { if (InputFilenames.size() == 0) InputFilenames.push_back("a.out"); - // -d is the only flag that is currently implemented, so just print help if - // it is not set. - if (!Disassemble) { + if (!Disassemble + && !Relocations + && !SectionHeaders + && !SectionContents + && !SymbolTable + && !UnwindInfo + && !PrivateHeaders + && !ExportsTrie + && !Rebase + && !Bind + && !LazyBind + && !WeakBind + && !(UniversalHeaders && MachOOpt) + && !(ArchiveHeaders && MachOOpt) + && !(IndirectSymbols && MachOOpt) + && !(DataInCode && MachOOpt) + && !(LinkOptHints && MachOOpt) + && !(InfoPlist && MachOOpt) + && !(DylibsUsed && MachOOpt) + && !(DylibId && MachOOpt) + && !(DumpSections.size() != 0 && MachOOpt)) { cl::PrintHelpMessage(); return 2; } std::for_each(InputFilenames.begin(), InputFilenames.end(), - DisassembleInput); + DumpInput); - return 0; + return ReturnValue; }