File: debugregs.go

package info (click to toggle)
delve 1.24.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 14,092 kB
  • sloc: ansic: 111,943; sh: 169; asm: 141; makefile: 43; python: 23
file content (130 lines) | stat: -rw-r--r-- 3,175 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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 errors.New("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
}