llvm-6502/tools/llvm-objdump/MachODump.cpp
Ahmed Bougacha ef99356dfe MC: Disassembled CFG reconstruction.
This patch builds on some existing code to do CFG reconstruction from
a disassembled binary:
- MCModule represents the binary, and has a list of MCAtoms.
- MCAtom represents either disassembled instructions (MCTextAtom), or
  contiguous data (MCDataAtom), and covers a specific range of addresses.
- MCBasicBlock and MCFunction form the reconstructed CFG. An MCBB is
  backed by an MCTextAtom, and has the usual successors/predecessors.
- MCObjectDisassembler creates a module from an ObjectFile using a
  disassembler. It first builds an atom for each section. It can also
  construct the CFG, and this splits the text atoms into basic blocks.

MCModule and MCAtom were only sketched out; MCFunction and MCBB were
implemented under the experimental "-cfg" llvm-objdump -macho option.
This cleans them up for further use; llvm-objdump -d -cfg now generates
graphviz files for each function found in the binary.

In the future, MCObjectDisassembler may be the right place to do
"intelligent" disassembly: for example, handling constant islands is just
a matter of splitting the atom, using information that may be available
in the ObjectFile. Also, better initial atom formation than just using
sections is possible using symbols (and things like Mach-O's
function_starts load command).

This brings two minor regressions in llvm-objdump -macho -cfg:
- The printing of a relocation's referenced symbol.
- An annotation on loop BBs, i.e., which are their own successor.

Relocation printing is replaced by the MCSymbolizer; the basic CFG
annotation will be superseded by more related functionality.



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@182628 91177308-0d34-0410-b5e6-96231b3b80d8
2013-05-24 01:07:04 +00:00

365 lines
12 KiB
C++

//===-- MachODump.cpp - Object file dumping utility for llvm --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the MachO-specific dumper for llvm-objdump.
//
//===----------------------------------------------------------------------===//
#include "llvm-objdump.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/MachO.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/system_error.h"
#include <algorithm>
#include <cstring>
using namespace llvm;
using namespace object;
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 MachOObjectFile *MachOObj) {
// Figure out the target triple.
if (TripleName.empty()) {
llvm::Triple TT("unknown-unknown-unknown");
TT.setArch(Triple::ArchType(MachOObj->getArch()));
TripleName = TT.str();
}
// Get the target specific parser.
std::string Error;
const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
if (TheTarget)
return TheTarget;
errs() << "llvm-objdump: error: unable to get target for '" << TripleName
<< "', see --version and --triple.\n";
return 0;
}
struct SymbolSorter {
bool operator()(const SymbolRef &A, const SymbolRef &B) {
SymbolRef::Type AType, BType;
A.getType(AType);
B.getType(BType);
uint64_t AAddr, BAddr;
if (AType != SymbolRef::ST_Function)
AAddr = 0;
else
A.getAddress(AAddr);
if (BType != SymbolRef::ST_Function)
BAddr = 0;
else
B.getAddress(BAddr);
return AAddr < BAddr;
}
};
static void
getSectionsAndSymbols(const macho::Header Header,
MachOObjectFile *MachOObj,
std::vector<SectionRef> &Sections,
std::vector<SymbolRef> &Symbols,
SmallVectorImpl<uint64_t> &FoundFns) {
error_code ec;
for (symbol_iterator SI = MachOObj->begin_symbols(),
SE = MachOObj->end_symbols(); SI != SE; SI.increment(ec))
Symbols.push_back(*SI);
for (section_iterator SI = MachOObj->begin_sections(),
SE = MachOObj->end_sections(); SI != SE; SI.increment(ec)) {
SectionRef SR = *SI;
StringRef SectName;
SR.getName(SectName);
Sections.push_back(*SI);
}
MachOObjectFile::LoadCommandInfo Command =
MachOObj->getFirstLoadCommandInfo();
for (unsigned i = 0; ; ++i) {
if (Command.C.Type == macho::LCT_FunctionStarts) {
// We found a function starts segment, parse the addresses for later
// consumption.
macho::LinkeditDataLoadCommand LLC =
MachOObj->getLinkeditDataLoadCommand(Command);
MachOObj->ReadULEB128s(LLC.DataOffset, FoundFns);
}
if (i == Header.NumLoadCommands - 1)
break;
else
Command = MachOObj->getNextLoadCommandInfo(Command);
}
}
static void DisassembleInputMachO2(StringRef Filename,
MachOObjectFile *MachOOF);
void llvm::DisassembleInputMachO(StringRef Filename) {
OwningPtr<MemoryBuffer> Buff;
if (error_code ec = MemoryBuffer::getFileOrSTDIN(Filename, Buff)) {
errs() << "llvm-objdump: " << Filename << ": " << ec.message() << "\n";
return;
}
OwningPtr<MachOObjectFile> MachOOF(static_cast<MachOObjectFile*>(
ObjectFile::createMachOObjectFile(Buff.take())));
DisassembleInputMachO2(Filename, MachOOF.get());
}
static void DisassembleInputMachO2(StringRef Filename,
MachOObjectFile *MachOOF) {
const Target *TheTarget = GetTarget(MachOOF);
if (!TheTarget) {
// GetTarget prints out stuff.
return;
}
OwningPtr<const MCInstrInfo> InstrInfo(TheTarget->createMCInstrInfo());
OwningPtr<MCInstrAnalysis>
InstrAnalysis(TheTarget->createMCInstrAnalysis(InstrInfo.get()));
// Set up disassembler.
OwningPtr<const MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
OwningPtr<const MCAsmInfo> AsmInfo(
TheTarget->createMCAsmInfo(*MRI, TripleName));
OwningPtr<const MCSubtargetInfo>
STI(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
OwningPtr<const MCDisassembler> DisAsm(TheTarget->createMCDisassembler(*STI));
int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
OwningPtr<MCInstPrinter>
IP(TheTarget->createMCInstPrinter(AsmPrinterVariant, *AsmInfo, *InstrInfo,
*MRI, *STI));
if (!InstrAnalysis || !AsmInfo || !STI || !DisAsm || !IP) {
errs() << "error: couldn't initialize disassembler for target "
<< TripleName << '\n';
return;
}
outs() << '\n' << Filename << ":\n\n";
macho::Header Header = MachOOF->getHeader();
// FIXME: FoundFns isn't used anymore. Using symbols/LC_FUNCTION_STARTS to
// determine function locations will eventually go in MCObjectDisassembler.
// FIXME: Using the -cfg command line option, this code used to be able to
// annotate relocations with the referenced symbol's name, and if this was
// inside a __[cf]string section, the data it points to. This is now replaced
// by the upcoming MCSymbolizer, which needs the appropriate setup done above.
std::vector<SectionRef> Sections;
std::vector<SymbolRef> Symbols;
SmallVector<uint64_t, 8> FoundFns;
getSectionsAndSymbols(Header, MachOOF, Sections, Symbols, FoundFns);
// Make a copy of the unsorted symbol list. FIXME: duplication
std::vector<SymbolRef> UnsortedSymbols(Symbols);
// Sort the symbols by address, just in case they didn't come in that way.
std::sort(Symbols.begin(), Symbols.end(), SymbolSorter());
#ifndef NDEBUG
raw_ostream &DebugOut = DebugFlag ? dbgs() : nulls();
#else
raw_ostream &DebugOut = nulls();
#endif
OwningPtr<DIContext> diContext;
ObjectFile *DbgObj = MachOOF;
// Try to find debug info and set up the DIContext for it.
if (UseDbg) {
// 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;
}
DbgObj = ObjectFile::createMachOObjectFile(Buf.take());
}
// Setup the DIContext
diContext.reset(DIContext::getDWARFContext(DbgObj));
}
for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) {
bool SectIsText = false;
Sections[SectIdx].isText(SectIsText);
if (SectIsText == false)
continue;
StringRef SectName;
if (Sections[SectIdx].getName(SectName) ||
SectName != "__text")
continue; // Skip non-text sections
DataRefImpl DR = Sections[SectIdx].getRawDataRefImpl();
StringRef SegmentName = MachOOF->getSectionFinalSegmentName(DR);
if (SegmentName != "__TEXT")
continue;
StringRef Bytes;
Sections[SectIdx].getContents(Bytes);
StringRefMemoryObject memoryObject(Bytes);
bool symbolTableWorked = false;
// Parse relocations.
std::vector<std::pair<uint64_t, SymbolRef> > Relocs;
error_code ec;
for (relocation_iterator RI = Sections[SectIdx].begin_relocations(),
RE = Sections[SectIdx].end_relocations(); RI != RE; RI.increment(ec)) {
uint64_t RelocOffset, SectionAddress;
RI->getOffset(RelocOffset);
Sections[SectIdx].getAddress(SectionAddress);
RelocOffset -= SectionAddress;
SymbolRef RelocSym;
RI->getSymbol(RelocSym);
Relocs.push_back(std::make_pair(RelocOffset, RelocSym));
}
array_pod_sort(Relocs.begin(), Relocs.end());
// Disassemble symbol by symbol.
for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) {
StringRef SymName;
Symbols[SymIdx].getName(SymName);
SymbolRef::Type ST;
Symbols[SymIdx].getType(ST);
if (ST != SymbolRef::ST_Function)
continue;
// Make sure the symbol is defined in this section.
bool containsSym = false;
Sections[SectIdx].containsSymbol(Symbols[SymIdx], containsSym);
if (!containsSym)
continue;
// Start at the address of the symbol relative to the section's address.
uint64_t SectionAddress = 0;
uint64_t Start = 0;
Sections[SectIdx].getAddress(SectionAddress);
Symbols[SymIdx].getAddress(Start);
Start -= SectionAddress;
// Stop disassembling either at the beginning of the next symbol or at
// the end of the section.
bool containsNextSym = false;
uint64_t NextSym = 0;
uint64_t NextSymIdx = SymIdx+1;
while (Symbols.size() > NextSymIdx) {
SymbolRef::Type NextSymType;
Symbols[NextSymIdx].getType(NextSymType);
if (NextSymType == SymbolRef::ST_Function) {
Sections[SectIdx].containsSymbol(Symbols[NextSymIdx],
containsNextSym);
Symbols[NextSymIdx].getAddress(NextSym);
NextSym -= SectionAddress;
break;
}
++NextSymIdx;
}
uint64_t SectSize;
Sections[SectIdx].getSize(SectSize);
uint64_t End = containsNextSym ? NextSym : SectSize;
uint64_t Size;
symbolTableWorked = true;
outs() << SymName << ":\n";
DILineInfo lastLine;
for (uint64_t Index = Start; Index < End; Index += Size) {
MCInst Inst;
if (DisAsm->getInstruction(Inst, Size, memoryObject, Index,
DebugOut, nulls())) {
uint64_t SectAddress = 0;
Sections[SectIdx].getAddress(SectAddress);
outs() << format("%8" PRIx64 ":\t", SectAddress + Index);
DumpBytes(StringRef(Bytes.data() + Index, Size));
IP->printInst(&Inst, outs(), "");
// Print debug info.
if (diContext) {
DILineInfo dli =
diContext->getLineInfoForAddress(SectAddress + 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";
if (Size == 0)
Size = 1; // skip illegible bytes
}
}
}
if (!symbolTableWorked) {
// Reading the symbol table didn't work, disassemble the whole section.
uint64_t SectAddress;
Sections[SectIdx].getAddress(SectAddress);
uint64_t SectSize;
Sections[SectIdx].getSize(SectSize);
uint64_t InstSize;
for (uint64_t Index = 0; Index < SectSize; Index += InstSize) {
MCInst Inst;
if (DisAsm->getInstruction(Inst, InstSize, memoryObject, Index,
DebugOut, nulls())) {
outs() << format("%8" PRIx64 ":\t", SectAddress + Index);
DumpBytes(StringRef(Bytes.data() + Index, InstSize));
IP->printInst(&Inst, outs(), "");
outs() << "\n";
} else {
errs() << "llvm-objdump: warning: invalid instruction encoding\n";
if (InstSize == 0)
InstSize = 1; // skip illegible bytes
}
}
}
}
}