diff --git a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp --- a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp +++ b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp @@ -255,6 +255,7 @@ switch (host_triple.getArch()) { case llvm::Triple::aarch64: case llvm::Triple::arm: + case llvm::Triple::mips64: case llvm::Triple::x86: case llvm::Triple::x86_64: use_legacy_plugin = !!getenv("FREEBSD_LEGACY_PLUGIN"); diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt b/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt --- a/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt +++ b/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt @@ -3,6 +3,7 @@ NativeRegisterContextFreeBSD.cpp NativeRegisterContextFreeBSD_arm.cpp NativeRegisterContextFreeBSD_arm64.cpp + NativeRegisterContextFreeBSD_mips64.cpp NativeRegisterContextFreeBSD_x86_64.cpp NativeThreadFreeBSD.cpp diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h --- a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h @@ -10,6 +10,8 @@ #define liblldb_NativeProcessFreeBSD_H_ #include "Plugins/Process/POSIX/NativeProcessELF.h" +#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h" + #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/FileSpec.h" @@ -25,7 +27,8 @@ /// for debugging. /// /// Changes in the inferior process state are broadcasted. -class NativeProcessFreeBSD : public NativeProcessELF { +class NativeProcessFreeBSD : public NativeProcessELF, + private NativeProcessSoftwareSingleStep { public: class Factory : public NativeProcessProtocol::Factory { public: @@ -84,6 +87,8 @@ static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, int data = 0, int *result = nullptr); + bool SupportHardwareSingleStepping() const; + protected: llvm::Expected> GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp --- a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp @@ -265,7 +265,17 @@ switch (info.pl_siginfo.si_code) { case TRAP_BRKPT: if (thread) { - thread->SetStoppedByBreakpoint(); + auto thread_info = + m_threads_stepping_with_breakpoint.find(thread->GetID()); + if (thread_info != m_threads_stepping_with_breakpoint.end()) { + thread->SetStoppedByTrace(); + Status brkpt_error = RemoveBreakpoint(thread_info->second); + if (brkpt_error.Fail()) + LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", + thread_info->first, brkpt_error); + m_threads_stepping_with_breakpoint.erase(thread_info); + } else + thread->SetStoppedByBreakpoint(); FixupBreakpointPCAsNeeded(*thread); } SetState(StateType::eStateStopped, true); @@ -899,3 +909,7 @@ return error; } + +bool NativeProcessFreeBSD::SupportHardwareSingleStepping() const { + return !m_arch.IsMIPS(); +} diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_mips64.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_mips64.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_mips64.h @@ -0,0 +1,71 @@ +//===-- NativeRegisterContextFreeBSD_mips64.h -------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if defined(__mips64__) + +#ifndef lldb_NativeRegisterContextFreeBSD_mips64_h +#define lldb_NativeRegisterContextFreeBSD_mips64_h + +// clang-format off +#include +#include +// clang-format on + +#include "Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" + +#include + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD_mips64 + : public NativeRegisterContextFreeBSD { +public: + NativeRegisterContextFreeBSD_mips64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override; + +private: + enum RegSetKind { + GPRegSet, + }; + std::array m_reg_data; + + Status ReadRegisterSet(RegSetKind set); + Status WriteRegisterSet(RegSetKind set); + + RegisterContextFreeBSD_mips64 &GetRegisterInfo() const; +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_mips64_h + +#endif // defined (__mips64__) diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_mips64.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_mips64.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_mips64.cpp @@ -0,0 +1,186 @@ +//===-- NativeRegisterContextFreeBSD_mips64.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__mips64__) + +#include "NativeRegisterContextFreeBSD_mips64.h" + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h" + +// clang-format off +#include +#include +#include +// clang-format on + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +NativeRegisterContextFreeBSD * +NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { + return new NativeRegisterContextFreeBSD_mips64(target_arch, native_thread); +} + +NativeRegisterContextFreeBSD_mips64::NativeRegisterContextFreeBSD_mips64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, new RegisterContextFreeBSD_mips64(target_arch)) {} + +RegisterContextFreeBSD_mips64 & +NativeRegisterContextFreeBSD_mips64::GetRegisterInfo() const { + return static_cast( + *m_register_info_interface_up); +} + +uint32_t NativeRegisterContextFreeBSD_mips64::GetRegisterSetCount() const { + return GetRegisterInfo().GetRegisterSetCount(); +} + +const RegisterSet * +NativeRegisterContextFreeBSD_mips64::GetRegisterSet(uint32_t set_index) const { + return GetRegisterInfo().GetRegisterSet(set_index); +} + +uint32_t NativeRegisterContextFreeBSD_mips64::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) + count += GetRegisterSet(set_index)->num_registers; + return count; +} + +Status NativeRegisterContextFreeBSD_mips64::ReadRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), + m_reg_data.data()); + } + llvm_unreachable("NativeRegisterContextFreeBSD_mips64::ReadRegisterSet"); +} + +Status NativeRegisterContextFreeBSD_mips64::WriteRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), + m_reg_data.data()); + } + llvm_unreachable("NativeRegisterContextFreeBSD_mips64::WriteRegisterSet"); +} + +Status +NativeRegisterContextFreeBSD_mips64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : ""); + + RegSetKind set = GPRegSet; + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset, + reg_info->byte_size, endian::InlHostByteOrder()); + return error; +} + +Status NativeRegisterContextFreeBSD_mips64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : ""); + + RegSetKind set = GPRegSet; + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(), + reg_info->byte_size); + + return WriteRegisterSet(set); +} + +Status NativeRegisterContextFreeBSD_mips64::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + Status error; + + error = ReadRegisterSet(GPRegSet); + if (error.Fail()) + return error; + + data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0)); + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, m_reg_data.data(), m_reg_data.size()); + + return error; +} + +Status NativeRegisterContextFreeBSD_mips64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_mips64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != m_reg_data.size()) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_mips64::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize()); + return error; + } + + uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_mips64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + ::memcpy(m_reg_data.data(), src, m_reg_data.size()); + + return WriteRegisterSet(GPRegSet); +} + +llvm::Error NativeRegisterContextFreeBSD_mips64::CopyHardwareWatchpointsFrom( + NativeRegisterContextFreeBSD &source) { + return llvm::Error::success(); +} + +#endif // defined (__mips64__) diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp --- a/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp @@ -46,6 +46,11 @@ if (!ret.Success()) return ret; ret = NativeProcessFreeBSD::PtraceWrapper(PT_CLEARSTEP, GetID()); + // we can get EINVAL if the architecture in question does not support + // hardware single-stepping -- that's fine, we have nothing to clear + // then + if (ret.GetError() == EINVAL) + ret.Clear(); if (ret.Success()) SetRunning(); return ret; diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -22,6 +22,7 @@ #include "NativeThreadLinux.h" #include "Plugins/Process/POSIX/NativeProcessELF.h" +#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h" #include "ProcessorTrace.h" namespace lldb_private { @@ -36,7 +37,8 @@ /// for debugging. /// /// Changes in the inferior process state are broadcasted. -class NativeProcessLinux : public NativeProcessELF { +class NativeProcessLinux : public NativeProcessELF, + private NativeProcessSoftwareSingleStep { public: class Factory : public NativeProcessProtocol::Factory { public: @@ -141,10 +143,6 @@ lldb::tid_t m_pending_notification_tid = LLDB_INVALID_THREAD_ID; - // List of thread ids stepping with a breakpoint with the address of - // the relevan breakpoint - std::map m_threads_stepping_with_breakpoint; - /// Inferior memory (allocated by us) and its size. llvm::DenseMap m_allocated_memory; @@ -173,8 +171,6 @@ void MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited); - Status SetupSoftwareSingleStepping(NativeThreadLinux &thread); - bool HasThreadNoLock(lldb::tid_t thread_id); bool StopTrackingThread(lldb::tid_t thread_id); diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -23,7 +23,6 @@ #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "Plugins/Process/Utility/LinuxProcMaps.h" #include "Procfs.h" -#include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostProcess.h" @@ -38,7 +37,6 @@ #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" -#include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StringExtractor.h" @@ -860,169 +858,6 @@ StopRunningThreads(thread.GetID()); } -namespace { - -struct EmulatorBaton { - NativeProcessLinux &m_process; - NativeRegisterContext &m_reg_context; - - // eRegisterKindDWARF -> RegsiterValue - std::unordered_map m_register_values; - - EmulatorBaton(NativeProcessLinux &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(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(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(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 -NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread) { - Status error; - NativeRegisterContext& register_context = thread.GetRegisterContext(); - - std::unique_ptr emulator_up( - EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, - nullptr)); - - if (emulator_up == nullptr) - return Status("Instruction emulator not found!"); - - EmulatorBaton baton(*this, 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 = - 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."); - } - - if (m_arch.GetMachine() == llvm::Triple::arm) { - if (next_flags & 0x20) { - // Thumb mode - error = SetSoftwareBreakpoint(next_pc, 2); - } else { - // Arm mode - error = SetSoftwareBreakpoint(next_pc, 4); - } - } else if (m_arch.IsMIPS() || m_arch.GetTriple().isPPC64()) - error = SetSoftwareBreakpoint(next_pc, 4); - else { - // No size hint is given for the next breakpoint - error = SetSoftwareBreakpoint(next_pc, 0); - } - - // 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(); -} - bool NativeProcessLinux::SupportHardwareSingleStepping() const { if (m_arch.GetMachine() == llvm::Triple::arm || m_arch.IsMIPS()) return false; diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -9,6 +9,7 @@ LinuxProcMaps.cpp LinuxSignals.cpp MipsLinuxSignals.cpp + NativeProcessSoftwareSingleStep.cpp NativeRegisterContextRegisterInfo.cpp NativeRegisterContextWatchpoint_x86.cpp NetBSDSignals.cpp diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h @@ -0,0 +1,31 @@ +//===-- NativeProcessSoftwareSingleStep.h -----------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeProcessSoftwareSingleStep_h +#define lldb_NativeProcessSoftwareSingleStep_h + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include + +namespace lldb_private { + +class NativeProcessSoftwareSingleStep { +public: + Status SetupSoftwareSingleStepping(NativeThreadProtocol &thread); + +protected: + // List of thread ids stepping with a breakpoint with the address of + // the relevan breakpoint + std::map m_threads_stepping_with_breakpoint; +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeProcessSoftwareSingleStep_h diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp @@ -0,0 +1,182 @@ +//===-- 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 + +using namespace lldb; +using namespace lldb_private; + +namespace { + +struct EmulatorBaton { + NativeProcessProtocol &m_process; + NativeRegisterContext &m_reg_context; + + // eRegisterKindDWARF -> RegsiterValue + std::unordered_map 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(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(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(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 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 = + 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()) + 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(); +} diff --git a/lldb/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp b/lldb/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp --- a/lldb/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp +++ b/lldb/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp @@ -17,13 +17,15 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "Plugins/Process/Utility/lldb-x86-register-enums.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" #include "Plugins/Process/Utility/lldb-arm-register-enums.h" #include "Plugins/Process/Utility/lldb-arm64-register-enums.h" +#include "Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" using namespace lldb; using namespace lldb_private; @@ -398,3 +400,61 @@ } #endif // defined(__aarch64__) + +#if defined(__mips64__) + +#define EXPECT_GPR_MIPS64(lldb_reg, fbsd_regno) \ + EXPECT_THAT(GetRegParams(reg_ctx, gpr_##lldb_reg##_mips64), \ + ::testing::Pair(offsetof(reg, r_regs[fbsd_regno]), \ + sizeof(reg::r_regs[fbsd_regno]))) + +TEST(RegisterContextFreeBSDTest, mips64) { + ArchSpec arch{"mips64-unknown-freebsd"}; + RegisterContextFreeBSD_mips64 reg_ctx{arch}; + + // we can not use aliases from because macros defined + // there are not namespaced and collide a lot, e.g. 'A1' + + EXPECT_GPR_MIPS64(zero, 0); + EXPECT_GPR_MIPS64(r1, 1); + EXPECT_GPR_MIPS64(r2, 2); + EXPECT_GPR_MIPS64(r3, 3); + EXPECT_GPR_MIPS64(r4, 4); + EXPECT_GPR_MIPS64(r5, 5); + EXPECT_GPR_MIPS64(r6, 6); + EXPECT_GPR_MIPS64(r7, 7); + EXPECT_GPR_MIPS64(r8, 8); + EXPECT_GPR_MIPS64(r9, 9); + EXPECT_GPR_MIPS64(r10, 10); + EXPECT_GPR_MIPS64(r11, 11); + EXPECT_GPR_MIPS64(r12, 12); + EXPECT_GPR_MIPS64(r13, 13); + EXPECT_GPR_MIPS64(r14, 14); + EXPECT_GPR_MIPS64(r15, 15); + EXPECT_GPR_MIPS64(r16, 16); + EXPECT_GPR_MIPS64(r17, 17); + EXPECT_GPR_MIPS64(r18, 18); + EXPECT_GPR_MIPS64(r19, 19); + EXPECT_GPR_MIPS64(r20, 20); + EXPECT_GPR_MIPS64(r21, 21); + EXPECT_GPR_MIPS64(r22, 22); + EXPECT_GPR_MIPS64(r23, 23); + EXPECT_GPR_MIPS64(r24, 24); + EXPECT_GPR_MIPS64(r25, 25); + EXPECT_GPR_MIPS64(r26, 26); + EXPECT_GPR_MIPS64(r27, 27); + EXPECT_GPR_MIPS64(gp, 28); + EXPECT_GPR_MIPS64(sp, 29); + EXPECT_GPR_MIPS64(r30, 30); + EXPECT_GPR_MIPS64(ra, 31); + EXPECT_GPR_MIPS64(sr, 32); + EXPECT_GPR_MIPS64(mullo, 33); + EXPECT_GPR_MIPS64(mulhi, 34); + EXPECT_GPR_MIPS64(badvaddr, 35); + EXPECT_GPR_MIPS64(cause, 36); + EXPECT_GPR_MIPS64(pc, 37); + EXPECT_GPR_MIPS64(ic, 38); + EXPECT_GPR_MIPS64(dummy, 39); +} + +#endif // defined(__mips64__)