| //===-- Disassembler.cpp ----------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Core/Disassembler.h" |
| |
| // C Includes |
| // C++ Includes |
| #include <cstdio> |
| #include <cstring> |
| |
| // Other libraries and framework includes |
| // Project includes |
| #include "lldb/Core/DataBufferHeap.h" |
| #include "lldb/Core/DataExtractor.h" |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/EmulateInstruction.h" |
| #include "lldb/Core/Error.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/RegularExpression.h" |
| #include "lldb/Core/Timer.h" |
| #include "lldb/Host/FileSystem.h" |
| #include "lldb/Interpreter/OptionValue.h" |
| #include "lldb/Interpreter/OptionValueArray.h" |
| #include "lldb/Interpreter/OptionValueDictionary.h" |
| #include "lldb/Interpreter/OptionValueString.h" |
| #include "lldb/Interpreter/OptionValueUInt64.h" |
| #include "lldb/Symbol/Function.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Target/ExecutionContext.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/SectionLoadList.h" |
| #include "lldb/Target/StackFrame.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/lldb-private.h" |
| |
| #define DEFAULT_DISASM_BYTE_SIZE 32 |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch, |
| const char *flavor, |
| const char *plugin_name) { |
| Timer scoped_timer(LLVM_PRETTY_FUNCTION, |
| "Disassembler::FindPlugin (arch = %s, plugin_name = %s)", |
| arch.GetArchitectureName(), plugin_name); |
| |
| DisassemblerCreateInstance create_callback = nullptr; |
| |
| if (plugin_name) { |
| ConstString const_plugin_name(plugin_name); |
| create_callback = PluginManager::GetDisassemblerCreateCallbackForPluginName( |
| const_plugin_name); |
| if (create_callback) { |
| DisassemblerSP disassembler_sp(create_callback(arch, flavor)); |
| |
| if (disassembler_sp) |
| return disassembler_sp; |
| } |
| } else { |
| for (uint32_t idx = 0; |
| (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex( |
| idx)) != nullptr; |
| ++idx) { |
| DisassemblerSP disassembler_sp(create_callback(arch, flavor)); |
| |
| if (disassembler_sp) |
| return disassembler_sp; |
| } |
| } |
| return DisassemblerSP(); |
| } |
| |
| DisassemblerSP Disassembler::FindPluginForTarget(const TargetSP target_sp, |
| const ArchSpec &arch, |
| const char *flavor, |
| const char *plugin_name) { |
| if (target_sp && flavor == nullptr) { |
| // FIXME - we don't have the mechanism in place to do per-architecture |
| // settings. But since we know that for now |
| // we only support flavors on x86 & x86_64, |
| if (arch.GetTriple().getArch() == llvm::Triple::x86 || |
| arch.GetTriple().getArch() == llvm::Triple::x86_64) |
| flavor = target_sp->GetDisassemblyFlavor(); |
| } |
| return FindPlugin(arch, flavor, plugin_name); |
| } |
| |
| static void ResolveAddress(const ExecutionContext &exe_ctx, const Address &addr, |
| Address &resolved_addr) { |
| if (!addr.IsSectionOffset()) { |
| // If we weren't passed in a section offset address range, |
| // try and resolve it to something |
| Target *target = exe_ctx.GetTargetPtr(); |
| if (target) { |
| if (target->GetSectionLoadList().IsEmpty()) { |
| target->GetImages().ResolveFileAddress(addr.GetOffset(), resolved_addr); |
| } else { |
| target->GetSectionLoadList().ResolveLoadAddress(addr.GetOffset(), |
| resolved_addr); |
| } |
| // We weren't able to resolve the address, just treat it as a |
| // raw address |
| if (resolved_addr.IsValid()) |
| return; |
| } |
| } |
| resolved_addr = addr; |
| } |
| |
| size_t Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, |
| const char *plugin_name, const char *flavor, |
| const ExecutionContext &exe_ctx, |
| SymbolContextList &sc_list, |
| uint32_t num_instructions, |
| uint32_t num_mixed_context_lines, |
| uint32_t options, Stream &strm) { |
| size_t success_count = 0; |
| const size_t count = sc_list.GetSize(); |
| SymbolContext sc; |
| AddressRange range; |
| const uint32_t scope = |
| eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol; |
| const bool use_inline_block_range = true; |
| for (size_t i = 0; i < count; ++i) { |
| if (!sc_list.GetContextAtIndex(i, sc)) |
| break; |
| for (uint32_t range_idx = 0; |
| sc.GetAddressRange(scope, range_idx, use_inline_block_range, range); |
| ++range_idx) { |
| if (Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range, |
| num_instructions, num_mixed_context_lines, options, |
| strm)) { |
| ++success_count; |
| strm.EOL(); |
| } |
| } |
| } |
| return success_count; |
| } |
| |
| bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, |
| const char *plugin_name, const char *flavor, |
| const ExecutionContext &exe_ctx, |
| const ConstString &name, Module *module, |
| uint32_t num_instructions, |
| uint32_t num_mixed_context_lines, |
| uint32_t options, Stream &strm) { |
| SymbolContextList sc_list; |
| if (name) { |
| const bool include_symbols = true; |
| const bool include_inlines = true; |
| if (module) { |
| module->FindFunctions(name, nullptr, eFunctionNameTypeAuto, |
| include_symbols, include_inlines, true, sc_list); |
| } else if (exe_ctx.GetTargetPtr()) { |
| exe_ctx.GetTargetPtr()->GetImages().FindFunctions( |
| name, eFunctionNameTypeAuto, include_symbols, include_inlines, false, |
| sc_list); |
| } |
| } |
| |
| if (sc_list.GetSize()) { |
| return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, sc_list, |
| num_instructions, num_mixed_context_lines, options, |
| strm); |
| } |
| return false; |
| } |
| |
| lldb::DisassemblerSP Disassembler::DisassembleRange( |
| const ArchSpec &arch, const char *plugin_name, const char *flavor, |
| const ExecutionContext &exe_ctx, const AddressRange &range, |
| bool prefer_file_cache) { |
| lldb::DisassemblerSP disasm_sp; |
| if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid()) { |
| disasm_sp = Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, |
| flavor, plugin_name); |
| |
| if (disasm_sp) { |
| size_t bytes_disassembled = disasm_sp->ParseInstructions( |
| &exe_ctx, range, nullptr, prefer_file_cache); |
| if (bytes_disassembled == 0) |
| disasm_sp.reset(); |
| } |
| } |
| return disasm_sp; |
| } |
| |
| lldb::DisassemblerSP |
| Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name, |
| const char *flavor, const Address &start, |
| const void *src, size_t src_len, |
| uint32_t num_instructions, bool data_from_file) { |
| lldb::DisassemblerSP disasm_sp; |
| |
| if (src) { |
| disasm_sp = Disassembler::FindPlugin(arch, flavor, plugin_name); |
| |
| if (disasm_sp) { |
| DataExtractor data(src, src_len, arch.GetByteOrder(), |
| arch.GetAddressByteSize()); |
| |
| (void)disasm_sp->DecodeInstructions(start, data, 0, num_instructions, |
| false, data_from_file); |
| } |
| } |
| |
| return disasm_sp; |
| } |
| |
| bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, |
| const char *plugin_name, const char *flavor, |
| const ExecutionContext &exe_ctx, |
| const AddressRange &disasm_range, |
| uint32_t num_instructions, |
| uint32_t num_mixed_context_lines, |
| uint32_t options, Stream &strm) { |
| if (disasm_range.GetByteSize()) { |
| lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget( |
| exe_ctx.GetTargetSP(), arch, flavor, plugin_name)); |
| |
| if (disasm_sp) { |
| AddressRange range; |
| ResolveAddress(exe_ctx, disasm_range.GetBaseAddress(), |
| range.GetBaseAddress()); |
| range.SetByteSize(disasm_range.GetByteSize()); |
| const bool prefer_file_cache = false; |
| size_t bytes_disassembled = disasm_sp->ParseInstructions( |
| &exe_ctx, range, &strm, prefer_file_cache); |
| if (bytes_disassembled == 0) |
| return false; |
| |
| return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx, |
| num_instructions, num_mixed_context_lines, |
| options, strm); |
| } |
| } |
| return false; |
| } |
| |
| bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, |
| const char *plugin_name, const char *flavor, |
| const ExecutionContext &exe_ctx, |
| const Address &start_address, |
| uint32_t num_instructions, |
| uint32_t num_mixed_context_lines, |
| uint32_t options, Stream &strm) { |
| if (num_instructions > 0) { |
| lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget( |
| exe_ctx.GetTargetSP(), arch, flavor, plugin_name)); |
| if (disasm_sp) { |
| Address addr; |
| ResolveAddress(exe_ctx, start_address, addr); |
| const bool prefer_file_cache = false; |
| size_t bytes_disassembled = disasm_sp->ParseInstructions( |
| &exe_ctx, addr, num_instructions, prefer_file_cache); |
| if (bytes_disassembled == 0) |
| return false; |
| return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx, |
| num_instructions, num_mixed_context_lines, |
| options, strm); |
| } |
| } |
| return false; |
| } |
| |
| bool Disassembler::PrintInstructions(Disassembler *disasm_ptr, |
| Debugger &debugger, const ArchSpec &arch, |
| const ExecutionContext &exe_ctx, |
| uint32_t num_instructions, |
| uint32_t num_mixed_context_lines, |
| uint32_t options, Stream &strm) { |
| // We got some things disassembled... |
| size_t num_instructions_found = disasm_ptr->GetInstructionList().GetSize(); |
| |
| if (num_instructions > 0 && num_instructions < num_instructions_found) |
| num_instructions_found = num_instructions; |
| |
| const uint32_t max_opcode_byte_size = |
| disasm_ptr->GetInstructionList().GetMaxOpcocdeByteSize(); |
| uint32_t offset = 0; |
| SymbolContext sc; |
| SymbolContext prev_sc; |
| AddressRange sc_range; |
| const Address *pc_addr_ptr = nullptr; |
| StackFrame *frame = exe_ctx.GetFramePtr(); |
| |
| TargetSP target_sp(exe_ctx.GetTargetSP()); |
| SourceManager &source_manager = |
| target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager(); |
| |
| if (frame) { |
| pc_addr_ptr = &frame->GetFrameCodeAddress(); |
| } |
| const uint32_t scope = |
| eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol; |
| const bool use_inline_block_range = false; |
| |
| const FormatEntity::Entry *disassembly_format = nullptr; |
| FormatEntity::Entry format; |
| if (exe_ctx.HasTargetScope()) { |
| disassembly_format = |
| exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat(); |
| } else { |
| FormatEntity::Parse("${addr}: ", format); |
| disassembly_format = &format; |
| } |
| |
| // First pass: step through the list of instructions, |
| // find how long the initial addresses strings are, insert padding |
| // in the second pass so the opcodes all line up nicely. |
| size_t address_text_size = 0; |
| for (size_t i = 0; i < num_instructions_found; ++i) { |
| Instruction *inst = |
| disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get(); |
| if (inst) { |
| const Address &addr = inst->GetAddress(); |
| ModuleSP module_sp(addr.GetModule()); |
| if (module_sp) { |
| const uint32_t resolve_mask = |
| eSymbolContextFunction | eSymbolContextSymbol; |
| uint32_t resolved_mask = |
| module_sp->ResolveSymbolContextForAddress(addr, resolve_mask, sc); |
| if (resolved_mask) { |
| StreamString strmstr; |
| Debugger::FormatDisassemblerAddress(disassembly_format, &sc, nullptr, |
| &exe_ctx, &addr, strmstr); |
| size_t cur_line = strmstr.GetSizeOfLastLine(); |
| if (cur_line > address_text_size) |
| address_text_size = cur_line; |
| } |
| sc.Clear(false); |
| } |
| } |
| } |
| |
| for (size_t i = 0; i < num_instructions_found; ++i) { |
| Instruction *inst = |
| disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get(); |
| if (inst) { |
| const Address &addr = inst->GetAddress(); |
| const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr; |
| |
| prev_sc = sc; |
| |
| ModuleSP module_sp(addr.GetModule()); |
| if (module_sp) { |
| uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress( |
| addr, eSymbolContextEverything, sc); |
| if (resolved_mask) { |
| if (num_mixed_context_lines) { |
| if (!sc_range.ContainsFileAddress(addr)) { |
| sc.GetAddressRange(scope, 0, use_inline_block_range, sc_range); |
| |
| if (sc != prev_sc) { |
| if (offset != 0) |
| strm.EOL(); |
| |
| sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, |
| true, false, false, true); |
| strm.EOL(); |
| |
| if (sc.comp_unit && sc.line_entry.IsValid()) { |
| source_manager.DisplaySourceLinesWithLineNumbers( |
| sc.line_entry.file, sc.line_entry.line, |
| num_mixed_context_lines, num_mixed_context_lines, |
| ((inst_is_at_pc && (options & eOptionMarkPCSourceLine)) |
| ? "->" |
| : ""), |
| &strm); |
| } |
| } |
| } |
| } |
| } else { |
| sc.Clear(true); |
| } |
| } |
| |
| const bool show_bytes = (options & eOptionShowBytes) != 0; |
| inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, |
| &prev_sc, nullptr, address_text_size); |
| strm.EOL(); |
| } else { |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, |
| const char *plugin_name, const char *flavor, |
| const ExecutionContext &exe_ctx, |
| uint32_t num_instructions, |
| uint32_t num_mixed_context_lines, |
| uint32_t options, Stream &strm) { |
| AddressRange range; |
| StackFrame *frame = exe_ctx.GetFramePtr(); |
| if (frame) { |
| SymbolContext sc( |
| frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); |
| if (sc.function) { |
| range = sc.function->GetAddressRange(); |
| } else if (sc.symbol && sc.symbol->ValueIsAddress()) { |
| range.GetBaseAddress() = sc.symbol->GetAddressRef(); |
| range.SetByteSize(sc.symbol->GetByteSize()); |
| } else { |
| range.GetBaseAddress() = frame->GetFrameCodeAddress(); |
| } |
| |
| if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0) |
| range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); |
| } |
| |
| return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range, |
| num_instructions, num_mixed_context_lines, options, strm); |
| } |
| |
| Instruction::Instruction(const Address &address, AddressClass addr_class) |
| : m_address(address), m_address_class(addr_class), m_opcode(), |
| m_calculated_strings(false) {} |
| |
| Instruction::~Instruction() = default; |
| |
| AddressClass Instruction::GetAddressClass() { |
| if (m_address_class == eAddressClassInvalid) |
| m_address_class = m_address.GetAddressClass(); |
| return m_address_class; |
| } |
| |
| void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size, |
| bool show_address, bool show_bytes, |
| const ExecutionContext *exe_ctx, |
| const SymbolContext *sym_ctx, |
| const SymbolContext *prev_sym_ctx, |
| const FormatEntity::Entry *disassembly_addr_format, |
| size_t max_address_text_size) { |
| size_t opcode_column_width = 7; |
| const size_t operand_column_width = 25; |
| |
| CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx); |
| |
| StreamString ss; |
| |
| if (show_address) { |
| Debugger::FormatDisassemblerAddress(disassembly_addr_format, sym_ctx, |
| prev_sym_ctx, exe_ctx, &m_address, ss); |
| ss.FillLastLineToColumn(max_address_text_size, ' '); |
| } |
| |
| 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 or MIPS 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); |
| } |
| } |
| |
| const size_t opcode_pos = ss.GetSizeOfLastLine(); |
| |
| // The default opcode size of 7 characters is plenty for most architectures |
| // but some like arm can pull out the occasional vqrshrun.s16. We won't get |
| // consistent column spacing in these cases, unfortunately. |
| if (m_opcode_name.length() >= opcode_column_width) { |
| opcode_column_width = m_opcode_name.length() + 1; |
| } |
| |
| ss.PutCString(m_opcode_name.c_str()); |
| ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' '); |
| ss.PutCString(m_mnemonics.c_str()); |
| |
| if (!m_comment.empty()) { |
| ss.FillLastLineToColumn( |
| opcode_pos + opcode_column_width + operand_column_width, ' '); |
| ss.PutCString(" ; "); |
| ss.PutCString(m_comment.c_str()); |
| } |
| s->Write(ss.GetData(), ss.GetSize()); |
| } |
| |
| bool Instruction::DumpEmulation(const ArchSpec &arch) { |
| std::unique_ptr<EmulateInstruction> insn_emulator_ap( |
| EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr)); |
| if (insn_emulator_ap) { |
| insn_emulator_ap->SetInstruction(GetOpcode(), GetAddress(), nullptr); |
| return insn_emulator_ap->EvaluateInstruction(0); |
| } |
| |
| return false; |
| } |
| |
| bool Instruction::HasDelaySlot() { |
| // Default is false. |
| return false; |
| } |
| |
| OptionValueSP Instruction::ReadArray(FILE *in_file, Stream *out_stream, |
| OptionValue::Type data_type) { |
| bool done = false; |
| char buffer[1024]; |
| |
| OptionValueSP option_value_sp(new OptionValueArray(1u << data_type)); |
| |
| int idx = 0; |
| while (!done) { |
| if (!fgets(buffer, 1023, in_file)) { |
| out_stream->Printf( |
| "Instruction::ReadArray: Error reading file (fgets).\n"); |
| option_value_sp.reset(); |
| return option_value_sp; |
| } |
| |
| std::string line(buffer); |
| |
| size_t len = line.size(); |
| if (line[len - 1] == '\n') { |
| line[len - 1] = '\0'; |
| line.resize(len - 1); |
| } |
| |
| if ((line.size() == 1) && line[0] == ']') { |
| done = true; |
| line.clear(); |
| } |
| |
| if (!line.empty()) { |
| std::string value; |
| static RegularExpression g_reg_exp("^[ \t]*([^ \t]+)[ \t]*$"); |
| RegularExpression::Match regex_match(1); |
| bool reg_exp_success = g_reg_exp.Execute(line.c_str(), ®ex_match); |
| if (reg_exp_success) |
| regex_match.GetMatchAtIndex(line.c_str(), 1, value); |
| else |
| value = line; |
| |
| OptionValueSP data_value_sp; |
| switch (data_type) { |
| case OptionValue::eTypeUInt64: |
| data_value_sp.reset(new OptionValueUInt64(0, 0)); |
| data_value_sp->SetValueFromString(value); |
| break; |
| // Other types can be added later as needed. |
| default: |
| data_value_sp.reset(new OptionValueString(value.c_str(), "")); |
| break; |
| } |
| |
| option_value_sp->GetAsArray()->InsertValue(idx, data_value_sp); |
| ++idx; |
| } |
| } |
| |
| return option_value_sp; |
| } |
| |
| OptionValueSP Instruction::ReadDictionary(FILE *in_file, Stream *out_stream) { |
| bool done = false; |
| char buffer[1024]; |
| |
| OptionValueSP option_value_sp(new OptionValueDictionary()); |
| static ConstString encoding_key("data_encoding"); |
| OptionValue::Type data_type = OptionValue::eTypeInvalid; |
| |
| while (!done) { |
| // Read the next line in the file |
| if (!fgets(buffer, 1023, in_file)) { |
| out_stream->Printf( |
| "Instruction::ReadDictionary: Error reading file (fgets).\n"); |
| option_value_sp.reset(); |
| return option_value_sp; |
| } |
| |
| // Check to see if the line contains the end-of-dictionary marker ("}") |
| std::string line(buffer); |
| |
| size_t len = line.size(); |
| if (line[len - 1] == '\n') { |
| line[len - 1] = '\0'; |
| line.resize(len - 1); |
| } |
| |
| if ((line.size() == 1) && (line[0] == '}')) { |
| done = true; |
| line.clear(); |
| } |
| |
| // Try to find a key-value pair in the current line and add it to the |
| // dictionary. |
| if (!line.empty()) { |
| static RegularExpression g_reg_exp( |
| "^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$"); |
| RegularExpression::Match regex_match(2); |
| |
| bool reg_exp_success = g_reg_exp.Execute(line.c_str(), ®ex_match); |
| std::string key; |
| std::string value; |
| if (reg_exp_success) { |
| regex_match.GetMatchAtIndex(line.c_str(), 1, key); |
| regex_match.GetMatchAtIndex(line.c_str(), 2, value); |
| } else { |
| out_stream->Printf("Instruction::ReadDictionary: Failure executing " |
| "regular expression.\n"); |
| option_value_sp.reset(); |
| return option_value_sp; |
| } |
| |
| ConstString const_key(key.c_str()); |
| // Check value to see if it's the start of an array or dictionary. |
| |
| lldb::OptionValueSP value_sp; |
| assert(value.empty() == false); |
| assert(key.empty() == false); |
| |
| if (value[0] == '{') { |
| assert(value.size() == 1); |
| // value is a dictionary |
| value_sp = ReadDictionary(in_file, out_stream); |
| if (!value_sp) { |
| option_value_sp.reset(); |
| return option_value_sp; |
| } |
| } else if (value[0] == '[') { |
| assert(value.size() == 1); |
| // value is an array |
| value_sp = ReadArray(in_file, out_stream, data_type); |
| if (!value_sp) { |
| option_value_sp.reset(); |
| return option_value_sp; |
| } |
| // We've used the data_type to read an array; re-set the type to Invalid |
| data_type = OptionValue::eTypeInvalid; |
| } else if ((value[0] == '0') && (value[1] == 'x')) { |
| value_sp.reset(new OptionValueUInt64(0, 0)); |
| value_sp->SetValueFromString(value); |
| } else { |
| size_t len = value.size(); |
| if ((value[0] == '"') && (value[len - 1] == '"')) |
| value = value.substr(1, len - 2); |
| value_sp.reset(new OptionValueString(value.c_str(), "")); |
| } |
| |
| if (const_key == encoding_key) { |
| // A 'data_encoding=..." is NOT a normal key-value pair; it is meta-data |
| // indicating the |
| // data type of an upcoming array (usually the next bit of data to be |
| // read in). |
| if (strcmp(value.c_str(), "uint32_t") == 0) |
| data_type = OptionValue::eTypeUInt64; |
| } else |
| option_value_sp->GetAsDictionary()->SetValueForKey(const_key, value_sp, |
| false); |
| } |
| } |
| |
| return option_value_sp; |
| } |
| |
| bool Instruction::TestEmulation(Stream *out_stream, const char *file_name) { |
| if (!out_stream) |
| return false; |
| |
| if (!file_name) { |
| out_stream->Printf("Instruction::TestEmulation: Missing file_name."); |
| return false; |
| } |
| FILE *test_file = FileSystem::Fopen(file_name, "r"); |
| if (!test_file) { |
| out_stream->Printf( |
| "Instruction::TestEmulation: Attempt to open test file failed."); |
| return false; |
| } |
| |
| char buffer[256]; |
| if (!fgets(buffer, 255, test_file)) { |
| out_stream->Printf( |
| "Instruction::TestEmulation: Error reading first line of test file.\n"); |
| fclose(test_file); |
| return false; |
| } |
| |
| if (strncmp(buffer, "InstructionEmulationState={", 27) != 0) { |
| out_stream->Printf("Instructin::TestEmulation: Test file does not contain " |
| "emulation state dictionary\n"); |
| fclose(test_file); |
| return false; |
| } |
| |
| // Read all the test information from the test file into an |
| // OptionValueDictionary. |
| |
| OptionValueSP data_dictionary_sp(ReadDictionary(test_file, out_stream)); |
| if (!data_dictionary_sp) { |
| out_stream->Printf( |
| "Instruction::TestEmulation: Error reading Dictionary Object.\n"); |
| fclose(test_file); |
| return false; |
| } |
| |
| fclose(test_file); |
| |
| OptionValueDictionary *data_dictionary = |
| data_dictionary_sp->GetAsDictionary(); |
| static ConstString description_key("assembly_string"); |
| static ConstString triple_key("triple"); |
| |
| OptionValueSP value_sp = data_dictionary->GetValueForKey(description_key); |
| |
| if (!value_sp) { |
| out_stream->Printf("Instruction::TestEmulation: Test file does not " |
| "contain description string.\n"); |
| return false; |
| } |
| |
| SetDescription(value_sp->GetStringValue()); |
| |
| value_sp = data_dictionary->GetValueForKey(triple_key); |
| if (!value_sp) { |
| out_stream->Printf( |
| "Instruction::TestEmulation: Test file does not contain triple.\n"); |
| return false; |
| } |
| |
| ArchSpec arch; |
| arch.SetTriple(llvm::Triple(value_sp->GetStringValue())); |
| |
| bool success = false; |
| std::unique_ptr<EmulateInstruction> insn_emulator_ap( |
| EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr)); |
| if (insn_emulator_ap) |
| success = |
| insn_emulator_ap->TestEmulation(out_stream, arch, data_dictionary); |
| |
| if (success) |
| out_stream->Printf("Emulation test succeeded."); |
| else |
| out_stream->Printf("Emulation test failed."); |
| |
| return success; |
| } |
| |
| bool Instruction::Emulate( |
| const ArchSpec &arch, uint32_t evaluate_options, void *baton, |
| EmulateInstruction::ReadMemoryCallback read_mem_callback, |
| EmulateInstruction::WriteMemoryCallback write_mem_callback, |
| EmulateInstruction::ReadRegisterCallback read_reg_callback, |
| EmulateInstruction::WriteRegisterCallback write_reg_callback) { |
| std::unique_ptr<EmulateInstruction> insn_emulator_ap( |
| EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr)); |
| if (insn_emulator_ap) { |
| insn_emulator_ap->SetBaton(baton); |
| insn_emulator_ap->SetCallbacks(read_mem_callback, write_mem_callback, |
| read_reg_callback, write_reg_callback); |
| insn_emulator_ap->SetInstruction(GetOpcode(), GetAddress(), nullptr); |
| return insn_emulator_ap->EvaluateInstruction(evaluate_options); |
| } |
| |
| return false; |
| } |
| |
| uint32_t Instruction::GetData(DataExtractor &data) { |
| return m_opcode.GetData(data); |
| } |
| |
| InstructionList::InstructionList() : m_instructions() {} |
| |
| InstructionList::~InstructionList() = default; |
| |
| size_t InstructionList::GetSize() const { return m_instructions.size(); } |
| |
| uint32_t InstructionList::GetMaxOpcocdeByteSize() const { |
| uint32_t max_inst_size = 0; |
| collection::const_iterator pos, end; |
| for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end; |
| ++pos) { |
| uint32_t inst_size = (*pos)->GetOpcode().GetByteSize(); |
| if (max_inst_size < inst_size) |
| max_inst_size = inst_size; |
| } |
| return max_inst_size; |
| } |
| |
| InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const { |
| InstructionSP inst_sp; |
| if (idx < m_instructions.size()) |
| inst_sp = m_instructions[idx]; |
| return inst_sp; |
| } |
| |
| void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes, |
| const ExecutionContext *exe_ctx) { |
| const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); |
| collection::const_iterator pos, begin, end; |
| |
| const FormatEntity::Entry *disassembly_format = nullptr; |
| FormatEntity::Entry format; |
| if (exe_ctx && exe_ctx->HasTargetScope()) { |
| disassembly_format = |
| exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); |
| } else { |
| FormatEntity::Parse("${addr}: ", format); |
| disassembly_format = &format; |
| } |
| |
| for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin; |
| pos != end; ++pos) { |
| if (pos != begin) |
| s->EOL(); |
| (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, |
| nullptr, nullptr, disassembly_format, 0); |
| } |
| } |
| |
| void InstructionList::Clear() { m_instructions.clear(); } |
| |
| void InstructionList::Append(lldb::InstructionSP &inst_sp) { |
| if (inst_sp) |
| m_instructions.push_back(inst_sp); |
| } |
| |
| uint32_t |
| InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, |
| Target &target) const { |
| size_t num_instructions = m_instructions.size(); |
| |
| uint32_t next_branch = UINT32_MAX; |
| size_t i; |
| for (i = start; i < num_instructions; i++) { |
| if (m_instructions[i]->DoesBranch()) { |
| next_branch = i; |
| break; |
| } |
| } |
| |
| // Hexagon needs the first instruction of the packet with the branch. |
| // Go backwards until we find an instruction marked end-of-packet, or |
| // until we hit start. |
| if (target.GetArchitecture().GetTriple().getArch() == llvm::Triple::hexagon) { |
| // If we didn't find a branch, find the last packet start. |
| if (next_branch == UINT32_MAX) { |
| i = num_instructions - 1; |
| } |
| |
| while (i > start) { |
| --i; |
| |
| Error error; |
| uint32_t inst_bytes; |
| bool prefer_file_cache = false; // Read from process if process is running |
| lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; |
| target.ReadMemory(m_instructions[i]->GetAddress(), prefer_file_cache, |
| &inst_bytes, sizeof(inst_bytes), error, &load_addr); |
| // If we have an error reading memory, return start |
| if (!error.Success()) |
| return start; |
| // check if this is the last instruction in a packet |
| // bits 15:14 will be 11b or 00b for a duplex |
| if (((inst_bytes & 0xC000) == 0xC000) || |
| ((inst_bytes & 0xC000) == 0x0000)) { |
| // instruction after this should be the start of next packet |
| next_branch = i + 1; |
| break; |
| } |
| } |
| |
| if (next_branch == UINT32_MAX) { |
| // We couldn't find the previous packet, so return start |
| next_branch = start; |
| } |
| } |
| return next_branch; |
| } |
| |
| uint32_t |
| InstructionList::GetIndexOfInstructionAtAddress(const Address &address) { |
| size_t num_instructions = m_instructions.size(); |
| uint32_t index = UINT32_MAX; |
| for (size_t i = 0; i < num_instructions; i++) { |
| if (m_instructions[i]->GetAddress() == address) { |
| index = i; |
| break; |
| } |
| } |
| return index; |
| } |
| |
| uint32_t |
| InstructionList::GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr, |
| Target &target) { |
| Address address; |
| address.SetLoadAddress(load_addr, &target); |
| return GetIndexOfInstructionAtAddress(address); |
| } |
| |
| size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx, |
| const AddressRange &range, |
| Stream *error_strm_ptr, |
| bool prefer_file_cache) { |
| if (exe_ctx) { |
| Target *target = exe_ctx->GetTargetPtr(); |
| const addr_t byte_size = range.GetByteSize(); |
| if (target == nullptr || byte_size == 0 || |
| !range.GetBaseAddress().IsValid()) |
| return 0; |
| |
| DataBufferHeap *heap_buffer = new DataBufferHeap(byte_size, '\0'); |
| DataBufferSP data_sp(heap_buffer); |
| |
| Error error; |
| lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; |
| const size_t bytes_read = target->ReadMemory( |
| range.GetBaseAddress(), prefer_file_cache, heap_buffer->GetBytes(), |
| heap_buffer->GetByteSize(), error, &load_addr); |
| |
| if (bytes_read > 0) { |
| if (bytes_read != heap_buffer->GetByteSize()) |
| heap_buffer->SetByteSize(bytes_read); |
| DataExtractor data(data_sp, m_arch.GetByteOrder(), |
| m_arch.GetAddressByteSize()); |
| const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; |
| return DecodeInstructions(range.GetBaseAddress(), data, 0, UINT32_MAX, |
| false, data_from_file); |
| } else if (error_strm_ptr) { |
| const char *error_cstr = error.AsCString(); |
| if (error_cstr) { |
| error_strm_ptr->Printf("error: %s\n", error_cstr); |
| } |
| } |
| } else if (error_strm_ptr) { |
| error_strm_ptr->PutCString("error: invalid execution context\n"); |
| } |
| return 0; |
| } |
| |
| size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx, |
| const Address &start, |
| uint32_t num_instructions, |
| bool prefer_file_cache) { |
| m_instruction_list.Clear(); |
| |
| if (exe_ctx == nullptr || num_instructions == 0 || !start.IsValid()) |
| return 0; |
| |
| Target *target = exe_ctx->GetTargetPtr(); |
| // Calculate the max buffer size we will need in order to disassemble |
| const addr_t byte_size = num_instructions * m_arch.GetMaximumOpcodeByteSize(); |
| |
| if (target == nullptr || byte_size == 0) |
| return 0; |
| |
| DataBufferHeap *heap_buffer = new DataBufferHeap(byte_size, '\0'); |
| DataBufferSP data_sp(heap_buffer); |
| |
| Error error; |
| lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; |
| const size_t bytes_read = |
| target->ReadMemory(start, prefer_file_cache, heap_buffer->GetBytes(), |
| byte_size, error, &load_addr); |
| |
| const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; |
| |
| if (bytes_read == 0) |
| return 0; |
| DataExtractor data(data_sp, m_arch.GetByteOrder(), |
| m_arch.GetAddressByteSize()); |
| |
| const bool append_instructions = true; |
| DecodeInstructions(start, data, 0, num_instructions, append_instructions, |
| data_from_file); |
| |
| return m_instruction_list.GetSize(); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Disassembler copy constructor |
| //---------------------------------------------------------------------- |
| Disassembler::Disassembler(const ArchSpec &arch, const char *flavor) |
| : m_arch(arch), m_instruction_list(), m_base_addr(LLDB_INVALID_ADDRESS), |
| m_flavor() { |
| if (flavor == nullptr) |
| m_flavor.assign("default"); |
| else |
| m_flavor.assign(flavor); |
| |
| // If this is an arm variant that can only include thumb (T16, T32) |
| // instructions, force the arch triple to be "thumbv.." instead of |
| // "armv..." |
| if (arch.IsAlwaysThumbInstructions()) { |
| std::string thumb_arch_name(arch.GetTriple().getArchName().str()); |
| // Replace "arm" with "thumb" so we get all thumb variants correct |
| if (thumb_arch_name.size() > 3) { |
| thumb_arch_name.erase(0, 3); |
| thumb_arch_name.insert(0, "thumb"); |
| } |
| m_arch.SetTriple(thumb_arch_name.c_str()); |
| } |
| } |
| |
| Disassembler::~Disassembler() = default; |
| |
| InstructionList &Disassembler::GetInstructionList() { |
| return m_instruction_list; |
| } |
| |
| const InstructionList &Disassembler::GetInstructionList() const { |
| return m_instruction_list; |
| } |
| |
| //---------------------------------------------------------------------- |
| // Class PseudoInstruction |
| //---------------------------------------------------------------------- |
| |
| PseudoInstruction::PseudoInstruction() |
| : Instruction(Address(), eAddressClassUnknown), m_description() {} |
| |
| PseudoInstruction::~PseudoInstruction() = default; |
| |
| bool PseudoInstruction::DoesBranch() { |
| // This is NOT a valid question for a pseudo instruction. |
| return false; |
| } |
| |
| bool PseudoInstruction::HasDelaySlot() { |
| // This is NOT a valid question for a pseudo instruction. |
| return false; |
| } |
| |
| size_t PseudoInstruction::Decode(const lldb_private::Disassembler &disassembler, |
| const lldb_private::DataExtractor &data, |
| lldb::offset_t data_offset) { |
| return m_opcode.GetByteSize(); |
| } |
| |
| void PseudoInstruction::SetOpcode(size_t opcode_size, void *opcode_data) { |
| if (!opcode_data) |
| return; |
| |
| switch (opcode_size) { |
| case 8: { |
| uint8_t value8 = *((uint8_t *)opcode_data); |
| m_opcode.SetOpcode8(value8, eByteOrderInvalid); |
| break; |
| } |
| case 16: { |
| uint16_t value16 = *((uint16_t *)opcode_data); |
| m_opcode.SetOpcode16(value16, eByteOrderInvalid); |
| break; |
| } |
| case 32: { |
| uint32_t value32 = *((uint32_t *)opcode_data); |
| m_opcode.SetOpcode32(value32, eByteOrderInvalid); |
| break; |
| } |
| case 64: { |
| uint64_t value64 = *((uint64_t *)opcode_data); |
| m_opcode.SetOpcode64(value64, eByteOrderInvalid); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| void PseudoInstruction::SetDescription(const char *description) { |
| if (description && strlen(description) > 0) |
| m_description = description; |
| } |