
When stack watchpoints go out of scope simultaneously they can hide (or duplicate the effect) of other breakpoints (including other watchpoints going out of scope) that are placed on the same physical memory location. This happens because we delete the watchpoint-out-of-scope breakpoint while we are evaluating hit breakpoints, mangling the breaklets list. This commit moves breakpoint deletion out of the watchpoint-out-of-scope condition, delaying it until all hit breakpoints have been evaluated. Also fix bug where on amd64 if all four watchpoints are in use the last one is not checked. Fixes #3739
131 lines
3.1 KiB
Go
131 lines
3.1 KiB
Go
package amd64util
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// DebugRegisters represents x86 debug registers described in the Intel 64
|
|
// and IA-32 Architectures Software Developer's Manual, Vol. 3B, section
|
|
// 17.2
|
|
type DebugRegisters struct {
|
|
pAddrs [4]*uint64
|
|
pDR6, pDR7 *uint64
|
|
Dirty bool
|
|
}
|
|
|
|
func NewDebugRegisters(pDR0, pDR1, pDR2, pDR3, pDR6, pDR7 *uint64) *DebugRegisters {
|
|
return &DebugRegisters{
|
|
pAddrs: [4]*uint64{pDR0, pDR1, pDR2, pDR3},
|
|
pDR6: pDR6,
|
|
pDR7: pDR7,
|
|
Dirty: false,
|
|
}
|
|
}
|
|
|
|
func lenrwBitsOffset(idx uint8) uint8 {
|
|
return 16 + idx*4
|
|
}
|
|
|
|
func enableBitOffset(idx uint8) uint8 {
|
|
return idx * 2
|
|
}
|
|
|
|
func (drs *DebugRegisters) breakpoint(idx uint8) (addr uint64, read, write bool, sz int) {
|
|
enable := *(drs.pDR7) & (1 << enableBitOffset(idx))
|
|
if enable == 0 {
|
|
return 0, false, false, 0
|
|
}
|
|
|
|
addr = *(drs.pAddrs[idx])
|
|
lenrw := (*(drs.pDR7) >> lenrwBitsOffset(idx)) & 0xf
|
|
write = (lenrw & 0x1) != 0
|
|
read = (lenrw & 0x2) != 0
|
|
switch lenrw >> 2 {
|
|
case 0x0:
|
|
sz = 1
|
|
case 0x1:
|
|
sz = 2
|
|
case 0x2:
|
|
sz = 8 // sic
|
|
case 0x3:
|
|
sz = 4
|
|
}
|
|
return addr, read, write, sz
|
|
}
|
|
|
|
// SetBreakpoint sets hardware breakpoint at index 'idx' to the specified
|
|
// address, read/write flags and size.
|
|
// If the breakpoint is already in use but the parameters match it does
|
|
// nothing.
|
|
func (drs *DebugRegisters) SetBreakpoint(idx uint8, addr uint64, read, write bool, sz int) error {
|
|
if int(idx) >= len(drs.pAddrs) {
|
|
return fmt.Errorf("hardware breakpoints exhausted")
|
|
}
|
|
curaddr, curread, curwrite, cursz := drs.breakpoint(idx)
|
|
if curaddr != 0 {
|
|
if (curaddr != addr) || (curread != read) || (curwrite != write) || (cursz != sz) {
|
|
return fmt.Errorf("hardware breakpoint %d already in use (address %#x)", idx, curaddr)
|
|
}
|
|
// hardware breakpoint already set
|
|
return nil
|
|
}
|
|
|
|
if read && !write {
|
|
return errors.New("break on read only not supported")
|
|
}
|
|
|
|
*(drs.pAddrs[idx]) = addr
|
|
var lenrw uint64
|
|
if write {
|
|
lenrw |= 0x1
|
|
}
|
|
if read {
|
|
lenrw |= 0x2
|
|
}
|
|
switch sz {
|
|
case 1:
|
|
// already ok
|
|
case 2:
|
|
lenrw |= 0x1 << 2
|
|
case 4:
|
|
lenrw |= 0x3 << 2
|
|
case 8:
|
|
lenrw |= 0x2 << 2
|
|
default:
|
|
return fmt.Errorf("data breakpoint of size %d not supported", sz)
|
|
}
|
|
*(drs.pDR7) &^= (0xf << lenrwBitsOffset(idx)) // clear old settings
|
|
*(drs.pDR7) |= lenrw << lenrwBitsOffset(idx)
|
|
*(drs.pDR7) |= 1 << enableBitOffset(idx) // enable
|
|
drs.Dirty = true
|
|
return nil
|
|
}
|
|
|
|
// ClearBreakpoint disables the hardware breakpoint at index 'idx'. If the
|
|
// breakpoint was already disabled it does nothing.
|
|
func (drs *DebugRegisters) ClearBreakpoint(idx uint8) {
|
|
if *(drs.pDR7)&(1<<enableBitOffset(idx)) == 0 {
|
|
return
|
|
}
|
|
*(drs.pDR7) &^= (1 << enableBitOffset(idx))
|
|
drs.Dirty = true
|
|
}
|
|
|
|
// GetActiveBreakpoint returns the active hardware breakpoint and resets the
|
|
// condition flags.
|
|
func (drs *DebugRegisters) GetActiveBreakpoint() (ok bool, idx uint8) {
|
|
for idx := uint8(0); idx <= 3; idx++ {
|
|
enable := *(drs.pDR7) & (1 << enableBitOffset(idx))
|
|
if enable == 0 {
|
|
continue
|
|
}
|
|
if *(drs.pDR6)&(1<<idx) != 0 {
|
|
*drs.pDR6 &^= 0xf // it is our responsibility to clear the condition bits
|
|
drs.Dirty = true
|
|
return true, idx
|
|
}
|
|
}
|
|
return false, 0
|
|
}
|