You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
8.0 KiB
241 lines
8.0 KiB
//===-- 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<Architecture> ArchitectureMips::Create(const ArchSpec &arch) {
|
|
return arch.IsMIPS() ?
|
|
std::unique_ptr<Architecture>(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;
|
|
}
|