Skip to content

Commit ac61e5a

Browse files
committed
WoA hardware breakpoint/watchpoint support
1 parent 5537ae8 commit ac61e5a

10 files changed

+135
-92
lines changed

lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -491,24 +491,47 @@ NativeProcessWindows::OnDebugException(bool first_chance,
491491
return ExceptionResult::MaskException;
492492
}
493493
case DWORD(STATUS_BREAKPOINT):
494-
case STATUS_WX86_BREAKPOINT:
495-
if (FindSoftwareBreakpoint(record.GetExceptionAddress())) {
496-
LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
497-
record.GetExceptionAddress());
498-
499-
StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint);
500-
501-
if (NativeThreadWindows *stop_thread =
502-
GetThreadByID(record.GetThreadID())) {
503-
auto &register_context = stop_thread->GetRegisterContext();
494+
case STATUS_WX86_BREAKPOINT: {
495+
bool breakpoint_hit = false;
496+
NativeThreadWindows *stop_thread = GetThreadByID(record.GetThreadID());
497+
498+
if (stop_thread) {
499+
uint32_t hw_id = LLDB_INVALID_INDEX32;
500+
auto &reg_ctx = stop_thread->GetRegisterContext();
501+
reg_ctx.GetHardwareBreakHitIndex(hw_id, record.GetExceptionAddress());
502+
if (hw_id != LLDB_INVALID_INDEX32) {
503+
breakpoint_hit = true;
504+
LLDB_LOG(log, "Hit hardware breakpoint at address {0:x}.",
505+
record.GetExceptionAddress());
506+
} else if (FindSoftwareBreakpoint(record.GetExceptionAddress())) {
507+
breakpoint_hit = true;
508+
LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
509+
record.GetExceptionAddress());
504510
uint32_t breakpoint_size = GetSoftwareBreakpointPCOffset();
505511
// The current PC is AFTER the BP opcode, on all architectures.
506-
uint64_t pc = register_context.GetPC() - breakpoint_size;
507-
register_context.SetPC(pc);
512+
uint64_t pc = reg_ctx.GetPC() - breakpoint_size;
513+
reg_ctx.SetPC(pc);
508514
}
509515

510-
SetState(eStateStopped, true);
511-
return ExceptionResult::MaskException;
516+
if (breakpoint_hit) {
517+
StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint);
518+
SetState(eStateStopped, true);
519+
return ExceptionResult::MaskException;
520+
} else {
521+
const std::vector<ULONG_PTR> &args = record.GetExceptionArguments();
522+
if (args.size() >= 2) {
523+
reg_ctx.GetWatchpointHitIndex(hw_id, args[1]);
524+
if (hw_id != LLDB_INVALID_INDEX32) {
525+
addr_t wp_pc = record.GetExceptionAddress();
526+
std::string desc =
527+
formatv("{0} {1} {2}", args[1], hw_id, wp_pc).str();
528+
StopThread(record.GetThreadID(), StopReason::eStopReasonWatchpoint,
529+
desc);
530+
SetState(eStateStopped, true);
531+
return ExceptionResult::MaskException;
532+
}
533+
}
534+
}
512535
}
513536

514537
if (!initial_stop) {
@@ -531,6 +554,7 @@ NativeProcessWindows::OnDebugException(bool first_chance,
531554
// Hit the initial stop. Continue the application.
532555
return ExceptionResult::BreakInDebugger;
533556
}
557+
}
534558

535559
[[fallthrough]];
536560
default:

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818
using namespace lldb;
1919
using namespace lldb_private;
2020

21-
NativeRegisterContextWindows::NativeRegisterContextWindows(
22-
NativeThreadProtocol &thread, RegisterInfoInterface *reg_info_interface_p)
23-
: NativeRegisterContextRegisterInfo(thread, reg_info_interface_p) {}
2421

2522
lldb::thread_t NativeRegisterContextWindows::GetThreadHandle() const {
2623
auto wthread = static_cast<NativeThreadWindows *>(&m_thread);

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@ namespace lldb_private {
1717

1818
class NativeThreadWindows;
1919

20-
class NativeRegisterContextWindows : public NativeRegisterContextRegisterInfo {
20+
class NativeRegisterContextWindows
21+
: public virtual NativeRegisterContextRegisterInfo {
2122
public:
22-
NativeRegisterContextWindows(
23-
NativeThreadProtocol &native_thread,
24-
RegisterInfoInterface *reg_info_interface_p);
2523

2624
static std::unique_ptr<NativeRegisterContextWindows>
2725
CreateHostNativeRegisterContextWindows(const ArchSpec &target_arch,

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ static Status SetWoW64ThreadContextHelper(lldb::thread_t thread_handle,
8888

8989
NativeRegisterContextWindows_WoW64::NativeRegisterContextWindows_WoW64(
9090
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
91-
: NativeRegisterContextWindows(native_thread,
92-
CreateRegisterInfoInterface(target_arch)) {}
91+
: NativeRegisterContextRegisterInfo(
92+
native_thread, CreateRegisterInfoInterface(target_arch)) {}
9393

9494
bool NativeRegisterContextWindows_WoW64::IsGPR(uint32_t reg_index) const {
9595
return (reg_index >= k_first_gpr_i386 && reg_index < k_first_alias_i386);

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
128128

129129
NativeRegisterContextWindows_arm::NativeRegisterContextWindows_arm(
130130
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
131-
: NativeRegisterContextWindows(native_thread,
132-
CreateRegisterInfoInterface(target_arch)) {}
131+
: NativeRegisterContextRegisterInfo(
132+
native_thread, CreateRegisterInfoInterface(target_arch)) {}
133133

134134
bool NativeRegisterContextWindows_arm::IsGPR(uint32_t reg_index) const {
135135
return (reg_index >= k_first_gpr_arm && reg_index <= k_last_gpr_arm);

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
#include "NativeRegisterContextWindows_arm64.h"
1212
#include "NativeThreadWindows.h"
13-
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
1413
#include "ProcessWindowsLog.h"
1514
#include "lldb/Host/HostInfo.h"
1615
#include "lldb/Host/HostThread.h"
@@ -143,8 +142,14 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
143142

144143
NativeRegisterContextWindows_arm64::NativeRegisterContextWindows_arm64(
145144
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
146-
: NativeRegisterContextWindows(native_thread,
147-
CreateRegisterInfoInterface(target_arch)) {}
145+
: NativeRegisterContextRegisterInfo(
146+
native_thread, CreateRegisterInfoInterface(target_arch)) {
147+
// Currently, there is no API to query the maximum supported hardware
148+
// breakpoints and watchpoints on Windows. The values set below are based
149+
// on tests conducted on Windows 11 with Snapdragon Elite X hardware.
150+
m_max_hwp_supported = 1;
151+
m_max_hbp_supported = 6;
152+
}
148153

149154
bool NativeRegisterContextWindows_arm64::IsGPR(uint32_t reg_index) const {
150155
return (reg_index >= k_first_gpr_arm64 && reg_index <= k_last_gpr_arm64);
@@ -709,48 +714,49 @@ Status NativeRegisterContextWindows_arm64::WriteAllRegisterValues(
709714
return SetThreadContextHelper(GetThreadHandle(), &tls_context);
710715
}
711716

712-
Status NativeRegisterContextWindows_arm64::IsWatchpointHit(uint32_t wp_index,
713-
bool &is_hit) {
714-
return Status::FromErrorString("unimplemented");
715-
}
716-
717-
Status NativeRegisterContextWindows_arm64::GetWatchpointHitIndex(
718-
uint32_t &wp_index, lldb::addr_t trap_addr) {
719-
return Status::FromErrorString("unimplemented");
720-
}
721-
722-
Status NativeRegisterContextWindows_arm64::IsWatchpointVacant(uint32_t wp_index,
723-
bool &is_vacant) {
724-
return Status::FromErrorString("unimplemented");
725-
}
726-
727-
Status NativeRegisterContextWindows_arm64::SetHardwareWatchpointWithIndex(
728-
lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
729-
return Status::FromErrorString("unimplemented");
730-
}
731-
732-
bool NativeRegisterContextWindows_arm64::ClearHardwareWatchpoint(
733-
uint32_t wp_index) {
734-
return false;
735-
}
717+
llvm::Error NativeRegisterContextWindows_arm64::ReadHardwareDebugInfo() {
718+
::CONTEXT tls_context;
719+
Status error = GetThreadContextHelper(GetThreadHandle(), &tls_context,
720+
CONTEXT_DEBUG_REGISTERS);
721+
if (error.Fail())
722+
return error.ToError();
736723

737-
Status NativeRegisterContextWindows_arm64::ClearAllHardwareWatchpoints() {
738-
return Status::FromErrorString("unimplemented");
739-
}
724+
for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
725+
m_hwp_regs[i].address = tls_context.Wvr[i];
726+
m_hwp_regs[i].control = tls_context.Wcr[i];
727+
}
740728

741-
uint32_t NativeRegisterContextWindows_arm64::SetHardwareWatchpoint(
742-
lldb::addr_t addr, size_t size, uint32_t watch_flags) {
743-
return LLDB_INVALID_INDEX32;
729+
for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
730+
m_hbp_regs[i].address = tls_context.Bvr[i];
731+
m_hbp_regs[i].control = tls_context.Bcr[i];
732+
}
733+
return llvm::Error::success();
744734
}
745735

746-
lldb::addr_t
747-
NativeRegisterContextWindows_arm64::GetWatchpointAddress(uint32_t wp_index) {
748-
return LLDB_INVALID_ADDRESS;
749-
}
736+
llvm::Error
737+
NativeRegisterContextWindows_arm64::WriteHardwareDebugRegs(DREGType hwbType) {
738+
::CONTEXT tls_context;
739+
Status error = GetThreadContextHelper(GetThreadHandle(), &tls_context,
740+
CONTEXT_DEBUG_REGISTERS);
741+
if (error.Fail())
742+
return error.ToError();
743+
744+
switch (hwbType) {
745+
case eDREGTypeWATCH:
746+
for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
747+
tls_context.Wvr[i] = m_hwp_regs[i].address;
748+
tls_context.Wcr[i] = m_hwp_regs[i].control;
749+
}
750+
break;
751+
case eDREGTypeBREAK:
752+
for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
753+
tls_context.Bvr[i] = m_hbp_regs[i].address;
754+
tls_context.Bcr[i] = m_hbp_regs[i].control;
755+
}
756+
break;
757+
}
750758

751-
uint32_t NativeRegisterContextWindows_arm64::NumSupportedHardwareWatchpoints() {
752-
// Not implemented
753-
return 0;
759+
return SetThreadContextHelper(GetThreadHandle(), &tls_context).ToError();
754760
}
755761

756762
#endif // defined(__aarch64__) || defined(_M_ARM64)

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#ifndef liblldb_NativeRegisterContextWindows_arm64_h_
1111
#define liblldb_NativeRegisterContextWindows_arm64_h_
1212

13+
#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h"
14+
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
1315
#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
1416

1517
#include "NativeRegisterContextWindows.h"
@@ -18,7 +20,9 @@ namespace lldb_private {
1820

1921
class NativeThreadWindows;
2022

21-
class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows {
23+
class NativeRegisterContextWindows_arm64
24+
: public NativeRegisterContextWindows,
25+
public NativeRegisterContextDBReg_arm64 {
2226
public:
2327
NativeRegisterContextWindows_arm64(const ArchSpec &target_arch,
2428
NativeThreadProtocol &native_thread);
@@ -37,28 +41,6 @@ class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows {
3741

3842
Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
3943

40-
Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
41-
42-
Status GetWatchpointHitIndex(uint32_t &wp_index,
43-
lldb::addr_t trap_addr) override;
44-
45-
Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
46-
47-
bool ClearHardwareWatchpoint(uint32_t wp_index) override;
48-
49-
Status ClearAllHardwareWatchpoints() override;
50-
51-
Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
52-
uint32_t watch_flags,
53-
uint32_t wp_index);
54-
55-
uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
56-
uint32_t watch_flags) override;
57-
58-
lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
59-
60-
uint32_t NumSupportedHardwareWatchpoints() override;
61-
6244
protected:
6345
Status GPRRead(const uint32_t reg, RegisterValue &reg_value);
6446

@@ -72,6 +54,10 @@ class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows {
7254
bool IsGPR(uint32_t reg_index) const;
7355

7456
bool IsFPR(uint32_t reg_index) const;
57+
58+
llvm::Error ReadHardwareDebugInfo() override;
59+
60+
llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
7561
};
7662

7763
} // namespace lldb_private

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
9292

9393
NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386(
9494
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
95-
: NativeRegisterContextWindows(native_thread,
96-
CreateRegisterInfoInterface(target_arch)) {}
95+
: NativeRegisterContextRegisterInfo(
96+
native_thread, CreateRegisterInfoInterface(target_arch)) {}
9797

9898
bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const {
9999
return (reg_index < k_first_alias_i386);

lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
110110

111111
NativeRegisterContextWindows_x86_64::NativeRegisterContextWindows_x86_64(
112112
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
113-
: NativeRegisterContextWindows(native_thread,
114-
CreateRegisterInfoInterface(target_arch)) {}
113+
: NativeRegisterContextRegisterInfo(
114+
native_thread, CreateRegisterInfoInterface(target_arch)) {}
115115

116116
bool NativeRegisterContextWindows_x86_64::IsGPR(uint32_t reg_index) const {
117117
return (reg_index >= k_first_gpr_x86_64 && reg_index < k_first_alias_x86_64);

lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,41 @@ Status NativeThreadWindows::RemoveWatchpoint(lldb::addr_t addr) {
178178

179179
Status NativeThreadWindows::SetHardwareBreakpoint(lldb::addr_t addr,
180180
size_t size) {
181+
#if defined(__aarch64__) || defined(_M_ARM64)
182+
if (m_state == eStateLaunching)
183+
return Status();
184+
185+
Status error = RemoveHardwareBreakpoint(addr);
186+
if (error.Fail())
187+
return error;
188+
189+
uint32_t bp_index = m_reg_context_up->SetHardwareBreakpoint(addr, size);
190+
191+
if (bp_index == LLDB_INVALID_INDEX32)
192+
return Status::FromErrorString("Setting hardware breakpoint failed.");
193+
194+
m_hw_breakpoint_index_map.insert({addr, bp_index});
195+
196+
return Status();
197+
#else
181198
return Status::FromErrorString("unimplemented.");
199+
#endif
182200
}
183201

184202
Status NativeThreadWindows::RemoveHardwareBreakpoint(lldb::addr_t addr) {
203+
#if defined(__aarch64__) || defined(_M_ARM64)
204+
auto bp = m_hw_breakpoint_index_map.find(addr);
205+
if (bp == m_hw_breakpoint_index_map.end())
206+
return Status();
207+
208+
uint32_t bp_index = bp->second;
209+
if (m_reg_context_up->ClearHardwareBreakpoint(bp_index)) {
210+
m_hw_breakpoint_index_map.erase(bp);
211+
return Status();
212+
}
213+
214+
return Status::FromErrorString("Clearing hardware breakpoint failed.");
215+
#else
185216
return Status::FromErrorString("unimplemented.");
217+
#endif
186218
}

0 commit comments

Comments
 (0)