blob: dd6f9e9f561aefdc516df7c0be17063017523a19 [file] [log] [blame]
// 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