
* 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
109 lines
2.4 KiB
Go
109 lines
2.4 KiB
Go
package proc
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
sys "golang.org/x/sys/unix"
|
|
)
|
|
|
|
type WaitStatus sys.WaitStatus
|
|
|
|
// OSSpecificDetails hold Linux specific
|
|
// process details.
|
|
type OSSpecificDetails struct {
|
|
registers sys.PtraceRegs
|
|
}
|
|
|
|
func (t *Thread) halt() (err error) {
|
|
err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP)
|
|
if err != nil {
|
|
err = fmt.Errorf("halt err %s on thread %d", err, t.ID)
|
|
return
|
|
}
|
|
_, _, err = t.dbp.wait(t.ID, 0)
|
|
if err != nil {
|
|
err = fmt.Errorf("wait err %s on thread %d", err, t.ID)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (t *Thread) stopped() bool {
|
|
state := status(t.ID, t.dbp.os.comm)
|
|
return state == StatusTraceStop || state == StatusTraceStopT
|
|
}
|
|
|
|
func (t *Thread) resume() error {
|
|
return t.resumeWithSig(0)
|
|
}
|
|
|
|
func (t *Thread) resumeWithSig(sig int) (err error) {
|
|
t.running = true
|
|
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.ID, sig) })
|
|
return
|
|
}
|
|
|
|
func (t *Thread) singleStep() (err error) {
|
|
for {
|
|
t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.ID) })
|
|
if err != nil {
|
|
return err
|
|
}
|
|
wpid, status, err := t.dbp.waitFast(t.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if (status == nil || status.Exited()) && wpid == t.dbp.pid {
|
|
t.dbp.postExit()
|
|
rs := 0
|
|
if status != nil {
|
|
rs = status.ExitStatus()
|
|
}
|
|
return ProcessExitedError{Pid: t.dbp.pid, Status: rs}
|
|
}
|
|
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (t *Thread) blocked() bool {
|
|
pc, _ := t.PC()
|
|
fn := t.dbp.bi.goSymTable.PCToFunc(pc)
|
|
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Thread) saveRegisters() (Registers, error) {
|
|
var err error
|
|
t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not save register contents")
|
|
}
|
|
return &Regs{&t.os.registers, nil}, nil
|
|
}
|
|
|
|
func (t *Thread) restoreRegisters() (err error) {
|
|
t.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(t.ID, &t.os.registers) })
|
|
return
|
|
}
|
|
|
|
func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
|
if len(data) == 0 {
|
|
return
|
|
}
|
|
t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, addr, data) })
|
|
return
|
|
}
|
|
|
|
func (t *Thread) readMemory(addr uintptr, size int) (data []byte, err error) {
|
|
if size == 0 {
|
|
return
|
|
}
|
|
data = make([]byte, size)
|
|
t.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(t.ID, addr, data) })
|
|
return
|
|
}
|