//===-- 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 #include "llvm-c/Disassembler.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCContext.h" #include "lldb/Core/Address.h" #include "lldb/Core/Opcode.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Stream.h" #include "lldb/Symbol/UnwindPlan.h" #include "llvm/ADT/STLExtras.h" #include "Plugins/Process/Utility/InstructionUtils.h" #include "Plugins/Process/Utility/RegisterContext_mips64.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 Error; llvm::Triple triple = arch.GetTriple(); const llvm::Target *target = llvm::TargetRegistry::lookupTarget (triple.getTriple(), Error); /* * 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(), Error); } #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; } 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, "")); 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()); } 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) { std::auto_ptr emulate_insn_ap (new EmulateInstructionMIPS (arch)); if (emulate_insn_ap.get()) return emulate_insn_ap.release(); } } 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 gcc_dwarf_sp_mips64: return "r29"; case gcc_dwarf_r30_mips64: return "r30"; case gcc_dwarf_ra_mips64: return "r31"; default: break; } return nullptr; } switch (reg_num) { case gcc_dwarf_zero_mips64: return "r0"; case gcc_dwarf_r1_mips64: return "r1"; case gcc_dwarf_r2_mips64: return "r2"; case gcc_dwarf_r3_mips64: return "r3"; case gcc_dwarf_r4_mips64: return "r4"; case gcc_dwarf_r5_mips64: return "r5"; case gcc_dwarf_r6_mips64: return "r6"; case gcc_dwarf_r7_mips64: return "r7"; case gcc_dwarf_r8_mips64: return "r8"; case gcc_dwarf_r9_mips64: return "r9"; case gcc_dwarf_r10_mips64: return "r10"; case gcc_dwarf_r11_mips64: return "r11"; case gcc_dwarf_r12_mips64: return "r12"; case gcc_dwarf_r13_mips64: return "r13"; case gcc_dwarf_r14_mips64: return "r14"; case gcc_dwarf_r15_mips64: return "r15"; case gcc_dwarf_r16_mips64: return "r16"; case gcc_dwarf_r17_mips64: return "r17"; case gcc_dwarf_r18_mips64: return "r18"; case gcc_dwarf_r19_mips64: return "r19"; case gcc_dwarf_r20_mips64: return "r20"; case gcc_dwarf_r21_mips64: return "r21"; case gcc_dwarf_r22_mips64: return "r22"; case gcc_dwarf_r23_mips64: return "r23"; case gcc_dwarf_r24_mips64: return "r24"; case gcc_dwarf_r25_mips64: return "r25"; case gcc_dwarf_r26_mips64: return "r26"; case gcc_dwarf_r27_mips64: return "r27"; case gcc_dwarf_gp_mips64: return "gp"; case gcc_dwarf_sp_mips64: return "sp"; case gcc_dwarf_r30_mips64: return "fp"; case gcc_dwarf_ra_mips64: return "ra"; case gcc_dwarf_sr_mips64: return "sr"; case gcc_dwarf_lo_mips64: return "lo"; case gcc_dwarf_hi_mips64: return "hi"; case gcc_dwarf_bad_mips64: return "bad"; case gcc_dwarf_cause_mips64: return "cause"; case gcc_dwarf_pc_mips64: return "pc"; } 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 = gcc_dwarf_pc_mips64; break; case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_sp_mips64; break; case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_r30_mips64; break; case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_ra_mips64; break; case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_sr_mips64; 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 ((int)reg_num >= gcc_dwarf_zero_mips64 && (int)reg_num <= gcc_dwarf_pc_mips64) { reg_info.byte_size = 4; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; reg_info.name = GetRegisterName (reg_num, false); reg_info.alt_name = GetRegisterName (reg_num, true); reg_info.kinds[eRegisterKindDWARF] = reg_num; } else { return false; } switch (reg_num) { case gcc_dwarf_r30_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; break; case gcc_dwarf_ra_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; break; case gcc_dwarf_sp_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; break; case gcc_dwarf_pc_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; break; case gcc_dwarf_sr_mips64: 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)" }, }; 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; } 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 raw_insn (data.GetDataStart(), data.GetByteSize()); 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:: we would need "MipsGenInstrInfo.inc". */ const char *op_name = m_insn_info->getName (mc_insn.getOpcode ()); 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, gcc_dwarf_pc_mips64, 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, gcc_dwarf_pc_mips64, 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, gcc_dwarf_pc_mips64, 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(gcc_dwarf_sp_mips64, 0); // Our previous PC is in the RA row->SetRegisterLocationToRegister(gcc_dwarf_pc_mips64, gcc_dwarf_ra_mips64, can_replace); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("EmulateInstructionMIPS"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); return true; } bool EmulateInstructionMIPS::nonvolatile_reg_p (uint32_t regnum) { switch (regnum) { case gcc_dwarf_r16_mips64: case gcc_dwarf_r17_mips64: case gcc_dwarf_r18_mips64: case gcc_dwarf_r19_mips64: case gcc_dwarf_r20_mips64: case gcc_dwarf_r21_mips64: case gcc_dwarf_r22_mips64: case gcc_dwarf_r23_mips64: case gcc_dwarf_gp_mips64: case gcc_dwarf_sp_mips64: case gcc_dwarf_r30_mips64: case gcc_dwarf_ra_mips64: return true; default: return false; } return false; } bool EmulateInstructionMIPS::Emulate_ADDiu (llvm::MCInst& insn) { bool success = false; const uint32_t imm16 = insn.getOperand(2).getImm(); uint32_t imm = SignedBits(imm16, 15, 0); uint64_t result; uint32_t src, dst; dst = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); src = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); /* Check if this is addiu sp,,imm16 */ if (dst == gcc_dwarf_sp_mips64) { /* read register */ uint64_t src_opd_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, 0, &success); if (!success) return false; result = src_opd_val + imm; Context context; RegisterInfo reg_info_sp; if (GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_sp_mips64, reg_info_sp)) context.SetRegisterPlusOffset (reg_info_sp, imm); /* We are allocating bytes on stack */ context.type = eContextAdjustStackPointer; WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_sp_mips64, result); } 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; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); /* We look for sp based non-volatile register stores */ if (base == gcc_dwarf_sp_mips64 && nonvolatile_reg_p (src)) { uint32_t address; RegisterInfo reg_info_base; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base) || !GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, reg_info_src)) return false; /* read SP */ address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); if (!success) return false; /* destination address */ address = address + imm; Context context; RegisterValue data_src; context.type = eContextPushRegisterOnStack; context.SetRegisterToRegisterPlusOffset (reg_info_src, reg_info_base, 0); uint8_t buffer [RegisterValue::kMaxRegisterByteSize]; Error 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) { uint32_t src, base; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); if (base == gcc_dwarf_sp_mips64 && nonvolatile_reg_p (src)) { RegisterValue data_src; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, reg_info_src)) return false; Context context; context.type = eContextRegisterLoad; if (!WriteRegister (context, ®_info_src, data_src)) return false; return true; } return false; }