1 //===-- NativeRegisterContextDBReg_x86.cpp --------------------------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "NativeRegisterContextDBReg_x86.h" 10 #include "lldb/Utility/LLDBLog.h" 11 #include "lldb/Utility/RegisterValue.h" 12 13 #include "Plugins/Process/Utility/lldb-x86-register-enums.h" 14 15 using namespace lldb_private; 16 17 // Returns mask/value for status bit of wp_index in DR6 18 static inline uint64_t GetStatusBit(uint32_t wp_index) { 19 // DR6: ...BBBB 20 // 3210 <- status bits for bp./wp. i; 1 if hit 21 return 1 << wp_index; 22 } 23 24 // Returns mask/value for global enable bit of wp_index in DR7 25 static inline uint64_t GetEnableBit(uint32_t wp_index) { 26 // DR7: ...GLGLGLGL 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 1 << (2 * wp_index + 1); 31 } 32 33 // Returns mask for both enable bits of wp_index in DR7 34 static inline uint64_t GetBothEnableBitMask(uint32_t wp_index) { 35 // DR7: ...GLGLGLGL 36 // 33221100 <- global/local enable for bp./wp.; 1 if enabled 37 return 3 << (2 * wp_index + 1); 38 } 39 40 // Returns value for type bits of wp_index in DR7 41 static inline uint64_t GetWatchTypeBits(uint32_t watch_flags, 42 uint32_t wp_index) { 43 // DR7: 44 // bit: 3322222222221111... 45 // 1098765432109876... 46 // val: SSTTSSTTSSTTSSTT... 47 // wp.: 3333222211110000... 48 // 49 // where T - type is 01 for write, 11 for r/w 50 return watch_flags << (16 + 4 * wp_index); 51 } 52 53 // Returns value for size bits of wp_index in DR7 54 static inline uint64_t GetWatchSizeBits(uint32_t size, uint32_t wp_index) { 55 // DR7: 56 // bit: 3322222222221111... 57 // 1098765432109876... 58 // val: SSTTSSTTSSTTSSTT... 59 // wp.: 3333222211110000... 60 // 61 // where S - size is: 62 // 00 for 1 byte 63 // 01 for 2 bytes 64 // 10 for 8 bytes 65 // 11 for 4 bytes 66 return (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); 67 } 68 69 // Returns bitmask for all bits controlling wp_index in DR7 70 static inline uint64_t GetWatchControlBitmask(uint32_t wp_index) { 71 // DR7: 72 // bit: 33222222222211111111110000000000 73 // 10987654321098765432109876543210 74 // val: SSTTSSTTSSTTSSTTxxxxxxGLGLGLGLGL 75 // wp.: 3333222211110000xxxxxxEE33221100 76 return GetBothEnableBitMask(wp_index) | (0xF << (16 + 4 * wp_index)); 77 } 78 79 // Bit mask for control bits regarding all watchpoints. 80 static constexpr uint64_t watchpoint_all_control_bit_mask = 0xFFFF00FF; 81 82 const RegisterInfo *NativeRegisterContextDBReg_x86::GetDR(int num) const { 83 assert(num >= 0 && num <= 7); 84 switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { 85 case llvm::Triple::x86: 86 return GetRegisterInfoAtIndex(lldb_dr0_i386 + num); 87 case llvm::Triple::x86_64: 88 return GetRegisterInfoAtIndex(lldb_dr0_x86_64 + num); 89 default: 90 llvm_unreachable("Unhandled target architecture."); 91 } 92 } 93 94 Status NativeRegisterContextDBReg_x86::IsWatchpointHit(uint32_t wp_index, 95 bool &is_hit) { 96 if (wp_index >= NumSupportedHardwareWatchpoints()) 97 return Status("Watchpoint index out of range"); 98 99 RegisterValue dr6; 100 Status error = ReadRegister(GetDR(6), dr6); 101 if (error.Fail()) 102 is_hit = false; 103 else 104 is_hit = dr6.GetAsUInt64() & GetStatusBit(wp_index); 105 106 return error; 107 } 108 109 Status 110 NativeRegisterContextDBReg_x86::GetWatchpointHitIndex(uint32_t &wp_index, 111 lldb::addr_t trap_addr) { 112 uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); 113 for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { 114 bool is_hit; 115 Status error = IsWatchpointHit(wp_index, is_hit); 116 if (error.Fail()) { 117 wp_index = LLDB_INVALID_INDEX32; 118 return error; 119 } else if (is_hit) { 120 return error; 121 } 122 } 123 wp_index = LLDB_INVALID_INDEX32; 124 return Status(); 125 } 126 127 Status NativeRegisterContextDBReg_x86::IsWatchpointVacant(uint32_t wp_index, 128 bool &is_vacant) { 129 if (wp_index >= NumSupportedHardwareWatchpoints()) 130 return Status("Watchpoint index out of range"); 131 132 RegisterValue dr7; 133 Status error = ReadRegister(GetDR(7), dr7); 134 if (error.Fail()) 135 is_vacant = false; 136 else 137 is_vacant = !(dr7.GetAsUInt64() & GetEnableBit(wp_index)); 138 139 return error; 140 } 141 142 Status NativeRegisterContextDBReg_x86::SetHardwareWatchpointWithIndex( 143 lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { 144 145 if (wp_index >= NumSupportedHardwareWatchpoints()) 146 return Status("Watchpoint index out of range"); 147 148 // Read only watchpoints aren't supported on x86_64. Fall back to read/write 149 // waitchpoints instead. 150 // TODO: Add logic to detect when a write happens and ignore that watchpoint 151 // hit. 152 if (watch_flags == 2) 153 watch_flags = 3; 154 155 if (watch_flags != 1 && watch_flags != 3) 156 return Status("Invalid read/write bits for watchpoint"); 157 if (size != 1 && size != 2 && size != 4 && size != 8) 158 return Status("Invalid size for watchpoint"); 159 160 bool is_vacant; 161 Status error = IsWatchpointVacant(wp_index, is_vacant); 162 if (error.Fail()) 163 return error; 164 if (!is_vacant) 165 return Status("Watchpoint index not vacant"); 166 167 RegisterValue dr7, drN; 168 error = ReadRegister(GetDR(7), dr7); 169 if (error.Fail()) 170 return error; 171 error = ReadRegister(GetDR(wp_index), drN); 172 if (error.Fail()) 173 return error; 174 175 uint64_t control_bits = dr7.GetAsUInt64() & ~GetWatchControlBitmask(wp_index); 176 control_bits |= GetEnableBit(wp_index) | 177 GetWatchTypeBits(watch_flags, wp_index) | 178 GetWatchSizeBits(size, wp_index); 179 180 // Clear dr6 if address or bits changed (i.e. we're not reenabling the same 181 // watchpoint). This can not be done when clearing watchpoints since 182 // the gdb-remote protocol repeatedly clears and readds watchpoints on all 183 // program threads, effectively clearing pending events on NetBSD. 184 // NB: enable bits in dr7 are always 0 here since we're (re)adding it 185 if (drN.GetAsUInt64() != addr || 186 (dr7.GetAsUInt64() & GetWatchControlBitmask(wp_index)) != 187 (GetWatchTypeBits(watch_flags, wp_index) | 188 GetWatchSizeBits(size, wp_index))) { 189 ClearWatchpointHit(wp_index); 190 191 // We skip update to drN if neither address nor mode changed. 192 error = WriteRegister(GetDR(wp_index), RegisterValue(addr)); 193 if (error.Fail()) 194 return error; 195 } 196 197 error = WriteRegister(GetDR(7), RegisterValue(control_bits)); 198 if (error.Fail()) 199 return error; 200 201 return error; 202 } 203 204 bool NativeRegisterContextDBReg_x86::ClearHardwareWatchpoint( 205 uint32_t wp_index) { 206 if (wp_index >= NumSupportedHardwareWatchpoints()) 207 return false; 208 209 RegisterValue dr7; 210 Status error = ReadRegister(GetDR(7), dr7); 211 if (error.Fail()) 212 return false; 213 214 return WriteRegister(GetDR(7), RegisterValue(dr7.GetAsUInt64() & 215 ~GetBothEnableBitMask(wp_index))) 216 .Success(); 217 } 218 219 Status NativeRegisterContextDBReg_x86::ClearWatchpointHit(uint32_t wp_index) { 220 if (wp_index >= NumSupportedHardwareWatchpoints()) 221 return Status("Watchpoint index out of range"); 222 223 RegisterValue dr6; 224 Status error = ReadRegister(GetDR(6), dr6); 225 if (error.Fail()) 226 return error; 227 228 return WriteRegister( 229 GetDR(6), RegisterValue(dr6.GetAsUInt64() & ~GetStatusBit(wp_index))); 230 } 231 232 Status NativeRegisterContextDBReg_x86::ClearAllHardwareWatchpoints() { 233 RegisterValue dr7; 234 Status error = ReadRegister(GetDR(7), dr7); 235 if (error.Fail()) 236 return error; 237 return WriteRegister( 238 GetDR(7), 239 RegisterValue(dr7.GetAsUInt64() & ~watchpoint_all_control_bit_mask)); 240 } 241 242 uint32_t NativeRegisterContextDBReg_x86::SetHardwareWatchpoint( 243 lldb::addr_t addr, size_t size, uint32_t watch_flags) { 244 Log *log = GetLog(LLDBLog::Watchpoints); 245 const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); 246 for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) { 247 bool is_vacant; 248 Status error = IsWatchpointVacant(wp_index, is_vacant); 249 if (is_vacant) { 250 error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); 251 if (error.Success()) 252 return wp_index; 253 } 254 if (error.Fail() && log) { 255 LLDB_LOGF(log, "NativeRegisterContextDBReg_x86::%s Error: %s", 256 __FUNCTION__, error.AsCString()); 257 } 258 } 259 return LLDB_INVALID_INDEX32; 260 } 261 262 lldb::addr_t 263 NativeRegisterContextDBReg_x86::GetWatchpointAddress(uint32_t wp_index) { 264 if (wp_index >= NumSupportedHardwareWatchpoints()) 265 return LLDB_INVALID_ADDRESS; 266 RegisterValue drN; 267 if (ReadRegister(GetDR(wp_index), drN).Fail()) 268 return LLDB_INVALID_ADDRESS; 269 return drN.GetAsUInt64(); 270 } 271 272 uint32_t NativeRegisterContextDBReg_x86::NumSupportedHardwareWatchpoints() { 273 // Available debug address registers: dr0, dr1, dr2, dr3 274 return 4; 275 } 276