Skip to content

Commit

Permalink
llvm-objdump: Output line info next to the disassembly if available.
Browse files Browse the repository at this point in the history
MachO-only at the moment, sorry.

Usage:
$ llvm-objdump -d -m -g -dsym=a.out.dSYM/Contents/Resources/DWARF/a.out a.out
_main:
100000e90:	55	pushq %rbp	## test.c:11:3
…

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@140224 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
d0k committed Sep 21, 2011
1 parent ab6acef commit 8c93097
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 53 deletions.
1 change: 1 addition & 0 deletions tools/llvm-objdump/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
DebugInfo
MC
MCParser
MCDisassembler
Expand Down
205 changes: 153 additions & 52 deletions tools/llvm-objdump/MachODump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
Expand All @@ -44,6 +45,12 @@ static cl::opt<bool>
CFG("cfg", cl::desc("Create a CFG for every symbol in the object file and"
"write it to a graphviz file (MachO-only)"));

static cl::opt<bool>
UseDbg("g", cl::desc("Print line information from debug info if available"));

static cl::opt<std::string>
DSYMFile("dsym", cl::desc("Use .dSYM file for debug info"));

static const Target *GetTarget(const MachOObject *MachOObj) {
// Figure out the target triple.
llvm::Triple TT("unknown-unknown-unknown");
Expand Down Expand Up @@ -94,7 +101,6 @@ struct Symbol {
bool operator<(const Symbol &RHS) const { return Value < RHS.Value; }
};


template <typename T>
static Section copySection(const T &Sect) {
Section S;
Expand Down Expand Up @@ -205,6 +211,67 @@ static void emitDOTFile(const char *FileName, const MCFunction &f,
Out << "}\n";
}

static void getSectionsAndSymbols(const macho::Header &Header,
MachOObject *MachOObj,
InMemoryStruct<macho::SymtabLoadCommand> *SymtabLC,
std::vector<Section> &Sections,
std::vector<Symbol> &Symbols,
SmallVectorImpl<uint64_t> &FoundFns) {
// Make a list of all symbols in the object file.
for (unsigned i = 0; i != Header.NumLoadCommands; ++i) {
const MachOObject::LoadCommandInfo &LCI = MachOObj->getLoadCommandInfo(i);
if (LCI.Command.Type == macho::LCT_Segment) {
InMemoryStruct<macho::SegmentLoadCommand> SegmentLC;
MachOObj->ReadSegmentLoadCommand(LCI, SegmentLC);

// Store the sections in this segment.
for (unsigned SectNum = 0; SectNum != SegmentLC->NumSections; ++SectNum) {
InMemoryStruct<macho::Section> Sect;
MachOObj->ReadSection(LCI, SectNum, Sect);
Sections.push_back(copySection(Sect));

// Store the symbols in this section.
if (SymtabLC) {
for (unsigned i = 0; i != (*SymtabLC)->NumSymbolTableEntries; ++i) {
InMemoryStruct<macho::SymbolTableEntry> STE;
MachOObj->ReadSymbolTableEntry((*SymtabLC)->SymbolTableOffset, i,
STE);
Symbols.push_back(copySymbol(STE));
}
}
}
} else if (LCI.Command.Type == macho::LCT_Segment64) {
InMemoryStruct<macho::Segment64LoadCommand> Segment64LC;
MachOObj->ReadSegment64LoadCommand(LCI, Segment64LC);

// Store the sections in this segment.
for (unsigned SectNum = 0; SectNum != Segment64LC->NumSections;
++SectNum) {
InMemoryStruct<macho::Section64> Sect64;
MachOObj->ReadSection64(LCI, SectNum, Sect64);
Sections.push_back(copySection(Sect64));

// Store the symbols in this section.
if (SymtabLC) {
for (unsigned i = 0; i != (*SymtabLC)->NumSymbolTableEntries; ++i) {
InMemoryStruct<macho::Symbol64TableEntry> STE;
MachOObj->ReadSymbol64TableEntry((*SymtabLC)->SymbolTableOffset, i,
STE);
Symbols.push_back(copySymbol(STE));
}
}
}
} else if (LCI.Command.Type == macho::LCT_FunctionStarts) {
// We found a function starts segment, parse the addresses for later
// consumption.
InMemoryStruct<macho::LinkeditDataLoadCommand> LLC;
MachOObj->ReadLinkeditDataLoadCommand(LCI, LLC);

MachOObj->ReadULEB128s(LLC->DataOffset, FoundFns);
}
}
}

void llvm::DisassembleInputMachO(StringRef Filename) {
OwningPtr<MemoryBuffer> Buff;

Expand Down Expand Up @@ -260,60 +327,13 @@ void llvm::DisassembleInputMachO(StringRef Filename) {

std::vector<Section> Sections;
std::vector<Symbol> Symbols;
std::vector<Symbol> UnsortedSymbols; // FIXME: duplication
SmallVector<uint64_t, 8> FoundFns;

// Make a list of all symbols in the object file.
for (unsigned i = 0; i != Header.NumLoadCommands; ++i) {
const MachOObject::LoadCommandInfo &LCI = MachOObj->getLoadCommandInfo(i);
if (LCI.Command.Type == macho::LCT_Segment) {
InMemoryStruct<macho::SegmentLoadCommand> SegmentLC;
MachOObj->ReadSegmentLoadCommand(LCI, SegmentLC);

// Store the sections in this segment.
for (unsigned SectNum = 0; SectNum != SegmentLC->NumSections; ++SectNum) {
InMemoryStruct<macho::Section> Sect;
MachOObj->ReadSection(LCI, SectNum, Sect);
Sections.push_back(copySection(Sect));

// Store the symbols in this section.
for (unsigned i = 0; i != SymtabLC->NumSymbolTableEntries; ++i) {
InMemoryStruct<macho::SymbolTableEntry> STE;
MachOObj->ReadSymbolTableEntry(SymtabLC->SymbolTableOffset, i, STE);
Symbols.push_back(copySymbol(STE));
UnsortedSymbols.push_back(Symbols.back());
}
}
} else if (LCI.Command.Type == macho::LCT_Segment64) {
InMemoryStruct<macho::Segment64LoadCommand> Segment64LC;
MachOObj->ReadSegment64LoadCommand(LCI, Segment64LC);

// Store the sections in this segment.
for (unsigned SectNum = 0; SectNum != Segment64LC->NumSections;
++SectNum) {
InMemoryStruct<macho::Section64> Sect64;
MachOObj->ReadSection64(LCI, SectNum, Sect64);
Sections.push_back(copySection(Sect64));

// Store the symbols in this section.
for (unsigned i = 0; i != SymtabLC->NumSymbolTableEntries; ++i) {
InMemoryStruct<macho::Symbol64TableEntry> STE;
MachOObj->ReadSymbol64TableEntry(SymtabLC->SymbolTableOffset, i, STE);
Symbols.push_back(copySymbol(STE));
UnsortedSymbols.push_back(Symbols.back());
}
}
} else if (LCI.Command.Type == macho::LCT_FunctionStarts) {
// We found a function starts segment, parse the addresses for later
// consumption.
InMemoryStruct<macho::LinkeditDataLoadCommand> LLC;
MachOObj->ReadLinkeditDataLoadCommand(LCI, LLC);

MachOObj->ReadULEB128s(LLC->DataOffset, FoundFns);
}
}

getSectionsAndSymbols(Header, MachOObj.get(), &SymtabLC, Sections, Symbols,
FoundFns);

// Make a copy of the unsorted symbol list. FIXME: duplication
std::vector<Symbol> UnsortedSymbols(Symbols);
// Sort the symbols by address, just in case they didn't come in that way.
array_pod_sort(Symbols.begin(), Symbols.end());

Expand All @@ -323,6 +343,61 @@ void llvm::DisassembleInputMachO(StringRef Filename) {
raw_ostream &DebugOut = nulls();
#endif

StringRef DebugAbbrevSection, DebugInfoSection, DebugArangesSection,
DebugLineSection, DebugStrSection;
OwningPtr<DIContext> diContext;
// Try to find debug info and set up the DIContext for it.
if (UseDbg) {
ArrayRef<Section> DebugSections = Sections;
std::vector<Section> DSYMSections;
OwningPtr<MachOObject> DSYMObj;

// A separate DSym file path was specified, parse it as a macho file,
// get the sections and supply it to the section name parsing machinery.
if (!DSYMFile.empty()) {
OwningPtr<MemoryBuffer> Buf;
if (error_code ec = MemoryBuffer::getFileOrSTDIN(DSYMFile.c_str(), Buf)) {
errs() << "llvm-objdump: " << Filename << ": " << ec.message() << '\n';
return;
}
DSYMObj.reset(MachOObject::LoadFromBuffer(Buf.take()));
const macho::Header &Header = DSYMObj->getHeader();

std::vector<Symbol> Symbols;
SmallVector<uint64_t, 8> FoundFns;
getSectionsAndSymbols(Header, DSYMObj.get(), 0, DSYMSections, Symbols,
FoundFns);
DebugSections = DSYMSections;
}

// Find the named debug info sections.
for (unsigned SectIdx = 0; SectIdx != DebugSections.size(); SectIdx++) {
if (!strcmp(DebugSections[SectIdx].Name, "__debug_abbrev"))
DebugAbbrevSection = DSYMObj->getData(DebugSections[SectIdx].Offset,
DebugSections[SectIdx].Size);
else if (!strcmp(DebugSections[SectIdx].Name, "__debug_info"))
DebugInfoSection = DSYMObj->getData(DebugSections[SectIdx].Offset,
DebugSections[SectIdx].Size);
else if (!strcmp(DebugSections[SectIdx].Name, "__debug_aranges"))
DebugArangesSection = DSYMObj->getData(DebugSections[SectIdx].Offset,
DebugSections[SectIdx].Size);
else if (!strcmp(DebugSections[SectIdx].Name, "__debug_line"))
DebugLineSection = DSYMObj->getData(DebugSections[SectIdx].Offset,
DebugSections[SectIdx].Size);
else if (!strcmp(DebugSections[SectIdx].Name, "__debug_str"))
DebugStrSection = DSYMObj->getData(DebugSections[SectIdx].Offset,
DebugSections[SectIdx].Size);
}

// Setup the DIContext.
diContext.reset(DIContext::getDWARFContext(MachOObj->isLittleEndian(),
DebugInfoSection,
DebugAbbrevSection,
DebugArangesSection,
DebugLineSection,
DebugStrSection));
}

FunctionMapTy FunctionMap;
FunctionListTy Functions;

Expand Down Expand Up @@ -374,6 +449,7 @@ void llvm::DisassembleInputMachO(StringRef Filename) {
// Normal disassembly, print addresses, bytes and mnemonic form.
outs() << MachOObj->getStringAtIndex(Symbols[SymIdx].StringIndex)
<< ":\n";
DILineInfo lastLine;
for (uint64_t Index = Start; Index < End; Index += Size) {
MCInst Inst;

Expand All @@ -382,6 +458,18 @@ void llvm::DisassembleInputMachO(StringRef Filename) {
outs() << format("%8llx:\t", Sections[SectIdx].Address + Index);
DumpBytes(StringRef(Bytes.data() + Index, Size));
IP->printInst(&Inst, outs(), "");

// Print debug info.
if (diContext) {
DILineInfo dli =
diContext->getLineInfoForAddress(Sections[SectIdx].Address +
Index);
// Print valid line info if it changed.
if (dli != lastLine && dli.getLine() != 0)
outs() << "\t## " << dli.getFileName() << ':'
<< dli.getLine() << ':' << dli.getColumn();
lastLine = dli;
}
outs() << "\n";
} else {
errs() << "llvm-objdump: warning: invalid instruction encoding\n";
Expand Down Expand Up @@ -464,6 +552,7 @@ void llvm::DisassembleInputMachO(StringRef Filename) {
if (fi->second.contains(fi->first)) // Print a header for simple loops
outs() << "# Loop begin:\n";

DILineInfo lastLine;
// Walk over the instructions and print them.
for (unsigned ii = 0, ie = fi->second.getInsts().size(); ii != ie;
++ii) {
Expand Down Expand Up @@ -506,6 +595,18 @@ void llvm::DisassembleInputMachO(StringRef Filename) {
if (targ != -1ULL)
DumpAddress(targ, Sections, MachOObj.get(), outs());

// Print debug info.
if (diContext) {
DILineInfo dli =
diContext->getLineInfoForAddress(Sections[SectIdx].Address +
Inst.Address);
// Print valid line info if it changed.
if (dli != lastLine && dli.getLine() != 0)
outs() << "\t## " << dli.getFileName() << ':'
<< dli.getLine() << ':' << dli.getColumn();
lastLine = dli;
}

outs() << '\n';
}
}
Expand Down
3 changes: 2 additions & 1 deletion tools/llvm-objdump/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
LEVEL = ../..

TOOLNAME = llvm-objdump
LINK_COMPONENTS = $(TARGETS_TO_BUILD) MC MCParser MCDisassembler Object
LINK_COMPONENTS = $(TARGETS_TO_BUILD) DebugInfo MC MCParser MCDisassembler \
Object

# This tool has no plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
Expand Down

0 comments on commit 8c93097

Please sign in to comment.