1 //===-- NativeRegisterContextDBReg_x86.cpp --------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "NativeRegisterContextDBReg_x86.h"
10 #include "lldb/Utility/LLDBLog.h"
11 #include "lldb/Utility/RegisterValue.h"
13 #include "Plugins/Process/Utility/lldb-x86-register-enums.h"
15 using namespace lldb_private
;
17 // Returns mask/value for status bit of wp_index in DR6
18 static inline uint64_t GetStatusBit(uint32_t wp_index
) {
20 // 3210 <- status bits for bp./wp. i; 1 if hit
21 return 1ULL << wp_index
;
24 // Returns mask/value for global enable bit of wp_index in DR7
25 static inline uint64_t GetEnableBit(uint32_t wp_index
) {
27 // 33221100 <- global/local enable for bp./wp.; 1 if enabled
28 // we use global bits because NetBSD kernel does not preserve local
29 // bits reliably; Linux seems fine with either
30 return 1ULL << (2 * wp_index
+ 1);
33 // Returns mask for both enable bits of wp_index in DR7
34 static inline uint64_t GetBothEnableBitMask(uint32_t wp_index
) {
36 // 33221100 <- global/local enable for bp./wp.; 1 if enabled
37 return 3ULL << (2 * wp_index
+ 1);
40 // Returns value for type bits of wp_index in DR7
41 static inline uint64_t GetWatchTypeBits(uint32_t watch_flags
,
44 // bit: 3322222222221111...
45 // 1098765432109876...
46 // val: SSTTSSTTSSTTSSTT...
47 // wp.: 3333222211110000...
49 // where T - type is 01 for write, 11 for r/w
50 return static_cast<uint64_t>(watch_flags
) << (16 + 4 * wp_index
);
53 // Returns value for size bits of wp_index in DR7
54 static inline uint64_t GetWatchSizeBits(uint32_t size
, uint32_t wp_index
) {
56 // bit: 3322222222221111...
57 // 1098765432109876...
58 // val: SSTTSSTTSSTTSSTT...
59 // wp.: 3333222211110000...
66 return static_cast<uint64_t>(size
== 8 ? 0x2 : size
- 1)
67 << (18 + 4 * wp_index
);
70 // Returns bitmask for all bits controlling wp_index in DR7
71 static inline uint64_t GetWatchControlBitmask(uint32_t wp_index
) {
73 // bit: 33222222222211111111110000000000
74 // 10987654321098765432109876543210
75 // val: SSTTSSTTSSTTSSTTxxxxxxGLGLGLGLGL
76 // wp.: 3333222211110000xxxxxxEE33221100
77 return GetBothEnableBitMask(wp_index
) | (0xF << (16 + 4 * wp_index
));
80 // Bit mask for control bits regarding all watchpoints.
81 static constexpr uint64_t watchpoint_all_control_bit_mask
= 0xFFFF00FF;
83 const RegisterInfo
*NativeRegisterContextDBReg_x86::GetDR(int num
) const {
84 assert(num
>= 0 && num
<= 7);
85 switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
86 case llvm::Triple::x86
:
87 return GetRegisterInfoAtIndex(lldb_dr0_i386
+ num
);
88 case llvm::Triple::x86_64
:
89 return GetRegisterInfoAtIndex(lldb_dr0_x86_64
+ num
);
91 llvm_unreachable("Unhandled target architecture.");
95 Status
NativeRegisterContextDBReg_x86::IsWatchpointHit(uint32_t wp_index
,
97 if (wp_index
>= NumSupportedHardwareWatchpoints())
98 return Status("Watchpoint index out of range");
101 Status error
= ReadRegister(GetDR(6), dr6
);
105 is_hit
= dr6
.GetAsUInt64() & GetStatusBit(wp_index
);
111 NativeRegisterContextDBReg_x86::GetWatchpointHitIndex(uint32_t &wp_index
,
112 lldb::addr_t trap_addr
) {
113 uint32_t num_hw_wps
= NumSupportedHardwareWatchpoints();
114 for (wp_index
= 0; wp_index
< num_hw_wps
; ++wp_index
) {
116 Status error
= IsWatchpointHit(wp_index
, is_hit
);
118 wp_index
= LLDB_INVALID_INDEX32
;
124 wp_index
= LLDB_INVALID_INDEX32
;
128 Status
NativeRegisterContextDBReg_x86::IsWatchpointVacant(uint32_t wp_index
,
130 if (wp_index
>= NumSupportedHardwareWatchpoints())
131 return Status("Watchpoint index out of range");
134 Status error
= ReadRegister(GetDR(7), dr7
);
138 is_vacant
= !(dr7
.GetAsUInt64() & GetEnableBit(wp_index
));
143 Status
NativeRegisterContextDBReg_x86::SetHardwareWatchpointWithIndex(
144 lldb::addr_t addr
, size_t size
, uint32_t watch_flags
, uint32_t wp_index
) {
146 if (wp_index
>= NumSupportedHardwareWatchpoints())
147 return Status("Watchpoint index out of range");
149 // Read only watchpoints aren't supported on x86_64. Fall back to read/write
150 // waitchpoints instead.
151 // TODO: Add logic to detect when a write happens and ignore that watchpoint
153 if (watch_flags
== 2)
156 if (watch_flags
!= 1 && watch_flags
!= 3)
157 return Status("Invalid read/write bits for watchpoint");
158 if (size
!= 1 && size
!= 2 && size
!= 4 && size
!= 8)
159 return Status("Invalid size for watchpoint");
162 Status error
= IsWatchpointVacant(wp_index
, is_vacant
);
166 return Status("Watchpoint index not vacant");
168 RegisterValue dr7
, drN
;
169 error
= ReadRegister(GetDR(7), dr7
);
172 error
= ReadRegister(GetDR(wp_index
), drN
);
176 uint64_t control_bits
= dr7
.GetAsUInt64() & ~GetWatchControlBitmask(wp_index
);
177 control_bits
|= GetEnableBit(wp_index
) |
178 GetWatchTypeBits(watch_flags
, wp_index
) |
179 GetWatchSizeBits(size
, wp_index
);
181 // Clear dr6 if address or bits changed (i.e. we're not reenabling the same
182 // watchpoint). This can not be done when clearing watchpoints since
183 // the gdb-remote protocol repeatedly clears and readds watchpoints on all
184 // program threads, effectively clearing pending events on NetBSD.
185 // NB: enable bits in dr7 are always 0 here since we're (re)adding it
186 if (drN
.GetAsUInt64() != addr
||
187 (dr7
.GetAsUInt64() & GetWatchControlBitmask(wp_index
)) !=
188 (GetWatchTypeBits(watch_flags
, wp_index
) |
189 GetWatchSizeBits(size
, wp_index
))) {
190 ClearWatchpointHit(wp_index
);
192 // We skip update to drN if neither address nor mode changed.
193 error
= WriteRegister(GetDR(wp_index
), RegisterValue(addr
));
198 error
= WriteRegister(GetDR(7), RegisterValue(control_bits
));
205 bool NativeRegisterContextDBReg_x86::ClearHardwareWatchpoint(
207 if (wp_index
>= NumSupportedHardwareWatchpoints())
211 Status error
= ReadRegister(GetDR(7), dr7
);
215 return WriteRegister(GetDR(7), RegisterValue(dr7
.GetAsUInt64() &
216 ~GetBothEnableBitMask(wp_index
)))
220 Status
NativeRegisterContextDBReg_x86::ClearWatchpointHit(uint32_t wp_index
) {
221 if (wp_index
>= NumSupportedHardwareWatchpoints())
222 return Status("Watchpoint index out of range");
225 Status error
= ReadRegister(GetDR(6), dr6
);
229 return WriteRegister(
230 GetDR(6), RegisterValue(dr6
.GetAsUInt64() & ~GetStatusBit(wp_index
)));
233 Status
NativeRegisterContextDBReg_x86::ClearAllHardwareWatchpoints() {
235 Status error
= ReadRegister(GetDR(7), dr7
);
238 return WriteRegister(
240 RegisterValue(dr7
.GetAsUInt64() & ~watchpoint_all_control_bit_mask
));
243 uint32_t NativeRegisterContextDBReg_x86::SetHardwareWatchpoint(
244 lldb::addr_t addr
, size_t size
, uint32_t watch_flags
) {
245 Log
*log
= GetLog(LLDBLog::Watchpoints
);
246 const uint32_t num_hw_watchpoints
= NumSupportedHardwareWatchpoints();
247 for (uint32_t wp_index
= 0; wp_index
< num_hw_watchpoints
; ++wp_index
) {
249 Status error
= IsWatchpointVacant(wp_index
, is_vacant
);
251 error
= SetHardwareWatchpointWithIndex(addr
, size
, watch_flags
, wp_index
);
255 if (error
.Fail() && log
) {
256 LLDB_LOGF(log
, "NativeRegisterContextDBReg_x86::%s Error: %s",
257 __FUNCTION__
, error
.AsCString());
260 return LLDB_INVALID_INDEX32
;
264 NativeRegisterContextDBReg_x86::GetWatchpointAddress(uint32_t wp_index
) {
265 if (wp_index
>= NumSupportedHardwareWatchpoints())
266 return LLDB_INVALID_ADDRESS
;
268 if (ReadRegister(GetDR(wp_index
), drN
).Fail())
269 return LLDB_INVALID_ADDRESS
;
270 return drN
.GetAsUInt64();
273 uint32_t NativeRegisterContextDBReg_x86::NumSupportedHardwareWatchpoints() {
274 // Available debug address registers: dr0, dr1, dr2, dr3