blob: edecaa1748642ba9e7cd2a074a78138b3acb14ef [file] [log] [blame]
//===-- DisassemblerLLVM.cpp ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "DisassemblerLLVM.h"
#include "llvm-c/EnhancedDisassembly.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
#include <assert.h>
using namespace lldb;
using namespace lldb_private;
static
int DataExtractorByteReader(uint8_t *byte, uint64_t address, void *arg)
{
DataExtractor &extractor = *((DataExtractor *)arg);
if (extractor.ValidOffset(address))
{
*byte = *(extractor.GetDataStart() + address);
return 0;
}
else
{
return -1;
}
}
namespace {
struct RegisterReaderArg {
const lldb::addr_t instructionPointer;
const EDDisassemblerRef disassembler;
RegisterReaderArg(lldb::addr_t ip,
EDDisassemblerRef dis) :
instructionPointer(ip),
disassembler(dis)
{
}
};
}
static int IPRegisterReader(uint64_t *value, unsigned regID, void* arg)
{
uint64_t instructionPointer = ((RegisterReaderArg*)arg)->instructionPointer;
EDDisassemblerRef disassembler = ((RegisterReaderArg*)arg)->disassembler;
if(EDRegisterIsProgramCounter(disassembler, regID)) {
*value = instructionPointer;
return 0;
}
return -1;
}
DisassemblerLLVM::InstructionLLVM::InstructionLLVM (EDDisassemblerRef disassembler, const Address &addr) :
Instruction (addr),
m_disassembler (disassembler)
{
}
DisassemblerLLVM::InstructionLLVM::~InstructionLLVM()
{
}
static void
PadString(Stream *s, const std::string &str, size_t width)
{
int diff = width - str.length();
if (diff > 0)
s->Printf("%s%*.*s", str.c_str(), diff, diff, "");
else
s->Printf("%s ", str.c_str());
}
void
DisassemblerLLVM::InstructionLLVM::Dump
(
Stream *s,
bool show_address,
const DataExtractor *bytes,
uint32_t bytes_offset,
const lldb_private::ExecutionContext* exe_ctx,
bool raw
)
{
const size_t opcodeColumnWidth = 7;
const size_t operandColumnWidth = 25;
ExecutionContextScope *exe_scope = NULL;
if (exe_ctx)
exe_scope = exe_ctx->GetBestExecutionContextScope();
// If we have an address, print it out
if (GetAddress().IsValid() && show_address)
{
if (GetAddress().Dump (s,
exe_scope,
Address::DumpStyleLoadAddress,
Address::DumpStyleModuleWithFileAddress,
0))
s->PutCString(": ");
}
// If we are supposed to show bytes, "bytes" will be non-NULL.
if (bytes)
{
uint32_t bytes_dumped = bytes->Dump(s, bytes_offset, eFormatBytes, 1, EDInstByteSize(m_inst), UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0) - bytes_offset;
// Allow for 15 bytes of opcodes since this is the max for x86_64.
// TOOD: We need to taylor this better for different architectures. For
// ARM we would want to show 16 bit opcodes for Thumb as properly byte
// swapped uint16_t values, or 32 bit values swapped values for ARM.
const uint32_t default_num_opcode_bytes = 15;
if (bytes_dumped * 3 < (default_num_opcode_bytes*3))
{
uint32_t indent_level = (default_num_opcode_bytes*3) - (bytes_dumped * 3) + 1;
s->Printf("%*.*s", indent_level, indent_level, "");
}
}
int numTokens = EDNumTokens(m_inst);
int currentOpIndex = -1;
std::auto_ptr<RegisterReaderArg> rra;
if (!raw)
{
addr_t base_addr = LLDB_INVALID_ADDRESS;
if (exe_ctx && exe_ctx->target && !exe_ctx->target->GetSectionLoadList().IsEmpty())
base_addr = GetAddress().GetLoadAddress (exe_ctx->target);
if (base_addr == LLDB_INVALID_ADDRESS)
base_addr = GetAddress().GetFileAddress ();
rra.reset(new RegisterReaderArg(base_addr + EDInstByteSize(m_inst), m_disassembler));
}
bool printTokenized = false;
if (numTokens != -1)
{
printTokenized = true;
// Handle the opcode column.
StreamString opcode;
int tokenIndex = 0;
EDTokenRef token;
const char *tokenStr;
if (EDGetToken(&token, m_inst, tokenIndex))
printTokenized = false;
if (!printTokenized || !EDTokenIsOpcode(token))
printTokenized = false;
if (!printTokenized || EDGetTokenString(&tokenStr, token))
printTokenized = false;
// Put the token string into our opcode string
opcode.PutCString(tokenStr);
// If anything follows, it probably starts with some whitespace. Skip it.
tokenIndex++;
if (printTokenized && tokenIndex < numTokens)
{
if(!printTokenized || EDGetToken(&token, m_inst, tokenIndex))
printTokenized = false;
if(!printTokenized || !EDTokenIsWhitespace(token))
printTokenized = false;
}
tokenIndex++;
// Handle the operands and the comment.
StreamString operands;
StreamString comment;
if (printTokenized)
{
bool show_token;
for (; tokenIndex < numTokens; ++tokenIndex)
{
if (EDGetToken(&token, m_inst, tokenIndex))
return;
if (raw)
{
show_token = true;
}
else
{
int operandIndex = EDOperandIndexForToken(token);
if (operandIndex >= 0)
{
if (operandIndex != currentOpIndex)
{
show_token = true;
currentOpIndex = operandIndex;
EDOperandRef operand;
if (!EDGetOperand(&operand, m_inst, currentOpIndex))
{
if (EDOperandIsMemory(operand))
{
uint64_t operand_value;
if (!EDEvaluateOperand(&operand_value, operand, IPRegisterReader, rra.get()))
{
if (EDInstIsBranch(m_inst))
{
operands.Printf("0x%llx ", operand_value);
show_token = false;
}
else
{
// Put the address value into the comment
comment.Printf("0x%llx ", operand_value);
}
lldb_private::Address so_addr;
if (exe_ctx && exe_ctx->target && !exe_ctx->target->GetSectionLoadList().IsEmpty())
{
if (exe_ctx->target->GetSectionLoadList().ResolveLoadAddress (operand_value, so_addr))
so_addr.Dump(&comment, exe_scope, Address::DumpStyleResolvedDescriptionNoModule, Address::DumpStyleSectionNameOffset);
}
else
{
Module *module = GetAddress().GetModule();
if (module)
{
if (module->ResolveFileAddress (operand_value, so_addr))
so_addr.Dump(&comment, exe_scope, Address::DumpStyleResolvedDescriptionNoModule, Address::DumpStyleSectionNameOffset);
}
}
} // EDEvaluateOperand
} // EDOperandIsMemory
} // EDGetOperand
} // operandIndex != currentOpIndex
} // operandIndex >= 0
} // else(raw)
if (show_token)
{
if(EDGetTokenString(&tokenStr, token))
{
printTokenized = false;
break;
}
operands.PutCString(tokenStr);
}
} // for (tokenIndex)
if (printTokenized)
{
if (operands.GetString().empty())
{
s->PutCString(opcode.GetString().c_str());
}
else
{
PadString(s, opcode.GetString(), opcodeColumnWidth);
if (comment.GetString().empty())
{
s->PutCString(operands.GetString().c_str());
}
else
{
PadString(s, operands.GetString(), operandColumnWidth);
s->PutCString("; ");
s->PutCString(comment.GetString().c_str());
} // else (comment.GetString().empty())
} // else (operands.GetString().empty())
} // printTokenized
} // for (tokenIndex)
} // numTokens != -1
if (!printTokenized)
{
const char *str;
if (EDGetInstString(&str, m_inst))
return;
else
s->PutCString(str);
}
}
bool
DisassemblerLLVM::InstructionLLVM::DoesBranch() const
{
return EDInstIsBranch(m_inst);
}
size_t
DisassemblerLLVM::InstructionLLVM::GetByteSize() const
{
return EDInstByteSize(m_inst);
}
size_t
DisassemblerLLVM::InstructionLLVM::Extract(const DataExtractor &data, uint32_t data_offset)
{
if (EDCreateInsts(&m_inst, 1, m_disassembler, DataExtractorByteReader, data_offset, (void*)(&data)))
return EDInstByteSize(m_inst);
else
return 0;
}
static inline EDAssemblySyntax_t
SyntaxForArchSpec (const ArchSpec &arch)
{
switch (arch.GetMachine ())
{
case llvm::Triple::x86:
case llvm::Triple::x86_64:
return kEDAssemblySyntaxX86ATT;
default:
break;
}
return (EDAssemblySyntax_t)0; // default
}
Disassembler *
DisassemblerLLVM::CreateInstance(const ArchSpec &arch)
{
std::auto_ptr<DisassemblerLLVM> disasm_ap (new DisassemblerLLVM(arch));
if (disasm_ap->IsValid())
return disasm_ap.release();
return NULL;
}
DisassemblerLLVM::DisassemblerLLVM(const ArchSpec &arch) :
Disassembler (arch),
m_disassembler (NULL)
{
const std::string &arch_triple = arch.GetTriple().str();
if (!arch_triple.empty())
{
if (EDGetDisassembler(&m_disassembler, arch_triple.c_str(), SyntaxForArchSpec (arch)))
m_disassembler = NULL;
}
}
DisassemblerLLVM::~DisassemblerLLVM()
{
}
size_t
DisassemblerLLVM::DecodeInstructions
(
const Address &base_addr,
const DataExtractor& data,
uint32_t data_offset,
uint32_t num_instructions
)
{
if (m_disassembler == NULL)
return 0;
size_t total_inst_byte_size = 0;
m_instruction_list.Clear();
while (data.ValidOffset(data_offset) && num_instructions)
{
Address inst_addr (base_addr);
inst_addr.Slide(data_offset);
InstructionSP inst_sp (new InstructionLLVM(m_disassembler, inst_addr));
size_t inst_byte_size = inst_sp->Extract (data, data_offset);
if (inst_byte_size == 0)
break;
m_instruction_list.Append (inst_sp);
total_inst_byte_size += inst_byte_size;
data_offset += inst_byte_size;
num_instructions--;
}
return total_inst_byte_size;
}
void
DisassemblerLLVM::Initialize()
{
PluginManager::RegisterPlugin (GetPluginNameStatic(),
GetPluginDescriptionStatic(),
CreateInstance);
}
void
DisassemblerLLVM::Terminate()
{
PluginManager::UnregisterPlugin (CreateInstance);
}
const char *
DisassemblerLLVM::GetPluginNameStatic()
{
return "disassembler.llvm";
}
const char *
DisassemblerLLVM::GetPluginDescriptionStatic()
{
return "Disassembler that uses LLVM opcode tables to disassemble i386 and x86_64.";
}
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
const char *
DisassemblerLLVM::GetPluginName()
{
return "DisassemblerLLVM";
}
const char *
DisassemblerLLVM::GetShortPluginName()
{
return GetPluginNameStatic();
}
uint32_t
DisassemblerLLVM::GetPluginVersion()
{
return 1;
}
void
DisassemblerLLVM::GetPluginCommandHelp (const char *command, Stream *strm)
{
}
Error
DisassemblerLLVM::ExecutePluginCommand (Args &command, Stream *strm)
{
Error error;
error.SetErrorString("No plug-in command are currently supported.");
return error;
}
Log *
DisassemblerLLVM::EnablePluginLogging (Stream *strm, Args &command)
{
return NULL;
}