186 lines
6.9 KiB
C++
186 lines
6.9 KiB
C++
//===-- NativeProcessSoftwareSingleStep.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 "NativeProcessSoftwareSingleStep.h"
|
|
|
|
#include "lldb/Core/EmulateInstruction.h"
|
|
#include "lldb/Host/common/NativeRegisterContext.h"
|
|
#include "lldb/Utility/RegisterValue.h"
|
|
|
|
#include <unordered_map>
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
namespace {
|
|
|
|
struct EmulatorBaton {
|
|
NativeProcessProtocol &m_process;
|
|
NativeRegisterContext &m_reg_context;
|
|
|
|
// eRegisterKindDWARF -> RegsiterValue
|
|
std::unordered_map<uint32_t, RegisterValue> m_register_values;
|
|
|
|
EmulatorBaton(NativeProcessProtocol &process,
|
|
NativeRegisterContext ®_context)
|
|
: m_process(process), m_reg_context(reg_context) {}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
|
|
const EmulateInstruction::Context &context,
|
|
lldb::addr_t addr, void *dst, size_t length) {
|
|
EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
|
|
|
|
size_t bytes_read;
|
|
emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read);
|
|
return bytes_read;
|
|
}
|
|
|
|
static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
|
|
const RegisterInfo *reg_info,
|
|
RegisterValue ®_value) {
|
|
EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
|
|
|
|
auto it = emulator_baton->m_register_values.find(
|
|
reg_info->kinds[eRegisterKindDWARF]);
|
|
if (it != emulator_baton->m_register_values.end()) {
|
|
reg_value = it->second;
|
|
return true;
|
|
}
|
|
|
|
// The emulator only fill in the dwarf regsiter numbers (and in some case the
|
|
// generic register numbers). Get the full register info from the register
|
|
// context based on the dwarf register numbers.
|
|
const RegisterInfo *full_reg_info =
|
|
emulator_baton->m_reg_context.GetRegisterInfo(
|
|
eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
|
|
|
|
Status error =
|
|
emulator_baton->m_reg_context.ReadRegister(full_reg_info, reg_value);
|
|
if (error.Success())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
|
|
const EmulateInstruction::Context &context,
|
|
const RegisterInfo *reg_info,
|
|
const RegisterValue ®_value) {
|
|
EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
|
|
emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] =
|
|
reg_value;
|
|
return true;
|
|
}
|
|
|
|
static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton,
|
|
const EmulateInstruction::Context &context,
|
|
lldb::addr_t addr, const void *dst,
|
|
size_t length) {
|
|
return length;
|
|
}
|
|
|
|
static lldb::addr_t ReadFlags(NativeRegisterContext ®siter_context) {
|
|
const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo(
|
|
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
|
|
return regsiter_context.ReadRegisterAsUnsigned(flags_info,
|
|
LLDB_INVALID_ADDRESS);
|
|
}
|
|
|
|
Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
|
|
NativeThreadProtocol &thread) {
|
|
Status error;
|
|
NativeProcessProtocol &process = thread.GetProcess();
|
|
NativeRegisterContext ®ister_context = thread.GetRegisterContext();
|
|
const ArchSpec &arch = process.GetArchitecture();
|
|
|
|
std::unique_ptr<EmulateInstruction> emulator_up(
|
|
EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
|
|
nullptr));
|
|
|
|
if (emulator_up == nullptr)
|
|
return Status("Instruction emulator not found!");
|
|
|
|
EmulatorBaton baton(process, register_context);
|
|
emulator_up->SetBaton(&baton);
|
|
emulator_up->SetReadMemCallback(&ReadMemoryCallback);
|
|
emulator_up->SetReadRegCallback(&ReadRegisterCallback);
|
|
emulator_up->SetWriteMemCallback(&WriteMemoryCallback);
|
|
emulator_up->SetWriteRegCallback(&WriteRegisterCallback);
|
|
|
|
if (!emulator_up->ReadInstruction())
|
|
return Status("Read instruction failed!");
|
|
|
|
bool emulation_result =
|
|
emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
|
|
|
|
const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
|
|
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
|
const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
|
|
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
|
|
|
|
auto pc_it =
|
|
baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
|
|
auto flags_it = reg_info_flags == nullptr
|
|
? baton.m_register_values.end()
|
|
: baton.m_register_values.find(
|
|
reg_info_flags->kinds[eRegisterKindDWARF]);
|
|
|
|
lldb::addr_t next_pc;
|
|
lldb::addr_t next_flags;
|
|
if (emulation_result) {
|
|
assert(pc_it != baton.m_register_values.end() &&
|
|
"Emulation was successfull but PC wasn't updated");
|
|
next_pc = pc_it->second.GetAsUInt64();
|
|
|
|
if (flags_it != baton.m_register_values.end())
|
|
next_flags = flags_it->second.GetAsUInt64();
|
|
else
|
|
next_flags = ReadFlags(register_context);
|
|
} else if (pc_it == baton.m_register_values.end()) {
|
|
// Emulate instruction failed and it haven't changed PC. Advance PC with
|
|
// the size of the current opcode because the emulation of all
|
|
// PC modifying instruction should be successful. The failure most
|
|
// likely caused by a not supported instruction which don't modify PC.
|
|
next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize();
|
|
next_flags = ReadFlags(register_context);
|
|
} else {
|
|
// The instruction emulation failed after it modified the PC. It is an
|
|
// unknown error where we can't continue because the next instruction is
|
|
// modifying the PC but we don't know how.
|
|
return Status("Instruction emulation failed unexpectedly.");
|
|
}
|
|
|
|
int size_hint = 0;
|
|
if (arch.GetMachine() == llvm::Triple::arm) {
|
|
if (next_flags & 0x20) {
|
|
// Thumb mode
|
|
size_hint = 2;
|
|
} else {
|
|
// Arm mode
|
|
size_hint = 4;
|
|
}
|
|
} else if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
|
|
arch.GetTriple().isRISCV())
|
|
size_hint = 4;
|
|
error = process.SetBreakpoint(next_pc, size_hint, /*hardware=*/false);
|
|
|
|
// If setting the breakpoint fails because next_pc is out of the address
|
|
// space, ignore it and let the debugee segfault.
|
|
if (error.GetError() == EIO || error.GetError() == EFAULT) {
|
|
return Status();
|
|
} else if (error.Fail())
|
|
return error;
|
|
|
|
m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
|
|
|
|
return Status();
|
|
}
|