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

     1  package native
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"syscall"
     8  	"unsafe"
     9  
    10  	sys "golang.org/x/sys/windows"
    11  
    12  	"github.com/undoio/delve/pkg/proc"
    13  	"github.com/undoio/delve/pkg/proc/internal/ebpf"
    14  	"github.com/undoio/delve/pkg/proc/winutil"
    15  )
    16  
    17  // osProcessDetails holds Windows specific information.
    18  type osProcessDetails struct {
    19  	hProcess    syscall.Handle
    20  	breakThread int
    21  	entryPoint  uint64
    22  	running     bool
    23  }
    24  
    25  func (os *osProcessDetails) Close() {}
    26  
    27  // Launch creates and begins debugging a new process.
    28  func Launch(cmd []string, wd string, flags proc.LaunchFlags, _ []string, _ string, redirects [3]string) (*proc.Target, error) {
    29  	argv0Go, err := filepath.Abs(cmd[0])
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	env := proc.DisableAsyncPreemptEnv()
    35  
    36  	stdin, stdout, stderr, closefn, err := openRedirects(redirects, true)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	creationFlags := uint32(_DEBUG_ONLY_THIS_PROCESS)
    42  	if flags&proc.LaunchForeground == 0 {
    43  		creationFlags |= syscall.CREATE_NEW_PROCESS_GROUP
    44  	}
    45  
    46  	var p *os.Process
    47  	dbp := newProcess(0)
    48  	dbp.execPtraceFunc(func() {
    49  		attr := &os.ProcAttr{
    50  			Dir:   wd,
    51  			Files: []*os.File{stdin, stdout, stderr},
    52  			Sys: &syscall.SysProcAttr{
    53  				CreationFlags: creationFlags,
    54  			},
    55  			Env: env,
    56  		}
    57  		p, err = os.StartProcess(argv0Go, cmd, attr)
    58  	})
    59  	closefn()
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	defer p.Release()
    64  
    65  	dbp.pid = p.Pid
    66  	dbp.childProcess = true
    67  
    68  	tgt, err := dbp.initialize(argv0Go, []string{})
    69  	if err != nil {
    70  		dbp.Detach(true)
    71  		return nil, err
    72  	}
    73  	return tgt, nil
    74  }
    75  
    76  func initialize(dbp *nativeProcess) error {
    77  	// It should not actually be possible for the
    78  	// call to waitForDebugEvent to fail, since Windows
    79  	// will always fire a CREATE_PROCESS_DEBUG_EVENT event
    80  	// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
    81  	// Attaching with DebugActiveProcess has similar effect.
    82  	var err error
    83  	var tid, exitCode int
    84  	dbp.execPtraceFunc(func() {
    85  		tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
    86  	})
    87  	if err != nil {
    88  		return err
    89  	}
    90  	if tid == 0 {
    91  		dbp.postExit()
    92  		return proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
    93  	}
    94  	// Suspend all threads so that the call to _ContinueDebugEvent will
    95  	// not resume the target.
    96  	for _, thread := range dbp.threads {
    97  		if !thread.os.dbgUiRemoteBreakIn {
    98  			_, err := _SuspendThread(thread.os.hThread)
    99  			if err != nil {
   100  				return err
   101  			}
   102  		}
   103  	}
   104  
   105  	dbp.execPtraceFunc(func() {
   106  		err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
   107  	})
   108  	return err
   109  }
   110  
   111  // findExePath searches for process pid, and returns its executable path.
   112  func findExePath(pid int) (string, error) {
   113  	// Original code suggested different approach (see below).
   114  	// Maybe it could be useful in the future.
   115  	//
   116  	// Find executable path from PID/handle on Windows:
   117  	// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
   118  
   119  	p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
   120  	if err != nil {
   121  		return "", err
   122  	}
   123  	defer syscall.CloseHandle(p)
   124  
   125  	n := uint32(128)
   126  	for {
   127  		buf := make([]uint16, int(n))
   128  		err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
   129  		switch err {
   130  		case syscall.ERROR_INSUFFICIENT_BUFFER:
   131  			// try bigger buffer
   132  			n *= 2
   133  			// but stop if it gets too big
   134  			if n > 10000 {
   135  				return "", err
   136  			}
   137  		case nil:
   138  			return syscall.UTF16ToString(buf[:n]), nil
   139  		default:
   140  			return "", err
   141  		}
   142  	}
   143  }
   144  
   145  // Attach to an existing process with the given PID.
   146  func Attach(pid int, _ []string) (*proc.Target, error) {
   147  	dbp := newProcess(pid)
   148  	var err error
   149  	dbp.execPtraceFunc(func() {
   150  		// TODO: Probably should have SeDebugPrivilege before starting here.
   151  		err = _DebugActiveProcess(uint32(pid))
   152  	})
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	exepath, err := findExePath(pid)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	tgt, err := dbp.initialize(exepath, []string{})
   161  	if err != nil {
   162  		dbp.Detach(true)
   163  		return nil, err
   164  	}
   165  	return tgt, nil
   166  }
   167  
   168  // kill kills the process.
   169  func (dbp *nativeProcess) kill() error {
   170  	if dbp.exited {
   171  		return nil
   172  	}
   173  
   174  	p, err := os.FindProcess(dbp.pid)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	defer p.Release()
   179  
   180  	// TODO: Should not have to ignore failures here,
   181  	// but some tests appear to Kill twice causing
   182  	// this to fail on second attempt.
   183  	_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
   184  
   185  	dbp.execPtraceFunc(func() {
   186  		dbp.waitForDebugEvent(waitBlocking | waitDontHandleExceptions)
   187  	})
   188  
   189  	p.Wait()
   190  
   191  	dbp.postExit()
   192  	return nil
   193  }
   194  
   195  func (dbp *nativeProcess) requestManualStop() error {
   196  	if !dbp.os.running {
   197  		return nil
   198  	}
   199  	dbp.os.running = false
   200  	return _DebugBreakProcess(dbp.os.hProcess)
   201  }
   202  
   203  func (dbp *nativeProcess) updateThreadList() error {
   204  	// We ignore this request since threads are being
   205  	// tracked as they are created/killed in waitForDebugEvent.
   206  	return nil
   207  }
   208  
   209  func (dbp *nativeProcess) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool, dbgUiRemoteBreakIn bool) (*nativeThread, error) {
   210  	if thread, ok := dbp.threads[threadID]; ok {
   211  		return thread, nil
   212  	}
   213  	thread := &nativeThread{
   214  		ID:  threadID,
   215  		dbp: dbp,
   216  		os:  new(osSpecificDetails),
   217  	}
   218  	thread.os.dbgUiRemoteBreakIn = dbgUiRemoteBreakIn
   219  	thread.os.hThread = hThread
   220  	dbp.threads[threadID] = thread
   221  	if dbp.memthread == nil {
   222  		dbp.memthread = dbp.threads[threadID]
   223  	}
   224  	if suspendNewThreads && !dbgUiRemoteBreakIn {
   225  		_, err := _SuspendThread(thread.os.hThread)
   226  		if err != nil {
   227  			return nil, err
   228  		}
   229  	}
   230  
   231  	for _, bp := range dbp.Breakpoints().M {
   232  		if bp.WatchType != 0 {
   233  			err := thread.writeHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex)
   234  			if err != nil {
   235  				return nil, err
   236  			}
   237  		}
   238  	}
   239  
   240  	return thread, nil
   241  }
   242  
   243  type waitForDebugEventFlags int
   244  
   245  const (
   246  	waitBlocking waitForDebugEventFlags = 1 << iota
   247  	waitSuspendNewThreads
   248  	waitDontHandleExceptions
   249  )
   250  
   251  const _MS_VC_EXCEPTION = 0x406D1388 // part of VisualC protocol to set thread names
   252  
   253  func (dbp *nativeProcess) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
   254  	var debugEvent _DEBUG_EVENT
   255  	shouldExit := false
   256  	for {
   257  		continueStatus := uint32(_DBG_CONTINUE)
   258  		var milliseconds uint32 = 0
   259  		if flags&waitBlocking != 0 {
   260  			milliseconds = syscall.INFINITE
   261  		}
   262  		// Wait for a debug event...
   263  		err := _WaitForDebugEvent(&debugEvent, milliseconds)
   264  		if err != nil {
   265  			return 0, 0, err
   266  		}
   267  
   268  		// ... handle each event kind ...
   269  		unionPtr := unsafe.Pointer(&debugEvent.U[0])
   270  		switch debugEvent.DebugEventCode {
   271  		case _CREATE_PROCESS_DEBUG_EVENT:
   272  			debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
   273  			hFile := debugInfo.File
   274  			if hFile != 0 && hFile != syscall.InvalidHandle {
   275  				err = syscall.CloseHandle(hFile)
   276  				if err != nil {
   277  					return 0, 0, err
   278  				}
   279  			}
   280  			dbp.os.entryPoint = uint64(debugInfo.BaseOfImage)
   281  			dbp.os.hProcess = debugInfo.Process
   282  			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false,
   283  				flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr())
   284  			if err != nil {
   285  				return 0, 0, err
   286  			}
   287  			break
   288  		case _CREATE_THREAD_DEBUG_EVENT:
   289  			debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
   290  			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false,
   291  				flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr())
   292  			if err != nil {
   293  				return 0, 0, err
   294  			}
   295  			break
   296  		case _EXIT_THREAD_DEBUG_EVENT:
   297  			delete(dbp.threads, int(debugEvent.ThreadId))
   298  			break
   299  		case _OUTPUT_DEBUG_STRING_EVENT:
   300  			//TODO: Handle debug output strings
   301  			break
   302  		case _LOAD_DLL_DEBUG_EVENT:
   303  			debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
   304  			hFile := debugInfo.File
   305  			if hFile != 0 && hFile != syscall.InvalidHandle {
   306  				err = syscall.CloseHandle(hFile)
   307  				if err != nil {
   308  					return 0, 0, err
   309  				}
   310  			}
   311  			break
   312  		case _UNLOAD_DLL_DEBUG_EVENT:
   313  			break
   314  		case _RIP_EVENT:
   315  			break
   316  		case _EXCEPTION_DEBUG_EVENT:
   317  			if flags&waitDontHandleExceptions != 0 {
   318  				continueStatus = _DBG_EXCEPTION_NOT_HANDLED
   319  				break
   320  			}
   321  			exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
   322  			tid := int(debugEvent.ThreadId)
   323  
   324  			switch code := exception.ExceptionRecord.ExceptionCode; code {
   325  			case _EXCEPTION_BREAKPOINT:
   326  
   327  				// check if the exception address really is a breakpoint instruction, if
   328  				// it isn't we already removed that breakpoint and we can't deal with
   329  				// this exception anymore.
   330  				atbp := true
   331  				if thread, found := dbp.threads[tid]; found {
   332  					data := make([]byte, dbp.bi.Arch.BreakpointSize())
   333  					if _, err := thread.ReadMemory(data, uint64(exception.ExceptionRecord.ExceptionAddress)); err == nil {
   334  						instr := dbp.bi.Arch.BreakpointInstruction()
   335  						for i := range instr {
   336  							if data[i] != instr[i] {
   337  								atbp = false
   338  								break
   339  							}
   340  						}
   341  					}
   342  					if !atbp {
   343  						thread.setPC(uint64(exception.ExceptionRecord.ExceptionAddress))
   344  					}
   345  				}
   346  
   347  				if atbp {
   348  					dbp.os.breakThread = tid
   349  					if th := dbp.threads[tid]; th != nil {
   350  						th.os.setbp = true
   351  					}
   352  					return tid, 0, nil
   353  				} else {
   354  					continueStatus = _DBG_CONTINUE
   355  				}
   356  			case _EXCEPTION_SINGLE_STEP:
   357  				dbp.os.breakThread = tid
   358  				return tid, 0, nil
   359  			case _MS_VC_EXCEPTION:
   360  				// This exception is sent to set the thread name in VisualC, we should
   361  				// mask it or it might crash the program.
   362  				continueStatus = _DBG_CONTINUE
   363  			default:
   364  				continueStatus = _DBG_EXCEPTION_NOT_HANDLED
   365  			}
   366  		case _EXIT_PROCESS_DEBUG_EVENT:
   367  			debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
   368  			exitCode = int(debugInfo.ExitCode)
   369  			shouldExit = true
   370  		default:
   371  			return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
   372  		}
   373  
   374  		// .. and then continue unless we received an event that indicated we should break into debugger.
   375  		err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
   376  		if err != nil {
   377  			return 0, 0, err
   378  		}
   379  
   380  		if shouldExit {
   381  			return 0, exitCode, nil
   382  		}
   383  	}
   384  }
   385  
   386  func (dbp *nativeProcess) trapWait(pid int) (*nativeThread, error) {
   387  	var err error
   388  	var tid, exitCode int
   389  	dbp.execPtraceFunc(func() {
   390  		tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
   391  	})
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  	if tid == 0 {
   396  		dbp.postExit()
   397  		return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
   398  	}
   399  	th := dbp.threads[tid]
   400  	return th, nil
   401  }
   402  
   403  func (dbp *nativeProcess) wait(pid, options int) (int, *sys.WaitStatus, error) {
   404  	return 0, nil, fmt.Errorf("not implemented: wait")
   405  }
   406  
   407  func (dbp *nativeProcess) exitGuard(err error) error {
   408  	return err
   409  }
   410  
   411  func (dbp *nativeProcess) resume() error {
   412  	for _, thread := range dbp.threads {
   413  		if thread.CurrentBreakpoint.Breakpoint != nil {
   414  			if err := thread.StepInstruction(); err != nil {
   415  				return err
   416  			}
   417  			thread.CurrentBreakpoint.Clear()
   418  		}
   419  	}
   420  
   421  	for _, thread := range dbp.threads {
   422  		_, err := _ResumeThread(thread.os.hThread)
   423  		if err != nil {
   424  			return err
   425  		}
   426  	}
   427  	dbp.os.running = true
   428  
   429  	return nil
   430  }
   431  
   432  // stop stops all running threads threads and sets breakpoints
   433  func (dbp *nativeProcess) stop(cctx *proc.ContinueOnceContext, trapthread *nativeThread) (*nativeThread, error) {
   434  	if dbp.exited {
   435  		return nil, proc.ErrProcessExited{Pid: dbp.pid}
   436  	}
   437  
   438  	dbp.os.running = false
   439  	for _, th := range dbp.threads {
   440  		th.os.setbp = false
   441  	}
   442  	trapthread.os.setbp = true
   443  
   444  	// While the debug event that stopped the target was being propagated
   445  	// other target threads could generate other debug events.
   446  	// After this function we need to know about all the threads
   447  	// stopped on a breakpoint. To do that we first suspend all target
   448  	// threads and then repeatedly call _ContinueDebugEvent followed by
   449  	// waitForDebugEvent in non-blocking mode.
   450  	// We need to explicitly call SuspendThread because otherwise the
   451  	// call to _ContinueDebugEvent will resume execution of some of the
   452  	// target threads.
   453  
   454  	err := trapthread.SetCurrentBreakpoint(true)
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	context := winutil.NewCONTEXT()
   460  
   461  	for _, thread := range dbp.threads {
   462  		thread.os.delayErr = nil
   463  		if !thread.os.dbgUiRemoteBreakIn {
   464  			// Wait before reporting the error, the thread could be removed when we
   465  			// call waitForDebugEvent in the next loop.
   466  			_, thread.os.delayErr = _SuspendThread(thread.os.hThread)
   467  			if thread.os.delayErr == nil {
   468  				// This call will block until the thread has stopped.
   469  				_ = _GetThreadContext(thread.os.hThread, context)
   470  			}
   471  		}
   472  	}
   473  
   474  	for {
   475  		var err error
   476  		var tid int
   477  		dbp.execPtraceFunc(func() {
   478  			err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
   479  			if err == nil {
   480  				tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
   481  			}
   482  		})
   483  		if err != nil {
   484  			return nil, err
   485  		}
   486  		if tid == 0 {
   487  			break
   488  		}
   489  		err = dbp.threads[tid].SetCurrentBreakpoint(true)
   490  		if err != nil {
   491  			return nil, err
   492  		}
   493  	}
   494  
   495  	// Check if trapthread still exist, if the process is dying it could have
   496  	// been removed while we were stopping the other threads.
   497  	trapthreadFound := false
   498  	for _, thread := range dbp.threads {
   499  		if thread.ID == trapthread.ID {
   500  			trapthreadFound = true
   501  		}
   502  		if thread.os.delayErr != nil && thread.os.delayErr != syscall.Errno(0x5) {
   503  			// Do not report Access is denied error, it is caused by the thread
   504  			// having already died but we haven't been notified about it yet.
   505  			return nil, thread.os.delayErr
   506  		}
   507  	}
   508  
   509  	if !trapthreadFound {
   510  		wasDbgUiRemoteBreakIn := trapthread.os.dbgUiRemoteBreakIn
   511  		// trapthread exited during stop, pick another one
   512  		trapthread = nil
   513  		for _, thread := range dbp.threads {
   514  			if thread.CurrentBreakpoint.Breakpoint != nil && thread.os.delayErr == nil {
   515  				trapthread = thread
   516  				break
   517  			}
   518  		}
   519  		if trapthread == nil && wasDbgUiRemoteBreakIn {
   520  			// If this was triggered by a manual stop request we should stop
   521  			// regardless, pick a thread.
   522  			for _, thread := range dbp.threads {
   523  				return thread, nil
   524  			}
   525  		}
   526  	}
   527  
   528  	return trapthread, nil
   529  }
   530  
   531  func (dbp *nativeProcess) detach(kill bool) error {
   532  	if !kill {
   533  		//TODO(aarzilli): when debug.Target exist Detach should be moved to
   534  		// debug.Target and the call to RestoreAsyncPreempt should be moved there.
   535  		for _, thread := range dbp.threads {
   536  			_, err := _ResumeThread(thread.os.hThread)
   537  			if err != nil {
   538  				return err
   539  			}
   540  		}
   541  	}
   542  	return _DebugActiveProcessStop(uint32(dbp.pid))
   543  }
   544  
   545  func (dbp *nativeProcess) EntryPoint() (uint64, error) {
   546  	return dbp.os.entryPoint, nil
   547  }
   548  
   549  func (dbp *nativeProcess) SupportsBPF() bool {
   550  	return false
   551  }
   552  
   553  func (dbp *nativeProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf.UProbeArgMap) error {
   554  	return nil
   555  }
   556  
   557  func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
   558  	return nil
   559  }
   560  
   561  func killProcess(pid int) error {
   562  	p, err := os.FindProcess(pid)
   563  	if err != nil {
   564  		return err
   565  	}
   566  	defer p.Release()
   567  
   568  	return p.Kill()
   569  }