
This pull request makes several changes to delve to allow headless instancess that are started with the --accept-multiclient flag to keep running even if there is no connected client. Specifically: 1. Makes a headless instance started with --accept-multiclient quit after one of the clients sends a Detach request (previously they would never ever quit, which was a bug). 2. Changes proc/gdbserial and proc/native so that they mark the Process as exited after they detach, even if they did not kill the process during detach. This prevents bugs such as #1231 where we attempt to manipulate a target process after we detached from it. 3. On non --accept-multiclient instances do not kill the target process unless we started it or the client specifically requests it (previously if the client did not Detach before closing the connection we would kill the target process unconditionally) 4. Add a -c option to the quit command that detaches from the headless server after restarting the target. 5. Change terminal so that, when attached to --accept-multiclient, pressing ^C will prompt the user to either disconnect from the server or pause the target process. Also extend the exit prompt to ask if the user wants to keep the headless server running. Implements #245, #952, #1159, #1231
117 lines
3.1 KiB
Go
117 lines
3.1 KiB
Go
package proc
|
|
|
|
import "sort"
|
|
|
|
type AsmInstruction struct {
|
|
Loc Location
|
|
DestLoc *Location
|
|
Bytes []byte
|
|
Breakpoint bool
|
|
AtPC bool
|
|
Inst *ArchInst
|
|
}
|
|
|
|
type AssemblyFlavour int
|
|
|
|
const (
|
|
GNUFlavour = AssemblyFlavour(iota)
|
|
IntelFlavour
|
|
GoFlavour
|
|
)
|
|
|
|
// Disassemble disassembles target memory between startPC and endPC, marking
|
|
// the current instruction being executed in goroutine g.
|
|
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
|
|
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
|
|
func Disassemble(dbp Process, g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
|
|
if _, err := dbp.Valid(); err != nil {
|
|
return nil, err
|
|
}
|
|
if g == nil {
|
|
ct := dbp.CurrentThread()
|
|
regs, _ := ct.Registers(false)
|
|
return disassemble(ct, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
|
|
}
|
|
|
|
var regs Registers
|
|
var mem MemoryReadWriter = dbp.CurrentThread()
|
|
if g.Thread != nil {
|
|
mem = g.Thread
|
|
regs, _ = g.Thread.Registers(false)
|
|
}
|
|
|
|
return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
|
|
}
|
|
|
|
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startPC, endPC uint64, singleInstr bool) ([]AsmInstruction, error) {
|
|
mem := make([]byte, int(endPC-startPC))
|
|
_, err := memrw.ReadMemory(mem, uintptr(startPC))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r := make([]AsmInstruction, 0, len(mem)/15)
|
|
pc := startPC
|
|
|
|
var curpc uint64
|
|
if regs != nil {
|
|
curpc = regs.PC()
|
|
}
|
|
|
|
for len(mem) > 0 {
|
|
bp, atbp := breakpoints.M[pc]
|
|
if atbp {
|
|
for i := range bp.OriginalData {
|
|
mem[i] = bp.OriginalData[i]
|
|
}
|
|
}
|
|
file, line, fn := bi.PCToLine(pc)
|
|
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
|
|
inst, err := asmDecode(mem, pc)
|
|
if err == nil {
|
|
atpc := (regs != nil) && (curpc == pc)
|
|
destloc := resolveCallArg(inst, atpc, regs, memrw, bi)
|
|
r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
|
|
|
|
pc += uint64(inst.Size())
|
|
mem = mem[inst.Size():]
|
|
} else {
|
|
r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
|
|
pc++
|
|
mem = mem[1:]
|
|
}
|
|
if singleInstr {
|
|
break
|
|
}
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
// Looks up symbol (either functions or global variables) at address addr.
|
|
// Used by disassembly formatter.
|
|
func (bi *BinaryInfo) symLookup(addr uint64) (string, uint64) {
|
|
fn := bi.PCToFunc(addr)
|
|
if fn != nil {
|
|
if fn.Entry == addr {
|
|
// only report the function name if it's the exact address because it's
|
|
// easier to read the absolute address than function_name+offset.
|
|
return fn.Name, fn.Entry
|
|
}
|
|
return "", 0
|
|
}
|
|
i := sort.Search(len(bi.packageVars), func(i int) bool {
|
|
return bi.packageVars[i].addr >= addr
|
|
})
|
|
if i >= len(bi.packageVars) {
|
|
return "", 0
|
|
}
|
|
if bi.packageVars[i].addr > addr {
|
|
// report previous variable + offset if i-th variable starts after addr
|
|
i--
|
|
}
|
|
if i > 0 {
|
|
return bi.packageVars[i].name, bi.packageVars[i].addr
|
|
}
|
|
return "", 0
|
|
}
|