
* proc: refactor BinaryInfo part of proc.Process to own type The data structures and associated code used by proc.Process to implement target.BinaryInfo will also be useful to support a backend for examining core dumps, split this part of proc.Process to a different type. * proc: compile support for all executable formats unconditionally So far we only compiled in support for loading the executable format supported by the host operating system. Once support for core files is introduced it is however useful to support loading in all executable formats, there is no reason why it shouldn't be possible to examine a linux coredump on windows, or viceversa. * proc: bugfix: do not resume threads on detach if killing * Replace BinaryInfo interface with BinInfo() method returning proc.BinaryInfo
152 lines
3.3 KiB
Go
152 lines
3.3 KiB
Go
package proc
|
|
|
|
import (
|
|
"syscall"
|
|
|
|
sys "golang.org/x/sys/windows"
|
|
)
|
|
|
|
// WaitStatus is a synonym for the platform-specific WaitStatus
|
|
type WaitStatus sys.WaitStatus
|
|
|
|
// OSSpecificDetails holds information specific to the Windows
|
|
// operating system / kernel.
|
|
type OSSpecificDetails struct {
|
|
hThread syscall.Handle
|
|
}
|
|
|
|
func (t *Thread) halt() (err error) {
|
|
// Ignore the request to halt. On Windows, all threads are halted
|
|
// on return from WaitForDebugEvent.
|
|
return nil
|
|
|
|
// TODO - This may not be correct in all usages of dbp.Halt. There
|
|
// are some callers who use dbp.Halt() to stop the process when it is not
|
|
// already broken on a debug event.
|
|
}
|
|
|
|
func (t *Thread) singleStep() error {
|
|
context := newCONTEXT()
|
|
context.ContextFlags = _CONTEXT_ALL
|
|
|
|
// Set the processor TRAP flag
|
|
err := _GetThreadContext(t.os.hThread, context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
context.EFlags |= 0x100
|
|
|
|
err = _SetThreadContext(t.os.hThread, context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = _ResumeThread(t.os.hThread)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
var tid, exitCode int
|
|
t.dbp.execPtraceFunc(func() {
|
|
tid, exitCode, err = t.dbp.waitForDebugEvent(waitBlocking | waitSuspendNewThreads)
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tid == 0 {
|
|
t.dbp.postExit()
|
|
return ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
|
|
}
|
|
|
|
if t.dbp.os.breakThread == t.ID {
|
|
break
|
|
}
|
|
|
|
t.dbp.execPtraceFunc(func() {
|
|
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
|
|
})
|
|
}
|
|
|
|
_, err = _SuspendThread(t.os.hThread)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
t.dbp.execPtraceFunc(func() {
|
|
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Unset the processor TRAP flag
|
|
err = _GetThreadContext(t.os.hThread, context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
context.EFlags &= ^uint32(0x100)
|
|
|
|
return _SetThreadContext(t.os.hThread, context)
|
|
}
|
|
|
|
func (t *Thread) resume() error {
|
|
t.running = true
|
|
var err error
|
|
t.dbp.execPtraceFunc(func() {
|
|
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
|
//thread that we last broke on.
|
|
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (t *Thread) blocked() bool {
|
|
// TODO: Probably incorrect - what are the runtime functions that
|
|
// indicate blocking on Windows?
|
|
pc, err := t.PC()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
fn := t.dbp.bi.goSymTable.PCToFunc(pc)
|
|
if fn == nil {
|
|
return false
|
|
}
|
|
switch fn.Name {
|
|
case "runtime.kevent", "runtime.usleep":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (t *Thread) stopped() bool {
|
|
// TODO: We are assuming that threads are always stopped
|
|
// during command execution.
|
|
return true
|
|
}
|
|
|
|
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
|
var count uintptr
|
|
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return int(count), nil
|
|
}
|
|
|
|
func (t *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
|
|
if size == 0 {
|
|
return nil, nil
|
|
}
|
|
var count uintptr
|
|
buf := make([]byte, size)
|
|
err := _ReadProcessMemory(t.dbp.os.hProcess, addr, &buf[0], uintptr(size), &count)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buf[:count], nil
|
|
}
|