//===-- ArchitectureMips.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Plugins/Architecture/Mips/ArchitectureMips.h" #include "lldb/Core/Address.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Log.h" using namespace lldb_private; using namespace lldb; LLDB_PLUGIN_DEFINE(ArchitectureMips) ConstString ArchitectureMips::GetPluginNameStatic() { return ConstString("mips"); } void ArchitectureMips::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "Mips-specific algorithms", &ArchitectureMips::Create); } void ArchitectureMips::Terminate() { PluginManager::UnregisterPlugin(&ArchitectureMips::Create); } std::unique_ptr ArchitectureMips::Create(const ArchSpec &arch) { return arch.IsMIPS() ? std::unique_ptr(new ArchitectureMips(arch)) : nullptr; } ConstString ArchitectureMips::GetPluginName() { return GetPluginNameStatic(); } uint32_t ArchitectureMips::GetPluginVersion() { return 1; } addr_t ArchitectureMips::GetCallableLoadAddress(addr_t code_addr, AddressClass addr_class) const { bool is_alternate_isa = false; switch (addr_class) { case AddressClass::eData: case AddressClass::eDebug: return LLDB_INVALID_ADDRESS; case AddressClass::eCodeAlternateISA: is_alternate_isa = true; break; default: break; } if ((code_addr & 2ull) || is_alternate_isa) return code_addr | 1u; return code_addr; } addr_t ArchitectureMips::GetOpcodeLoadAddress(addr_t opcode_addr, AddressClass addr_class) const { switch (addr_class) { case AddressClass::eData: case AddressClass::eDebug: return LLDB_INVALID_ADDRESS; default: break; } return opcode_addr & ~(1ull); } lldb::addr_t ArchitectureMips::GetBreakableLoadAddress(lldb::addr_t addr, Target &target) const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); Address resolved_addr; SectionLoadList §ion_load_list = target.GetSectionLoadList(); if (section_load_list.IsEmpty()) // No sections are loaded, so we must assume we are not running yet and // need to operate only on file address. target.ResolveFileAddress(addr, resolved_addr); else target.ResolveLoadAddress(addr, resolved_addr); addr_t current_offset = 0; // Get the function boundaries to make sure we don't scan back before the // beginning of the current function. ModuleSP temp_addr_module_sp(resolved_addr.GetModule()); if (temp_addr_module_sp) { SymbolContext sc; SymbolContextItem resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr, resolve_scope, sc); Address sym_addr; if (sc.function) sym_addr = sc.function->GetAddressRange().GetBaseAddress(); else if (sc.symbol) sym_addr = sc.symbol->GetAddress(); addr_t function_start = sym_addr.GetLoadAddress(&target); if (function_start == LLDB_INVALID_ADDRESS) function_start = sym_addr.GetFileAddress(); if (function_start) current_offset = addr - function_start; } // If breakpoint address is start of function then we dont have to do // anything. if (current_offset == 0) return addr; auto insn = GetInstructionAtAddress(target, current_offset, addr); if (nullptr == insn || !insn->HasDelaySlot()) return addr; // Adjust the breakable address uint64_t breakable_addr = addr - insn->GetOpcode().GetByteSize(); LLDB_LOGF(log, "Target::%s Breakpoint at 0x%8.8" PRIx64 " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n", __FUNCTION__, addr, breakable_addr); return breakable_addr; } Instruction *ArchitectureMips::GetInstructionAtAddress( Target &target, const Address &resolved_addr, addr_t symbol_offset) const { auto loop_count = symbol_offset / 2; uint32_t arch_flags = m_arch.GetFlags(); bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16; bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips; if (loop_count > 3) { // Scan previous 6 bytes if (IsMips16 | IsMicromips) loop_count = 3; // For mips-only, instructions are always 4 bytes, so scan previous 4 // bytes only. else loop_count = 2; } // Create Disassembler Instance lldb::DisassemblerSP disasm_sp( Disassembler::FindPlugin(m_arch, nullptr, nullptr)); InstructionList instruction_list; InstructionSP prev_insn; bool prefer_file_cache = true; // Read from file uint32_t inst_to_choose = 0; Address addr = resolved_addr; for (uint32_t i = 1; i <= loop_count; i++) { // Adjust the address to read from. addr.Slide(-2); uint32_t insn_size = 0; disasm_sp->ParseInstructions(target, addr, {Disassembler::Limit::Bytes, i * 2}, nullptr, prefer_file_cache); uint32_t num_insns = disasm_sp->GetInstructionList().GetSize(); if (num_insns) { prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0); insn_size = prev_insn->GetOpcode().GetByteSize(); if (i == 1 && insn_size == 2) { // This looks like a valid 2-byte instruction (but it could be a part // of upper 4 byte instruction). instruction_list.Append(prev_insn); inst_to_choose = 1; } else if (i == 2) { // Here we may get one 4-byte instruction or two 2-byte instructions. if (num_insns == 2) { // Looks like there are two 2-byte instructions above our // breakpoint target address. Now the upper 2-byte instruction is // either a valid 2-byte instruction or could be a part of it's // upper 4-byte instruction. In both cases we don't care because in // this case lower 2-byte instruction is definitely a valid // instruction and whatever i=1 iteration has found out is true. inst_to_choose = 1; break; } else if (insn_size == 4) { // This instruction claims its a valid 4-byte instruction. But it // could be a part of it's upper 4-byte instruction. Lets try // scanning upper 2 bytes to verify this. instruction_list.Append(prev_insn); inst_to_choose = 2; } } else if (i == 3) { if (insn_size == 4) // FIXME: We reached here that means instruction at [target - 4] has // already claimed to be a 4-byte instruction, and now instruction // at [target - 6] is also claiming that it's a 4-byte instruction. // This can not be true. In this case we can not decide the valid // previous instruction so we let lldb set the breakpoint at the // address given by user. inst_to_choose = 0; else // This is straight-forward inst_to_choose = 2; break; } } else { // Decode failed, bytes do not form a valid instruction. So whatever // previous iteration has found out is true. if (i > 1) { inst_to_choose = i - 1; break; } } } // Check if we are able to find any valid instruction. if (inst_to_choose) { if (inst_to_choose > instruction_list.GetSize()) inst_to_choose--; return instruction_list.GetInstructionAtIndex(inst_to_choose - 1).get(); } return nullptr; }