2014-07-01 05:05:18 +08:00
//===-- SoftwareBreakpoint.cpp ----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
# include "SoftwareBreakpoint.h"
# include "lldb/Core/Error.h"
# include "lldb/Core/Log.h"
# include "lldb/Host/Debug.h"
# include "lldb/Host/Mutex.h"
# include "NativeProcessProtocol.h"
using namespace lldb_private ;
// -------------------------------------------------------------------
// static members
// -------------------------------------------------------------------
Error
SoftwareBreakpoint : : CreateSoftwareBreakpoint ( NativeProcessProtocol & process , lldb : : addr_t addr , size_t size_hint , NativeBreakpointSP & breakpoint_sp )
{
Log * log ( GetLogIfAnyCategoriesSet ( LIBLLDB_LOG_BREAKPOINTS ) ) ;
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 , __FUNCTION__ , addr ) ;
// Validate the address.
if ( addr = = LLDB_INVALID_ADDRESS )
return Error ( " SoftwareBreakpoint::%s invalid load address specified. " , __FUNCTION__ ) ;
// Ask the NativeProcessProtocol subclass to fill in the correct software breakpoint
// trap for the breakpoint site.
size_t bp_opcode_size = 0 ;
const uint8_t * bp_opcode_bytes = NULL ;
Error error = process . GetSoftwareBreakpointTrapOpcode ( size_hint , bp_opcode_size , bp_opcode_bytes ) ;
if ( error . Fail ( ) )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to retrieve software breakpoint trap opcode: %s " , __FUNCTION__ , error . AsCString ( ) ) ;
return error ;
}
// Validate size of trap opcode.
if ( bp_opcode_size = = 0 )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to retrieve any trap opcodes " , __FUNCTION__ ) ;
return Error ( " SoftwareBreakpoint::GetSoftwareBreakpointTrapOpcode() returned zero , unable to get breakpoint trap for address 0 x % " PRIx64, addr) ;
}
if ( bp_opcode_size > MAX_TRAP_OPCODE_SIZE )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s cannot support %lu trapcode bytes, max size is %lu " , __FUNCTION__ , bp_opcode_size , MAX_TRAP_OPCODE_SIZE ) ;
return Error ( " SoftwareBreakpoint::GetSoftwareBreakpointTrapOpcode() returned too many trap opcode bytes : requires % lu but we only support a max of % lu " , bp_opcode_size, MAX_TRAP_OPCODE_SIZE) ;
}
// Validate that we received opcodes.
if ( ! bp_opcode_bytes )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to retrieve trap opcode bytes " , __FUNCTION__ ) ;
return Error ( " SoftwareBreakpoint::GetSoftwareBreakpointTrapOpcode() returned NULL trap opcode bytes , unable to get breakpoint trap for address 0 x % " PRIx64, addr) ;
}
// Enable the breakpoint.
uint8_t saved_opcode_bytes [ MAX_TRAP_OPCODE_SIZE ] ;
error = EnableSoftwareBreakpoint ( process , addr , bp_opcode_size , bp_opcode_bytes , saved_opcode_bytes ) ;
if ( error . Fail ( ) )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s: failed to enable new breakpoint at 0x% " PRIx64 " : %s " , __FUNCTION__ , addr , error . AsCString ( ) ) ;
return error ;
}
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 " -- SUCCESS " , __FUNCTION__ , addr ) ;
// Set the breakpoint and verified it was written properly. Now
// create a breakpoint remover that understands how to undo this
// breakpoint.
breakpoint_sp . reset ( new SoftwareBreakpoint ( process , addr , saved_opcode_bytes , bp_opcode_bytes , bp_opcode_size ) ) ;
return Error ( ) ;
}
Error
SoftwareBreakpoint : : EnableSoftwareBreakpoint ( NativeProcessProtocol & process , lldb : : addr_t addr , size_t bp_opcode_size , const uint8_t * bp_opcode_bytes , uint8_t * saved_opcode_bytes )
{
assert ( bp_opcode_size < = MAX_TRAP_OPCODE_SIZE & & " bp_opcode_size out of valid range " ) ;
assert ( bp_opcode_bytes & & " bp_opcode_bytes is NULL " ) ;
assert ( saved_opcode_bytes & & " saved_opcode_bytes is NULL " ) ;
Log * log ( GetLogIfAnyCategoriesSet ( LIBLLDB_LOG_BREAKPOINTS ) ) ;
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 , __FUNCTION__ , addr ) ;
// Save the original opcodes by reading them so we can restore later.
lldb : : addr_t bytes_read = 0 ;
Error error = process . ReadMemory ( addr , saved_opcode_bytes , static_cast < lldb : : addr_t > ( bp_opcode_size ) , bytes_read ) ;
if ( error . Fail ( ) )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: %s " , __FUNCTION__ , error . AsCString ( ) ) ;
return error ;
}
// Ensure we read as many bytes as we expected.
if ( bytes_read ! = static_cast < lldb : : addr_t > ( bp_opcode_size ) )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read % " PRIu64 , __FUNCTION__ , bp_opcode_size , bytes_read ) ;
return Error ( " SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read % " PRIu64 , __FUNCTION__ , bp_opcode_size , bytes_read ) ;
}
2014-10-09 01:14:53 +08:00
// Log what we read.
if ( log )
{
int i = 0 ;
for ( const uint8_t * read_byte = saved_opcode_bytes ; read_byte < saved_opcode_bytes + bp_opcode_size ; + + read_byte )
{
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 " ovewriting byte index %d (was 0x%x) " , __FUNCTION__ , addr , i + + , static_cast < int > ( * read_byte ) ) ;
}
}
2014-07-01 05:05:18 +08:00
// Write a software breakpoint in place of the original opcode.
lldb : : addr_t bytes_written = 0 ;
error = process . WriteMemory ( addr , bp_opcode_bytes , static_cast < lldb : : addr_t > ( bp_opcode_size ) , bytes_written ) ;
if ( error . Fail ( ) )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to write memory while attempting to set breakpoint: %s " , __FUNCTION__ , error . AsCString ( ) ) ;
return error ;
}
// Ensure we wrote as many bytes as we expected.
if ( bytes_written ! = static_cast < lldb : : addr_t > ( bp_opcode_size ) )
{
error . SetErrorStringWithFormat ( " SoftwareBreakpoint::%s failed write memory while attempting to set breakpoint: attempted to write %lu bytes but only wrote % " PRIu64 , __FUNCTION__ , bp_opcode_size , bytes_written ) ;
if ( log )
log - > PutCString ( error . AsCString ( ) ) ;
return error ;
}
uint8_t verify_bp_opcode_bytes [ MAX_TRAP_OPCODE_SIZE ] ;
lldb : : addr_t verify_bytes_read = 0 ;
error = process . ReadMemory ( addr , verify_bp_opcode_bytes , static_cast < lldb : : addr_t > ( bp_opcode_size ) , verify_bytes_read ) ;
if ( error . Fail ( ) )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to read memory while attempting to verify the breakpoint set: %s " , __FUNCTION__ , error . AsCString ( ) ) ;
return error ;
}
// Ensure we read as many verification bytes as we expected.
if ( verify_bytes_read ! = static_cast < lldb : : addr_t > ( bp_opcode_size ) )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s failed to read memory while attempting to verify breakpoint: attempted to read %lu bytes but only read % " PRIu64 , __FUNCTION__ , bp_opcode_size , verify_bytes_read ) ;
return Error ( " SoftwareBreakpoint::%s failed to read memory while attempting to verify breakpoint: attempted to read %lu bytes but only read % " PRIu64 , __FUNCTION__ , bp_opcode_size , verify_bytes_read ) ;
}
if ( : : memcmp ( bp_opcode_bytes , verify_bp_opcode_bytes , bp_opcode_size ) ! = 0 )
{
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s: verification of software breakpoint writing failed - trap opcodes not successfully read back after writing when setting breakpoint at 0x% " PRIx64 , __FUNCTION__ , addr ) ;
return Error ( " SoftwareBreakpoint::%s: verification of software breakpoint writing failed - trap opcodes not successfully read back after writing when setting breakpoint at 0x% " PRIx64 , __FUNCTION__ , addr ) ;
}
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 " -- SUCCESS " , __FUNCTION__ , addr ) ;
return Error ( ) ;
}
// -------------------------------------------------------------------
// instance-level members
// -------------------------------------------------------------------
SoftwareBreakpoint : : SoftwareBreakpoint ( NativeProcessProtocol & process , lldb : : addr_t addr , const uint8_t * saved_opcodes , const uint8_t * trap_opcodes , size_t opcode_size ) :
NativeBreakpoint ( addr ) ,
m_process ( process ) ,
m_saved_opcodes ( ) ,
m_trap_opcodes ( ) ,
m_opcode_size ( opcode_size )
{
assert ( opcode_size > 0 & & " setting software breakpoint with no trap opcodes " ) ;
assert ( opcode_size < = MAX_TRAP_OPCODE_SIZE & & " trap opcode size too large " ) ;
: : memcpy ( m_saved_opcodes , saved_opcodes , opcode_size ) ;
: : memcpy ( m_trap_opcodes , trap_opcodes , opcode_size ) ;
}
Error
SoftwareBreakpoint : : DoEnable ( )
{
return EnableSoftwareBreakpoint ( m_process , m_addr , m_opcode_size , m_trap_opcodes , m_saved_opcodes ) ;
}
Error
SoftwareBreakpoint : : DoDisable ( )
{
Error error ;
assert ( m_addr & & ( m_addr ! = LLDB_INVALID_ADDRESS ) & & " can't remove a software breakpoint for an invalid address " ) ;
Log * log ( GetLogIfAnyCategoriesSet ( LIBLLDB_LOG_BREAKPOINTS ) ) ;
if ( log )
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 , __FUNCTION__ , m_addr ) ;
assert ( ( m_opcode_size > 0 ) & & " cannot restore opcodes when there are no opcodes " ) ;
if ( m_opcode_size > 0 )
{
2014-10-09 01:14:53 +08:00
// Clear a software breakpoint instruction
2014-07-01 05:05:18 +08:00
uint8_t curr_break_op [ MAX_TRAP_OPCODE_SIZE ] ;
bool break_op_found = false ;
assert ( m_opcode_size < = sizeof ( curr_break_op ) ) ;
// Read the breakpoint opcode
lldb : : addr_t bytes_read = 0 ;
error = m_process . ReadMemory ( m_addr , curr_break_op , m_opcode_size , bytes_read ) ;
if ( error . Success ( ) & & ( bytes_read < static_cast < lldb : : addr_t > ( m_opcode_size ) ) )
{
error . SetErrorStringWithFormat ( " SoftwareBreakpointr::%s addr=0x% " PRIx64 " : tried to read %lu bytes but only read % " PRIu64 , __FUNCTION__ , m_addr , m_opcode_size , bytes_read ) ;
}
if ( error . Success ( ) )
{
bool verify = false ;
// Make sure we have the a breakpoint opcode exists at this address
if ( : : memcmp ( curr_break_op , m_trap_opcodes , m_opcode_size ) = = 0 )
{
break_op_found = true ;
// We found a valid breakpoint opcode at this address, now restore
// the saved opcode.
lldb : : addr_t bytes_written = 0 ;
error = m_process . WriteMemory ( m_addr , m_saved_opcodes , m_opcode_size , bytes_written ) ;
if ( error . Success ( ) & & ( bytes_written < static_cast < lldb : : addr_t > ( m_opcode_size ) ) )
{
error . SetErrorStringWithFormat ( " SoftwareBreakpoint::%s addr=0x% " PRIx64 " : tried to write %lu bytes but only wrote % " PRIu64 , __FUNCTION__ , m_addr , m_opcode_size , bytes_written ) ;
}
if ( error . Success ( ) )
{
verify = true ;
}
}
else
{
error . SetErrorString ( " Original breakpoint trap is no longer in memory. " ) ;
// Set verify to true and so we can check if the original opcode has already been restored
verify = true ;
}
if ( verify )
{
uint8_t verify_opcode [ MAX_TRAP_OPCODE_SIZE ] ;
assert ( m_opcode_size < = sizeof ( verify_opcode ) ) ;
// Verify that our original opcode made it back to the inferior
lldb : : addr_t verify_bytes_read = 0 ;
error = m_process . ReadMemory ( m_addr , verify_opcode , m_opcode_size , verify_bytes_read ) ;
if ( error . Success ( ) & & ( verify_bytes_read < static_cast < lldb : : addr_t > ( m_opcode_size ) ) )
{
error . SetErrorStringWithFormat ( " SoftwareBreakpoint::%s addr=0x% " PRIx64 " : tried to read %lu verification bytes but only read % " PRIu64 , __FUNCTION__ , m_addr , m_opcode_size , verify_bytes_read ) ;
}
if ( error . Success ( ) )
{
// compare the memory we just read with the original opcode
if ( : : memcmp ( m_saved_opcodes , verify_opcode , m_opcode_size ) = = 0 )
{
// SUCCESS
if ( log )
2014-10-09 01:14:53 +08:00
{
int i = 0 ;
for ( const uint8_t * verify_byte = verify_opcode ; verify_byte < verify_opcode + m_opcode_size ; + + verify_byte )
{
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 " replaced byte index %d with 0x%x " , __FUNCTION__ , m_addr , i + + , static_cast < int > ( * verify_byte ) ) ;
}
2014-07-01 05:05:18 +08:00
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 " -- SUCCESS " , __FUNCTION__ , m_addr ) ;
2014-10-09 01:14:53 +08:00
}
2014-07-01 05:05:18 +08:00
return error ;
}
else
{
if ( break_op_found )
error . SetErrorString ( " Failed to restore original opcode. " ) ;
}
}
else
error . SetErrorString ( " Failed to read memory to verify that breakpoint trap was restored. " ) ;
}
}
}
if ( log & & error . Fail ( ) )
log - > Printf ( " SoftwareBreakpoint::%s addr = 0x% " PRIx64 " -- FAILED: %s " ,
__FUNCTION__ ,
m_addr ,
error . AsCString ( ) ) ;
return error ;
}
bool
SoftwareBreakpoint : : IsSoftwareBreakpoint ( ) const
{
return true ;
}