Index: source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp =================================================================== --- source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp +++ source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp @@ -224,36 +224,83 @@ PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); // Get details on the signal raised. - if (siginfo_err.Success()) { - switch (info.psi_siginfo.si_code) { - case TRAP_BRKPT: + if (siginfo_err.Fail()) { + return; + } + + switch (info.psi_siginfo.si_code) { + case TRAP_BRKPT: + for (const auto &thread_sp : m_threads) { + static_pointer_cast(thread_sp) + ->SetStoppedByBreakpoint(); + FixupBreakpointPCAsNeeded( + *static_pointer_cast(thread_sp)); + } + SetState(StateType::eStateStopped, true); + break; + case TRAP_TRACE: + for (const auto &thread_sp : m_threads) { + static_pointer_cast(thread_sp)->SetStoppedByTrace(); + } + SetState(StateType::eStateStopped, true); + break; + case TRAP_EXEC: { + Error error = ReinitializeThreads(); + if (error.Fail()) { + SetState(StateType::eStateInvalid); + return; + } + + // Let our delegate know we have just exec'd. + NotifyDidExec(); + + for (const auto &thread_sp : m_threads) { + static_pointer_cast(thread_sp)->SetStoppedByExec(); + } + SetState(StateType::eStateStopped, true); + } break; + case TRAP_DBREG: { + // If a watchpoint was hit, report it + uint32_t wp_index; + Error error = + static_pointer_cast(m_threads[info.psi_lwpid]) + ->GetRegisterContext() + ->GetWatchpointHitIndex(wp_index, + (uintptr_t)info.psi_siginfo.si_addr); + if (error.Fail()) + LLDB_LOG(log, + "received error while checking for watchpoint hits, pid = " + "{0}, LWP = {1}, error = {2}", + GetID(), info.psi_lwpid, error); + if (wp_index != LLDB_INVALID_INDEX32) { for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp) - ->SetStoppedByBreakpoint(); - FixupBreakpointPCAsNeeded( - *static_pointer_cast(thread_sp)); + ->SetStoppedByWatchpoint(wp_index); } SetState(StateType::eStateStopped, true); break; - case TRAP_TRACE: + } + + // If a breakpoint was hit, report it + uint32_t bp_index; + error = static_pointer_cast(m_threads[info.psi_lwpid]) + ->GetRegisterContext() + ->GetHardwareBreakHitIndex(bp_index, + (uintptr_t)info.psi_siginfo.si_addr); + if (error.Fail()) + LLDB_LOG(log, + "received error while checking for hardware " + "breakpoint hits, pid = {0}, LWP = {1}, error = {2}", + GetID(), info.psi_lwpid, error); + if (bp_index != LLDB_INVALID_INDEX32) { for (const auto &thread_sp : m_threads) { - static_pointer_cast(thread_sp)->SetStoppedByTrace(); + static_pointer_cast(thread_sp) + ->SetStoppedByBreakpoint(); } SetState(StateType::eStateStopped, true); break; - case TRAP_EXEC: { - Error error = ReinitializeThreads(); - if (error.Fail()) { - SetState(StateType::eStateInvalid); - return; - } - - // Let our delegate know we have just exec'd. - NotifyDidExec(); - - SetState(StateType::eStateStopped, true); - } break; } + } break; } } @@ -328,8 +375,8 @@ return error; } else LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size); - // First try probing for a breakpoint at a software breakpoint location: PC - - // breakpoint size. + // First try probing for a breakpoint at a software breakpoint location: PC + // - breakpoint size. const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation(); lldb::addr_t breakpoint_addr = initial_pc_addr; @@ -439,7 +486,7 @@ llvm_unreachable("Unexpected state"); default: - return Error("NativeProcessLinux::%s (): unexpected state %s specified " + return Error("NativeProcessNetBSD::%s (): unexpected state %s specified " "for pid %" PRIu64 ", tid %" PRIu64, __FUNCTION__, StateAsCString(action->state), GetID(), thread_sp->GetID()); @@ -540,8 +587,8 @@ "descending memory map entries detected, unexpected"); prev_base_address = proc_entry_info.GetRange().GetRangeBase(); UNUSED_IF_ASSERT_DISABLED(prev_base_address); - // If the target address comes before this entry, indicate distance to next - // region. + // If the target address comes before this entry, indicate distance to + // next region. if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetByteSize( @@ -561,9 +608,8 @@ } // If we made it here, we didn't find an entry that contained the given // address. Return the - // load_addr as start and the amount of bytes betwwen load address and the end - // of the memory as - // size. + // load_addr as start and the amount of bytes betwwen load address and the + // end of the memory as size. range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); @@ -722,8 +768,8 @@ LLDB_LOG(log, "waitpid for inferior failed with %s", error); // Mark the inferior as invalid. - // FIXME this could really use a new state - eStateLaunchFailure. For now, - // using eStateInvalid. + // FIXME this could really use a new state - eStateLaunchFailure. For + // now, using eStateInvalid. SetState(StateType::eStateInvalid); return error; @@ -766,6 +812,11 @@ return error; } + for (const auto &thread_sp : m_threads) { + static_pointer_cast(thread_sp)->SetStoppedBySignal( + SIGSTOP); + } + /* Set process stopped */ SetState(StateType::eStateStopped); @@ -894,6 +945,11 @@ return -1; } + for (const auto &thread_sp : m_threads) { + static_pointer_cast(thread_sp)->SetStoppedBySignal( + SIGSTOP); + } + // Let our process instance know the thread has stopped. SetState(StateType::eStateStopped); @@ -1007,7 +1063,6 @@ // Reinitialize from scratch threads and register them in process while (info.pl_lwpid != 0) { NativeThreadNetBSDSP thread_sp = AddThread(info.pl_lwpid); - thread_sp->SetStoppedByExec(); error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info)); if (error.Fail()) { return error; Index: source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h =================================================================== --- source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h +++ source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h @@ -41,6 +41,9 @@ virtual Error ReadFPR(); virtual Error WriteFPR(); + virtual Error ReadDBR(); + virtual Error WriteDBR(); + virtual void *GetGPRBuffer() { return nullptr; } virtual size_t GetGPRSize() { return GetRegisterInfoInterface().GetGPRSize(); @@ -49,12 +52,18 @@ virtual void *GetFPRBuffer() { return nullptr; } virtual size_t GetFPRSize() { return 0; } + virtual void *GetDBRBuffer() { return nullptr; } + virtual size_t GetDBRSize() { return 0; } + virtual Error DoReadGPR(void *buf); virtual Error DoWriteGPR(void *buf); virtual Error DoReadFPR(void *buf); virtual Error DoWriteFPR(void *buf); + virtual Error DoReadDBR(void *buf); + virtual Error DoWriteDBR(void *buf); + virtual NativeProcessNetBSD &GetProcess(); virtual ::pid_t GetProcessPid(); }; Index: source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp =================================================================== --- source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp +++ source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp @@ -57,6 +57,22 @@ return DoWriteFPR(buf); } +Error NativeRegisterContextNetBSD::ReadDBR() { + void *buf = GetDBRBuffer(); + if (!buf) + return Error("DBR buffer is NULL"); + + return DoReadDBR(buf); +} + +Error NativeRegisterContextNetBSD::WriteDBR() { + void *buf = GetDBRBuffer(); + if (!buf) + return Error("DBR buffer is NULL"); + + return DoWriteDBR(buf); +} + Error NativeRegisterContextNetBSD::DoReadGPR(void *buf) { return NativeProcessNetBSD::PtraceWrapper(PT_GETREGS, GetProcessPid(), buf, m_thread.GetID()); @@ -77,6 +93,16 @@ m_thread.GetID()); } +Error NativeRegisterContextNetBSD::DoReadDBR(void *buf) { + return NativeProcessNetBSD::PtraceWrapper(PT_GETDBREGS, GetProcessPid(), buf, + m_thread.GetID()); +} + +Error NativeRegisterContextNetBSD::DoWriteDBR(void *buf) { + return NativeProcessNetBSD::PtraceWrapper(PT_SETDBREGS, GetProcessPid(), buf, + m_thread.GetID()); +} + NativeProcessNetBSD &NativeRegisterContextNetBSD::GetProcess() { auto process_sp = std::static_pointer_cast(m_thread.GetProcess()); Index: source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h =================================================================== --- source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h +++ source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h @@ -46,17 +46,40 @@ Error WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + Error IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Error GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Error IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Error ClearAllHardwareWatchpoints() override; + + Error SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + protected: void *GetGPRBuffer() override { return &m_gpr_x86_64; } void *GetFPRBuffer() override { return &m_fpr_x86_64; } + void *GetDBRBuffer() override { return &m_dbr_x86_64; } private: // Private member types. - enum { GPRegSet, FPRegSet }; + enum { GPRegSet, FPRegSet, DBRegSet }; // Private member variables. struct reg m_gpr_x86_64; struct fpreg m_fpr_x86_64; + struct dbreg m_dbr_x86_64; int GetSetForNativeRegNum(int reg_num) const; Index: source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp =================================================================== --- source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp +++ source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp @@ -19,7 +19,15 @@ #include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h" +// clang-format off +#include +#include +#include #include +#include +#include +#include +// clang-format on using namespace lldb_private; using namespace lldb_private::process_netbsd; @@ -86,6 +94,57 @@ #define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize()) +const int fpu_present = []() -> int { + int mib[2]; + int error; + size_t len; + int val; + + len = sizeof(val); + mib[0] = CTL_MACHDEP; + mib[1] = CPU_FPU_PRESENT; + + error = sysctl(mib, __arraycount(mib), &val, &len, NULL, 0); + if (error) + errx(EXIT_FAILURE, "sysctl"); + + return val; +}(); + +const int osfxsr = []() -> int { + int mib[2]; + int error; + size_t len; + int val; + + len = sizeof(val); + mib[0] = CTL_MACHDEP; + mib[1] = CPU_OSFXSR; + + error = sysctl(mib, __arraycount(mib), &val, &len, NULL, 0); + if (error) + errx(EXIT_FAILURE, "sysctl"); + + return val; +}(); + +const int fpu_save = []() -> int { + int mib[2]; + int error; + size_t len; + int val; + + len = sizeof(val); + mib[0] = CTL_MACHDEP; + mib[1] = CPU_FPU_SAVE; + + error = sysctl(mib, __arraycount(mib), &val, &len, NULL, 0); + if (error) + errx(EXIT_FAILURE, "sysctl"); + + return val; +}(); + } // namespace NativeRegisterContextNetBSD * @@ -114,7 +173,7 @@ uint32_t concrete_frame_idx) : NativeRegisterContextNetBSD(native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)), - m_gpr_x86_64() {} + m_gpr_x86_64(), m_fpr_x86_64(), m_dbr_x86_64() {} // CONSIDER after local and llgs debugging are merged, register set support can // be moved into a base x86-64 class with IsRegisterSetAvailable made virtual. @@ -143,8 +202,18 @@ int NativeRegisterContextNetBSD_x86_64::GetSetForNativeRegNum( int reg_num) const { - if (reg_num < lldb_fctrl_x86_64) + if (reg_num <= k_last_gpr_x86_64) return GPRegSet; + else if (reg_num <= k_last_fpr_x86_64) + return (fpu_present == 1 && osfxsr == 1 && fpu_save >= 1) ? FPRegSet : -1; + else if (reg_num <= k_last_avx_x86_64) + return -1; // AVX + else if (reg_num <= k_last_mpxr_x86_64) + return -1; // MPXR + else if (reg_num <= k_last_mpxc_x86_64) + return -1; // MPXC + else if (reg_num <= lldb_dr7_x86_64) + return DBRegSet; // DBR else return -1; } @@ -157,6 +226,9 @@ case FPRegSet: ReadFPR(); return 0; + case DBRegSet: + ReadDBR(); + return 0; default: break; } @@ -170,6 +242,9 @@ case FPRegSet: WriteFPR(); return 0; + case DBRegSet: + WriteDBR(); + return 0; default: break; } @@ -285,6 +360,87 @@ case lldb_es_x86_64: reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_ES]; break; + case lldb_fctrl_x86_64: + reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_cw; + break; + case lldb_fstat_x86_64: + reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_sw; + break; + case lldb_ftag_x86_64: + reg_value = (uint8_t)m_fpr_x86_64.fxstate.fx_tw; + break; + case lldb_fop_x86_64: + reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_opcode; + break; + case lldb_fiseg_x86_64: + reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_ip.fa_64; + break; + case lldb_fioff_x86_64: + reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off; + break; + case lldb_foseg_x86_64: + reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_dp.fa_64; + break; + case lldb_fooff_x86_64: + reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off; + break; + case lldb_mxcsr_x86_64: + reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr; + break; + case lldb_mxcsrmask_x86_64: + reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr_mask; + break; + case lldb_st0_x86_64: + case lldb_st1_x86_64: + case lldb_st2_x86_64: + case lldb_st3_x86_64: + case lldb_st4_x86_64: + case lldb_st5_x86_64: + case lldb_st6_x86_64: + case lldb_st7_x86_64: + reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); + break; + case lldb_mm0_x86_64: + case lldb_mm1_x86_64: + case lldb_mm2_x86_64: + case lldb_mm3_x86_64: + case lldb_mm4_x86_64: + case lldb_mm5_x86_64: + case lldb_mm6_x86_64: + case lldb_mm7_x86_64: + reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_mm0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); + break; + case lldb_xmm0_x86_64: + case lldb_xmm1_x86_64: + case lldb_xmm2_x86_64: + case lldb_xmm3_x86_64: + case lldb_xmm4_x86_64: + case lldb_xmm5_x86_64: + case lldb_xmm6_x86_64: + case lldb_xmm7_x86_64: + case lldb_xmm8_x86_64: + case lldb_xmm9_x86_64: + case lldb_xmm10_x86_64: + case lldb_xmm11_x86_64: + case lldb_xmm12_x86_64: + case lldb_xmm13_x86_64: + case lldb_xmm14_x86_64: + case lldb_xmm15_x86_64: + reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); + break; + case lldb_dr0_x86_64: + case lldb_dr1_x86_64: + case lldb_dr2_x86_64: + case lldb_dr3_x86_64: + case lldb_dr4_x86_64: + case lldb_dr5_x86_64: + case lldb_dr6_x86_64: + case lldb_dr7_x86_64: + reg_value = (uint64_t)m_dbr_x86_64.dr[reg - lldb_dr0_x86_64]; + break; } return error; @@ -400,6 +556,87 @@ case lldb_es_x86_64: m_gpr_x86_64.regs[_REG_ES] = reg_value.GetAsUInt64(); break; + case lldb_fctrl_x86_64: + m_fpr_x86_64.fxstate.fx_cw = reg_value.GetAsUInt16(); + break; + case lldb_fstat_x86_64: + m_fpr_x86_64.fxstate.fx_sw = reg_value.GetAsUInt16(); + break; + case lldb_ftag_x86_64: + m_fpr_x86_64.fxstate.fx_tw = reg_value.GetAsUInt8(); + break; + case lldb_fop_x86_64: + m_fpr_x86_64.fxstate.fx_opcode = reg_value.GetAsUInt16(); + break; + case lldb_fiseg_x86_64: + m_fpr_x86_64.fxstate.fx_ip.fa_64 = reg_value.GetAsUInt64(); + break; + case lldb_fioff_x86_64: + m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off = reg_value.GetAsUInt32(); + break; + case lldb_foseg_x86_64: + m_fpr_x86_64.fxstate.fx_dp.fa_64 = reg_value.GetAsUInt64(); + break; + case lldb_fooff_x86_64: + m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off = reg_value.GetAsUInt32(); + break; + case lldb_mxcsr_x86_64: + m_fpr_x86_64.fxstate.fx_mxcsr = reg_value.GetAsUInt32(); + break; + case lldb_mxcsrmask_x86_64: + m_fpr_x86_64.fxstate.fx_mxcsr_mask = reg_value.GetAsUInt32(); + break; + case lldb_st0_x86_64: + case lldb_st1_x86_64: + case lldb_st2_x86_64: + case lldb_st3_x86_64: + case lldb_st4_x86_64: + case lldb_st5_x86_64: + case lldb_st6_x86_64: + case lldb_st7_x86_64: + ::memcpy(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64], + reg_value.GetBytes(), reg_value.GetByteSize()); + break; + case lldb_mm0_x86_64: + case lldb_mm1_x86_64: + case lldb_mm2_x86_64: + case lldb_mm3_x86_64: + case lldb_mm4_x86_64: + case lldb_mm5_x86_64: + case lldb_mm6_x86_64: + case lldb_mm7_x86_64: + ::memcpy(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_mm0_x86_64], + reg_value.GetBytes(), reg_value.GetByteSize()); + break; + case lldb_xmm0_x86_64: + case lldb_xmm1_x86_64: + case lldb_xmm2_x86_64: + case lldb_xmm3_x86_64: + case lldb_xmm4_x86_64: + case lldb_xmm5_x86_64: + case lldb_xmm6_x86_64: + case lldb_xmm7_x86_64: + case lldb_xmm8_x86_64: + case lldb_xmm9_x86_64: + case lldb_xmm10_x86_64: + case lldb_xmm11_x86_64: + case lldb_xmm12_x86_64: + case lldb_xmm13_x86_64: + case lldb_xmm14_x86_64: + case lldb_xmm15_x86_64: + ::memcpy(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64], + reg_value.GetBytes(), reg_value.GetByteSize()); + break; + case lldb_dr0_x86_64: + case lldb_dr1_x86_64: + case lldb_dr2_x86_64: + case lldb_dr3_x86_64: + case lldb_dr4_x86_64: + case lldb_dr5_x86_64: + case lldb_dr6_x86_64: + case lldb_dr7_x86_64: + m_dbr_x86_64.dr[reg - lldb_dr0_x86_64] = reg_value.GetAsUInt64(); + break; } if (WriteRegisterSet(set) != 0) @@ -480,4 +717,223 @@ return error; } +Error NativeRegisterContextNetBSD_x86_64::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + RegisterValue reg_value; + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr6_x86_64); + Error error = ReadRegister(reg_info, reg_value); + if (error.Fail()) { + is_hit = false; + return error; + } + + uint64_t status_bits = reg_value.GetAsUInt64(); + + is_hit = status_bits & (1 << wp_index); + + return error; +} + +Error NativeRegisterContextNetBSD_x86_64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { + bool is_hit; + Error error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + return error; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error NativeRegisterContextNetBSD_x86_64::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + RegisterValue reg_value; + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr7_x86_64); + Error error = ReadRegister(reg_info, reg_value); + if (error.Fail()) { + is_vacant = false; + return error; + } + + uint64_t control_bits = reg_value.GetAsUInt64(); + + is_vacant = !(control_bits & (1 << (2 * wp_index))); + + return error; +} + +Error NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + // Read only watchpoints aren't supported on x86_64. Fall back to read/write + // waitchpoints instead. + // TODO: Add logic to detect when a write happens and ignore that watchpoint + // hit. + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return Error("Invalid read/write bits for watchpoint"); + + if (size != 1 && size != 2 && size != 4 && size != 8) + return Error("Invalid size for watchpoint"); + + bool is_vacant; + Error error = IsWatchpointVacant(wp_index, is_vacant); + if (error.Fail()) + return error; + if (!is_vacant) + return Error("Watchpoint index not vacant"); + + RegisterValue reg_value; + const RegisterInfo *const reg_info_dr7 = + GetRegisterInfoAtIndex(lldb_dr7_x86_64); + error = ReadRegister(reg_info_dr7, reg_value); + if (error.Fail()) + return error; + + // for watchpoints 0, 1, 2, or 3, respectively, + // set bits 1, 3, 5, or 7 + uint64_t enable_bit = 1 << (2 * wp_index); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint64_t rw_bits = watch_flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + + control_bits |= enable_bit | rw_bits | size_bits; + + const RegisterInfo *const reg_info_drN = + GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index); + error = WriteRegister(reg_info_drN, RegisterValue(addr)); + if (error.Fail()) + return error; + + error = WriteRegister(reg_info_dr7, RegisterValue(control_bits)); + if (error.Fail()) + return error; + + error.Clear(); + return error; +} + +bool NativeRegisterContextNetBSD_x86_64::ClearHardwareWatchpoint( + uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + RegisterValue reg_value; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits 0, 1, 2, or 3 of the debug status register (DR6) + const RegisterInfo *const reg_info_dr6 = + GetRegisterInfoAtIndex(lldb_dr6_x86_64); + Error error = ReadRegister(reg_info_dr6, reg_value); + if (error.Fail()) + return false; + uint64_t bit_mask = 1 << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(reg_info_dr6, RegisterValue(status_bits)); + if (error.Fail()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits {0-1,16-19}, {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} + // of the debug control register (DR7) + const RegisterInfo *const reg_info_dr7 = + GetRegisterInfoAtIndex(lldb_dr7_x86_64); + error = ReadRegister(reg_info_dr7, reg_value); + if (error.Fail()) + return false; + bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(reg_info_dr7, RegisterValue(control_bits)).Success(); +} + +Error NativeRegisterContextNetBSD_x86_64::ClearAllHardwareWatchpoints() { + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + const RegisterInfo *const reg_info_dr6 = + GetRegisterInfoAtIndex(lldb_dr6_x86_64); + Error error = ReadRegister(reg_info_dr6, reg_value); + if (error.Fail()) + return error; + uint64_t bit_mask = 0xF; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(reg_info_dr6, RegisterValue(status_bits)); + if (error.Fail()) + return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + const RegisterInfo *const reg_info_dr7 = + GetRegisterInfoAtIndex(lldb_dr7_x86_64); + error = ReadRegister(reg_info_dr7, reg_value); + if (error.Fail()) + return error; + bit_mask = 0xFF | (0xFFFF << 16); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(reg_info_dr7, RegisterValue(control_bits)); +} + +uint32_t NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) { + bool is_vacant; + Error error = IsWatchpointVacant(wp_index, is_vacant); + if (is_vacant) { + error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); + if (error.Success()) + return wp_index; + } + if (error.Fail() && log) { + log->Printf("NativeRegisterContextNetBSD_x86_64::%s Error: %s", + __FUNCTION__, error.AsCString()); + } + } + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextNetBSD_x86_64::GetWatchpointAddress(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + RegisterValue reg_value; + const RegisterInfo *const reg_info_drN = + GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index); + if (ReadRegister(reg_info_drN, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + return reg_value.GetAsUInt64(); +} + +uint32_t NativeRegisterContextNetBSD_x86_64::NumSupportedHardwareWatchpoints() { + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} + #endif // defined(__x86_64__) Index: source/Plugins/Process/NetBSD/NativeThreadNetBSD.h =================================================================== --- source/Plugins/Process/NetBSD/NativeThreadNetBSD.h +++ source/Plugins/Process/NetBSD/NativeThreadNetBSD.h @@ -12,6 +12,9 @@ #include "lldb/Host/common/NativeThreadProtocol.h" +#include +#include + namespace lldb_private { namespace process_netbsd { @@ -53,6 +56,7 @@ void SetStoppedByBreakpoint(); void SetStoppedByTrace(); void SetStoppedByExec(); + void SetStoppedByWatchpoint(uint32_t wp_index); void SetStopped(); void SetRunning(); void SetStepping(); @@ -64,6 +68,9 @@ ThreadStopInfo m_stop_info; NativeRegisterContextSP m_reg_context_sp; std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; + WatchpointIndexMap m_hw_break_index_map; }; typedef std::shared_ptr NativeThreadNetBSDSP; Index: source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp =================================================================== --- source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp +++ source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp @@ -16,6 +16,9 @@ #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" +#include "lldb/Utility/LLDBAssert.h" + +#include using namespace lldb; using namespace lldb_private; @@ -68,6 +71,23 @@ m_stop_info.details.signal.signo = SIGTRAP; } +void NativeThreadNetBSD::SetStoppedByWatchpoint(uint32_t wp_index) { + SetStopped(); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index); + + m_stop_description = ostr.str(); + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.details.signal.signo = SIGTRAP; +} + void NativeThreadNetBSD::SetStopped() { const StateType new_state = StateType::eStateStopped; m_state = new_state; @@ -142,18 +162,61 @@ Error NativeThreadNetBSD::SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) { - return Error("Unimplemented"); + if (!hardware) + return Error("not implemented"); + if (m_state == eStateLaunching) + return Error(); + Error error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + NativeRegisterContextSP reg_ctx = GetRegisterContext(); + uint32_t wp_index = reg_ctx->SetHardwareWatchpoint(addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Error("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Error(); } Error NativeThreadNetBSD::RemoveWatchpoint(lldb::addr_t addr) { - return Error("Unimplemented"); + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Error(); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) + return Error(); + return Error("Clearing hardware watchpoint failed."); } Error NativeThreadNetBSD::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { - return Error("Unimplemented"); + if (m_state == eStateLaunching) + return Error(); + + Error error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + NativeRegisterContextSP reg_ctx = GetRegisterContext(); + uint32_t bp_index = reg_ctx->SetHardwareBreakpoint(addr, size); + + if (bp_index == LLDB_INVALID_INDEX32) + return Error("Setting hardware breakpoint failed."); + + m_hw_break_index_map.insert({addr, bp_index}); + return Error(); } Error NativeThreadNetBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { - return Error("Unimplemented"); + auto bp = m_hw_break_index_map.find(addr); + if (bp == m_hw_break_index_map.end()) + return Error(); + + uint32_t bp_index = bp->second; + if (GetRegisterContext()->ClearHardwareBreakpoint(bp_index)) { + m_hw_break_index_map.erase(bp); + return Error(); + } + + return Error("Clearing hardware breakpoint failed."); } Index: source/Plugins/Process/Utility/RegisterInfos_x86_64.h =================================================================== --- source/Plugins/Process/Utility/RegisterInfos_x86_64.h +++ source/Plugins/Process/Utility/RegisterInfos_x86_64.h @@ -148,7 +148,7 @@ DR_OFFSET(i), eEncodingUint, eFormatHex, \ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ - LLDB_INVALID_REGNUM }, \ + lldb_##reg##i##_x86_64 }, \ nullptr, nullptr, nullptr, 0 \ }