#include "hardware_breakpoint.hpp" #include "thread.hpp" namespace utilities::hardware_breakpoint { namespace { void set_bits(uint64_t& value, const uint32_t bit_index, const uint32_t bits, const uint64_t new_value) { const uint64_t range_mask = (1ull << bits) - 1ull; const uint64_t full_mask = ~(range_mask << bit_index); value = (value & full_mask) | (new_value << bit_index); } void validate_index(const uint32_t index) { if (index >= 4) { throw std::runtime_error("Invalid index"); } } uint32_t translate_length(const uint32_t length) { if (length != 1 && length != 2 && length != 4) { throw std::runtime_error("Invalid length"); } return length - 1; } class debug_context { public: debug_context(uint32_t thread_id) : handle_(thread_id, THREAD_SET_CONTEXT | THREAD_GET_CONTEXT) { if (!this->handle_) { throw std::runtime_error("Unable to access thread"); } this->context_.ContextFlags = CONTEXT_DEBUG_REGISTERS; if (!GetThreadContext(this->handle_, &this->context_)) { throw std::runtime_error("Unable to get thread context"); } } ~debug_context() { SetThreadContext(this->handle_, &this->context_); } debug_context(const debug_context&) = delete; debug_context& operator=(const debug_context&) = delete; debug_context(debug_context&& obj) noexcept = delete; debug_context& operator=(debug_context&& obj) noexcept = delete; CONTEXT* operator->() { return &this->context_; } operator CONTEXT&() { return this->context_; } private: thread::handle handle_; CONTEXT context_{}; }; uint32_t find_free_index(const CONTEXT& context) { for (uint32_t i = 0; i < 4; ++i) { if ((context.Dr7 & (1ull << (i << 1ull))) == 0) { return i; } } throw std::runtime_error("No free index"); } } uint32_t activate(const uint64_t address, uint32_t length, const condition cond, CONTEXT& context) { const auto index = find_free_index(context); length = translate_length(length); (&context.Dr0)[index] = address; set_bits(context.Dr7, 16 + (index << 2ull), 2, cond); set_bits(context.Dr7, 18 + (index << 2ull), 2, length); set_bits(context.Dr7, index << 1ull, 1, 1); return index; } uint32_t activate(void* address, const uint32_t length, const condition cond, const uint32_t thread_id) { return activate(reinterpret_cast(address), length, cond, thread_id); } uint32_t activate(const uint64_t address, const uint32_t length, const condition cond, const uint32_t thread_id) { debug_context context(thread_id); return activate(address, length, cond, context); } void deactivate(const uint32_t index, CONTEXT& context) { validate_index(index); set_bits(context.Dr7, index << 1ull, 1, 0); } void deactivate(const uint32_t index, const uint32_t thread_id) { debug_context context(thread_id); deactivate(index, context); } void deactivate_all(CONTEXT& context) { context.Dr7 = 0; } void deactivate_all(const uint32_t thread_id) { debug_context context(thread_id); deactivate_all(context); } }