| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "breakpoint.h" |
| |
| #include "lib/fxl/logging.h" |
| |
| #include "process.h" |
| #include "registers.h" |
| #include "thread.h" |
| |
| namespace inferior_control { |
| |
| namespace { |
| |
| // The i386 Int3 instruction. |
| const uint8_t kInt3 = 0xCC; |
| |
| } // namespace |
| |
| bool SoftwareBreakpoint::Insert() { |
| // TODO: Handle breakpoints in unloaded solibs. |
| |
| if (IsInserted()) { |
| FXL_LOG(WARNING) << "Breakpoint already inserted"; |
| return false; |
| } |
| |
| // We only support inserting the single byte Int3 instruction. |
| if (kind() != 1) { |
| FXL_LOG(ERROR) << "Software breakpoint kind must be 1 on amd64"; |
| return false; |
| } |
| |
| // Read the current contents at the address that we're about to overwrite, so |
| // that it can be restored later. |
| uint8_t orig; |
| if (!owner()->process()->ReadMemory(address(), &orig, 1)) { |
| FXL_LOG(ERROR) << "Failed to obtain current contents of memory"; |
| return false; |
| } |
| |
| // Insert the Int3 instruction. |
| if (!owner()->process()->WriteMemory(address(), &kInt3, 1)) { |
| FXL_LOG(ERROR) << "Failed to insert software breakpoint"; |
| return false; |
| } |
| |
| original_bytes_.push_back(orig); |
| return true; |
| } |
| |
| bool SoftwareBreakpoint::Remove() { |
| if (!IsInserted()) { |
| FXL_LOG(WARNING) << "Breakpoint not inserted"; |
| return false; |
| } |
| |
| FXL_DCHECK(original_bytes_.size() == 1); |
| |
| // Restore the original contents. |
| if (!owner()->process()->WriteMemory(address(), original_bytes_.data(), 1)) { |
| FXL_LOG(ERROR) << "Failed to restore original instructions"; |
| return false; |
| } |
| |
| original_bytes_.clear(); |
| return true; |
| } |
| |
| bool SoftwareBreakpoint::IsInserted() const { return !original_bytes_.empty(); } |
| |
| namespace { |
| |
| // Set the TF bit in the RFLAGS register of |thread|. |
| |
| bool SetRflagsTF(Thread* thread, bool enable) { |
| Registers* registers = thread->registers(); |
| |
| if (!registers->RefreshGeneralRegisters()) { |
| FXL_LOG(ERROR) << "Failed to refresh general regs"; |
| return false; |
| } |
| if (!registers->SetSingleStep(enable)) { |
| FXL_LOG(ERROR) << "Failed to set rflags.TF"; |
| return false; |
| } |
| if (!registers->WriteGeneralRegisters()) { |
| FXL_LOG(ERROR) << "Failed to write general regs"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // anonymous namespace |
| |
| bool SingleStepBreakpoint::Insert() { |
| if (IsInserted()) { |
| FXL_LOG(WARNING) << "Breakpoint already inserted"; |
| return false; |
| } |
| |
| // TODO: Manage things like the user having already set TF. |
| |
| if (!SetRflagsTF(owner()->thread(), true)) |
| return false; |
| |
| inserted_ = true; |
| return true; |
| } |
| |
| bool SingleStepBreakpoint::Remove() { |
| if (!IsInserted()) { |
| FXL_LOG(WARNING) << "Breakpoint not inserted"; |
| return false; |
| } |
| |
| if (!SetRflagsTF(owner()->thread(), false)) |
| return false; |
| |
| inserted_ = false; |
| return true; |
| } |
| |
| bool SingleStepBreakpoint::IsInserted() const { return inserted_; } |
| |
| } // namespace inferior_control |