runtime: support for debugger function calls

This adds a mechanism for debuggers to safely inject calls to Go
functions on amd64. Debuggers must participate in a protocol with the
runtime, and need to know how to lay out a call frame, but the runtime
support takes care of the details of handling live pointers in
registers, stack growth, and detecting the trickier conditions when it
is unsafe to inject a user function call.

Fixes #21678.
Updates derekparker/delve#119.

Change-Id: I56d8ca67700f1f77e19d89e7fc92ab337b228834
Reviewed-on: https://go-review.googlesource.com/109699
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Austin Clements 2018-04-26 21:43:19 -04:00
parent 9f95c9db23
commit c5ed10f3be
13 changed files with 706 additions and 7 deletions

View file

@ -1169,21 +1169,43 @@ func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args
minsize = sys.MinFrameSize
}
if size > minsize {
stackmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
if stackmap == nil || stackmap.n <= 0 {
var stkmap *stackmap
stackid := pcdata
if f.funcID != funcID_debugCallV1 {
stkmap = (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
} else {
// debugCallV1's stack map is the register map
// at its call site.
callerPC := frame.lr
caller := findfunc(callerPC)
if !caller.valid() {
println("runtime: debugCallV1 called by unknown caller", hex(callerPC))
throw("bad debugCallV1")
}
stackid = int32(-1)
if callerPC != caller.entry {
callerPC--
stackid = pcdatavalue(caller, _PCDATA_RegMapIndex, callerPC, cache)
}
if stackid == -1 {
stackid = 0 // in prologue
}
stkmap = (*stackmap)(funcdata(caller, _FUNCDATA_RegPointerMaps))
}
if stkmap == nil || stkmap.n <= 0 {
print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n")
throw("missing stackmap")
}
// If nbit == 0, there's no work to do.
if stackmap.nbit > 0 {
if pcdata < 0 || pcdata >= stackmap.n {
if stkmap.nbit > 0 {
if stackid < 0 || stackid >= stkmap.n {
// don't know where we are
print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
print("runtime: pcdata is ", stackid, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
throw("bad symbol table")
}
locals = stackmapdata(stackmap, pcdata)
locals = stackmapdata(stkmap, stackid)
if stackDebug >= 3 && debug {
print(" locals ", pcdata, "/", stackmap.n, " ", locals.n, " words ", locals.bytedata, "\n")
print(" locals ", stackid, "/", stkmap.n, " ", locals.n, " words ", locals.bytedata, "\n")
}
} else if stackDebug >= 3 && debug {
print(" no locals to adjust\n")