diff options
Diffstat (limited to 'lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp')
-rw-r--r-- | lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp new file mode 100644 index 00000000000..ed9b4e7c655 --- /dev/null +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp @@ -0,0 +1,580 @@ +//===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DisassemblerLLVMC.h" + +#include "llvm-c/Disassembler.h" +#include "llvm/Support/TargetSelect.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.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 "lldb/Target/StackFrame.h" + +#include <regex.h> + +using namespace lldb; +using namespace lldb_private; + +class InstructionLLVMC : public lldb_private::Instruction +{ +public: + InstructionLLVMC (DisassemblerLLVMC &disasm, + const lldb_private::Address &address, + lldb_private::AddressClass addr_class) : + Instruction(address, addr_class), + m_disasm(disasm), + m_is_valid(false), + m_no_comments(true), + m_comment_stream() + { + } + + virtual + ~InstructionLLVMC () + { + } + + static void + PadToWidth (lldb_private::StreamString &ss, + int new_width) + { + int old_width = ss.GetSize(); + + if (old_width < new_width) + { + ss.Printf("%*s", new_width - old_width, ""); + } + } + + virtual void + Dump (lldb_private::Stream *s, + uint32_t max_opcode_byte_size, + bool show_address, + bool show_bytes, + const lldb_private::ExecutionContext* exe_ctx, + bool raw) + { + const size_t opcode_column_width = 7; + const size_t operand_column_width = 25; + + StreamString ss; + + ExecutionContextScope *exe_scope = NULL; + + if ((!raw) && exe_ctx) + { + exe_scope = exe_ctx->GetBestExecutionContextScope(); + + DataExtractor extractor(m_raw_bytes.data(), + m_raw_bytes.size(), + m_disasm.GetArchitecture().GetByteOrder(), + m_disasm.GetArchitecture().GetAddressByteSize()); + + Parse <true> (m_address, + m_address_class, + extractor, + 0, + exe_scope); + } + + if (show_address) + { + m_address.Dump(&ss, + exe_scope, + Address::DumpStyleLoadAddress, + Address::DumpStyleModuleWithFileAddress, + 0); + + ss.PutCString(": "); + } + + if (show_bytes) + { + if (m_opcode.GetType() == Opcode::eTypeBytes) + { + // x86_64 and i386 are the only ones that use bytes right now so + // pad out the byte dump to be able to always show 15 bytes (3 chars each) + // plus a space + if (max_opcode_byte_size > 0) + m_opcode.Dump (&ss, max_opcode_byte_size * 3 + 1); + else + m_opcode.Dump (&ss, 15 * 3 + 1); + } + else + { + // Else, we have ARM which can show up to a uint32_t 0x00000000 (10 spaces) + // plus two for padding... + if (max_opcode_byte_size > 0) + m_opcode.Dump (&ss, max_opcode_byte_size * 3 + 1); + else + m_opcode.Dump (&ss, 12); + } + } + + int size_before_inst = ss.GetSize(); + + ss.PutCString(m_opcode_name.c_str()); + + PadToWidth(ss, size_before_inst + opcode_column_width); + + ss.PutCString(m_mnemocics.c_str()); + + PadToWidth(ss, size_before_inst + opcode_column_width + operand_column_width); + + if (!m_comment.empty()) + { + ss.PutCString(" ; "); + ss.PutCString(m_comment.c_str()); + } + + ss.Flush(); + + s->PutCString(ss.GetData()); + } + + virtual bool + DoesBranch () const + { + return false; + } + + virtual size_t + Decode (const lldb_private::Disassembler &disassembler, + const lldb_private::DataExtractor &data, + uint32_t data_offset) + { + Parse <false> (m_address, + m_address_class, + data, + data_offset, + NULL); + + return m_opcode.GetByteSize(); + } + + void + AddReferencedAddress (std::string &description) + { + if (m_no_comments) + m_comment_stream.PutCString(", "); + else + m_no_comments = true; + + m_comment_stream.PutCString(description.c_str()); + } + + virtual void + CalculateMnemonicOperandsAndComment (lldb_private::ExecutionContextScope *exe_scope) + { + DataExtractor extractor(m_raw_bytes.data(), + m_raw_bytes.size(), + m_disasm.GetArchitecture().GetByteOrder(), + m_disasm.GetArchitecture().GetAddressByteSize()); + + Parse <true> (m_address, + m_address_class, + extractor, + 0, + exe_scope); + } + + bool + IsValid () + { + return m_is_valid; + } + + size_t + GetByteSize () + { + return m_opcode.GetByteSize(); + } +protected: + void PopulateOpcode (const DataExtractor &extractor, + uint32_t offset, + size_t inst_size) + { + llvm::Triple::ArchType arch = m_disasm.GetArchitecture().GetMachine(); + + switch (arch) + { + default: + case llvm::Triple::x86: + case llvm::Triple::x86_64: + m_opcode.SetOpcodeBytes(extractor.PeekData(offset, inst_size), inst_size); + break; + case llvm::Triple::arm: + case llvm::Triple::thumb: + switch (inst_size) + { + case 2: + { + m_opcode.SetOpcode16 (extractor.GetU16 (&offset)); + break; + } + break; + case 4: + { + if (arch == llvm::Triple::arm && + m_address_class == eAddressClassCodeAlternateISA) + { + // If it is a 32-bit THUMB instruction, we need to swap the upper & lower halves. + uint32_t orig_bytes = extractor.GetU32 (&offset); + uint16_t upper_bits = (orig_bytes >> 16) & ((1u << 16) - 1); + uint16_t lower_bits = orig_bytes & ((1u << 16) - 1); + uint32_t swapped = (lower_bits << 16) | upper_bits; + m_opcode.SetOpcode32 (swapped); + } + else + { + m_opcode.SetOpcode32 (extractor.GetU32 (&offset)); + } + } + break; + default: + assert (!"Invalid ARM opcode size"); + break; + } + break; + } + } + + template <bool Reparse> bool Parse (const lldb_private::Address &address, + lldb_private::AddressClass addr_class, + const DataExtractor &extractor, + uint32_t data_offset, + lldb_private::ExecutionContextScope *exe_scope) + { + std::vector<char> out_string(256); + + const uint8_t *data_start = extractor.GetDataStart(); + + m_disasm.Lock(this, exe_scope); + + ::LLVMDisasmContextRef disasm_context; + + if (addr_class == eAddressClassCodeAlternateISA) + disasm_context = m_disasm.m_alternate_disasm_context; + else + disasm_context = m_disasm.m_disasm_context; + + m_comment_stream.Clear(); + + size_t inst_size = ::LLVMDisasmInstruction(disasm_context, + const_cast<uint8_t*>(data_start) + data_offset, + extractor.GetByteSize() - data_offset, + address.GetFileAddress(), + out_string.data(), + out_string.size()); + + m_comment_stream.Flush(); + m_no_comments = false; + + m_comment.swap(m_comment_stream.GetString()); + + m_disasm.Unlock(); + + if (Reparse) + { + if (inst_size != m_raw_bytes.size()) + return false; + } + else + { + if (!inst_size) + return false; + + PopulateOpcode(extractor, data_offset, inst_size); + + m_raw_bytes.resize(inst_size); + memcpy(m_raw_bytes.data(), data_start + data_offset, inst_size); + + if (!s_regex_compiled) + { + ::regcomp(&s_regex, "[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?", REG_EXTENDED); + s_regex_compiled = true; + } + + ::regmatch_t matches[3]; + + const char *out_data = out_string.data(); + + if (!::regexec(&s_regex, out_data, sizeof(matches) / sizeof(::regmatch_t), matches, 0)) + { + if (matches[1].rm_so != -1) + m_opcode_name.assign(out_data + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (matches[2].rm_so != -1) + m_mnemocics.assign(out_data + matches[2].rm_so, matches[2].rm_eo - matches[2].rm_so); + } + + m_is_valid = true; + } + + return true; + } + + bool m_is_valid; + DisassemblerLLVMC &m_disasm; + std::vector<uint8_t> m_raw_bytes; + + bool m_no_comments; + StreamString m_comment_stream; + + static bool s_regex_compiled; + static ::regex_t s_regex; +}; + +bool InstructionLLVMC::s_regex_compiled = false; +::regex_t InstructionLLVMC::s_regex; + +Disassembler * +DisassemblerLLVMC::CreateInstance (const ArchSpec &arch) +{ + std::auto_ptr<DisassemblerLLVMC> disasm_ap (new DisassemblerLLVMC(arch)); + + if (disasm_ap.get() && disasm_ap->IsValid()) + return disasm_ap.release(); + + return NULL; +} + +DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch) : + Disassembler(arch), + m_disasm_context(NULL), + m_alternate_disasm_context(NULL) +{ + m_disasm_context = ::LLVMCreateDisasm(arch.GetTriple().getTriple().c_str(), + (void*)this, + /*TagType=*/1, + DisassemblerLLVMC::OpInfoCallback, + DisassemblerLLVMC::SymbolLookupCallback); + + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + m_alternate_disasm_context = ::LLVMCreateDisasm("thumbv7-apple-darwin", + (void*)this, + /*TagType=*/1, + DisassemblerLLVMC::OpInfoCallback, + DisassemblerLLVMC::SymbolLookupCallback); + } +} + +DisassemblerLLVMC::~DisassemblerLLVMC() +{ +} + +size_t +DisassemblerLLVMC::DecodeInstructions (const Address &base_addr, + const DataExtractor& data, + uint32_t data_offset, + uint32_t num_instructions, + bool append) +{ + if (!append) + m_instruction_list.Clear(); + + if (!IsValid()) + return 0; + + uint32_t data_cursor = data_offset; + size_t data_byte_size = data.GetByteSize(); + uint32_t instructions_parsed = 0; + + uint64_t instruction_pointer = base_addr.GetFileAddress(); + + std::vector<char> out_string(256); + + while (data_offset < data_byte_size && instructions_parsed < num_instructions) + { + Address instr_address = base_addr; + instr_address.Slide(data_cursor); + + AddressClass address_class = eAddressClassUnknown; + + if (m_alternate_disasm_context) + address_class = instr_address.GetAddressClass (); + + InstructionSP inst_sp(new InstructionLLVMC(*this, + instr_address, + address_class)); + + if (!inst_sp) + return data_cursor - data_offset; + + uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor); + + if (!inst_size) + return data_cursor - data_offset; + + m_instruction_list.Append(inst_sp); + + instruction_pointer += inst_size; + data_cursor += inst_size; + instructions_parsed++; + } + + return data_cursor - data_offset; +} + +void +DisassemblerLLVMC::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllDisassemblers(); +} + +void +DisassemblerLLVMC::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +DisassemblerLLVMC::GetPluginNameStatic() +{ + return "llvm"; +} + +const char * +DisassemblerLLVMC::GetPluginDescriptionStatic() +{ + return "Disassembler that uses LLVM opcode tables to disassemble i386, x86_64 and ARM."; +} + +int DisassemblerLLVMC::OpInfoCallback (void *DisInfo, + uint64_t PC, + uint64_t Offset, + uint64_t Size, + int TagType, + void *TagBug) +{ + return static_cast<DisassemblerLLVMC*>(DisInfo)->OpInfo(PC, + Offset, + Size, + TagType, + TagBug); +} + +const char *DisassemblerLLVMC::SymbolLookupCallback(void *DisInfo, + uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName) +{ + return static_cast<DisassemblerLLVMC*>(DisInfo)->SymbolLookup(ReferenceValue, + ReferenceType, + ReferencePC, + ReferenceName); +} + +int DisassemblerLLVMC::OpInfo (uint64_t PC, + uint64_t Offset, + uint64_t Size, + int TagType, + void *TagBug) +{ + switch (TagType) + { + default: + break; + case 1: + bzero (TagBug, sizeof(::LLVMOpInfo1)); + break; + } + return 0; +} + +const char *DisassemblerLLVMC::SymbolLookup (uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName) +{ + const char *result_name = NULL; + uint64_t result_reference_type = LLVMDisassembler_ReferenceType_InOut_None; + const char *result_referred_name = NULL; + + if (m_exe_scope && m_inst) + { + Address reference_address; + + Target *target = m_exe_scope->CalculateTarget(); + + if (target) + { + if (!target->GetSectionLoadList().IsEmpty()) + target->GetSectionLoadList().ResolveLoadAddress(ReferenceValue, reference_address); + else + target->GetImages().ResolveFileAddress(ReferenceValue, reference_address); + + if (reference_address.IsValid()) + { + SymbolContext reference_sc; + + target->GetImages().ResolveSymbolContextForAddress(reference_address, + eSymbolContextFunction | eSymbolContextSymbol, + reference_sc); + + StreamString ss; + + const bool show_fullpaths = false; + const bool show_module = true; + const bool show_inlined_frames = false; + + reference_sc.DumpStopContext(&ss, + m_exe_scope, + reference_address, + show_fullpaths, + show_module, + show_inlined_frames); + + m_inst->AddReferencedAddress(ss.GetString()); + } + } + } + + *ReferenceType = result_reference_type; + *ReferenceName = result_referred_name; + + return result_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +DisassemblerLLVMC::GetPluginName() +{ + return "DisassemblerLLVMC"; +} + +const char * +DisassemblerLLVMC::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DisassemblerLLVMC::GetPluginVersion() +{ + return 1; +} + |