| //===-- EmulateInstructionMIPS.cpp -------------------------------*- C++-*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "EmulateInstructionMIPS.h" |
| |
| #include <stdlib.h> |
| |
| #include "lldb/Core/Address.h" |
| #include "lldb/Core/Opcode.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/RegisterValue.h" |
| #include "lldb/Symbol/UnwindPlan.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Utility/ArchSpec.h" |
| #include "lldb/Utility/ConstString.h" |
| #include "lldb/Utility/DataExtractor.h" |
| #include "lldb/Utility/Stream.h" |
| #include "llvm-c/Disassembler.h" |
| #include "llvm/MC/MCAsmInfo.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCDisassembler/MCDisassembler.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCInstrInfo.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/MC/MCSubtargetInfo.h" |
| #include "llvm/Support/TargetRegistry.h" |
| #include "llvm/Support/TargetSelect.h" |
| |
| #include "llvm/ADT/STLExtras.h" |
| |
| #include "Plugins/Process/Utility/InstructionUtils.h" |
| #include "Plugins/Process/Utility/RegisterContext_mips.h" //mips32 has same registers nos as mips64 |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| #define UInt(x) ((uint64_t)x) |
| #define integer int64_t |
| |
| //---------------------------------------------------------------------- |
| // |
| // EmulateInstructionMIPS implementation |
| // |
| //---------------------------------------------------------------------- |
| |
| #ifdef __mips__ |
| extern "C" { |
| void LLVMInitializeMipsTargetInfo(); |
| void LLVMInitializeMipsTarget(); |
| void LLVMInitializeMipsAsmPrinter(); |
| void LLVMInitializeMipsTargetMC(); |
| void LLVMInitializeMipsDisassembler(); |
| } |
| #endif |
| |
| EmulateInstructionMIPS::EmulateInstructionMIPS( |
| const lldb_private::ArchSpec &arch) |
| : EmulateInstruction(arch) { |
| /* Create instance of llvm::MCDisassembler */ |
| std::string Status; |
| llvm::Triple triple = arch.GetTriple(); |
| const llvm::Target *target = |
| llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status); |
| |
| /* |
| * If we fail to get the target then we haven't registered it. The |
| * SystemInitializerCommon |
| * does not initialize targets, MCs and disassemblers. However we need the |
| * MCDisassembler |
| * to decode the instructions so that the decoding complexity stays with LLVM. |
| * Initialize the MIPS targets and disassemblers. |
| */ |
| #ifdef __mips__ |
| if (!target) { |
| LLVMInitializeMipsTargetInfo(); |
| LLVMInitializeMipsTarget(); |
| LLVMInitializeMipsAsmPrinter(); |
| LLVMInitializeMipsTargetMC(); |
| LLVMInitializeMipsDisassembler(); |
| target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status); |
| } |
| #endif |
| |
| assert(target); |
| |
| llvm::StringRef cpu; |
| |
| switch (arch.GetCore()) { |
| case ArchSpec::eCore_mips32: |
| case ArchSpec::eCore_mips32el: |
| cpu = "mips32"; |
| break; |
| case ArchSpec::eCore_mips32r2: |
| case ArchSpec::eCore_mips32r2el: |
| cpu = "mips32r2"; |
| break; |
| case ArchSpec::eCore_mips32r3: |
| case ArchSpec::eCore_mips32r3el: |
| cpu = "mips32r3"; |
| break; |
| case ArchSpec::eCore_mips32r5: |
| case ArchSpec::eCore_mips32r5el: |
| cpu = "mips32r5"; |
| break; |
| case ArchSpec::eCore_mips32r6: |
| case ArchSpec::eCore_mips32r6el: |
| cpu = "mips32r6"; |
| break; |
| case ArchSpec::eCore_mips64: |
| case ArchSpec::eCore_mips64el: |
| cpu = "mips64"; |
| break; |
| case ArchSpec::eCore_mips64r2: |
| case ArchSpec::eCore_mips64r2el: |
| cpu = "mips64r2"; |
| break; |
| case ArchSpec::eCore_mips64r3: |
| case ArchSpec::eCore_mips64r3el: |
| cpu = "mips64r3"; |
| break; |
| case ArchSpec::eCore_mips64r5: |
| case ArchSpec::eCore_mips64r5el: |
| cpu = "mips64r5"; |
| break; |
| case ArchSpec::eCore_mips64r6: |
| case ArchSpec::eCore_mips64r6el: |
| cpu = "mips64r6"; |
| break; |
| default: |
| cpu = "generic"; |
| break; |
| } |
| |
| std::string features = ""; |
| uint32_t arch_flags = arch.GetFlags(); |
| if (arch_flags & ArchSpec::eMIPSAse_msa) |
| features += "+msa,"; |
| if (arch_flags & ArchSpec::eMIPSAse_dsp) |
| features += "+dsp,"; |
| if (arch_flags & ArchSpec::eMIPSAse_dspr2) |
| features += "+dspr2,"; |
| |
| m_reg_info.reset(target->createMCRegInfo(triple.getTriple())); |
| assert(m_reg_info.get()); |
| |
| m_insn_info.reset(target->createMCInstrInfo()); |
| assert(m_insn_info.get()); |
| |
| m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple.getTriple())); |
| m_subtype_info.reset( |
| target->createMCSubtargetInfo(triple.getTriple(), cpu, features)); |
| assert(m_asm_info.get() && m_subtype_info.get()); |
| |
| m_context.reset( |
| new llvm::MCContext(m_asm_info.get(), m_reg_info.get(), nullptr)); |
| assert(m_context.get()); |
| |
| m_disasm.reset(target->createMCDisassembler(*m_subtype_info, *m_context)); |
| assert(m_disasm.get()); |
| |
| /* Create alternate disassembler for microMIPS */ |
| if (arch_flags & ArchSpec::eMIPSAse_mips16) |
| features += "+mips16,"; |
| else if (arch_flags & ArchSpec::eMIPSAse_micromips) |
| features += "+micromips,"; |
| |
| m_alt_subtype_info.reset( |
| target->createMCSubtargetInfo(triple.getTriple(), cpu, features)); |
| assert(m_alt_subtype_info.get()); |
| |
| m_alt_disasm.reset( |
| target->createMCDisassembler(*m_alt_subtype_info, *m_context)); |
| assert(m_alt_disasm.get()); |
| |
| m_next_inst_size = 0; |
| m_use_alt_disaasm = false; |
| } |
| |
| void EmulateInstructionMIPS::Initialize() { |
| PluginManager::RegisterPlugin(GetPluginNameStatic(), |
| GetPluginDescriptionStatic(), CreateInstance); |
| } |
| |
| void EmulateInstructionMIPS::Terminate() { |
| PluginManager::UnregisterPlugin(CreateInstance); |
| } |
| |
| ConstString EmulateInstructionMIPS::GetPluginNameStatic() { |
| ConstString g_plugin_name("lldb.emulate-instruction.mips32"); |
| return g_plugin_name; |
| } |
| |
| lldb_private::ConstString EmulateInstructionMIPS::GetPluginName() { |
| static ConstString g_plugin_name("EmulateInstructionMIPS"); |
| return g_plugin_name; |
| } |
| |
| const char *EmulateInstructionMIPS::GetPluginDescriptionStatic() { |
| return "Emulate instructions for the MIPS32 architecture."; |
| } |
| |
| EmulateInstruction * |
| EmulateInstructionMIPS::CreateInstance(const ArchSpec &arch, |
| InstructionType inst_type) { |
| if (EmulateInstructionMIPS::SupportsEmulatingInstructionsOfTypeStatic( |
| inst_type)) { |
| if (arch.GetTriple().getArch() == llvm::Triple::mips || |
| arch.GetTriple().getArch() == llvm::Triple::mipsel) { |
| return new EmulateInstructionMIPS(arch); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| bool EmulateInstructionMIPS::SetTargetTriple(const ArchSpec &arch) { |
| if (arch.GetTriple().getArch() == llvm::Triple::mips || |
| arch.GetTriple().getArch() == llvm::Triple::mipsel) |
| return true; |
| return false; |
| } |
| |
| const char *EmulateInstructionMIPS::GetRegisterName(unsigned reg_num, |
| bool alternate_name) { |
| if (alternate_name) { |
| switch (reg_num) { |
| case dwarf_sp_mips: |
| return "r29"; |
| case dwarf_r30_mips: |
| return "r30"; |
| case dwarf_ra_mips: |
| return "r31"; |
| case dwarf_f0_mips: |
| return "f0"; |
| case dwarf_f1_mips: |
| return "f1"; |
| case dwarf_f2_mips: |
| return "f2"; |
| case dwarf_f3_mips: |
| return "f3"; |
| case dwarf_f4_mips: |
| return "f4"; |
| case dwarf_f5_mips: |
| return "f5"; |
| case dwarf_f6_mips: |
| return "f6"; |
| case dwarf_f7_mips: |
| return "f7"; |
| case dwarf_f8_mips: |
| return "f8"; |
| case dwarf_f9_mips: |
| return "f9"; |
| case dwarf_f10_mips: |
| return "f10"; |
| case dwarf_f11_mips: |
| return "f11"; |
| case dwarf_f12_mips: |
| return "f12"; |
| case dwarf_f13_mips: |
| return "f13"; |
| case dwarf_f14_mips: |
| return "f14"; |
| case dwarf_f15_mips: |
| return "f15"; |
| case dwarf_f16_mips: |
| return "f16"; |
| case dwarf_f17_mips: |
| return "f17"; |
| case dwarf_f18_mips: |
| return "f18"; |
| case dwarf_f19_mips: |
| return "f19"; |
| case dwarf_f20_mips: |
| return "f20"; |
| case dwarf_f21_mips: |
| return "f21"; |
| case dwarf_f22_mips: |
| return "f22"; |
| case dwarf_f23_mips: |
| return "f23"; |
| case dwarf_f24_mips: |
| return "f24"; |
| case dwarf_f25_mips: |
| return "f25"; |
| case dwarf_f26_mips: |
| return "f26"; |
| case dwarf_f27_mips: |
| return "f27"; |
| case dwarf_f28_mips: |
| return "f28"; |
| case dwarf_f29_mips: |
| return "f29"; |
| case dwarf_f30_mips: |
| return "f30"; |
| case dwarf_f31_mips: |
| return "f31"; |
| case dwarf_w0_mips: |
| return "w0"; |
| case dwarf_w1_mips: |
| return "w1"; |
| case dwarf_w2_mips: |
| return "w2"; |
| case dwarf_w3_mips: |
| return "w3"; |
| case dwarf_w4_mips: |
| return "w4"; |
| case dwarf_w5_mips: |
| return "w5"; |
| case dwarf_w6_mips: |
| return "w6"; |
| case dwarf_w7_mips: |
| return "w7"; |
| case dwarf_w8_mips: |
| return "w8"; |
| case dwarf_w9_mips: |
| return "w9"; |
| case dwarf_w10_mips: |
| return "w10"; |
| case dwarf_w11_mips: |
| return "w11"; |
| case dwarf_w12_mips: |
| return "w12"; |
| case dwarf_w13_mips: |
| return "w13"; |
| case dwarf_w14_mips: |
| return "w14"; |
| case dwarf_w15_mips: |
| return "w15"; |
| case dwarf_w16_mips: |
| return "w16"; |
| case dwarf_w17_mips: |
| return "w17"; |
| case dwarf_w18_mips: |
| return "w18"; |
| case dwarf_w19_mips: |
| return "w19"; |
| case dwarf_w20_mips: |
| return "w20"; |
| case dwarf_w21_mips: |
| return "w21"; |
| case dwarf_w22_mips: |
| return "w22"; |
| case dwarf_w23_mips: |
| return "w23"; |
| case dwarf_w24_mips: |
| return "w24"; |
| case dwarf_w25_mips: |
| return "w25"; |
| case dwarf_w26_mips: |
| return "w26"; |
| case dwarf_w27_mips: |
| return "w27"; |
| case dwarf_w28_mips: |
| return "w28"; |
| case dwarf_w29_mips: |
| return "w29"; |
| case dwarf_w30_mips: |
| return "w30"; |
| case dwarf_w31_mips: |
| return "w31"; |
| case dwarf_mir_mips: |
| return "mir"; |
| case dwarf_mcsr_mips: |
| return "mcsr"; |
| case dwarf_config5_mips: |
| return "config5"; |
| default: |
| break; |
| } |
| return nullptr; |
| } |
| |
| switch (reg_num) { |
| case dwarf_zero_mips: |
| return "r0"; |
| case dwarf_r1_mips: |
| return "r1"; |
| case dwarf_r2_mips: |
| return "r2"; |
| case dwarf_r3_mips: |
| return "r3"; |
| case dwarf_r4_mips: |
| return "r4"; |
| case dwarf_r5_mips: |
| return "r5"; |
| case dwarf_r6_mips: |
| return "r6"; |
| case dwarf_r7_mips: |
| return "r7"; |
| case dwarf_r8_mips: |
| return "r8"; |
| case dwarf_r9_mips: |
| return "r9"; |
| case dwarf_r10_mips: |
| return "r10"; |
| case dwarf_r11_mips: |
| return "r11"; |
| case dwarf_r12_mips: |
| return "r12"; |
| case dwarf_r13_mips: |
| return "r13"; |
| case dwarf_r14_mips: |
| return "r14"; |
| case dwarf_r15_mips: |
| return "r15"; |
| case dwarf_r16_mips: |
| return "r16"; |
| case dwarf_r17_mips: |
| return "r17"; |
| case dwarf_r18_mips: |
| return "r18"; |
| case dwarf_r19_mips: |
| return "r19"; |
| case dwarf_r20_mips: |
| return "r20"; |
| case dwarf_r21_mips: |
| return "r21"; |
| case dwarf_r22_mips: |
| return "r22"; |
| case dwarf_r23_mips: |
| return "r23"; |
| case dwarf_r24_mips: |
| return "r24"; |
| case dwarf_r25_mips: |
| return "r25"; |
| case dwarf_r26_mips: |
| return "r26"; |
| case dwarf_r27_mips: |
| return "r27"; |
| case dwarf_gp_mips: |
| return "gp"; |
| case dwarf_sp_mips: |
| return "sp"; |
| case dwarf_r30_mips: |
| return "fp"; |
| case dwarf_ra_mips: |
| return "ra"; |
| case dwarf_sr_mips: |
| return "sr"; |
| case dwarf_lo_mips: |
| return "lo"; |
| case dwarf_hi_mips: |
| return "hi"; |
| case dwarf_bad_mips: |
| return "bad"; |
| case dwarf_cause_mips: |
| return "cause"; |
| case dwarf_pc_mips: |
| return "pc"; |
| case dwarf_f0_mips: |
| return "f0"; |
| case dwarf_f1_mips: |
| return "f1"; |
| case dwarf_f2_mips: |
| return "f2"; |
| case dwarf_f3_mips: |
| return "f3"; |
| case dwarf_f4_mips: |
| return "f4"; |
| case dwarf_f5_mips: |
| return "f5"; |
| case dwarf_f6_mips: |
| return "f6"; |
| case dwarf_f7_mips: |
| return "f7"; |
| case dwarf_f8_mips: |
| return "f8"; |
| case dwarf_f9_mips: |
| return "f9"; |
| case dwarf_f10_mips: |
| return "f10"; |
| case dwarf_f11_mips: |
| return "f11"; |
| case dwarf_f12_mips: |
| return "f12"; |
| case dwarf_f13_mips: |
| return "f13"; |
| case dwarf_f14_mips: |
| return "f14"; |
| case dwarf_f15_mips: |
| return "f15"; |
| case dwarf_f16_mips: |
| return "f16"; |
| case dwarf_f17_mips: |
| return "f17"; |
| case dwarf_f18_mips: |
| return "f18"; |
| case dwarf_f19_mips: |
| return "f19"; |
| case dwarf_f20_mips: |
| return "f20"; |
| case dwarf_f21_mips: |
| return "f21"; |
| case dwarf_f22_mips: |
| return "f22"; |
| case dwarf_f23_mips: |
| return "f23"; |
| case dwarf_f24_mips: |
| return "f24"; |
| case dwarf_f25_mips: |
| return "f25"; |
| case dwarf_f26_mips: |
| return "f26"; |
| case dwarf_f27_mips: |
| return "f27"; |
| case dwarf_f28_mips: |
| return "f28"; |
| case dwarf_f29_mips: |
| return "f29"; |
| case dwarf_f30_mips: |
| return "f30"; |
| case dwarf_f31_mips: |
| return "f31"; |
| case dwarf_fcsr_mips: |
| return "fcsr"; |
| case dwarf_fir_mips: |
| return "fir"; |
| case dwarf_w0_mips: |
| return "w0"; |
| case dwarf_w1_mips: |
| return "w1"; |
| case dwarf_w2_mips: |
| return "w2"; |
| case dwarf_w3_mips: |
| return "w3"; |
| case dwarf_w4_mips: |
| return "w4"; |
| case dwarf_w5_mips: |
| return "w5"; |
| case dwarf_w6_mips: |
| return "w6"; |
| case dwarf_w7_mips: |
| return "w7"; |
| case dwarf_w8_mips: |
| return "w8"; |
| case dwarf_w9_mips: |
| return "w9"; |
| case dwarf_w10_mips: |
| return "w10"; |
| case dwarf_w11_mips: |
| return "w11"; |
| case dwarf_w12_mips: |
| return "w12"; |
| case dwarf_w13_mips: |
| return "w13"; |
| case dwarf_w14_mips: |
| return "w14"; |
| case dwarf_w15_mips: |
| return "w15"; |
| case dwarf_w16_mips: |
| return "w16"; |
| case dwarf_w17_mips: |
| return "w17"; |
| case dwarf_w18_mips: |
| return "w18"; |
| case dwarf_w19_mips: |
| return "w19"; |
| case dwarf_w20_mips: |
| return "w20"; |
| case dwarf_w21_mips: |
| return "w21"; |
| case dwarf_w22_mips: |
| return "w22"; |
| case dwarf_w23_mips: |
| return "w23"; |
| case dwarf_w24_mips: |
| return "w24"; |
| case dwarf_w25_mips: |
| return "w25"; |
| case dwarf_w26_mips: |
| return "w26"; |
| case dwarf_w27_mips: |
| return "w27"; |
| case dwarf_w28_mips: |
| return "w28"; |
| case dwarf_w29_mips: |
| return "w29"; |
| case dwarf_w30_mips: |
| return "w30"; |
| case dwarf_w31_mips: |
| return "w31"; |
| case dwarf_mcsr_mips: |
| return "mcsr"; |
| case dwarf_mir_mips: |
| return "mir"; |
| case dwarf_config5_mips: |
| return "config5"; |
| } |
| return nullptr; |
| } |
| |
| bool EmulateInstructionMIPS::GetRegisterInfo(RegisterKind reg_kind, |
| uint32_t reg_num, |
| RegisterInfo ®_info) { |
| if (reg_kind == eRegisterKindGeneric) { |
| switch (reg_num) { |
| case LLDB_REGNUM_GENERIC_PC: |
| reg_kind = eRegisterKindDWARF; |
| reg_num = dwarf_pc_mips; |
| break; |
| case LLDB_REGNUM_GENERIC_SP: |
| reg_kind = eRegisterKindDWARF; |
| reg_num = dwarf_sp_mips; |
| break; |
| case LLDB_REGNUM_GENERIC_FP: |
| reg_kind = eRegisterKindDWARF; |
| reg_num = dwarf_r30_mips; |
| break; |
| case LLDB_REGNUM_GENERIC_RA: |
| reg_kind = eRegisterKindDWARF; |
| reg_num = dwarf_ra_mips; |
| break; |
| case LLDB_REGNUM_GENERIC_FLAGS: |
| reg_kind = eRegisterKindDWARF; |
| reg_num = dwarf_sr_mips; |
| break; |
| default: |
| return false; |
| } |
| } |
| |
| if (reg_kind == eRegisterKindDWARF) { |
| ::memset(®_info, 0, sizeof(RegisterInfo)); |
| ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); |
| |
| if (reg_num == dwarf_sr_mips || reg_num == dwarf_fcsr_mips || |
| reg_num == dwarf_fir_mips || reg_num == dwarf_mcsr_mips || |
| reg_num == dwarf_mir_mips || reg_num == dwarf_config5_mips) { |
| reg_info.byte_size = 4; |
| reg_info.format = eFormatHex; |
| reg_info.encoding = eEncodingUint; |
| } else if ((int)reg_num >= dwarf_zero_mips && |
| (int)reg_num <= dwarf_f31_mips) { |
| reg_info.byte_size = 4; |
| reg_info.format = eFormatHex; |
| reg_info.encoding = eEncodingUint; |
| } else if ((int)reg_num >= dwarf_w0_mips && |
| (int)reg_num <= dwarf_w31_mips) { |
| reg_info.byte_size = 16; |
| reg_info.format = eFormatVectorOfUInt8; |
| reg_info.encoding = eEncodingVector; |
| } else { |
| return false; |
| } |
| |
| reg_info.name = GetRegisterName(reg_num, false); |
| reg_info.alt_name = GetRegisterName(reg_num, true); |
| reg_info.kinds[eRegisterKindDWARF] = reg_num; |
| |
| switch (reg_num) { |
| case dwarf_r30_mips: |
| reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; |
| break; |
| case dwarf_ra_mips: |
| reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; |
| break; |
| case dwarf_sp_mips: |
| reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; |
| break; |
| case dwarf_pc_mips: |
| reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; |
| break; |
| case dwarf_sr_mips: |
| reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| EmulateInstructionMIPS::MipsOpcode * |
| EmulateInstructionMIPS::GetOpcodeForInstruction(const char *op_name) { |
| static EmulateInstructionMIPS::MipsOpcode g_opcodes[] = { |
| //---------------------------------------------------------------------- |
| // Prologue/Epilogue instructions |
| //---------------------------------------------------------------------- |
| {"ADDiu", &EmulateInstructionMIPS::Emulate_ADDiu, |
| "ADDIU rt, rs, immediate"}, |
| {"SW", &EmulateInstructionMIPS::Emulate_SW, "SW rt, offset(rs)"}, |
| {"LW", &EmulateInstructionMIPS::Emulate_LW, "LW rt, offset(base)"}, |
| {"SUBU", &EmulateInstructionMIPS::Emulate_SUBU_ADDU, "SUBU rd, rs, rt"}, |
| {"ADDU", &EmulateInstructionMIPS::Emulate_SUBU_ADDU, "ADDU rd, rs, rt"}, |
| {"LUI", &EmulateInstructionMIPS::Emulate_LUI, "LUI rt, immediate"}, |
| |
| //---------------------------------------------------------------------- |
| // MicroMIPS Prologue/Epilogue instructions |
| //---------------------------------------------------------------------- |
| {"ADDIUSP_MM", &EmulateInstructionMIPS::Emulate_ADDIUSP, |
| "ADDIU immediate"}, |
| {"ADDIUS5_MM", &EmulateInstructionMIPS::Emulate_ADDIUS5, |
| "ADDIUS5 rd,immediate"}, |
| {"SWSP_MM", &EmulateInstructionMIPS::Emulate_SWSP, "SWSP rt,offset(sp)"}, |
| {"SWM16_MM", &EmulateInstructionMIPS::Emulate_SWM16_32, |
| "SWM16 reglist,offset(sp)"}, |
| {"SWM32_MM", &EmulateInstructionMIPS::Emulate_SWM16_32, |
| "SWM32 reglist,offset(base)"}, |
| {"SWP_MM", &EmulateInstructionMIPS::Emulate_SWM16_32, |
| "SWP rs1,offset(base)"}, |
| {"LWSP_MM", &EmulateInstructionMIPS::Emulate_LWSP, "LWSP rt,offset(sp)"}, |
| {"LWM16_MM", &EmulateInstructionMIPS::Emulate_LWM16_32, |
| "LWM16 reglist,offset(sp)"}, |
| {"LWM32_MM", &EmulateInstructionMIPS::Emulate_LWM16_32, |
| "LWM32 reglist,offset(base)"}, |
| {"LWP_MM", &EmulateInstructionMIPS::Emulate_LWM16_32, |
| "LWP rd,offset(base)"}, |
| {"JRADDIUSP", &EmulateInstructionMIPS::Emulate_JRADDIUSP, |
| "JRADDIUSP immediate"}, |
| //---------------------------------------------------------------------- |
| |
| // Load/Store instructions |
| //---------------------------------------------------------------------- |
| /* Following list of emulated instructions are required by implementation |
| of hardware watchpoint |
| for MIPS in lldb. As we just need the address accessed by instructions, |
| we have generalised |
| all these instructions in 2 functions depending on their addressing |
| modes */ |
| |
| {"LB", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LB rt, offset(base)"}, |
| {"LBE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LBE rt, offset(base)"}, |
| {"LBU", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LBU rt, offset(base)"}, |
| {"LBUE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LBUE rt, offset(base)"}, |
| {"LDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LDC1 ft, offset(base)"}, |
| {"LD", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LD rt, offset(base)"}, |
| {"LDL", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LDL rt, offset(base)"}, |
| {"LDR", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LDR rt, offset(base)"}, |
| {"LLD", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LLD rt, offset(base)"}, |
| {"LDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LDC2 rt, offset(base)"}, |
| {"LDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, |
| "LDXC1 fd, index (base)"}, |
| {"LH", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LH rt, offset(base)"}, |
| {"LHE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LHE rt, offset(base)"}, |
| {"LHU", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LHU rt, offset(base)"}, |
| {"LHUE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LHUE rt, offset(base)"}, |
| {"LL", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LL rt, offset(base)"}, |
| {"LLE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LLE rt, offset(base)"}, |
| {"LUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, |
| "LUXC1 fd, index (base)"}, |
| {"LW", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LW rt, offset(base)"}, |
| {"LWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWC1 ft, offset(base)"}, |
| {"LWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWC2 rt, offset(base)"}, |
| {"LWE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWE rt, offset(base)"}, |
| {"LWL", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWL rt, offset(base)"}, |
| {"LWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWLE rt, offset(base)"}, |
| {"LWR", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWR rt, offset(base)"}, |
| {"LWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWRE rt, offset(base)"}, |
| {"LWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, |
| "LWXC1 fd, index (base)"}, |
| {"LLX", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LLX rt, offset(base)"}, |
| {"LLXE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LLXE rt, offset(base)"}, |
| {"LLDX", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LLDX rt, offset(base)"}, |
| |
| {"SB", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SB rt, offset(base)"}, |
| {"SBE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SBE rt, offset(base)"}, |
| {"SC", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SC rt, offset(base)"}, |
| {"SCE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SCE rt, offset(base)"}, |
| {"SCD", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SCD rt, offset(base)"}, |
| {"SD", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SD rt, offset(base)"}, |
| {"SDL", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SDL rt, offset(base)"}, |
| {"SDR", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SDR rt, offset(base)"}, |
| {"SDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SDC1 ft, offset(base)"}, |
| {"SDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SDC2 rt, offset(base)"}, |
| {"SDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, |
| "SDXC1 fs, index(base)"}, |
| {"SH", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SH rt, offset(base)"}, |
| {"SHE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SHE rt, offset(base)"}, |
| {"SUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, |
| "SUXC1 fs, index (base)"}, |
| {"SWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWC1 ft, offset(base)"}, |
| {"SWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWC2 rt, offset(base)"}, |
| {"SWE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWE rt, offset(base)"}, |
| {"SWL", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWL rt, offset(base)"}, |
| {"SWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWLE rt, offset(base)"}, |
| {"SWR", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWR rt, offset(base)"}, |
| {"SWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWRE rt, offset(base)"}, |
| {"SWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, |
| "SWXC1 fs, index (base)"}, |
| {"SCX", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SCX rt, offset(base)"}, |
| {"SCXE", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SCXE rt, offset(base)"}, |
| {"SCDX", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SCDX rt, offset(base)"}, |
| |
| //---------------------------------------------------------------------- |
| // MicroMIPS Load/Store instructions |
| //---------------------------------------------------------------------- |
| {"LBU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LBU16 rt, decoded_offset(base)"}, |
| {"LHU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LHU16 rt, left_shifted_offset(base)"}, |
| {"LW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LW16 rt, left_shifted_offset(base)"}, |
| {"LWGP_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "LWGP rt, left_shifted_offset(gp)"}, |
| {"SH16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SH16 rt, left_shifted_offset(base)"}, |
| {"SW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SW16 rt, left_shifted_offset(base)"}, |
| {"SW_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SWSP rt, left_shifted_offset(base)"}, |
| {"SB16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, |
| "SB16 rt, offset(base)"}, |
| |
| //---------------------------------------------------------------------- |
| // Branch instructions |
| //---------------------------------------------------------------------- |
| {"BEQ", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQ rs,rt,offset"}, |
| {"BNE", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNE rs,rt,offset"}, |
| {"BEQL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQL rs,rt,offset"}, |
| {"BNEL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNEL rs,rt,offset"}, |
| {"BGEZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, |
| "BGEZALL rt,offset"}, |
| {"BAL", &EmulateInstructionMIPS::Emulate_BAL, "BAL offset"}, |
| {"BGEZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, |
| "BGEZAL rs,offset"}, |
| {"BALC", &EmulateInstructionMIPS::Emulate_BALC, "BALC offset"}, |
| {"BC", &EmulateInstructionMIPS::Emulate_BC, "BC offset"}, |
| {"BGEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZ rs,offset"}, |
| {"BLEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C, |
| "BLEZALC rs,offset"}, |
| {"BGEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C, |
| "BGEZALC rs,offset"}, |
| {"BLTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C, |
| "BLTZALC rs,offset"}, |
| {"BGTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C, |
| "BGTZALC rs,offset"}, |
| {"BEQZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C, |
| "BEQZALC rs,offset"}, |
| {"BNEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C, |
| "BNEZALC rs,offset"}, |
| {"BEQC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BEQC rs,rt,offset"}, |
| {"BNEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BNEC rs,rt,offset"}, |
| {"BLTC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BLTC rs,rt,offset"}, |
| {"BGEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BGEC rs,rt,offset"}, |
| {"BLTUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BLTUC rs,rt,offset"}, |
| {"BGEUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BGEUC rs,rt,offset"}, |
| {"BLTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLTZC rt,offset"}, |
| {"BLEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLEZC rt,offset"}, |
| {"BGEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGEZC rt,offset"}, |
| {"BGTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGTZC rt,offset"}, |
| {"BEQZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BEQZC rt,offset"}, |
| {"BNEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BNEZC rt,offset"}, |
| {"BGEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZL rt,offset"}, |
| {"BGTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZ rt,offset"}, |
| {"BGTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZL rt,offset"}, |
| {"BLEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZ rt,offset"}, |
| {"BLEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZL rt,offset"}, |
| {"BLTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZ rt,offset"}, |
| {"BLTZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, |
| "BLTZAL rt,offset"}, |
| {"BLTZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, |
| "BLTZALL rt,offset"}, |
| {"BLTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZL rt,offset"}, |
| {"BOVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BOVC rs,rt,offset"}, |
| {"BNVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, |
| "BNVC rs,rt,offset"}, |
| {"J", &EmulateInstructionMIPS::Emulate_J, "J target"}, |
| {"JAL", &EmulateInstructionMIPS::Emulate_JAL, "JAL target"}, |
| {"JALX", &EmulateInstructionMIPS::Emulate_JAL, "JALX target"}, |
| {"JALR", &EmulateInstructionMIPS::Emulate_JALR, "JALR target"}, |
| {"JALR_HB", &EmulateInstructionMIPS::Emulate_JALR, "JALR.HB target"}, |
| {"JIALC", &EmulateInstructionMIPS::Emulate_JIALC, "JIALC rt,offset"}, |
| {"JIC", &EmulateInstructionMIPS::Emulate_JIC, "JIC rt,offset"}, |
| {"JR", &EmulateInstructionMIPS::Emulate_JR, "JR target"}, |
| {"JR_HB", &EmulateInstructionMIPS::Emulate_JR, "JR.HB target"}, |
| {"BC1F", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1F cc, offset"}, |
| {"BC1T", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1T cc, offset"}, |
| {"BC1FL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1FL cc, offset"}, |
| {"BC1TL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1TL cc, offset"}, |
| {"BC1EQZ", &EmulateInstructionMIPS::Emulate_BC1EQZ, "BC1EQZ ft, offset"}, |
| {"BC1NEZ", &EmulateInstructionMIPS::Emulate_BC1NEZ, "BC1NEZ ft, offset"}, |
| {"BC1ANY2F", &EmulateInstructionMIPS::Emulate_3D_branch, |
| "BC1ANY2F cc, offset"}, |
| {"BC1ANY2T", &EmulateInstructionMIPS::Emulate_3D_branch, |
| "BC1ANY2T cc, offset"}, |
| {"BC1ANY4F", &EmulateInstructionMIPS::Emulate_3D_branch, |
| "BC1ANY4F cc, offset"}, |
| {"BC1ANY4T", &EmulateInstructionMIPS::Emulate_3D_branch, |
| "BC1ANY4T cc, offset"}, |
| {"BNZ_B", &EmulateInstructionMIPS::Emulate_BNZB, "BNZ.b wt,s16"}, |
| {"BNZ_H", &EmulateInstructionMIPS::Emulate_BNZH, "BNZ.h wt,s16"}, |
| {"BNZ_W", &EmulateInstructionMIPS::Emulate_BNZW, "BNZ.w wt,s16"}, |
| {"BNZ_D", &EmulateInstructionMIPS::Emulate_BNZD, "BNZ.d wt,s16"}, |
| {"BZ_B", &EmulateInstructionMIPS::Emulate_BZB, "BZ.b wt,s16"}, |
| {"BZ_H", &EmulateInstructionMIPS::Emulate_BZH, "BZ.h wt,s16"}, |
| {"BZ_W", &EmulateInstructionMIPS::Emulate_BZW, "BZ.w wt,s16"}, |
| {"BZ_D", &EmulateInstructionMIPS::Emulate_BZD, "BZ.d wt,s16"}, |
| {"BNZ_V", &EmulateInstructionMIPS::Emulate_BNZV, "BNZ.V wt,s16"}, |
| {"BZ_V", &EmulateInstructionMIPS::Emulate_BZV, "BZ.V wt,s16"}, |
| |
| //---------------------------------------------------------------------- |
| // MicroMIPS Branch instructions |
| //---------------------------------------------------------------------- |
| {"B16_MM", &EmulateInstructionMIPS::Emulate_B16_MM, "B16 offset"}, |
| {"BEQZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, |
| "BEQZ16 rs, offset"}, |
| {"BNEZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, |
| "BNEZ16 rs, offset"}, |
| {"BEQZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, |
| "BEQZC rs, offset"}, |
| {"BNEZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, |
| "BNEZC rs, offset"}, |
| {"BGEZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, |
| "BGEZALS rs, offset"}, |
| {"BLTZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, |
| "BLTZALS rs, offset"}, |
| {"JALR16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALR16 rs"}, |
| {"JALRS16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALRS16 rs"}, |
| {"JR16_MM", &EmulateInstructionMIPS::Emulate_JR, "JR16 rs rs"}, |
| {"JRC16_MM", &EmulateInstructionMIPS::Emulate_JR, "JRC16 rs rs"}, |
| {"JALS_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALS target"}, |
| {"JALX_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALX target"}, |
| {"JALRS_MM", &EmulateInstructionMIPS::Emulate_JALRS, "JALRS rt, rs"}, |
| }; |
| |
| static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes); |
| |
| for (size_t i = 0; i < k_num_mips_opcodes; ++i) { |
| if (!strcasecmp(g_opcodes[i].op_name, op_name)) |
| return &g_opcodes[i]; |
| } |
| |
| return NULL; |
| } |
| |
| uint32_t |
| EmulateInstructionMIPS::GetSizeOfInstruction(lldb_private::DataExtractor &data, |
| uint64_t inst_addr) { |
| uint64_t next_inst_size = 0; |
| llvm::MCInst mc_insn; |
| llvm::MCDisassembler::DecodeStatus decode_status; |
| llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize()); |
| |
| if (m_use_alt_disaasm) |
| decode_status = |
| m_alt_disasm->getInstruction(mc_insn, next_inst_size, raw_insn, |
| inst_addr, llvm::nulls(), llvm::nulls()); |
| else |
| decode_status = |
| m_disasm->getInstruction(mc_insn, next_inst_size, raw_insn, inst_addr, |
| llvm::nulls(), llvm::nulls()); |
| |
| if (decode_status != llvm::MCDisassembler::Success) |
| return false; |
| |
| return m_insn_info->get(mc_insn.getOpcode()).getSize(); |
| } |
| |
| bool EmulateInstructionMIPS::SetInstruction(const Opcode &insn_opcode, |
| const Address &inst_addr, |
| Target *target) { |
| m_use_alt_disaasm = false; |
| |
| if (EmulateInstruction::SetInstruction(insn_opcode, inst_addr, target)) { |
| if (inst_addr.GetAddressClass() == AddressClass::eCodeAlternateISA) { |
| Status error; |
| lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; |
| |
| /* |
| * The address belongs to microMIPS function. To find the size of |
| * next instruction use microMIPS disassembler. |
| */ |
| m_use_alt_disaasm = true; |
| |
| uint32_t current_inst_size = insn_opcode.GetByteSize(); |
| uint8_t buf[sizeof(uint32_t)]; |
| uint64_t next_inst_addr = (m_addr & (~1ull)) + current_inst_size; |
| Address next_addr(next_inst_addr); |
| |
| const size_t bytes_read = |
| target->ReadMemory(next_addr, /* Address of next instruction */ |
| true, /* prefer_file_cache */ |
| buf, sizeof(uint32_t), error, &load_addr); |
| |
| if (bytes_read == 0) |
| return true; |
| |
| DataExtractor data(buf, sizeof(uint32_t), GetByteOrder(), |
| GetAddressByteSize()); |
| m_next_inst_size = GetSizeOfInstruction(data, next_inst_addr); |
| return true; |
| } else { |
| /* |
| * If the address class is not AddressClass::eCodeAlternateISA then |
| * the function is not microMIPS. In this case instruction size is |
| * always 4 bytes. |
| */ |
| m_next_inst_size = 4; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool EmulateInstructionMIPS::ReadInstruction() { |
| bool success = false; |
| m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, |
| LLDB_INVALID_ADDRESS, &success); |
| if (success) { |
| Context read_inst_context; |
| read_inst_context.type = eContextReadOpcode; |
| read_inst_context.SetNoArgs(); |
| m_opcode.SetOpcode32( |
| ReadMemoryUnsigned(read_inst_context, m_addr, 4, 0, &success), |
| GetByteOrder()); |
| } |
| if (!success) |
| m_addr = LLDB_INVALID_ADDRESS; |
| return success; |
| } |
| |
| bool EmulateInstructionMIPS::EvaluateInstruction(uint32_t evaluate_options) { |
| bool success = false; |
| llvm::MCInst mc_insn; |
| uint64_t insn_size; |
| DataExtractor data; |
| |
| /* Keep the complexity of the decode logic with the llvm::MCDisassembler |
| * class. */ |
| if (m_opcode.GetData(data)) { |
| llvm::MCDisassembler::DecodeStatus decode_status; |
| llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize()); |
| if (m_use_alt_disaasm) |
| decode_status = m_alt_disasm->getInstruction( |
| mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls()); |
| else |
| decode_status = m_disasm->getInstruction( |
| mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls()); |
| |
| if (decode_status != llvm::MCDisassembler::Success) |
| return false; |
| } |
| |
| /* |
| * mc_insn.getOpcode() returns decoded opcode. However to make use |
| * of llvm::Mips::<insn> we would need "MipsGenInstrInfo.inc". |
| */ |
| const char *op_name = m_insn_info->getName(mc_insn.getOpcode()).data(); |
| |
| if (op_name == NULL) |
| return false; |
| |
| /* |
| * Decoding has been done already. Just get the call-back function |
| * and emulate the instruction. |
| */ |
| MipsOpcode *opcode_data = GetOpcodeForInstruction(op_name); |
| |
| if (opcode_data == NULL) |
| return false; |
| |
| uint64_t old_pc = 0, new_pc = 0; |
| const bool auto_advance_pc = |
| evaluate_options & eEmulateInstructionOptionAutoAdvancePC; |
| |
| if (auto_advance_pc) { |
| old_pc = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| } |
| |
| /* emulate instruction */ |
| success = (this->*opcode_data->callback)(mc_insn); |
| if (!success) |
| return false; |
| |
| if (auto_advance_pc) { |
| new_pc = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| /* If we haven't changed the PC, change it here */ |
| if (old_pc == new_pc) { |
| new_pc += 4; |
| Context context; |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| new_pc)) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::CreateFunctionEntryUnwind( |
| UnwindPlan &unwind_plan) { |
| unwind_plan.Clear(); |
| unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
| |
| UnwindPlan::RowSP row(new UnwindPlan::Row); |
| const bool can_replace = false; |
| |
| // Our previous Call Frame Address is the stack pointer |
| row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp_mips, 0); |
| |
| // Our previous PC is in the RA |
| row->SetRegisterLocationToRegister(dwarf_pc_mips, dwarf_ra_mips, can_replace); |
| |
| unwind_plan.AppendRow(row); |
| |
| // All other registers are the same. |
| unwind_plan.SetSourceName("EmulateInstructionMIPS"); |
| unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
| unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); |
| unwind_plan.SetReturnAddressRegister(dwarf_ra_mips); |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::nonvolatile_reg_p(uint32_t regnum) { |
| switch (regnum) { |
| case dwarf_r16_mips: |
| case dwarf_r17_mips: |
| case dwarf_r18_mips: |
| case dwarf_r19_mips: |
| case dwarf_r20_mips: |
| case dwarf_r21_mips: |
| case dwarf_r22_mips: |
| case dwarf_r23_mips: |
| case dwarf_gp_mips: |
| case dwarf_sp_mips: |
| case dwarf_r30_mips: |
| case dwarf_ra_mips: |
| return true; |
| default: |
| return false; |
| } |
| return false; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_ADDiu(llvm::MCInst &insn) { |
| // ADDIU rt, rs, immediate |
| // GPR[rt] <- GPR[rs] + sign_extend(immediate) |
| |
| uint8_t dst, src; |
| bool success = false; |
| const uint32_t imm16 = insn.getOperand(2).getImm(); |
| int64_t imm = SignedBits(imm16, 15, 0); |
| |
| dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| |
| // If immediate value is greater then 2^16 - 1 then clang generate LUI, |
| // ADDIU, SUBU instructions in prolog. Example lui $1, 0x2 addiu $1, $1, |
| // -0x5920 subu $sp, $sp, $1 In this case, ADDIU dst and src will be same |
| // and not equal to sp |
| if (dst == src) { |
| Context context; |
| |
| /* read <src> register */ |
| const int64_t src_opd_val = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success); |
| if (!success) |
| return false; |
| |
| /* Check if this is daddiu sp, sp, imm16 */ |
| if (dst == dwarf_sp_mips) { |
| uint64_t result = src_opd_val + imm; |
| RegisterInfo reg_info_sp; |
| |
| if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) |
| context.SetRegisterPlusOffset(reg_info_sp, imm); |
| |
| /* We are allocating bytes on stack */ |
| context.type = eContextAdjustStackPointer; |
| |
| WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result); |
| return true; |
| } |
| |
| imm += src_opd_val; |
| context.SetImmediateSigned(imm); |
| context.type = eContextImmediate; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, |
| dwarf_zero_mips + dst, imm)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_SW(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t imm16 = insn.getOperand(2).getImm(); |
| uint32_t imm = SignedBits(imm16, 15, 0); |
| uint32_t src, base; |
| int32_t address; |
| Context bad_vaddr_context; |
| |
| RegisterInfo reg_info_base; |
| |
| src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base, |
| reg_info_base)) |
| return false; |
| |
| /* read base register */ |
| address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| /* destination address */ |
| address = address + imm; |
| |
| /* Set the bad_vaddr register with base address used in the instruction */ |
| bad_vaddr_context.type = eContextInvalid; |
| WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, |
| address); |
| |
| /* We look for sp based non-volatile register stores */ |
| if (nonvolatile_reg_p(src)) { |
| |
| RegisterInfo reg_info_src; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src, |
| reg_info_src)) |
| return false; |
| |
| Context context; |
| RegisterValue data_src; |
| context.type = eContextPushRegisterOnStack; |
| context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0); |
| |
| uint8_t buffer[RegisterValue::kMaxRegisterByteSize]; |
| Status error; |
| |
| if (!ReadRegister(®_info_base, data_src)) |
| return false; |
| |
| if (data_src.GetAsMemoryData(®_info_src, buffer, reg_info_src.byte_size, |
| eByteOrderLittle, error) == 0) |
| return false; |
| |
| if (!WriteMemory(context, address, buffer, reg_info_src.byte_size)) |
| return false; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_LW(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t src, base; |
| int32_t imm, address; |
| Context bad_vaddr_context; |
| |
| src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| imm = insn.getOperand(2).getImm(); |
| |
| RegisterInfo reg_info_base; |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base, |
| reg_info_base)) |
| return false; |
| |
| /* read base register */ |
| address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| /* destination address */ |
| address = address + imm; |
| |
| /* Set the bad_vaddr register with base address used in the instruction */ |
| bad_vaddr_context.type = eContextInvalid; |
| WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, |
| address); |
| |
| if (nonvolatile_reg_p(src)) { |
| RegisterValue data_src; |
| RegisterInfo reg_info_src; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src, |
| reg_info_src)) |
| return false; |
| |
| Context context; |
| context.type = eContextPopRegisterOffStack; |
| context.SetAddress(address); |
| |
| if (!WriteRegister(context, ®_info_src, data_src)) |
| return false; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_SUBU_ADDU(llvm::MCInst &insn) { |
| // SUBU sp, <src>, <rt> |
| // ADDU sp, <src>, <rt> |
| // ADDU dst, sp, <rt> |
| |
| bool success = false; |
| uint64_t result; |
| uint8_t src, dst, rt; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| |
| /* Check if sp is destination register */ |
| if (dst == dwarf_sp_mips) { |
| rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg()); |
| |
| /* read <src> register */ |
| uint64_t src_opd_val = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success); |
| if (!success) |
| return false; |
| |
| /* read <rt > register */ |
| uint64_t rt_opd_val = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "SUBU")) |
| result = src_opd_val - rt_opd_val; |
| else |
| result = src_opd_val + rt_opd_val; |
| |
| Context context; |
| RegisterInfo reg_info_sp; |
| if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) |
| context.SetRegisterPlusOffset(reg_info_sp, rt_opd_val); |
| |
| /* We are allocating bytes on stack */ |
| context.type = eContextAdjustStackPointer; |
| |
| WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result); |
| |
| return true; |
| } else if (src == dwarf_sp_mips) { |
| rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg()); |
| |
| /* read <src> register */ |
| uint64_t src_opd_val = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success); |
| if (!success) |
| return false; |
| |
| /* read <rt> register */ |
| uint64_t rt_opd_val = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); |
| if (!success) |
| return false; |
| |
| Context context; |
| |
| if (!strcasecmp(op_name, "SUBU")) |
| result = src_opd_val - rt_opd_val; |
| else |
| result = src_opd_val + rt_opd_val; |
| |
| context.SetImmediateSigned(result); |
| context.type = eContextImmediate; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, |
| dwarf_zero_mips + dst, result)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_LUI(llvm::MCInst &insn) { |
| // LUI rt, immediate |
| // GPR[rt] <- sign_extend(immediate << 16) |
| |
| const uint32_t imm32 = insn.getOperand(1).getImm() << 16; |
| int64_t imm = SignedBits(imm32, 31, 0); |
| uint8_t rt; |
| Context context; |
| |
| rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| context.SetImmediateSigned(imm); |
| context.type = eContextImmediate; |
| |
| if (WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_zero_mips + rt, |
| imm)) |
| return true; |
| |
| return false; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_ADDIUSP(llvm::MCInst &insn) { |
| bool success = false; |
| const uint32_t imm9 = insn.getOperand(0).getImm(); |
| uint64_t result; |
| |
| // This instruction operates implicitly on stack pointer, so read <sp> |
| // register. |
| uint64_t src_opd_val = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_sp_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| result = src_opd_val + imm9; |
| |
| Context context; |
| RegisterInfo reg_info_sp; |
| if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) |
| context.SetRegisterPlusOffset(reg_info_sp, imm9); |
| |
| // We are adjusting the stack. |
| context.type = eContextAdjustStackPointer; |
| |
| WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result); |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_ADDIUS5(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t base; |
| const uint32_t imm4 = insn.getOperand(2).getImm(); |
| uint64_t result; |
| |
| // The source and destination register is same for this instruction. |
| base = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| |
| // We are looking for stack adjustment only |
| if (base == dwarf_sp_mips) { |
| // Read stack pointer register |
| uint64_t src_opd_val = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| result = src_opd_val + imm4; |
| |
| Context context; |
| RegisterInfo reg_info_sp; |
| if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) |
| context.SetRegisterPlusOffset(reg_info_sp, imm4); |
| |
| // We are adjusting the stack. |
| context.type = eContextAdjustStackPointer; |
| |
| WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result); |
| } |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_SWSP(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t imm5 = insn.getOperand(2).getImm(); |
| uint32_t src, base; |
| Context bad_vaddr_context; |
| uint32_t address; |
| |
| src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| |
| RegisterInfo reg_info_base; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base, |
| reg_info_base)) |
| return false; |
| |
| // read base register |
| address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + base, 0, |
| &success); |
| if (!success) |
| return false; |
| |
| // destination address |
| address = address + imm5; |
| |
| // We use bad_vaddr_context to store base address which is used by H/W |
| // watchpoint Set the bad_vaddr register with base address used in the |
| // instruction |
| bad_vaddr_context.type = eContextInvalid; |
| WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, |
| address); |
| |
| // We look for sp based non-volatile register stores. |
| if (base == dwarf_sp_mips && nonvolatile_reg_p(src)) { |
| RegisterInfo reg_info_src = {}; |
| Context context; |
| RegisterValue data_src; |
| context.type = eContextPushRegisterOnStack; |
| context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0); |
| |
| uint8_t buffer[RegisterValue::kMaxRegisterByteSize]; |
| Status error; |
| |
| if (!ReadRegister(®_info_base, data_src)) |
| return false; |
| |
| if (data_src.GetAsMemoryData(®_info_src, buffer, reg_info_src.byte_size, |
| eByteOrderLittle, error) == 0) |
| return false; |
| |
| if (!WriteMemory(context, address, buffer, reg_info_src.byte_size)) |
| return false; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Emulate SWM16,SWM32 and SWP instruction. |
| |
| SWM16 always has stack pointer as a base register (but it is still available |
| in MCInst as an operand). |
| SWM32 and SWP can have base register other than stack pointer. |
| */ |
| bool EmulateInstructionMIPS::Emulate_SWM16_32(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t src, base; |
| uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on |
| // no of regs to store. |
| |
| // Base register is second last operand of the instruction. |
| base = |
| m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg()); |
| |
| // We are looking for sp based stores so if base is not a stack pointer then |
| // don't proceed. |
| if (base != dwarf_sp_mips) |
| return false; |
| |
| // offset is always the last operand. |
| uint32_t offset = insn.getOperand(num_operands - 1).getImm(); |
| |
| RegisterInfo reg_info_base; |
| RegisterInfo reg_info_src; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base, |
| reg_info_base)) |
| return false; |
| |
| // read SP |
| uint32_t base_address = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| // Resulting base addrss |
| base_address = base_address + offset; |
| |
| // Total no of registers to be stored are num_operands-2. |
| for (uint32_t i = 0; i < num_operands - 2; i++) { |
| // Get the register number to be stored. |
| src = m_reg_info->getEncodingValue(insn.getOperand(i).getReg()); |
| |
| /* |
| Record only non-volatile stores. |
| This check is required for SWP instruction because source operand could |
| be any register. |
| SWM16 and SWM32 instruction always has saved registers as source |
| operands. |
| */ |
| if (!nonvolatile_reg_p(src)) |
| return false; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src, |
| reg_info_src)) |
| return false; |
| |
| Context context; |
| RegisterValue data_src; |
| context.type = eContextPushRegisterOnStack; |
| context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0); |
| |
| uint8_t buffer[RegisterValue::kMaxRegisterByteSize]; |
| Status error; |
| |
| if (!ReadRegister(®_info_base, data_src)) |
| return false; |
| |
| if (data_src.GetAsMemoryData(®_info_src, buffer, reg_info_src.byte_size, |
| eByteOrderLittle, error) == 0) |
| return false; |
| |
| if (!WriteMemory(context, base_address, buffer, reg_info_src.byte_size)) |
| return false; |
| |
| // Stack address for next register |
| base_address = base_address + reg_info_src.byte_size; |
| } |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_LWSP(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| uint32_t base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| uint32_t imm5 = insn.getOperand(2).getImm(); |
| Context bad_vaddr_context; |
| |
| RegisterInfo reg_info_base; |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base, |
| reg_info_base)) |
| return false; |
| |
| // read base register |
| uint32_t base_address = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| base_address = base_address + imm5; |
| |
| // We use bad_vaddr_context to store base address which is used by H/W |
| // watchpoint Set the bad_vaddr register with base address used in the |
| // instruction |
| bad_vaddr_context.type = eContextInvalid; |
| WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, |
| base_address); |
| |
| if (base == dwarf_sp_mips && nonvolatile_reg_p(src)) { |
| RegisterValue data_src; |
| RegisterInfo reg_info_src; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src, |
| reg_info_src)) |
| return false; |
| |
| Context context; |
| context.type = eContextPopRegisterOffStack; |
| context.SetAddress(base_address); |
| |
| if (!WriteRegister(context, ®_info_src, data_src)) |
| return false; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Emulate LWM16, LWM32 and LWP instructions. |
| |
| LWM16 always has stack pointer as a base register (but it is still available |
| in MCInst as an operand). |
| LWM32 and LWP can have base register other than stack pointer. |
| */ |
| bool EmulateInstructionMIPS::Emulate_LWM16_32(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t dst, base; |
| uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on |
| // no of regs to store. |
| uint32_t imm = insn.getOperand(num_operands - 1) |
| .getImm(); // imm is the last operand in the instruction. |
| |
| // Base register is second last operand of the instruction. |
| base = |
| m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg()); |
| |
| // We are looking for sp based loads so if base is not a stack pointer then |
| // don't proceed. |
| if (base != dwarf_sp_mips) |
| return false; |
| |
| uint32_t base_address = ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| base_address = base_address + imm; |
| |
| RegisterValue data_dst; |
| RegisterInfo reg_info_dst; |
| |
| // Total no of registers to be re-stored are num_operands-2. |
| for (uint32_t i = 0; i < num_operands - 2; i++) { |
| // Get the register number to be re-stored. |
| dst = m_reg_info->getEncodingValue(insn.getOperand(i).getReg()); |
| |
| /* |
| Record only non-volatile loads. |
| This check is required for LWP instruction because destination operand |
| could be any register. |
| LWM16 and LWM32 instruction always has saved registers as destination |
| operands. |
| */ |
| if (!nonvolatile_reg_p(dst)) |
| return false; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + dst, |
| reg_info_dst)) |
| return false; |
| |
| Context context; |
| context.type = eContextPopRegisterOffStack; |
| context.SetAddress(base_address + (i * 4)); |
| |
| if (!WriteRegister(context, ®_info_dst, data_dst)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_JRADDIUSP(llvm::MCInst &insn) { |
| bool success = false; |
| int32_t imm5 = insn.getOperand(0).getImm(); |
| |
| /* JRADDIUSP immediate |
| * PC <- RA |
| * SP <- SP + zero_extend(Immediate << 2) |
| */ |
| |
| // This instruction operates implicitly on stack pointer, so read <sp> |
| // register. |
| int32_t src_opd_val = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_sp_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| int32_t ra_val = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_ra_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| int32_t result = src_opd_val + imm5; |
| |
| Context context; |
| |
| // Update the PC |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| ra_val)) |
| return false; |
| |
| RegisterInfo reg_info_sp; |
| if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) |
| context.SetRegisterPlusOffset(reg_info_sp, imm5); |
| |
| // We are adjusting stack |
| context.type = eContextAdjustStackPointer; |
| |
| // update SP |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, |
| result)) |
| return false; |
| |
| return true; |
| } |
| |
| static int IsAdd64bitOverflow(int32_t a, int32_t b) { |
| int32_t r = (uint32_t)a + (uint32_t)b; |
| return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); |
| } |
| |
| /* |
| Emulate below MIPS branch instructions. |
| BEQ, BNE : Branch on condition |
| BEQL, BNEL : Branch likely |
| */ |
| bool EmulateInstructionMIPS::Emulate_BXX_3ops(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs, rt; |
| int32_t offset, pc, target = 0, rs_val, rt_val; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| offset = insn.getOperand(2).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rt, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "BEQ") || !strcasecmp(op_name, "BEQL")) { |
| if (rs_val == rt_val) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BNE") || !strcasecmp(op_name, "BNEL")) { |
| if (rs_val != rt_val) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| context.SetImmediate(offset); |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| Emulate below MIPS branch instructions. |
| BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch |
| instructions with no delay slot |
| */ |
| bool EmulateInstructionMIPS::Emulate_BXX_3ops_C(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs, rt; |
| int32_t offset, pc, target = 0, rs_val, rt_val; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); |
| |
| rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| offset = insn.getOperand(2).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rt, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "BEQC")) { |
| if (rs_val == rt_val) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BNEC")) { |
| if (rs_val != rt_val) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BLTC")) { |
| if (rs_val < rt_val) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BGEC")) { |
| if (rs_val >= rt_val) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BLTUC")) { |
| if (rs_val < rt_val) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BGEUC")) { |
| if ((uint32_t)rs_val >= (uint32_t)rt_val) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BOVC")) { |
| if (IsAdd64bitOverflow(rs_val, rt_val)) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BNVC")) { |
| if (!IsAdd64bitOverflow(rs_val, rt_val)) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| context.SetImmediate(current_inst_size + offset); |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| Emulate below MIPS conditional branch and link instructions. |
| BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches |
| */ |
| bool EmulateInstructionMIPS::Emulate_Bcond_Link_C(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs; |
| int32_t offset, pc, target = 0; |
| int32_t rs_val; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "BLEZALC")) { |
| if (rs_val <= 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BGEZALC")) { |
| if (rs_val >= 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BLTZALC")) { |
| if (rs_val < 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BGTZALC")) { |
| if (rs_val > 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BEQZALC")) { |
| if (rs_val == 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BNEZALC")) { |
| if (rs_val != 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + 4)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| Emulate below MIPS Non-Compact conditional branch and link instructions. |
| BLTZAL, BGEZAL : |
| BLTZALL, BGEZALL : Branch likely |
| */ |
| bool EmulateInstructionMIPS::Emulate_Bcond_Link(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs; |
| int32_t offset, pc, target = 0; |
| int32_t rs_val; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "BLTZAL") || !strcasecmp(op_name, "BLTZALL")) { |
| if ((int32_t)rs_val < 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BGEZAL") || |
| !strcasecmp(op_name, "BGEZALL")) { |
| if ((int32_t)rs_val >= 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + 8)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| Emulate below MIPS branch instructions. |
| BLTZL, BGEZL, BGTZL, BLEZL : Branch likely |
| BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches |
| */ |
| bool EmulateInstructionMIPS::Emulate_BXX_2ops(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs; |
| int32_t offset, pc, target = 0; |
| int32_t rs_val; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "BLTZL") || !strcasecmp(op_name, "BLTZ")) { |
| if (rs_val < 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BGEZL") || !strcasecmp(op_name, "BGEZ")) { |
| if (rs_val >= 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BGTZL") || !strcasecmp(op_name, "BGTZ")) { |
| if (rs_val > 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BLEZL") || !strcasecmp(op_name, "BLEZ")) { |
| if (rs_val <= 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| context.SetImmediate(offset); |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| Emulate below MIPS branch instructions. |
| BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches |
| */ |
| bool EmulateInstructionMIPS::Emulate_BXX_2ops_C(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs; |
| int32_t offset, pc, target = 0; |
| int32_t rs_val; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); |
| |
| rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "BLTZC")) { |
| if (rs_val < 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BLEZC")) { |
| if (rs_val <= 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BGEZC")) { |
| if (rs_val >= 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BGTZC")) { |
| if (rs_val > 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BEQZC")) { |
| if (rs_val == 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } else if (!strcasecmp(op_name, "BNEZC")) { |
| if (rs_val != 0) |
| target = pc + offset; |
| else |
| target = pc + 4; |
| } |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| context.SetImmediate(current_inst_size + offset); |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_B16_MM(llvm::MCInst &insn) { |
| bool success = false; |
| int32_t offset, pc, target; |
| uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); |
| |
| offset = insn.getOperand(0).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| // unconditional branch |
| target = pc + offset; |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| context.SetImmediate(current_inst_size + offset); |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| BEQZC, BNEZC are 32 bit compact instructions without a delay slot. |
| BEQZ16, BNEZ16 are 16 bit instructions with delay slot. |
| BGEZALS, BLTZALS are 16 bit instructions with short (2-byte) delay slot. |
| */ |
| bool EmulateInstructionMIPS::Emulate_Branch_MM(llvm::MCInst &insn) { |
| bool success = false; |
| int32_t target = 0; |
| uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| bool update_ra = false; |
| uint32_t ra_offset = 0; |
| |
| /* |
| * BEQZ16 rs, offset |
| * condition <- (GPR[rs] = 0) |
| * if condition then |
| * PC = PC + sign_ext (offset || 0) |
| * |
| * BNEZ16 rs, offset |
| * condition <- (GPR[rs] != 0) |
| * if condition then |
| * PC = PC + sign_ext (offset || 0) |
| * |
| * BEQZC rs, offset (compact instruction: No delay slot) |
| * condition <- (GPR[rs] == 0) |
| * if condition then |
| * PC = PC + 4 + sign_ext (offset || 0) |
| */ |
| |
| uint32_t rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| int32_t offset = insn.getOperand(1).getImm(); |
| |
| int32_t pc = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| int32_t rs_val = (int32_t)ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "BEQZ16_MM")) { |
| if (rs_val == 0) |
| target = pc + offset; |
| else |
| target = pc + current_inst_size + |
| m_next_inst_size; // Skip delay slot instruction. |
| } else if (!strcasecmp(op_name, "BNEZ16_MM")) { |
| if (rs_val != 0) |
| target = pc + offset; |
| else |
| target = pc + current_inst_size + |
| m_next_inst_size; // Skip delay slot instruction. |
| } else if (!strcasecmp(op_name, "BEQZC_MM")) { |
| if (rs_val == 0) |
| target = pc + 4 + offset; |
| else |
| target = |
| pc + |
| 4; // 32 bit instruction and does not have delay slot instruction. |
| } else if (!strcasecmp(op_name, "BNEZC_MM")) { |
| if (rs_val != 0) |
| target = pc + 4 + offset; |
| else |
| target = |
| pc + |
| 4; // 32 bit instruction and does not have delay slot instruction. |
| } else if (!strcasecmp(op_name, "BGEZALS_MM")) { |
| if (rs_val >= 0) |
| target = pc + offset; |
| else |
| target = pc + 6; // 32 bit instruction with short (2-byte) delay slot |
| |
| update_ra = true; |
| ra_offset = 6; |
| } else if (!strcasecmp(op_name, "BLTZALS_MM")) { |
| if (rs_val >= 0) |
| target = pc + offset; |
| else |
| target = pc + 6; // 32 bit instruction with short (2-byte) delay slot |
| |
| update_ra = true; |
| ra_offset = 6; |
| } |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| context.SetImmediate(current_inst_size + offset); |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (update_ra) { |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + ra_offset)) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Emulate micromips jump instructions. |
| JALR16,JALRS16 |
| */ |
| bool EmulateInstructionMIPS::Emulate_JALRx16_MM(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t ra_offset = 0; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| uint32_t rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| |
| uint32_t pc = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| uint32_t rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| if (!strcasecmp(op_name, "JALR16_MM")) |
| ra_offset = 6; // 2-byte instruction with 4-byte delay slot. |
| else if (!strcasecmp(op_name, "JALRS16_MM")) |
| ra_offset = 4; // 2-byte instruction with 2-byte delay slot. |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| rs_val)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + ra_offset)) |
| return false; |
| |
| return true; |
| } |
| |
| /* Emulate JALS and JALX instructions. |
| JALS 32 bit instruction with short (2-byte) delay slot. |
| JALX 32 bit instruction with 4-byte delay slot. |
| */ |
| bool EmulateInstructionMIPS::Emulate_JALx(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t offset = 0, target = 0, pc = 0, ra_offset = 0; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| /* |
| * JALS target |
| * RA = PC + 6 |
| * offset = sign_ext (offset << 1) |
| * PC = PC[31-27] | offset |
| * JALX target |
| * RA = PC + 8 |
| * offset = sign_ext (offset << 2) |
| * PC = PC[31-28] | offset |
| */ |
| offset = insn.getOperand(0).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| // These are PC-region branches and not PC-relative. |
| if (!strcasecmp(op_name, "JALS_MM")) { |
| // target address is in the “current” 128 MB-aligned region |
| target = (pc & 0xF8000000UL) | offset; |
| ra_offset = 6; |
| } else if (!strcasecmp(op_name, "JALX_MM")) { |
| // target address is in the “current” 256 MB-aligned region |
| target = (pc & 0xF0000000UL) | offset; |
| ra_offset = 8; |
| } |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + ra_offset)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_JALRS(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs = 0, rt = 0; |
| int32_t pc = 0, rs_val = 0; |
| |
| /* |
| JALRS rt, rs |
| GPR[rt] <- PC + 6 |
| PC <- GPR[rs] |
| */ |
| |
| rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| |
| rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rs, 0, &success); |
| if (!success) |
| return false; |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| rs_val)) |
| return false; |
| |
| // This is 4-byte instruction with 2-byte delay slot. |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_zero_mips + rt, |
| pc + 6)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BAL(llvm::MCInst &insn) { |
| bool success = false; |
| int32_t offset, pc, target; |
| |
| /* |
| * BAL offset |
| * offset = sign_ext (offset << 2) |
| * RA = PC + 8 |
| * PC = PC + offset |
| */ |
| offset = insn.getOperand(0).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| target = pc + offset; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + 8)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BALC(llvm::MCInst &insn) { |
| bool success = false; |
| int32_t offset, pc, target; |
| |
| /* |
| * BALC offset |
| * offset = sign_ext (offset << 2) |
| * RA = PC + 4 |
| * PC = PC + 4 + offset |
| */ |
| offset = insn.getOperand(0).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| target = pc + offset; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + 4)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BC(llvm::MCInst &insn) { |
| bool success = false; |
| int32_t offset, pc, target; |
| |
| /* |
| * BC offset |
| * offset = sign_ext (offset << 2) |
| * PC = PC + 4 + offset |
| */ |
| offset = insn.getOperand(0).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| target = pc + offset; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_J(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t offset, pc; |
| |
| /* |
| * J offset |
| * offset = sign_ext (offset << 2) |
| * PC = PC[63-28] | offset |
| */ |
| offset = insn.getOperand(0).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| /* This is a PC-region branch and not PC-relative */ |
| pc = (pc & 0xF0000000UL) | offset; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, pc)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_JAL(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t offset, target, pc; |
| |
| /* |
| * JAL offset |
| * offset = sign_ext (offset << 2) |
| * PC = PC[63-28] | offset |
| */ |
| offset = insn.getOperand(0).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| /* This is a PC-region branch and not PC-relative */ |
| target = (pc & 0xF0000000UL) | offset; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + 8)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_JALR(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs, rt; |
| uint32_t pc, rs_val; |
| |
| /* |
| * JALR rt, rs |
| * GPR[rt] = PC + 8 |
| * PC = GPR[rs] |
| */ |
| rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg()); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + rs, 0, |
| &success); |
| if (!success) |
| return false; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| rs_val)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_zero_mips + rt, |
| pc + 8)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_JIALC(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rt; |
| int32_t target, offset, pc, rt_val; |
| |
| /* |
| * JIALC rt, offset |
| * offset = sign_ext (offset) |
| * PC = GPR[rt] + offset |
| * RA = PC + 4 |
| */ |
| rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rt, 0, &success); |
| if (!success) |
| return false; |
| |
| target = rt_val + offset; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips, |
| pc + 4)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_JIC(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rt; |
| int32_t target, offset, rt_val; |
| |
| /* |
| * JIC rt, offset |
| * offset = sign_ext (offset) |
| * PC = GPR[rt] + offset |
| */ |
| rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + rt, 0, &success); |
| if (!success) |
| return false; |
| |
| target = rt_val + offset; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_JR(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t rs; |
| uint32_t rs_val; |
| |
| /* |
| * JR rs |
| * PC = GPR[rs] |
| */ |
| rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| |
| rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + rs, 0, |
| &success); |
| if (!success) |
| return false; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| rs_val)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| Emulate Branch on FP True/False |
| BC1F, BC1FL : Branch on FP False (L stands for branch likely) |
| BC1T, BC1TL : Branch on FP True (L stands for branch likely) |
| */ |
| bool EmulateInstructionMIPS::Emulate_FP_branch(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t cc, fcsr; |
| int32_t pc, offset, target = 0; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| fcsr = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| /* fcsr[23], fcsr[25-31] are vaild condition bits */ |
| fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); |
| |
| if (!strcasecmp(op_name, "BC1F") || !strcasecmp(op_name, "BC1FL")) { |
| if ((fcsr & (1 << cc)) == 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BC1T") || !strcasecmp(op_name, "BC1TL")) { |
| if ((fcsr & (1 << cc)) != 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BC1EQZ(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t ft; |
| uint32_t ft_val; |
| int32_t target, pc, offset; |
| |
| /* |
| * BC1EQZ ft, offset |
| * condition <- (FPR[ft].bit0 == 0) |
| * if condition then |
| * offset = sign_ext (offset) |
| * PC = PC + 4 + offset |
| */ |
| ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + ft, 0, |
| &success); |
| if (!success) |
| return false; |
| |
| if ((ft_val & 1) == 0) |
| target = pc + 4 + offset; |
| else |
| target = pc + 8; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BC1NEZ(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t ft; |
| uint32_t ft_val; |
| int32_t target, pc, offset; |
| |
| /* |
| * BC1NEZ ft, offset |
| * condition <- (FPR[ft].bit0 != 0) |
| * if condition then |
| * offset = sign_ext (offset) |
| * PC = PC + 4 + offset |
| */ |
| ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + ft, 0, |
| &success); |
| if (!success) |
| return false; |
| |
| if ((ft_val & 1) != 0) |
| target = pc + 4 + offset; |
| else |
| target = pc + 8; |
| |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| Emulate MIPS-3D Branch instructions |
| BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes |
| False/True |
| BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes |
| False/True |
| */ |
| bool EmulateInstructionMIPS::Emulate_3D_branch(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t cc, fcsr; |
| int32_t pc, offset, target = 0; |
| const char *op_name = m_insn_info->getName(insn.getOpcode()).data(); |
| |
| cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| offset = insn.getOperand(1).getImm(); |
| |
| pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| fcsr = (uint32_t)ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips, 0, |
| &success); |
| if (!success) |
| return false; |
| |
| /* fcsr[23], fcsr[25-31] are vaild condition bits */ |
| fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); |
| |
| if (!strcasecmp(op_name, "BC1ANY2F")) { |
| /* if any one bit is 0 */ |
| if (((fcsr >> cc) & 3) != 3) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BC1ANY2T")) { |
| /* if any one bit is 1 */ |
| if (((fcsr >> cc) & 3) != 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BC1ANY4F")) { |
| /* if any one bit is 0 */ |
| if (((fcsr >> cc) & 0xf) != 0xf) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } else if (!strcasecmp(op_name, "BC1ANY4T")) { |
| /* if any one bit is 1 */ |
| if (((fcsr >> cc) & 0xf) != 0) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| } |
| Context context; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BNZB(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 1, true); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BNZH(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 2, true); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BNZW(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 4, true); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BNZD(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 8, true); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BZB(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 1, false); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BZH(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 2, false); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BZW(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 4, false); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BZD(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_DF(insn, 8, false); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_MSA_Branch_DF(llvm::MCInst &insn, |
| int element_byte_size, |
| bool bnz) { |
| bool success = false, branch_hit = true; |
| int32_t target = 0; |
| RegisterValue reg_value; |
| const uint8_t *ptr = NULL; |
| |
| uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| int32_t offset = insn.getOperand(1).getImm(); |
| |
| int32_t pc = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value)) |
| ptr = (const uint8_t *)reg_value.GetBytes(); |
| else |
| return false; |
| |
| for (int i = 0; i < 16 / element_byte_size; i++) { |
| switch (element_byte_size) { |
| case 1: |
| if ((*ptr == 0 && bnz) || (*ptr != 0 && !bnz)) |
| branch_hit = false; |
| break; |
| case 2: |
| if ((*(const uint16_t *)ptr == 0 && bnz) || |
| (*(const uint16_t *)ptr != 0 && !bnz)) |
| branch_hit = false; |
| break; |
| case 4: |
| if ((*(const uint32_t *)ptr == 0 && bnz) || |
| (*(const uint32_t *)ptr != 0 && !bnz)) |
| branch_hit = false; |
| break; |
| case 8: |
| if ((*(const uint64_t *)ptr == 0 && bnz) || |
| (*(const uint64_t *)ptr != 0 && !bnz)) |
| branch_hit = false; |
| break; |
| } |
| if (!branch_hit) |
| break; |
| ptr = ptr + element_byte_size; |
| } |
| |
| if (branch_hit) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BNZV(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_V(insn, true); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_BZV(llvm::MCInst &insn) { |
| return Emulate_MSA_Branch_V(insn, false); |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_MSA_Branch_V(llvm::MCInst &insn, |
| bool bnz) { |
| bool success = false; |
| int32_t target = 0; |
| llvm::APInt wr_val = llvm::APInt::getNullValue(128); |
| llvm::APInt fail_value = llvm::APInt::getMaxValue(128); |
| llvm::APInt zero_value = llvm::APInt::getNullValue(128); |
| RegisterValue reg_value; |
| |
| uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg()); |
| int32_t offset = insn.getOperand(1).getImm(); |
| |
| int32_t pc = |
| ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success); |
| if (!success) |
| return false; |
| |
| if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value)) |
| wr_val = reg_value.GetAsUInt128(fail_value); |
| else |
| return false; |
| |
| if ((llvm::APInt::isSameValue(zero_value, wr_val) && !bnz) || |
| (!llvm::APInt::isSameValue(zero_value, wr_val) && bnz)) |
| target = pc + offset; |
| else |
| target = pc + 8; |
| |
| Context context; |
| context.type = eContextRelativeBranchImmediate; |
| |
| if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, |
| target)) |
| return false; |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_LDST_Imm(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t base; |
| int32_t imm, address; |
| Context bad_vaddr_context; |
| |
| uint32_t num_operands = insn.getNumOperands(); |
| base = |
| m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg()); |
| imm = insn.getOperand(num_operands - 1).getImm(); |
| |
| RegisterInfo reg_info_base; |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base, |
| reg_info_base)) |
| return false; |
| |
| /* read base register */ |
| address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| /* destination address */ |
| address = address + imm; |
| |
| /* Set the bad_vaddr register with base address used in the instruction */ |
| bad_vaddr_context.type = eContextInvalid; |
| WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, |
| address); |
| |
| return true; |
| } |
| |
| bool EmulateInstructionMIPS::Emulate_LDST_Reg(llvm::MCInst &insn) { |
| bool success = false; |
| uint32_t base, index; |
| int32_t address, index_address; |
| Context bad_vaddr_context; |
| |
| uint32_t num_operands = insn.getNumOperands(); |
| base = |
| m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg()); |
| index = |
| m_reg_info->getEncodingValue(insn.getOperand(num_operands - 1).getReg()); |
| |
| RegisterInfo reg_info_base, reg_info_index; |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base, |
| reg_info_base)) |
| return false; |
| |
| if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + index, |
| reg_info_index)) |
| return false; |
| |
| /* read base register */ |
| address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF, |
| dwarf_zero_mips + base, 0, &success); |
| if (!success) |
| return false; |
| |
| /* read index register */ |
| index_address = (int32_t)ReadRegisterUnsigned( |
| eRegisterKindDWARF, dwarf_zero_mips + index, 0, &success); |
| if (!success) |
| return false; |
| |
| /* destination address */ |
| address = address + index_address; |
| |
| /* Set the bad_vaddr register with base address used in the instruction */ |
| bad_vaddr_context.type = eContextInvalid; |
| WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, |
| address); |
| |
| return true; |
| } |