github.com/undoio/delve@v1.9.0/pkg/proc/native/threads_windows.go (about)

     1  package native
     2  
     3  import (
     4  	"errors"
     5  	"syscall"
     6  
     7  	sys "golang.org/x/sys/windows"
     8  
     9  	"github.com/undoio/delve/pkg/proc"
    10  	"github.com/undoio/delve/pkg/proc/amd64util"
    11  	"github.com/undoio/delve/pkg/proc/winutil"
    12  )
    13  
    14  const enableHardwareBreakpoints = false // see https://github.com/go-delve/delve/issues/2768
    15  
    16  // waitStatus is a synonym for the platform-specific WaitStatus
    17  type waitStatus sys.WaitStatus
    18  
    19  // osSpecificDetails holds information specific to the Windows
    20  // operating system / kernel.
    21  type osSpecificDetails struct {
    22  	hThread            syscall.Handle
    23  	dbgUiRemoteBreakIn bool // whether thread is an auxiliary DbgUiRemoteBreakIn thread created by Windows
    24  	delayErr           error
    25  	setbp              bool
    26  }
    27  
    28  func (t *nativeThread) singleStep() error {
    29  	context := winutil.NewCONTEXT()
    30  	context.ContextFlags = _CONTEXT_ALL
    31  
    32  	// Set the processor TRAP flag
    33  	err := _GetThreadContext(t.os.hThread, context)
    34  	if err != nil {
    35  		return err
    36  	}
    37  
    38  	context.EFlags |= 0x100
    39  
    40  	err = _SetThreadContext(t.os.hThread, context)
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	suspendcnt := 0
    46  
    47  	// If a thread simultaneously hits a breakpoint and is suspended by the Go
    48  	// runtime it will have a suspend count greater than 1 and to actually take
    49  	// a single step we have to resume it multiple times here.
    50  	// We keep a counter of how many times it was suspended so that after
    51  	// single-stepping we can re-suspend it the corrent number of times.
    52  	for {
    53  		n, err := _ResumeThread(t.os.hThread)
    54  		if err != nil {
    55  			return err
    56  		}
    57  		suspendcnt++
    58  		if n == 1 {
    59  			break
    60  		}
    61  	}
    62  
    63  	for {
    64  		var tid, exitCode int
    65  		t.dbp.execPtraceFunc(func() {
    66  			tid, exitCode, err = t.dbp.waitForDebugEvent(waitBlocking | waitSuspendNewThreads)
    67  		})
    68  		if err != nil {
    69  			return err
    70  		}
    71  		if tid == 0 {
    72  			t.dbp.postExit()
    73  			return proc.ErrProcessExited{Pid: t.dbp.pid, Status: exitCode}
    74  		}
    75  
    76  		if t.dbp.os.breakThread == t.ID {
    77  			break
    78  		}
    79  
    80  		t.dbp.execPtraceFunc(func() {
    81  			err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
    82  		})
    83  	}
    84  
    85  	for i := 0; i < suspendcnt; i++ {
    86  		if !t.os.dbgUiRemoteBreakIn {
    87  			_, err = _SuspendThread(t.os.hThread)
    88  			if err != nil {
    89  				return err
    90  			}
    91  		}
    92  	}
    93  
    94  	t.dbp.execPtraceFunc(func() {
    95  		err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
    96  	})
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	// Unset the processor TRAP flag
   102  	err = _GetThreadContext(t.os.hThread, context)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	context.EFlags &= ^uint32(0x100)
   108  
   109  	return _SetThreadContext(t.os.hThread, context)
   110  }
   111  
   112  func (t *nativeThread) resume() error {
   113  	var err error
   114  	t.dbp.execPtraceFunc(func() {
   115  		//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
   116  		//thread that we last broke on.
   117  		err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
   118  	})
   119  	return err
   120  }
   121  
   122  // Stopped returns whether the thread is stopped at the operating system
   123  // level. On windows this always returns true.
   124  func (t *nativeThread) Stopped() bool {
   125  	return true
   126  }
   127  
   128  func (t *nativeThread) WriteMemory(addr uint64, data []byte) (int, error) {
   129  	if t.dbp.exited {
   130  		return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
   131  	}
   132  	if len(data) == 0 {
   133  		return 0, nil
   134  	}
   135  	var count uintptr
   136  	err := _WriteProcessMemory(t.dbp.os.hProcess, uintptr(addr), &data[0], uintptr(len(data)), &count)
   137  	if err != nil {
   138  		return 0, err
   139  	}
   140  	return int(count), nil
   141  }
   142  
   143  var ErrShortRead = errors.New("short read")
   144  
   145  func (t *nativeThread) ReadMemory(buf []byte, addr uint64) (int, error) {
   146  	if t.dbp.exited {
   147  		return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
   148  	}
   149  	if len(buf) == 0 {
   150  		return 0, nil
   151  	}
   152  	var count uintptr
   153  	err := _ReadProcessMemory(t.dbp.os.hProcess, uintptr(addr), &buf[0], uintptr(len(buf)), &count)
   154  	if err == nil && count != uintptr(len(buf)) {
   155  		err = ErrShortRead
   156  	}
   157  	return int(count), err
   158  }
   159  
   160  func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error {
   161  	return _SetThreadContext(t.os.hThread, savedRegs.(*winutil.AMD64Registers).Context)
   162  }
   163  
   164  func (t *nativeThread) withDebugRegisters(f func(*amd64util.DebugRegisters) error) error {
   165  	if !enableHardwareBreakpoints {
   166  		return errors.New("hardware breakpoints not supported")
   167  	}
   168  
   169  	context := winutil.NewCONTEXT()
   170  	context.ContextFlags = _CONTEXT_DEBUG_REGISTERS
   171  
   172  	err := _GetThreadContext(t.os.hThread, context)
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	drs := amd64util.NewDebugRegisters(&context.Dr0, &context.Dr1, &context.Dr2, &context.Dr3, &context.Dr6, &context.Dr7)
   178  
   179  	err = f(drs)
   180  	if err != nil {
   181  		return err
   182  	}
   183  
   184  	if drs.Dirty {
   185  		return _SetThreadContext(t.os.hThread, context)
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  // SoftExc returns true if this thread received a software exception during the last resume.
   192  func (t *nativeThread) SoftExc() bool {
   193  	return t.os.setbp
   194  }