277 lines · cpp
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-exception6//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 15using namespace lldb_private;16 17// Returns mask/value for status bit of wp_index in DR618static inline uint64_t GetStatusBit(uint32_t wp_index) {19 // DR6: ...BBBB20 // 3210 <- status bits for bp./wp. i; 1 if hit21 return 1ULL << wp_index;22}23 24// Returns mask/value for global enable bit of wp_index in DR725static inline uint64_t GetEnableBit(uint32_t wp_index) {26 // DR7: ...GLGLGLGL27 // 33221100 <- global/local enable for bp./wp.; 1 if enabled28 // we use global bits because NetBSD kernel does not preserve local29 // bits reliably; Linux seems fine with either30 return 1ULL << (2 * wp_index + 1);31}32 33// Returns mask for both enable bits of wp_index in DR734static inline uint64_t GetBothEnableBitMask(uint32_t wp_index) {35 // DR7: ...GLGLGLGL36 // 33221100 <- global/local enable for bp./wp.; 1 if enabled37 return 3ULL << (2 * wp_index + 1);38}39 40// Returns value for type bits of wp_index in DR741static 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/w50 return static_cast<uint64_t>(watch_flags) << (16 + 4 * wp_index);51}52 53// Returns value for size bits of wp_index in DR754static 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 byte63 // 01 for 2 bytes64 // 10 for 8 bytes65 // 11 for 4 bytes66 return static_cast<uint64_t>(size == 8 ? 0x2 : size - 1)67 << (18 + 4 * wp_index);68}69 70// Returns bitmask for all bits controlling wp_index in DR771static inline uint64_t GetWatchControlBitmask(uint32_t wp_index) {72 // DR7:73 // bit: 3322222222221111111111000000000074 // 1098765432109876543210987654321075 // val: SSTTSSTTSSTTSSTTxxxxxxGLGLGLGLGL76 // wp.: 3333222211110000xxxxxxEE3322110077 return GetBothEnableBitMask(wp_index) | (0xF << (16 + 4 * wp_index));78}79 80// Bit mask for control bits regarding all watchpoints.81static constexpr uint64_t watchpoint_all_control_bit_mask = 0xFFFF00FF;82 83const 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);90 default:91 llvm_unreachable("Unhandled target architecture.");92 }93}94 95Status NativeRegisterContextDBReg_x86::IsWatchpointHit(uint32_t wp_index,96 bool &is_hit) {97 if (wp_index >= NumSupportedHardwareWatchpoints())98 return Status::FromErrorString("Watchpoint index out of range");99 100 RegisterValue dr6;101 Status error = ReadRegister(GetDR(6), dr6);102 if (error.Fail())103 is_hit = false;104 else105 is_hit = dr6.GetAsUInt64() & GetStatusBit(wp_index);106 107 return error;108}109 110Status111NativeRegisterContextDBReg_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) {115 bool is_hit;116 Status error = IsWatchpointHit(wp_index, is_hit);117 if (error.Fail()) {118 wp_index = LLDB_INVALID_INDEX32;119 return error;120 } else if (is_hit) {121 return error;122 }123 }124 wp_index = LLDB_INVALID_INDEX32;125 return Status();126}127 128Status NativeRegisterContextDBReg_x86::IsWatchpointVacant(uint32_t wp_index,129 bool &is_vacant) {130 if (wp_index >= NumSupportedHardwareWatchpoints())131 return Status::FromErrorString("Watchpoint index out of range");132 133 RegisterValue dr7;134 Status error = ReadRegister(GetDR(7), dr7);135 if (error.Fail())136 is_vacant = false;137 else138 is_vacant = !(dr7.GetAsUInt64() & GetEnableBit(wp_index));139 140 return error;141}142 143Status NativeRegisterContextDBReg_x86::SetHardwareWatchpointWithIndex(144 lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {145 146 if (wp_index >= NumSupportedHardwareWatchpoints())147 return Status::FromErrorString("Watchpoint index out of range");148 149 // Read only watchpoints aren't supported on x86_64. Fall back to read/write150 // waitchpoints instead.151 // TODO: Add logic to detect when a write happens and ignore that watchpoint152 // hit.153 if (watch_flags == 2)154 watch_flags = 3;155 156 if (watch_flags != 1 && watch_flags != 3)157 return Status::FromErrorString("Invalid read/write bits for watchpoint");158 if (size != 1 && size != 2 && size != 4 && size != 8)159 return Status::FromErrorString("Invalid size for watchpoint");160 161 bool is_vacant;162 Status error = IsWatchpointVacant(wp_index, is_vacant);163 if (error.Fail())164 return error;165 if (!is_vacant)166 return Status::FromErrorString("Watchpoint index not vacant");167 168 RegisterValue dr7, drN;169 error = ReadRegister(GetDR(7), dr7);170 if (error.Fail())171 return error;172 error = ReadRegister(GetDR(wp_index), drN);173 if (error.Fail())174 return error;175 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);180 181 // Clear dr6 if address or bits changed (i.e. we're not reenabling the same182 // watchpoint). This can not be done when clearing watchpoints since183 // the gdb-remote protocol repeatedly clears and readds watchpoints on all184 // program threads, effectively clearing pending events on NetBSD.185 // NB: enable bits in dr7 are always 0 here since we're (re)adding it186 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);191 192 // We skip update to drN if neither address nor mode changed.193 error = WriteRegister(GetDR(wp_index), RegisterValue(addr));194 if (error.Fail())195 return error;196 }197 198 error = WriteRegister(GetDR(7), RegisterValue(control_bits));199 if (error.Fail())200 return error;201 202 return error;203}204 205bool NativeRegisterContextDBReg_x86::ClearHardwareWatchpoint(206 uint32_t wp_index) {207 if (wp_index >= NumSupportedHardwareWatchpoints())208 return false;209 210 RegisterValue dr7;211 Status error = ReadRegister(GetDR(7), dr7);212 if (error.Fail())213 return false;214 215 return WriteRegister(GetDR(7), RegisterValue(dr7.GetAsUInt64() &216 ~GetBothEnableBitMask(wp_index)))217 .Success();218}219 220Status NativeRegisterContextDBReg_x86::ClearWatchpointHit(uint32_t wp_index) {221 if (wp_index >= NumSupportedHardwareWatchpoints())222 return Status::FromErrorString("Watchpoint index out of range");223 224 RegisterValue dr6;225 Status error = ReadRegister(GetDR(6), dr6);226 if (error.Fail())227 return error;228 229 return WriteRegister(230 GetDR(6), RegisterValue(dr6.GetAsUInt64() & ~GetStatusBit(wp_index)));231}232 233Status NativeRegisterContextDBReg_x86::ClearAllHardwareWatchpoints() {234 RegisterValue dr7;235 Status error = ReadRegister(GetDR(7), dr7);236 if (error.Fail())237 return error;238 return WriteRegister(239 GetDR(7),240 RegisterValue(dr7.GetAsUInt64() & ~watchpoint_all_control_bit_mask));241}242 243uint32_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) {248 bool is_vacant;249 Status error = IsWatchpointVacant(wp_index, is_vacant);250 if (is_vacant) {251 error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index);252 if (error.Success())253 return wp_index;254 }255 if (error.Fail() && log) {256 LLDB_LOGF(log, "NativeRegisterContextDBReg_x86::%s Error: %s",257 __FUNCTION__, error.AsCString());258 }259 }260 return LLDB_INVALID_INDEX32;261}262 263lldb::addr_t264NativeRegisterContextDBReg_x86::GetWatchpointAddress(uint32_t wp_index) {265 if (wp_index >= NumSupportedHardwareWatchpoints())266 return LLDB_INVALID_ADDRESS;267 RegisterValue drN;268 if (ReadRegister(GetDR(wp_index), drN).Fail())269 return LLDB_INVALID_ADDRESS;270 return drN.GetAsUInt64();271}272 273uint32_t NativeRegisterContextDBReg_x86::NumSupportedHardwareWatchpoints() {274 // Available debug address registers: dr0, dr1, dr2, dr3275 return 4;276}277