| //===-- EmulateInstruction.h ------------------------------------*- 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/EmulateInstruction.h" |
| |
| #include "lldb/Core/Address.h" |
| #include "lldb/Core/DataExtractor.h" |
| #include "lldb/Core/Error.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/RegisterValue.h" |
| #include "lldb/Core/StreamFile.h" |
| #include "lldb/Core/StreamString.h" |
| #include "lldb/Host/Endian.h" |
| #include "lldb/Symbol/UnwindPlan.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/RegisterContext.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| EmulateInstruction* |
| EmulateInstruction::FindPlugin (const ArchSpec &arch, InstructionType supported_inst_type, const char *plugin_name) |
| { |
| EmulateInstructionCreateInstance create_callback = NULL; |
| if (plugin_name) |
| { |
| create_callback = PluginManager::GetEmulateInstructionCreateCallbackForPluginName (plugin_name); |
| if (create_callback) |
| { |
| EmulateInstruction *emulate_insn_ptr = create_callback(arch, supported_inst_type); |
| if (emulate_insn_ptr) |
| return emulate_insn_ptr; |
| } |
| } |
| else |
| { |
| for (uint32_t idx = 0; (create_callback = PluginManager::GetEmulateInstructionCreateCallbackAtIndex(idx)) != NULL; ++idx) |
| { |
| EmulateInstruction *emulate_insn_ptr = create_callback(arch, supported_inst_type); |
| if (emulate_insn_ptr) |
| return emulate_insn_ptr; |
| } |
| } |
| return NULL; |
| } |
| |
| EmulateInstruction::EmulateInstruction (const ArchSpec &arch) : |
| m_arch (arch), |
| m_baton (NULL), |
| m_read_mem_callback (&ReadMemoryDefault), |
| m_write_mem_callback (&WriteMemoryDefault), |
| m_read_reg_callback (&ReadRegisterDefault), |
| m_write_reg_callback (&WriteRegisterDefault), |
| m_addr (LLDB_INVALID_ADDRESS) |
| { |
| ::memset (&m_opcode, 0, sizeof (m_opcode)); |
| } |
| |
| |
| bool |
| EmulateInstruction::ReadRegister (const RegisterInfo *reg_info, RegisterValue& reg_value) |
| { |
| if (m_read_reg_callback) |
| return m_read_reg_callback (this, m_baton, reg_info, reg_value); |
| return false; |
| } |
| |
| bool |
| EmulateInstruction::ReadRegister (uint32_t reg_kind, uint32_t reg_num, RegisterValue& reg_value) |
| { |
| RegisterInfo reg_info; |
| if (GetRegisterInfo(reg_kind, reg_num, reg_info)) |
| return ReadRegister (®_info, reg_value); |
| return false; |
| } |
| |
| uint64_t |
| EmulateInstruction::ReadRegisterUnsigned (uint32_t reg_kind, |
| uint32_t reg_num, |
| uint64_t fail_value, |
| bool *success_ptr) |
| { |
| RegisterValue reg_value; |
| if (ReadRegister (reg_kind, reg_num, reg_value)) |
| return reg_value.GetAsUInt64(fail_value, success_ptr); |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| uint64_t |
| EmulateInstruction::ReadRegisterUnsigned (const RegisterInfo *reg_info, |
| uint64_t fail_value, |
| bool *success_ptr) |
| { |
| RegisterValue reg_value; |
| if (ReadRegister (reg_info, reg_value)) |
| return reg_value.GetAsUInt64(fail_value, success_ptr); |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| bool |
| EmulateInstruction::WriteRegister (const Context &context, |
| const RegisterInfo *reg_info, |
| const RegisterValue& reg_value) |
| { |
| if (m_write_reg_callback) |
| return m_write_reg_callback (this, m_baton, context, reg_info, reg_value); |
| return false; |
| } |
| |
| bool |
| EmulateInstruction::WriteRegister (const Context &context, |
| uint32_t reg_kind, |
| uint32_t reg_num, |
| const RegisterValue& reg_value) |
| { |
| RegisterInfo reg_info; |
| if (GetRegisterInfo(reg_kind, reg_num, reg_info)) |
| return WriteRegister (context, ®_info, reg_value); |
| return false; |
| } |
| |
| |
| bool |
| EmulateInstruction::WriteRegisterUnsigned (const Context &context, |
| uint32_t reg_kind, |
| uint32_t reg_num, |
| uint64_t uint_value) |
| { |
| |
| RegisterInfo reg_info; |
| if (GetRegisterInfo(reg_kind, reg_num, reg_info)) |
| { |
| RegisterValue reg_value; |
| if (reg_value.SetUInt(uint_value, reg_info.byte_size)) |
| return WriteRegister (context, ®_info, reg_value); |
| } |
| return false; |
| } |
| |
| bool |
| EmulateInstruction::WriteRegisterUnsigned (const Context &context, |
| const RegisterInfo *reg_info, |
| uint64_t uint_value) |
| { |
| |
| if (reg_info) |
| { |
| RegisterValue reg_value; |
| if (reg_value.SetUInt(uint_value, reg_info->byte_size)) |
| return WriteRegister (context, reg_info, reg_value); |
| } |
| return false; |
| } |
| |
| size_t |
| EmulateInstruction::ReadMemory (const Context &context, |
| lldb::addr_t addr, |
| void *dst, |
| size_t dst_len) |
| { |
| if (m_read_mem_callback) |
| return m_read_mem_callback (this, m_baton, context, addr, dst, dst_len) == dst_len; |
| return false; |
| } |
| |
| uint64_t |
| EmulateInstruction::ReadMemoryUnsigned (const Context &context, lldb::addr_t addr, size_t byte_size, uint64_t fail_value, bool *success_ptr) |
| { |
| uint64_t uval64 = 0; |
| bool success = false; |
| if (byte_size <= 8) |
| { |
| uint8_t buf[sizeof(uint64_t)]; |
| size_t bytes_read = m_read_mem_callback (this, m_baton, context, addr, buf, byte_size); |
| if (bytes_read == byte_size) |
| { |
| uint32_t offset = 0; |
| DataExtractor data (buf, byte_size, GetByteOrder(), GetAddressByteSize()); |
| uval64 = data.GetMaxU64 (&offset, byte_size); |
| success = true; |
| } |
| } |
| |
| if (success_ptr) |
| *success_ptr = success; |
| |
| if (!success) |
| uval64 = fail_value; |
| return uval64; |
| } |
| |
| |
| bool |
| EmulateInstruction::WriteMemoryUnsigned (const Context &context, |
| lldb::addr_t addr, |
| uint64_t uval, |
| size_t uval_byte_size) |
| { |
| StreamString strm(Stream::eBinary, GetAddressByteSize(), GetByteOrder()); |
| strm.PutMaxHex64 (uval, uval_byte_size); |
| |
| size_t bytes_written = m_write_mem_callback (this, m_baton, context, addr, strm.GetData(), uval_byte_size); |
| if (bytes_written == uval_byte_size) |
| return true; |
| return false; |
| } |
| |
| bool |
| EmulateInstruction::WriteMemory (const Context &context, |
| lldb::addr_t addr, |
| const void *src, |
| size_t src_len) |
| { |
| if (m_write_mem_callback) |
| return m_write_mem_callback (this, m_baton, context, addr, src, src_len) == src_len; |
| return false; |
| } |
| |
| |
| void |
| EmulateInstruction::SetBaton (void *baton) |
| { |
| m_baton = baton; |
| } |
| |
| void |
| EmulateInstruction::SetCallbacks (ReadMemoryCallback read_mem_callback, |
| WriteMemoryCallback write_mem_callback, |
| ReadRegisterCallback read_reg_callback, |
| WriteRegisterCallback write_reg_callback) |
| { |
| m_read_mem_callback = read_mem_callback; |
| m_write_mem_callback = write_mem_callback; |
| m_read_reg_callback = read_reg_callback; |
| m_write_reg_callback = write_reg_callback; |
| } |
| |
| void |
| EmulateInstruction::SetReadMemCallback (ReadMemoryCallback read_mem_callback) |
| { |
| m_read_mem_callback = read_mem_callback; |
| } |
| |
| |
| void |
| EmulateInstruction::SetWriteMemCallback (WriteMemoryCallback write_mem_callback) |
| { |
| m_write_mem_callback = write_mem_callback; |
| } |
| |
| |
| void |
| EmulateInstruction::SetReadRegCallback (ReadRegisterCallback read_reg_callback) |
| { |
| m_read_reg_callback = read_reg_callback; |
| } |
| |
| |
| void |
| EmulateInstruction::SetWriteRegCallback (WriteRegisterCallback write_reg_callback) |
| { |
| m_write_reg_callback = write_reg_callback; |
| } |
| |
| |
| |
| // |
| // Read & Write Memory and Registers callback functions. |
| // |
| |
| size_t |
| EmulateInstruction::ReadMemoryFrame (EmulateInstruction *instruction, |
| void *baton, |
| const Context &context, |
| lldb::addr_t addr, |
| void *dst, |
| size_t dst_len) |
| { |
| if (!baton || dst == NULL || dst_len == 0) |
| return 0; |
| |
| StackFrame *frame = (StackFrame *) baton; |
| |
| ProcessSP process_sp (frame->CalculateProcess()); |
| if (process_sp) |
| { |
| Error error; |
| return process_sp->ReadMemory (addr, dst, dst_len, error); |
| } |
| return 0; |
| } |
| |
| size_t |
| EmulateInstruction::WriteMemoryFrame (EmulateInstruction *instruction, |
| void *baton, |
| const Context &context, |
| lldb::addr_t addr, |
| const void *src, |
| size_t src_len) |
| { |
| if (!baton || src == NULL || src_len == 0) |
| return 0; |
| |
| StackFrame *frame = (StackFrame *) baton; |
| |
| ProcessSP process_sp (frame->CalculateProcess()); |
| if (process_sp) |
| { |
| Error error; |
| return process_sp->WriteMemory (addr, src, src_len, error); |
| } |
| |
| return 0; |
| } |
| |
| bool |
| EmulateInstruction::ReadRegisterFrame (EmulateInstruction *instruction, |
| void *baton, |
| const RegisterInfo *reg_info, |
| RegisterValue ®_value) |
| { |
| if (!baton) |
| return false; |
| |
| StackFrame *frame = (StackFrame *) baton; |
| return frame->GetRegisterContext()->ReadRegister (reg_info, reg_value); |
| } |
| |
| bool |
| EmulateInstruction::WriteRegisterFrame (EmulateInstruction *instruction, |
| void *baton, |
| const Context &context, |
| const RegisterInfo *reg_info, |
| const RegisterValue ®_value) |
| { |
| if (!baton) |
| return false; |
| |
| StackFrame *frame = (StackFrame *) baton; |
| return frame->GetRegisterContext()->WriteRegister (reg_info, reg_value); |
| } |
| |
| size_t |
| EmulateInstruction::ReadMemoryDefault (EmulateInstruction *instruction, |
| void *baton, |
| const Context &context, |
| lldb::addr_t addr, |
| void *dst, |
| size_t length) |
| { |
| StreamFile strm (stdout, false); |
| strm.Printf (" Read from Memory (address = 0x%llx, length = %llu, context = ", addr, (uint64_t)length); |
| context.Dump (strm, instruction); |
| strm.EOL(); |
| *((uint64_t *) dst) = 0xdeadbeef; |
| return length; |
| } |
| |
| size_t |
| EmulateInstruction::WriteMemoryDefault (EmulateInstruction *instruction, |
| void *baton, |
| const Context &context, |
| lldb::addr_t addr, |
| const void *dst, |
| size_t length) |
| { |
| StreamFile strm (stdout, false); |
| strm.Printf (" Write to Memory (address = 0x%llx, length = %llu, context = ", addr, (uint64_t)length); |
| context.Dump (strm, instruction); |
| strm.EOL(); |
| return length; |
| } |
| |
| bool |
| EmulateInstruction::ReadRegisterDefault (EmulateInstruction *instruction, |
| void *baton, |
| const RegisterInfo *reg_info, |
| RegisterValue ®_value) |
| { |
| StreamFile strm (stdout, false); |
| strm.Printf (" Read Register (%s)\n", reg_info->name); |
| uint32_t reg_kind, reg_num; |
| if (GetBestRegisterKindAndNumber (reg_info, reg_kind, reg_num)) |
| reg_value.SetUInt64((uint64_t)reg_kind << 24 | reg_num); |
| else |
| reg_value.SetUInt64(0); |
| |
| return true; |
| } |
| |
| bool |
| EmulateInstruction::WriteRegisterDefault (EmulateInstruction *instruction, |
| void *baton, |
| const Context &context, |
| const RegisterInfo *reg_info, |
| const RegisterValue ®_value) |
| { |
| StreamFile strm (stdout, false); |
| strm.Printf (" Write to Register (name = %s, value = " , reg_info->name); |
| reg_value.Dump(&strm, reg_info, false, false, eFormatDefault); |
| strm.PutCString (", context = "); |
| context.Dump (strm, instruction); |
| strm.EOL(); |
| return true; |
| } |
| |
| void |
| EmulateInstruction::Context::Dump (Stream &strm, |
| EmulateInstruction *instruction) const |
| { |
| switch (type) |
| { |
| case eContextReadOpcode: |
| strm.PutCString ("reading opcode"); |
| break; |
| |
| case eContextImmediate: |
| strm.PutCString ("immediate"); |
| break; |
| |
| case eContextPushRegisterOnStack: |
| strm.PutCString ("push register"); |
| break; |
| |
| case eContextPopRegisterOffStack: |
| strm.PutCString ("pop register"); |
| break; |
| |
| case eContextAdjustStackPointer: |
| strm.PutCString ("adjust sp"); |
| break; |
| |
| case eContextSetFramePointer: |
| strm.PutCString ("set frame pointer"); |
| break; |
| |
| case eContextAdjustBaseRegister: |
| strm.PutCString ("adjusting (writing value back to) a base register"); |
| break; |
| |
| case eContextRegisterPlusOffset: |
| strm.PutCString ("register + offset"); |
| break; |
| |
| case eContextRegisterStore: |
| strm.PutCString ("store register"); |
| break; |
| |
| case eContextRegisterLoad: |
| strm.PutCString ("load register"); |
| break; |
| |
| case eContextRelativeBranchImmediate: |
| strm.PutCString ("relative branch immediate"); |
| break; |
| |
| case eContextAbsoluteBranchRegister: |
| strm.PutCString ("absolute branch register"); |
| break; |
| |
| case eContextSupervisorCall: |
| strm.PutCString ("supervisor call"); |
| break; |
| |
| case eContextTableBranchReadMemory: |
| strm.PutCString ("table branch read memory"); |
| break; |
| |
| case eContextWriteRegisterRandomBits: |
| strm.PutCString ("write random bits to a register"); |
| break; |
| |
| case eContextWriteMemoryRandomBits: |
| strm.PutCString ("write random bits to a memory address"); |
| break; |
| |
| case eContextArithmetic: |
| strm.PutCString ("arithmetic"); |
| break; |
| |
| case eContextReturnFromException: |
| strm.PutCString ("return from exception"); |
| break; |
| |
| default: |
| strm.PutCString ("unrecognized context."); |
| break; |
| } |
| |
| switch (info_type) |
| { |
| case eInfoTypeRegisterPlusOffset: |
| { |
| strm.Printf (" (reg_plus_offset = %s%+lld)", |
| info.RegisterPlusOffset.reg.name, |
| info.RegisterPlusOffset.signed_offset); |
| } |
| break; |
| |
| case eInfoTypeRegisterPlusIndirectOffset: |
| { |
| strm.Printf (" (reg_plus_reg = %s + %s)", |
| info.RegisterPlusIndirectOffset.base_reg.name, |
| info.RegisterPlusIndirectOffset.offset_reg.name); |
| } |
| break; |
| |
| case eInfoTypeRegisterToRegisterPlusOffset: |
| { |
| strm.Printf (" (base_and_imm_offset = %s%+lld, data_reg = %s)", |
| info.RegisterToRegisterPlusOffset.base_reg.name, |
| info.RegisterToRegisterPlusOffset.offset, |
| info.RegisterToRegisterPlusOffset.data_reg.name); |
| } |
| break; |
| |
| case eInfoTypeRegisterToRegisterPlusIndirectOffset: |
| { |
| strm.Printf (" (base_and_reg_offset = %s + %s, data_reg = %s)", |
| info.RegisterToRegisterPlusIndirectOffset.base_reg.name, |
| info.RegisterToRegisterPlusIndirectOffset.offset_reg.name, |
| info.RegisterToRegisterPlusIndirectOffset.data_reg.name); |
| } |
| break; |
| |
| case eInfoTypeRegisterRegisterOperands: |
| { |
| strm.Printf (" (register to register binary op: %s and %s)", |
| info.RegisterRegisterOperands.operand1.name, |
| info.RegisterRegisterOperands.operand2.name); |
| } |
| break; |
| |
| case eInfoTypeOffset: |
| strm.Printf (" (signed_offset = %+lld)", info.signed_offset); |
| break; |
| |
| case eInfoTypeRegister: |
| strm.Printf (" (reg = %s)", info.reg.name); |
| break; |
| |
| case eInfoTypeImmediate: |
| strm.Printf (" (unsigned_immediate = %llu (0x%16.16llx))", |
| info.unsigned_immediate, |
| info.unsigned_immediate); |
| break; |
| |
| case eInfoTypeImmediateSigned: |
| strm.Printf (" (signed_immediate = %+lld (0x%16.16llx))", |
| info.signed_immediate, |
| info.signed_immediate); |
| break; |
| |
| case eInfoTypeAddress: |
| strm.Printf (" (address = 0x%llx)", info.address); |
| break; |
| |
| case eInfoTypeISAAndImmediate: |
| strm.Printf (" (isa = %u, unsigned_immediate = %u (0x%8.8x))", |
| info.ISAAndImmediate.isa, |
| info.ISAAndImmediate.unsigned_data32, |
| info.ISAAndImmediate.unsigned_data32); |
| break; |
| |
| case eInfoTypeISAAndImmediateSigned: |
| strm.Printf (" (isa = %u, signed_immediate = %i (0x%8.8x))", |
| info.ISAAndImmediateSigned.isa, |
| info.ISAAndImmediateSigned.signed_data32, |
| info.ISAAndImmediateSigned.signed_data32); |
| break; |
| |
| case eInfoTypeISA: |
| strm.Printf (" (isa = %u)", info.isa); |
| break; |
| |
| case eInfoTypeNoArgs: |
| break; |
| |
| default: |
| strm.Printf (" (unknown <info_type>)"); |
| break; |
| } |
| } |
| |
| bool |
| EmulateInstruction::SetInstruction (const Opcode &opcode, const Address &inst_addr, Target *target) |
| { |
| m_opcode = opcode; |
| m_addr = LLDB_INVALID_ADDRESS; |
| if (inst_addr.IsValid()) |
| { |
| if (target) |
| m_addr = inst_addr.GetLoadAddress (target); |
| if (m_addr == LLDB_INVALID_ADDRESS) |
| m_addr = inst_addr.GetFileAddress (); |
| } |
| return true; |
| } |
| |
| bool |
| EmulateInstruction::GetBestRegisterKindAndNumber (const RegisterInfo *reg_info, |
| uint32_t ®_kind, |
| uint32_t ®_num) |
| { |
| // Generic and DWARF should be the two most popular register kinds when |
| // emulating instructions since they are the most platform agnostic... |
| reg_num = reg_info->kinds[eRegisterKindGeneric]; |
| if (reg_num != LLDB_INVALID_REGNUM) |
| { |
| reg_kind = eRegisterKindGeneric; |
| return true; |
| } |
| |
| reg_num = reg_info->kinds[eRegisterKindDWARF]; |
| if (reg_num != LLDB_INVALID_REGNUM) |
| { |
| reg_kind = eRegisterKindDWARF; |
| return true; |
| } |
| |
| reg_num = reg_info->kinds[eRegisterKindLLDB]; |
| if (reg_num != LLDB_INVALID_REGNUM) |
| { |
| reg_kind = eRegisterKindLLDB; |
| return true; |
| } |
| |
| reg_num = reg_info->kinds[eRegisterKindGCC]; |
| if (reg_num != LLDB_INVALID_REGNUM) |
| { |
| reg_kind = eRegisterKindGCC; |
| return true; |
| } |
| |
| reg_num = reg_info->kinds[eRegisterKindGDB]; |
| if (reg_num != LLDB_INVALID_REGNUM) |
| { |
| reg_kind = eRegisterKindGDB; |
| return true; |
| } |
| return false; |
| } |
| |
| uint32_t |
| EmulateInstruction::GetInternalRegisterNumber (RegisterContext *reg_ctx, const RegisterInfo ®_info) |
| { |
| uint32_t reg_kind, reg_num; |
| if (reg_ctx && GetBestRegisterKindAndNumber (®_info, reg_kind, reg_num)) |
| return reg_ctx->ConvertRegisterKindToRegisterNumber (reg_kind, reg_num); |
| return LLDB_INVALID_REGNUM; |
| } |
| |
| |
| bool |
| EmulateInstruction::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) |
| { |
| unwind_plan.Clear(); |
| return false; |
| } |
| |
| |