Path: blob/main/contrib/llvm-project/lldb/source/Core/Disassembler.cpp
39587 views
//===-- Disassembler.cpp --------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Core/Disassembler.h"910#include "lldb/Core/AddressRange.h"11#include "lldb/Core/Debugger.h"12#include "lldb/Core/EmulateInstruction.h"13#include "lldb/Core/Mangled.h"14#include "lldb/Core/Module.h"15#include "lldb/Core/ModuleList.h"16#include "lldb/Core/PluginManager.h"17#include "lldb/Core/SourceManager.h"18#include "lldb/Host/FileSystem.h"19#include "lldb/Interpreter/OptionValue.h"20#include "lldb/Interpreter/OptionValueArray.h"21#include "lldb/Interpreter/OptionValueDictionary.h"22#include "lldb/Interpreter/OptionValueRegex.h"23#include "lldb/Interpreter/OptionValueString.h"24#include "lldb/Interpreter/OptionValueUInt64.h"25#include "lldb/Symbol/Function.h"26#include "lldb/Symbol/Symbol.h"27#include "lldb/Symbol/SymbolContext.h"28#include "lldb/Target/ExecutionContext.h"29#include "lldb/Target/SectionLoadList.h"30#include "lldb/Target/StackFrame.h"31#include "lldb/Target/Target.h"32#include "lldb/Target/Thread.h"33#include "lldb/Utility/DataBufferHeap.h"34#include "lldb/Utility/DataExtractor.h"35#include "lldb/Utility/RegularExpression.h"36#include "lldb/Utility/Status.h"37#include "lldb/Utility/Stream.h"38#include "lldb/Utility/StreamString.h"39#include "lldb/Utility/Timer.h"40#include "lldb/lldb-private-enumerations.h"41#include "lldb/lldb-private-interfaces.h"42#include "lldb/lldb-private-types.h"43#include "llvm/Support/Compiler.h"44#include "llvm/TargetParser/Triple.h"4546#include <cstdint>47#include <cstring>48#include <utility>4950#include <cassert>5152#define DEFAULT_DISASM_BYTE_SIZE 325354using namespace lldb;55using namespace lldb_private;5657DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch,58const char *flavor,59const char *plugin_name) {60LLDB_SCOPED_TIMERF("Disassembler::FindPlugin (arch = %s, plugin_name = %s)",61arch.GetArchitectureName(), plugin_name);6263DisassemblerCreateInstance create_callback = nullptr;6465if (plugin_name) {66create_callback =67PluginManager::GetDisassemblerCreateCallbackForPluginName(plugin_name);68if (create_callback) {69if (auto disasm_sp = create_callback(arch, flavor))70return disasm_sp;71}72} else {73for (uint32_t idx = 0;74(create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(75idx)) != nullptr;76++idx) {77if (auto disasm_sp = create_callback(arch, flavor))78return disasm_sp;79}80}81return DisassemblerSP();82}8384DisassemblerSP Disassembler::FindPluginForTarget(const Target &target,85const ArchSpec &arch,86const char *flavor,87const char *plugin_name) {88if (flavor == nullptr) {89// FIXME - we don't have the mechanism in place to do per-architecture90// settings. But since we know that for now we only support flavors on x8691// & x86_64,92if (arch.GetTriple().getArch() == llvm::Triple::x86 ||93arch.GetTriple().getArch() == llvm::Triple::x86_64)94flavor = target.GetDisassemblyFlavor();95}96return FindPlugin(arch, flavor, plugin_name);97}9899static Address ResolveAddress(Target &target, const Address &addr) {100if (!addr.IsSectionOffset()) {101Address resolved_addr;102// If we weren't passed in a section offset address range, try and resolve103// it to something104bool is_resolved = target.GetSectionLoadList().IsEmpty()105? target.GetImages().ResolveFileAddress(106addr.GetOffset(), resolved_addr)107: target.GetSectionLoadList().ResolveLoadAddress(108addr.GetOffset(), resolved_addr);109110// We weren't able to resolve the address, just treat it as a raw address111if (is_resolved && resolved_addr.IsValid())112return resolved_addr;113}114return addr;115}116117lldb::DisassemblerSP Disassembler::DisassembleRange(118const ArchSpec &arch, const char *plugin_name, const char *flavor,119Target &target, const AddressRange &range, bool force_live_memory) {120if (range.GetByteSize() <= 0)121return {};122123if (!range.GetBaseAddress().IsValid())124return {};125126lldb::DisassemblerSP disasm_sp =127Disassembler::FindPluginForTarget(target, arch, flavor, plugin_name);128129if (!disasm_sp)130return {};131132const size_t bytes_disassembled = disasm_sp->ParseInstructions(133target, range.GetBaseAddress(), {Limit::Bytes, range.GetByteSize()},134nullptr, force_live_memory);135if (bytes_disassembled == 0)136return {};137138return disasm_sp;139}140141lldb::DisassemblerSP142Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name,143const char *flavor, const Address &start,144const void *src, size_t src_len,145uint32_t num_instructions, bool data_from_file) {146if (!src)147return {};148149lldb::DisassemblerSP disasm_sp =150Disassembler::FindPlugin(arch, flavor, plugin_name);151152if (!disasm_sp)153return {};154155DataExtractor data(src, src_len, arch.GetByteOrder(),156arch.GetAddressByteSize());157158(void)disasm_sp->DecodeInstructions(start, data, 0, num_instructions, false,159data_from_file);160return disasm_sp;161}162163bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,164const char *plugin_name, const char *flavor,165const ExecutionContext &exe_ctx,166const Address &address, Limit limit,167bool mixed_source_and_assembly,168uint32_t num_mixed_context_lines,169uint32_t options, Stream &strm) {170if (!exe_ctx.GetTargetPtr())171return false;172173lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget(174exe_ctx.GetTargetRef(), arch, flavor, plugin_name));175if (!disasm_sp)176return false;177178const bool force_live_memory = true;179size_t bytes_disassembled = disasm_sp->ParseInstructions(180exe_ctx.GetTargetRef(), address, limit, &strm, force_live_memory);181if (bytes_disassembled == 0)182return false;183184disasm_sp->PrintInstructions(debugger, arch, exe_ctx,185mixed_source_and_assembly,186num_mixed_context_lines, options, strm);187return true;188}189190Disassembler::SourceLine191Disassembler::GetFunctionDeclLineEntry(const SymbolContext &sc) {192if (!sc.function)193return {};194195if (!sc.line_entry.IsValid())196return {};197198LineEntry prologue_end_line = sc.line_entry;199FileSpec func_decl_file;200uint32_t func_decl_line;201sc.function->GetStartLineSourceInfo(func_decl_file, func_decl_line);202203if (func_decl_file != prologue_end_line.GetFile() &&204func_decl_file != prologue_end_line.original_file_sp->GetSpecOnly())205return {};206207SourceLine decl_line;208decl_line.file = func_decl_file;209decl_line.line = func_decl_line;210// TODO: Do we care about column on these entries? If so, we need to plumb211// that through GetStartLineSourceInfo.212decl_line.column = 0;213return decl_line;214}215216void Disassembler::AddLineToSourceLineTables(217SourceLine &line,218std::map<FileSpec, std::set<uint32_t>> &source_lines_seen) {219if (line.IsValid()) {220auto source_lines_seen_pos = source_lines_seen.find(line.file);221if (source_lines_seen_pos == source_lines_seen.end()) {222std::set<uint32_t> lines;223lines.insert(line.line);224source_lines_seen.emplace(line.file, lines);225} else {226source_lines_seen_pos->second.insert(line.line);227}228}229}230231bool Disassembler::ElideMixedSourceAndDisassemblyLine(232const ExecutionContext &exe_ctx, const SymbolContext &sc,233SourceLine &line) {234235// TODO: should we also check target.process.thread.step-avoid-libraries ?236237const RegularExpression *avoid_regex = nullptr;238239// Skip any line #0 entries - they are implementation details240if (line.line == 0)241return true;242243ThreadSP thread_sp = exe_ctx.GetThreadSP();244if (thread_sp) {245avoid_regex = thread_sp->GetSymbolsToAvoidRegexp();246} else {247TargetSP target_sp = exe_ctx.GetTargetSP();248if (target_sp) {249Status error;250OptionValueSP value_sp = target_sp->GetDebugger().GetPropertyValue(251&exe_ctx, "target.process.thread.step-avoid-regexp", error);252if (value_sp && value_sp->GetType() == OptionValue::eTypeRegex) {253OptionValueRegex *re = value_sp->GetAsRegex();254if (re) {255avoid_regex = re->GetCurrentValue();256}257}258}259}260if (avoid_regex && sc.symbol != nullptr) {261const char *function_name =262sc.GetFunctionName(Mangled::ePreferDemangledWithoutArguments)263.GetCString();264if (function_name && avoid_regex->Execute(function_name)) {265// skip this source line266return true;267}268}269// don't skip this source line270return false;271}272273void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,274const ExecutionContext &exe_ctx,275bool mixed_source_and_assembly,276uint32_t num_mixed_context_lines,277uint32_t options, Stream &strm) {278// We got some things disassembled...279size_t num_instructions_found = GetInstructionList().GetSize();280281const uint32_t max_opcode_byte_size =282GetInstructionList().GetMaxOpcocdeByteSize();283SymbolContext sc;284SymbolContext prev_sc;285AddressRange current_source_line_range;286const Address *pc_addr_ptr = nullptr;287StackFrame *frame = exe_ctx.GetFramePtr();288289TargetSP target_sp(exe_ctx.GetTargetSP());290SourceManager &source_manager =291target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager();292293if (frame) {294pc_addr_ptr = &frame->GetFrameCodeAddress();295}296const uint32_t scope =297eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol;298const bool use_inline_block_range = false;299300const FormatEntity::Entry *disassembly_format = nullptr;301FormatEntity::Entry format;302if (exe_ctx.HasTargetScope()) {303disassembly_format =304exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat();305} else {306FormatEntity::Parse("${addr}: ", format);307disassembly_format = &format;308}309310// First pass: step through the list of instructions, find how long the311// initial addresses strings are, insert padding in the second pass so the312// opcodes all line up nicely.313314// Also build up the source line mapping if this is mixed source & assembly315// mode. Calculate the source line for each assembly instruction (eliding316// inlined functions which the user wants to skip).317318std::map<FileSpec, std::set<uint32_t>> source_lines_seen;319Symbol *previous_symbol = nullptr;320321size_t address_text_size = 0;322for (size_t i = 0; i < num_instructions_found; ++i) {323Instruction *inst = GetInstructionList().GetInstructionAtIndex(i).get();324if (inst) {325const Address &addr = inst->GetAddress();326ModuleSP module_sp(addr.GetModule());327if (module_sp) {328const SymbolContextItem resolve_mask = eSymbolContextFunction |329eSymbolContextSymbol |330eSymbolContextLineEntry;331uint32_t resolved_mask =332module_sp->ResolveSymbolContextForAddress(addr, resolve_mask, sc);333if (resolved_mask) {334StreamString strmstr;335Debugger::FormatDisassemblerAddress(disassembly_format, &sc, nullptr,336&exe_ctx, &addr, strmstr);337size_t cur_line = strmstr.GetSizeOfLastLine();338if (cur_line > address_text_size)339address_text_size = cur_line;340341// Add entries to our "source_lines_seen" map+set which list which342// sources lines occur in this disassembly session. We will print343// lines of context around a source line, but we don't want to print344// a source line that has a line table entry of its own - we'll leave345// that source line to be printed when it actually occurs in the346// disassembly.347348if (mixed_source_and_assembly && sc.line_entry.IsValid()) {349if (sc.symbol != previous_symbol) {350SourceLine decl_line = GetFunctionDeclLineEntry(sc);351if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, decl_line))352AddLineToSourceLineTables(decl_line, source_lines_seen);353}354if (sc.line_entry.IsValid()) {355SourceLine this_line;356this_line.file = sc.line_entry.GetFile();357this_line.line = sc.line_entry.line;358this_line.column = sc.line_entry.column;359if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, this_line))360AddLineToSourceLineTables(this_line, source_lines_seen);361}362}363}364sc.Clear(false);365}366}367}368369previous_symbol = nullptr;370SourceLine previous_line;371for (size_t i = 0; i < num_instructions_found; ++i) {372Instruction *inst = GetInstructionList().GetInstructionAtIndex(i).get();373374if (inst) {375const Address &addr = inst->GetAddress();376const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr;377SourceLinesToDisplay source_lines_to_display;378379prev_sc = sc;380381ModuleSP module_sp(addr.GetModule());382if (module_sp) {383uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress(384addr, eSymbolContextEverything, sc);385if (resolved_mask) {386if (mixed_source_and_assembly) {387388// If we've started a new function (non-inlined), print all of the389// source lines from the function declaration until the first line390// table entry - typically the opening curly brace of the function.391if (previous_symbol != sc.symbol) {392// The default disassembly format puts an extra blank line393// between functions - so when we're displaying the source394// context for a function, we don't want to add a blank line395// after the source context or we'll end up with two of them.396if (previous_symbol != nullptr)397source_lines_to_display.print_source_context_end_eol = false;398399previous_symbol = sc.symbol;400if (sc.function && sc.line_entry.IsValid()) {401LineEntry prologue_end_line = sc.line_entry;402if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc,403prologue_end_line)) {404FileSpec func_decl_file;405uint32_t func_decl_line;406sc.function->GetStartLineSourceInfo(func_decl_file,407func_decl_line);408if (func_decl_file == prologue_end_line.GetFile() ||409func_decl_file ==410prologue_end_line.original_file_sp->GetSpecOnly()) {411// Add all the lines between the function declaration and412// the first non-prologue source line to the list of lines413// to print.414for (uint32_t lineno = func_decl_line;415lineno <= prologue_end_line.line; lineno++) {416SourceLine this_line;417this_line.file = func_decl_file;418this_line.line = lineno;419source_lines_to_display.lines.push_back(this_line);420}421// Mark the last line as the "current" one. Usually this422// is the open curly brace.423if (source_lines_to_display.lines.size() > 0)424source_lines_to_display.current_source_line =425source_lines_to_display.lines.size() - 1;426}427}428}429sc.GetAddressRange(scope, 0, use_inline_block_range,430current_source_line_range);431}432433// If we've left a previous source line's address range, print a434// new source line435if (!current_source_line_range.ContainsFileAddress(addr)) {436sc.GetAddressRange(scope, 0, use_inline_block_range,437current_source_line_range);438439if (sc != prev_sc && sc.comp_unit && sc.line_entry.IsValid()) {440SourceLine this_line;441this_line.file = sc.line_entry.GetFile();442this_line.line = sc.line_entry.line;443444if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc,445this_line)) {446// Only print this source line if it is different from the447// last source line we printed. There may have been inlined448// functions between these lines that we elided, resulting in449// the same line being printed twice in a row for a450// contiguous block of assembly instructions.451if (this_line != previous_line) {452453std::vector<uint32_t> previous_lines;454for (uint32_t i = 0;455i < num_mixed_context_lines &&456(this_line.line - num_mixed_context_lines) > 0;457i++) {458uint32_t line =459this_line.line - num_mixed_context_lines + i;460auto pos = source_lines_seen.find(this_line.file);461if (pos != source_lines_seen.end()) {462if (pos->second.count(line) == 1) {463previous_lines.clear();464} else {465previous_lines.push_back(line);466}467}468}469for (size_t i = 0; i < previous_lines.size(); i++) {470SourceLine previous_line;471previous_line.file = this_line.file;472previous_line.line = previous_lines[i];473auto pos = source_lines_seen.find(previous_line.file);474if (pos != source_lines_seen.end()) {475pos->second.insert(previous_line.line);476}477source_lines_to_display.lines.push_back(previous_line);478}479480source_lines_to_display.lines.push_back(this_line);481source_lines_to_display.current_source_line =482source_lines_to_display.lines.size() - 1;483484for (uint32_t i = 0; i < num_mixed_context_lines; i++) {485SourceLine next_line;486next_line.file = this_line.file;487next_line.line = this_line.line + i + 1;488auto pos = source_lines_seen.find(next_line.file);489if (pos != source_lines_seen.end()) {490if (pos->second.count(next_line.line) == 1)491break;492pos->second.insert(next_line.line);493}494source_lines_to_display.lines.push_back(next_line);495}496}497previous_line = this_line;498}499}500}501}502} else {503sc.Clear(true);504}505}506507if (source_lines_to_display.lines.size() > 0) {508strm.EOL();509for (size_t idx = 0; idx < source_lines_to_display.lines.size();510idx++) {511SourceLine ln = source_lines_to_display.lines[idx];512const char *line_highlight = "";513if (inst_is_at_pc && (options & eOptionMarkPCSourceLine)) {514line_highlight = "->";515} else if (idx == source_lines_to_display.current_source_line) {516line_highlight = "**";517}518source_manager.DisplaySourceLinesWithLineNumbers(519ln.file, ln.line, ln.column, 0, 0, line_highlight, &strm);520}521if (source_lines_to_display.print_source_context_end_eol)522strm.EOL();523}524525const bool show_bytes = (options & eOptionShowBytes) != 0;526const bool show_control_flow_kind =527(options & eOptionShowControlFlowKind) != 0;528inst->Dump(&strm, max_opcode_byte_size, true, show_bytes,529show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,530address_text_size);531strm.EOL();532} else {533break;534}535}536}537538bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,539StackFrame &frame, Stream &strm) {540AddressRange range;541SymbolContext sc(542frame.GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol));543if (sc.function) {544range = sc.function->GetAddressRange();545} else if (sc.symbol && sc.symbol->ValueIsAddress()) {546range.GetBaseAddress() = sc.symbol->GetAddressRef();547range.SetByteSize(sc.symbol->GetByteSize());548} else {549range.GetBaseAddress() = frame.GetFrameCodeAddress();550}551552if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0)553range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);554555Disassembler::Limit limit = {Disassembler::Limit::Bytes,556range.GetByteSize()};557if (limit.value == 0)558limit.value = DEFAULT_DISASM_BYTE_SIZE;559560return Disassemble(debugger, arch, nullptr, nullptr, frame,561range.GetBaseAddress(), limit, false, 0, 0, strm);562}563564Instruction::Instruction(const Address &address, AddressClass addr_class)565: m_address(address), m_address_class(addr_class), m_opcode(),566m_calculated_strings(false) {}567568Instruction::~Instruction() = default;569570AddressClass Instruction::GetAddressClass() {571if (m_address_class == AddressClass::eInvalid)572m_address_class = m_address.GetAddressClass();573return m_address_class;574}575576const char *Instruction::GetNameForInstructionControlFlowKind(577lldb::InstructionControlFlowKind instruction_control_flow_kind) {578switch (instruction_control_flow_kind) {579case eInstructionControlFlowKindUnknown:580return "unknown";581case eInstructionControlFlowKindOther:582return "other";583case eInstructionControlFlowKindCall:584return "call";585case eInstructionControlFlowKindReturn:586return "return";587case eInstructionControlFlowKindJump:588return "jump";589case eInstructionControlFlowKindCondJump:590return "cond jump";591case eInstructionControlFlowKindFarCall:592return "far call";593case eInstructionControlFlowKindFarReturn:594return "far return";595case eInstructionControlFlowKindFarJump:596return "far jump";597}598llvm_unreachable("Fully covered switch above!");599}600601void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,602bool show_address, bool show_bytes,603bool show_control_flow_kind,604const ExecutionContext *exe_ctx,605const SymbolContext *sym_ctx,606const SymbolContext *prev_sym_ctx,607const FormatEntity::Entry *disassembly_addr_format,608size_t max_address_text_size) {609size_t opcode_column_width = 7;610const size_t operand_column_width = 25;611612CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx);613614StreamString ss;615616if (show_address) {617Debugger::FormatDisassemblerAddress(disassembly_addr_format, sym_ctx,618prev_sym_ctx, exe_ctx, &m_address, ss);619ss.FillLastLineToColumn(max_address_text_size, ' ');620}621622if (show_bytes) {623if (m_opcode.GetType() == Opcode::eTypeBytes) {624// x86_64 and i386 are the only ones that use bytes right now so pad out625// the byte dump to be able to always show 15 bytes (3 chars each) plus a626// space627if (max_opcode_byte_size > 0)628m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1);629else630m_opcode.Dump(&ss, 15 * 3 + 1);631} else {632// Else, we have ARM or MIPS which can show up to a uint32_t 0x00000000633// (10 spaces) plus two for padding...634if (max_opcode_byte_size > 0)635m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1);636else637m_opcode.Dump(&ss, 12);638}639}640641if (show_control_flow_kind) {642lldb::InstructionControlFlowKind instruction_control_flow_kind =643GetControlFlowKind(exe_ctx);644ss.Printf("%-12s", GetNameForInstructionControlFlowKind(645instruction_control_flow_kind));646}647648bool show_color = false;649if (exe_ctx) {650if (TargetSP target_sp = exe_ctx->GetTargetSP()) {651show_color = target_sp->GetDebugger().GetUseColor();652}653}654const size_t opcode_pos = ss.GetSizeOfLastLine();655const std::string &opcode_name =656show_color ? m_markup_opcode_name : m_opcode_name;657const std::string &mnemonics = show_color ? m_markup_mnemonics : m_mnemonics;658659// The default opcode size of 7 characters is plenty for most architectures660// but some like arm can pull out the occasional vqrshrun.s16. We won't get661// consistent column spacing in these cases, unfortunately. Also note that we662// need to directly use m_opcode_name here (instead of opcode_name) so we663// don't include color codes as characters.664if (m_opcode_name.length() >= opcode_column_width) {665opcode_column_width = m_opcode_name.length() + 1;666}667668ss.PutCString(opcode_name);669ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' ');670ss.PutCString(mnemonics);671672if (!m_comment.empty()) {673ss.FillLastLineToColumn(674opcode_pos + opcode_column_width + operand_column_width, ' ');675ss.PutCString(" ; ");676ss.PutCString(m_comment);677}678s->PutCString(ss.GetString());679}680681bool Instruction::DumpEmulation(const ArchSpec &arch) {682std::unique_ptr<EmulateInstruction> insn_emulator_up(683EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));684if (insn_emulator_up) {685insn_emulator_up->SetInstruction(GetOpcode(), GetAddress(), nullptr);686return insn_emulator_up->EvaluateInstruction(0);687}688689return false;690}691692bool Instruction::CanSetBreakpoint () {693return !HasDelaySlot();694}695696bool Instruction::HasDelaySlot() {697// Default is false.698return false;699}700701OptionValueSP Instruction::ReadArray(FILE *in_file, Stream &out_stream,702OptionValue::Type data_type) {703bool done = false;704char buffer[1024];705706auto option_value_sp = std::make_shared<OptionValueArray>(1u << data_type);707708int idx = 0;709while (!done) {710if (!fgets(buffer, 1023, in_file)) {711out_stream.Printf(712"Instruction::ReadArray: Error reading file (fgets).\n");713option_value_sp.reset();714return option_value_sp;715}716717std::string line(buffer);718719size_t len = line.size();720if (line[len - 1] == '\n') {721line[len - 1] = '\0';722line.resize(len - 1);723}724725if ((line.size() == 1) && line[0] == ']') {726done = true;727line.clear();728}729730if (!line.empty()) {731std::string value;732static RegularExpression g_reg_exp(733llvm::StringRef("^[ \t]*([^ \t]+)[ \t]*$"));734llvm::SmallVector<llvm::StringRef, 2> matches;735if (g_reg_exp.Execute(line, &matches))736value = matches[1].str();737else738value = line;739740OptionValueSP data_value_sp;741switch (data_type) {742case OptionValue::eTypeUInt64:743data_value_sp = std::make_shared<OptionValueUInt64>(0, 0);744data_value_sp->SetValueFromString(value);745break;746// Other types can be added later as needed.747default:748data_value_sp = std::make_shared<OptionValueString>(value.c_str(), "");749break;750}751752option_value_sp->GetAsArray()->InsertValue(idx, data_value_sp);753++idx;754}755}756757return option_value_sp;758}759760OptionValueSP Instruction::ReadDictionary(FILE *in_file, Stream &out_stream) {761bool done = false;762char buffer[1024];763764auto option_value_sp = std::make_shared<OptionValueDictionary>();765static constexpr llvm::StringLiteral encoding_key("data_encoding");766OptionValue::Type data_type = OptionValue::eTypeInvalid;767768while (!done) {769// Read the next line in the file770if (!fgets(buffer, 1023, in_file)) {771out_stream.Printf(772"Instruction::ReadDictionary: Error reading file (fgets).\n");773option_value_sp.reset();774return option_value_sp;775}776777// Check to see if the line contains the end-of-dictionary marker ("}")778std::string line(buffer);779780size_t len = line.size();781if (line[len - 1] == '\n') {782line[len - 1] = '\0';783line.resize(len - 1);784}785786if ((line.size() == 1) && (line[0] == '}')) {787done = true;788line.clear();789}790791// Try to find a key-value pair in the current line and add it to the792// dictionary.793if (!line.empty()) {794static RegularExpression g_reg_exp(llvm::StringRef(795"^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$"));796797llvm::SmallVector<llvm::StringRef, 3> matches;798799bool reg_exp_success = g_reg_exp.Execute(line, &matches);800std::string key;801std::string value;802if (reg_exp_success) {803key = matches[1].str();804value = matches[2].str();805} else {806out_stream.Printf("Instruction::ReadDictionary: Failure executing "807"regular expression.\n");808option_value_sp.reset();809return option_value_sp;810}811812// Check value to see if it's the start of an array or dictionary.813814lldb::OptionValueSP value_sp;815assert(value.empty() == false);816assert(key.empty() == false);817818if (value[0] == '{') {819assert(value.size() == 1);820// value is a dictionary821value_sp = ReadDictionary(in_file, out_stream);822if (!value_sp) {823option_value_sp.reset();824return option_value_sp;825}826} else if (value[0] == '[') {827assert(value.size() == 1);828// value is an array829value_sp = ReadArray(in_file, out_stream, data_type);830if (!value_sp) {831option_value_sp.reset();832return option_value_sp;833}834// We've used the data_type to read an array; re-set the type to835// Invalid836data_type = OptionValue::eTypeInvalid;837} else if ((value[0] == '0') && (value[1] == 'x')) {838value_sp = std::make_shared<OptionValueUInt64>(0, 0);839value_sp->SetValueFromString(value);840} else {841size_t len = value.size();842if ((value[0] == '"') && (value[len - 1] == '"'))843value = value.substr(1, len - 2);844value_sp = std::make_shared<OptionValueString>(value.c_str(), "");845}846847if (key == encoding_key) {848// A 'data_encoding=..." is NOT a normal key-value pair; it is meta-data849// indicating the data type of an upcoming array (usually the next bit850// of data to be read in).851if (llvm::StringRef(value) == "uint32_t")852data_type = OptionValue::eTypeUInt64;853} else854option_value_sp->GetAsDictionary()->SetValueForKey(key, value_sp,855false);856}857}858859return option_value_sp;860}861862bool Instruction::TestEmulation(Stream &out_stream, const char *file_name) {863if (!file_name) {864out_stream.Printf("Instruction::TestEmulation: Missing file_name.");865return false;866}867FILE *test_file = FileSystem::Instance().Fopen(file_name, "r");868if (!test_file) {869out_stream.Printf(870"Instruction::TestEmulation: Attempt to open test file failed.");871return false;872}873874char buffer[256];875if (!fgets(buffer, 255, test_file)) {876out_stream.Printf(877"Instruction::TestEmulation: Error reading first line of test file.\n");878fclose(test_file);879return false;880}881882if (strncmp(buffer, "InstructionEmulationState={", 27) != 0) {883out_stream.Printf("Instructin::TestEmulation: Test file does not contain "884"emulation state dictionary\n");885fclose(test_file);886return false;887}888889// Read all the test information from the test file into an890// OptionValueDictionary.891892OptionValueSP data_dictionary_sp(ReadDictionary(test_file, out_stream));893if (!data_dictionary_sp) {894out_stream.Printf(895"Instruction::TestEmulation: Error reading Dictionary Object.\n");896fclose(test_file);897return false;898}899900fclose(test_file);901902OptionValueDictionary *data_dictionary =903data_dictionary_sp->GetAsDictionary();904static constexpr llvm::StringLiteral description_key("assembly_string");905static constexpr llvm::StringLiteral triple_key("triple");906907OptionValueSP value_sp = data_dictionary->GetValueForKey(description_key);908909if (!value_sp) {910out_stream.Printf("Instruction::TestEmulation: Test file does not "911"contain description string.\n");912return false;913}914915SetDescription(value_sp->GetValueAs<llvm::StringRef>().value_or(""));916917value_sp = data_dictionary->GetValueForKey(triple_key);918if (!value_sp) {919out_stream.Printf(920"Instruction::TestEmulation: Test file does not contain triple.\n");921return false;922}923924ArchSpec arch;925arch.SetTriple(926llvm::Triple(value_sp->GetValueAs<llvm::StringRef>().value_or("")));927928bool success = false;929std::unique_ptr<EmulateInstruction> insn_emulator_up(930EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));931if (insn_emulator_up)932success =933insn_emulator_up->TestEmulation(out_stream, arch, data_dictionary);934935if (success)936out_stream.Printf("Emulation test succeeded.");937else938out_stream.Printf("Emulation test failed.");939940return success;941}942943bool Instruction::Emulate(944const ArchSpec &arch, uint32_t evaluate_options, void *baton,945EmulateInstruction::ReadMemoryCallback read_mem_callback,946EmulateInstruction::WriteMemoryCallback write_mem_callback,947EmulateInstruction::ReadRegisterCallback read_reg_callback,948EmulateInstruction::WriteRegisterCallback write_reg_callback) {949std::unique_ptr<EmulateInstruction> insn_emulator_up(950EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));951if (insn_emulator_up) {952insn_emulator_up->SetBaton(baton);953insn_emulator_up->SetCallbacks(read_mem_callback, write_mem_callback,954read_reg_callback, write_reg_callback);955insn_emulator_up->SetInstruction(GetOpcode(), GetAddress(), nullptr);956return insn_emulator_up->EvaluateInstruction(evaluate_options);957}958959return false;960}961962uint32_t Instruction::GetData(DataExtractor &data) {963return m_opcode.GetData(data);964}965966InstructionList::InstructionList() : m_instructions() {}967968InstructionList::~InstructionList() = default;969970size_t InstructionList::GetSize() const { return m_instructions.size(); }971972uint32_t InstructionList::GetMaxOpcocdeByteSize() const {973uint32_t max_inst_size = 0;974collection::const_iterator pos, end;975for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end;976++pos) {977uint32_t inst_size = (*pos)->GetOpcode().GetByteSize();978if (max_inst_size < inst_size)979max_inst_size = inst_size;980}981return max_inst_size;982}983984InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const {985InstructionSP inst_sp;986if (idx < m_instructions.size())987inst_sp = m_instructions[idx];988return inst_sp;989}990991InstructionSP InstructionList::GetInstructionAtAddress(const Address &address) {992uint32_t index = GetIndexOfInstructionAtAddress(address);993if (index != UINT32_MAX)994return GetInstructionAtIndex(index);995return nullptr;996}997998void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,999bool show_control_flow_kind,1000const ExecutionContext *exe_ctx) {1001const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize();1002collection::const_iterator pos, begin, end;10031004const FormatEntity::Entry *disassembly_format = nullptr;1005FormatEntity::Entry format;1006if (exe_ctx && exe_ctx->HasTargetScope()) {1007disassembly_format =1008exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();1009} else {1010FormatEntity::Parse("${addr}: ", format);1011disassembly_format = &format;1012}10131014for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin;1015pos != end; ++pos) {1016if (pos != begin)1017s->EOL();1018(*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes,1019show_control_flow_kind, exe_ctx, nullptr, nullptr,1020disassembly_format, 0);1021}1022}10231024void InstructionList::Clear() { m_instructions.clear(); }10251026void InstructionList::Append(lldb::InstructionSP &inst_sp) {1027if (inst_sp)1028m_instructions.push_back(inst_sp);1029}10301031uint32_t1032InstructionList::GetIndexOfNextBranchInstruction(uint32_t start,1033bool ignore_calls,1034bool *found_calls) const {1035size_t num_instructions = m_instructions.size();10361037uint32_t next_branch = UINT32_MAX;10381039if (found_calls)1040*found_calls = false;1041for (size_t i = start; i < num_instructions; i++) {1042if (m_instructions[i]->DoesBranch()) {1043if (ignore_calls && m_instructions[i]->IsCall()) {1044if (found_calls)1045*found_calls = true;1046continue;1047}1048next_branch = i;1049break;1050}1051}10521053return next_branch;1054}10551056uint32_t1057InstructionList::GetIndexOfInstructionAtAddress(const Address &address) {1058size_t num_instructions = m_instructions.size();1059uint32_t index = UINT32_MAX;1060for (size_t i = 0; i < num_instructions; i++) {1061if (m_instructions[i]->GetAddress() == address) {1062index = i;1063break;1064}1065}1066return index;1067}10681069uint32_t1070InstructionList::GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr,1071Target &target) {1072Address address;1073address.SetLoadAddress(load_addr, &target);1074return GetIndexOfInstructionAtAddress(address);1075}10761077size_t Disassembler::ParseInstructions(Target &target, Address start,1078Limit limit, Stream *error_strm_ptr,1079bool force_live_memory) {1080m_instruction_list.Clear();10811082if (!start.IsValid())1083return 0;10841085start = ResolveAddress(target, start);10861087addr_t byte_size = limit.value;1088if (limit.kind == Limit::Instructions)1089byte_size *= m_arch.GetMaximumOpcodeByteSize();1090auto data_sp = std::make_shared<DataBufferHeap>(byte_size, '\0');10911092Status error;1093lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;1094const size_t bytes_read =1095target.ReadMemory(start, data_sp->GetBytes(), data_sp->GetByteSize(),1096error, force_live_memory, &load_addr);1097const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;10981099if (bytes_read == 0) {1100if (error_strm_ptr) {1101if (const char *error_cstr = error.AsCString())1102error_strm_ptr->Printf("error: %s\n", error_cstr);1103}1104return 0;1105}11061107if (bytes_read != data_sp->GetByteSize())1108data_sp->SetByteSize(bytes_read);1109DataExtractor data(data_sp, m_arch.GetByteOrder(),1110m_arch.GetAddressByteSize());1111return DecodeInstructions(start, data, 0,1112limit.kind == Limit::Instructions ? limit.value1113: UINT32_MAX,1114false, data_from_file);1115}11161117// Disassembler copy constructor1118Disassembler::Disassembler(const ArchSpec &arch, const char *flavor)1119: m_arch(arch), m_instruction_list(), m_flavor() {1120if (flavor == nullptr)1121m_flavor.assign("default");1122else1123m_flavor.assign(flavor);11241125// If this is an arm variant that can only include thumb (T16, T32)1126// instructions, force the arch triple to be "thumbv.." instead of "armv..."1127if (arch.IsAlwaysThumbInstructions()) {1128std::string thumb_arch_name(arch.GetTriple().getArchName().str());1129// Replace "arm" with "thumb" so we get all thumb variants correct1130if (thumb_arch_name.size() > 3) {1131thumb_arch_name.erase(0, 3);1132thumb_arch_name.insert(0, "thumb");1133}1134m_arch.SetTriple(thumb_arch_name.c_str());1135}1136}11371138Disassembler::~Disassembler() = default;11391140InstructionList &Disassembler::GetInstructionList() {1141return m_instruction_list;1142}11431144const InstructionList &Disassembler::GetInstructionList() const {1145return m_instruction_list;1146}11471148// Class PseudoInstruction11491150PseudoInstruction::PseudoInstruction()1151: Instruction(Address(), AddressClass::eUnknown), m_description() {}11521153PseudoInstruction::~PseudoInstruction() = default;11541155bool PseudoInstruction::DoesBranch() {1156// This is NOT a valid question for a pseudo instruction.1157return false;1158}11591160bool PseudoInstruction::HasDelaySlot() {1161// This is NOT a valid question for a pseudo instruction.1162return false;1163}11641165bool PseudoInstruction::IsLoad() { return false; }11661167bool PseudoInstruction::IsAuthenticated() { return false; }11681169size_t PseudoInstruction::Decode(const lldb_private::Disassembler &disassembler,1170const lldb_private::DataExtractor &data,1171lldb::offset_t data_offset) {1172return m_opcode.GetByteSize();1173}11741175void PseudoInstruction::SetOpcode(size_t opcode_size, void *opcode_data) {1176if (!opcode_data)1177return;11781179switch (opcode_size) {1180case 8: {1181uint8_t value8 = *((uint8_t *)opcode_data);1182m_opcode.SetOpcode8(value8, eByteOrderInvalid);1183break;1184}1185case 16: {1186uint16_t value16 = *((uint16_t *)opcode_data);1187m_opcode.SetOpcode16(value16, eByteOrderInvalid);1188break;1189}1190case 32: {1191uint32_t value32 = *((uint32_t *)opcode_data);1192m_opcode.SetOpcode32(value32, eByteOrderInvalid);1193break;1194}1195case 64: {1196uint64_t value64 = *((uint64_t *)opcode_data);1197m_opcode.SetOpcode64(value64, eByteOrderInvalid);1198break;1199}1200default:1201break;1202}1203}12041205void PseudoInstruction::SetDescription(llvm::StringRef description) {1206m_description = std::string(description);1207}12081209Instruction::Operand Instruction::Operand::BuildRegister(ConstString &r) {1210Operand ret;1211ret.m_type = Type::Register;1212ret.m_register = r;1213return ret;1214}12151216Instruction::Operand Instruction::Operand::BuildImmediate(lldb::addr_t imm,1217bool neg) {1218Operand ret;1219ret.m_type = Type::Immediate;1220ret.m_immediate = imm;1221ret.m_negative = neg;1222return ret;1223}12241225Instruction::Operand Instruction::Operand::BuildImmediate(int64_t imm) {1226Operand ret;1227ret.m_type = Type::Immediate;1228if (imm < 0) {1229ret.m_immediate = -imm;1230ret.m_negative = true;1231} else {1232ret.m_immediate = imm;1233ret.m_negative = false;1234}1235return ret;1236}12371238Instruction::Operand1239Instruction::Operand::BuildDereference(const Operand &ref) {1240Operand ret;1241ret.m_type = Type::Dereference;1242ret.m_children = {ref};1243return ret;1244}12451246Instruction::Operand Instruction::Operand::BuildSum(const Operand &lhs,1247const Operand &rhs) {1248Operand ret;1249ret.m_type = Type::Sum;1250ret.m_children = {lhs, rhs};1251return ret;1252}12531254Instruction::Operand Instruction::Operand::BuildProduct(const Operand &lhs,1255const Operand &rhs) {1256Operand ret;1257ret.m_type = Type::Product;1258ret.m_children = {lhs, rhs};1259return ret;1260}12611262std::function<bool(const Instruction::Operand &)>1263lldb_private::OperandMatchers::MatchBinaryOp(1264std::function<bool(const Instruction::Operand &)> base,1265std::function<bool(const Instruction::Operand &)> left,1266std::function<bool(const Instruction::Operand &)> right) {1267return [base, left, right](const Instruction::Operand &op) -> bool {1268return (base(op) && op.m_children.size() == 2 &&1269((left(op.m_children[0]) && right(op.m_children[1])) ||1270(left(op.m_children[1]) && right(op.m_children[0]))));1271};1272}12731274std::function<bool(const Instruction::Operand &)>1275lldb_private::OperandMatchers::MatchUnaryOp(1276std::function<bool(const Instruction::Operand &)> base,1277std::function<bool(const Instruction::Operand &)> child) {1278return [base, child](const Instruction::Operand &op) -> bool {1279return (base(op) && op.m_children.size() == 1 && child(op.m_children[0]));1280};1281}12821283std::function<bool(const Instruction::Operand &)>1284lldb_private::OperandMatchers::MatchRegOp(const RegisterInfo &info) {1285return [&info](const Instruction::Operand &op) {1286return (op.m_type == Instruction::Operand::Type::Register &&1287(op.m_register == ConstString(info.name) ||1288op.m_register == ConstString(info.alt_name)));1289};1290}12911292std::function<bool(const Instruction::Operand &)>1293lldb_private::OperandMatchers::FetchRegOp(ConstString ®) {1294return [®](const Instruction::Operand &op) {1295if (op.m_type != Instruction::Operand::Type::Register) {1296return false;1297}1298reg = op.m_register;1299return true;1300};1301}13021303std::function<bool(const Instruction::Operand &)>1304lldb_private::OperandMatchers::MatchImmOp(int64_t imm) {1305return [imm](const Instruction::Operand &op) {1306return (op.m_type == Instruction::Operand::Type::Immediate &&1307((op.m_negative && op.m_immediate == (uint64_t)-imm) ||1308(!op.m_negative && op.m_immediate == (uint64_t)imm)));1309};1310}13111312std::function<bool(const Instruction::Operand &)>1313lldb_private::OperandMatchers::FetchImmOp(int64_t &imm) {1314return [&imm](const Instruction::Operand &op) {1315if (op.m_type != Instruction::Operand::Type::Immediate) {1316return false;1317}1318if (op.m_negative) {1319imm = -((int64_t)op.m_immediate);1320} else {1321imm = ((int64_t)op.m_immediate);1322}1323return true;1324};1325}13261327std::function<bool(const Instruction::Operand &)>1328lldb_private::OperandMatchers::MatchOpType(Instruction::Operand::Type type) {1329return [type](const Instruction::Operand &op) { return op.m_type == type; };1330}133113321333