breakpoints.go 5.0 KB
Newer Older
D
Derek Parker 已提交
1 2 3 4 5 6 7 8 9 10
package proctl

import (
	"fmt"
	"runtime"
)

// Represents a single breakpoint. Stores information on the break
// point including the byte of data that originally was stored at that
// address.
D
Derek Parker 已提交
11
type Breakpoint struct {
12
	// File & line information for printing.
D
Derek Parker 已提交
13 14 15
	FunctionName string
	File         string
	Line         int
16 17 18 19 20 21 22

	Addr         uint64 // Address breakpoint is set for.
	OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
	ID           int    // Monotonically increasing ID.
	Temp         bool   // Whether this is a temp breakpoint (for next'ing).
	hardware     bool   // Breakpoint using CPU debug registers.
	reg          int    // If hardware breakpoint, what debug register it belongs to.
23 24
}

D
Derek Parker 已提交
25
func (bp *Breakpoint) String() string {
26
	return fmt.Sprintf("Breakpoint %d at %#v %s:%d", bp.ID, bp.Addr, bp.File, bp.Line)
D
Derek Parker 已提交
27 28
}

29 30
// Clear this breakpoint appropriately depending on whether it is a
// hardware or software breakpoint.
D
Derek Parker 已提交
31
func (bp *Breakpoint) Clear(thread *ThreadContext) (*Breakpoint, error) {
32 33 34 35 36 37 38 39 40 41 42 43
	if bp.hardware {
		if err := clearHardwareBreakpoint(bp.reg, thread.Id); err != nil {
			return nil, err
		}
		return bp, nil
	}
	if _, err := writeMemory(thread, uintptr(bp.Addr), bp.OriginalData); err != nil {
		return nil, fmt.Errorf("could not clear breakpoint %s", err)
	}
	return bp, nil
}

D
Derek Parker 已提交
44 45
// Returned when trying to set a breakpoint at
// an address that already has a breakpoint set for it.
D
Derek Parker 已提交
46
type BreakpointExistsError struct {
D
Derek Parker 已提交
47 48 49 50 51
	file string
	line int
	addr uint64
}

D
Derek Parker 已提交
52
func (bpe BreakpointExistsError) Error() string {
D
Derek Parker 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
	return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
}

// InvalidAddressError represents the result of
// attempting to set a breakpoint at an invalid address.
type InvalidAddressError struct {
	address uint64
}

func (iae InvalidAddressError) Error() string {
	return fmt.Sprintf("Invalid address %#v\n", iae.address)
}

// Returns whether or not a breakpoint has been set for the given address.
func (dbp *DebuggedProcess) BreakpointExists(addr uint64) bool {
D
Derek Parker 已提交
68
	for _, bp := range dbp.arch.HardwareBreakpoints() {
D
Derek Parker 已提交
69 70 71 72 73 74 75 76
		// TODO(darwin)
		if runtime.GOOS == "darwin" {
			break
		}
		if bp != nil && bp.Addr == addr {
			return true
		}
	}
D
Derek Parker 已提交
77
	_, ok := dbp.Breakpoints[addr]
D
Derek Parker 已提交
78
	return ok
D
Derek Parker 已提交
79 80
}

D
Derek Parker 已提交
81
func (dbp *DebuggedProcess) newBreakpoint(fn, f string, l int, addr uint64, data []byte, temp bool) *Breakpoint {
82 83 84 85 86 87 88 89
	var id int
	if temp {
		dbp.tempBreakpointIDCounter++
		id = dbp.tempBreakpointIDCounter
	} else {
		dbp.breakpointIDCounter++
		id = dbp.breakpointIDCounter
	}
D
Derek Parker 已提交
90
	return &Breakpoint{
D
Derek Parker 已提交
91 92 93 94 95
		FunctionName: fn,
		File:         f,
		Line:         l,
		Addr:         addr,
		OriginalData: data,
96
		ID:           id,
97
		Temp:         temp,
D
Derek Parker 已提交
98 99 100
	}
}

D
Derek Parker 已提交
101
func (dbp *DebuggedProcess) newHardwareBreakpoint(fn, f string, l int, addr uint64, data []byte, temp bool, reg int) *Breakpoint {
102 103 104 105 106 107
	bp := dbp.newBreakpoint(fn, f, l, addr, data, temp)
	bp.hardware = true
	bp.reg = reg
	return bp
}

D
Derek Parker 已提交
108
func (dbp *DebuggedProcess) setBreakpoint(tid int, addr uint64, temp bool) (*Breakpoint, error) {
109
	var f, l, fn = dbp.goSymTable.PCToLine(uint64(addr))
D
Derek Parker 已提交
110 111 112 113
	if fn == nil {
		return nil, InvalidAddressError{address: addr}
	}
	if dbp.BreakpointExists(addr) {
D
Derek Parker 已提交
114
		return nil, BreakpointExistsError{f, l, addr}
D
Derek Parker 已提交
115 116
	}
	// Try and set a hardware breakpoint.
D
Derek Parker 已提交
117
	for i, v := range dbp.arch.HardwareBreakpoints() {
D
Derek Parker 已提交
118 119 120 121 122
		// TODO(darwin)
		if runtime.GOOS == "darwin" {
			break
		}
		if v == nil {
123 124 125 126
			for t, _ := range dbp.Threads {
				if err := setHardwareBreakpoint(i, t, addr); err != nil {
					return nil, fmt.Errorf("could not set hardware breakpoint on thread %d: %s", t, err)
				}
D
Derek Parker 已提交
127
			}
D
Derek Parker 已提交
128 129
			dbp.arch.HardwareBreakpoints()[i] = dbp.newHardwareBreakpoint(fn.Name, f, l, addr, nil, temp, i)
			return dbp.arch.HardwareBreakpoints()[i], nil
D
Derek Parker 已提交
130 131
		}
	}
D
Derek Parker 已提交
132
	// Fall back to software breakpoint. 0xCC is INT 3 trap interrupt.
D
Derek Parker 已提交
133
	thread := dbp.Threads[tid]
134
	originalData := make([]byte, dbp.arch.BreakpointSize())
D
Derek Parker 已提交
135 136 137
	if _, err := readMemory(thread, uintptr(addr), originalData); err != nil {
		return nil, err
	}
138
	if _, err := writeMemory(thread, uintptr(addr), dbp.arch.BreakpointInstruction()); err != nil {
D
Derek Parker 已提交
139 140
		return nil, err
	}
D
Derek Parker 已提交
141 142
	dbp.Breakpoints[addr] = dbp.newBreakpoint(fn.Name, f, l, addr, originalData, temp)
	return dbp.Breakpoints[addr], nil
D
Derek Parker 已提交
143
}
144

D
Derek Parker 已提交
145
// Error thrown when trying to clear a breakpoint that does not exist.
D
Derek Parker 已提交
146
type NoBreakpointError struct {
147 148 149
	addr uint64
}

D
Derek Parker 已提交
150
func (nbp NoBreakpointError) Error() string {
151 152 153
	return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
}

D
Derek Parker 已提交
154
func (dbp *DebuggedProcess) clearBreakpoint(tid int, addr uint64) (*Breakpoint, error) {
155 156
	thread := dbp.Threads[tid]
	// Check for hardware breakpoint
D
Derek Parker 已提交
157
	for i, bp := range dbp.arch.HardwareBreakpoints() {
158 159 160 161 162 163 164 165
		if bp == nil {
			continue
		}
		if bp.Addr == addr {
			_, err := bp.Clear(thread)
			if err != nil {
				return nil, err
			}
D
Derek Parker 已提交
166
			dbp.arch.HardwareBreakpoints()[i] = nil
167 168 169 170
			return bp, nil
		}
	}
	// Check for software breakpoint
D
Derek Parker 已提交
171
	if bp, ok := dbp.Breakpoints[addr]; ok {
172 173 174
		if _, err := bp.Clear(thread); err != nil {
			return nil, err
		}
D
Derek Parker 已提交
175
		delete(dbp.Breakpoints, addr)
176 177
		return bp, nil
	}
D
Derek Parker 已提交
178
	return nil, NoBreakpointError{addr: addr}
179
}