
* proc: support new Go 1.17 panic/defer mechanism Go 1.17 will create wrappers for deferred calls that take arguments. Change defer reading code so that wrappers are automatically unwrapped. Also the deferred function is called directly by runtime.gopanic, without going through runtime.callN which means that sometimes when a panic happens the stack is either: 0. deferred function call 1. deferred call wrapper 2. runtime.gopanic or: 0. deferred function call 1. runtime.gopanic instead of always being: 0. deferred function call 1. runtime.callN 2. runtime.gopanic the isPanicCall check is changed accordingly. * test: miscellaneous minor test fixes for Go 1.17 * proc: resolve inlined calls when stepping out of runtime.breakpoint Calls to runtime.Breakpoint are inlined in Go 1.17 when inlining is enabled, resolve inlined calls in stepInstructionOut. * proc: add support for debugCallV2 with regabi This change adds support for the new debug call protocol which had to change for the new register ABI introduced in Go 1.17. Summary of changes: - Abstracts over the debug call version depending on the Go version found in the binary. - Uses R12 instead of RAX as the debug protocol register when the binary is from Go 1.17 or later. - Creates a variable directly from the DWARF entry for function arguments to support passing arguments however the ABI expects. - Computes a very conservative stack frame size for the call when injecting a call into a Go process whose version is >=1.17. Co-authored-by: Michael Anthony Knyszek <mknyszek@google.com> Co-authored-by: Alessandro Arzilli <alessandro.arzilli@gmail.com> * TeamCity: enable tests on go-tip * goversion: version compatibility bump * TeamCity: fix go-tip builds on macOS/arm64 Co-authored-by: Michael Anthony Knyszek <mknyszek@google.com>
213 lines
4.2 KiB
Go
213 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
var call = "this is a variable named `call`"
|
|
|
|
func callstacktrace() (stacktrace string) {
|
|
for skip := 0; ; skip++ {
|
|
pc, file, line, ok := runtime.Caller(skip)
|
|
if !ok {
|
|
break
|
|
}
|
|
fn := runtime.FuncForPC(pc)
|
|
stacktrace += fmt.Sprintf("in %s at %s:%d\n", fn.Name(), file, line)
|
|
}
|
|
return stacktrace
|
|
}
|
|
|
|
func call0(a, b int) {
|
|
fmt.Printf("call0: first: %d second: %d\n", a, b)
|
|
}
|
|
|
|
func call1(a, b int) int {
|
|
fmt.Printf("first: %d second: %d\n", a, b)
|
|
return a + b
|
|
}
|
|
|
|
func call2(a, b int) (int, int) {
|
|
fmt.Printf("call2: first: %d second: %d\n", a, b)
|
|
return a, b
|
|
}
|
|
|
|
func callexit() {
|
|
fmt.Printf("about to exit\n")
|
|
os.Exit(0)
|
|
}
|
|
|
|
func callpanic() {
|
|
fmt.Printf("about to panic\n")
|
|
panic("callpanic panicked")
|
|
}
|
|
|
|
func callbreak() {
|
|
fmt.Printf("about to break")
|
|
runtime.Breakpoint()
|
|
}
|
|
|
|
func stringsJoin(v []string, sep string) string {
|
|
// This is needed because strings.Join is in an optimized package and
|
|
// because of a bug in the compiler arguments of optimized functions don't
|
|
// have a location.
|
|
return strings.Join(v, sep)
|
|
}
|
|
|
|
type astruct struct {
|
|
X int
|
|
}
|
|
|
|
type a2struct struct {
|
|
Y int
|
|
}
|
|
|
|
func (a astruct) VRcvr(x int) string {
|
|
return fmt.Sprintf("%d + %d = %d", x, a.X, x+a.X)
|
|
}
|
|
|
|
func (pa *astruct) PRcvr(x int) string {
|
|
return fmt.Sprintf("%d - %d = %d", x, pa.X, x-pa.X)
|
|
}
|
|
|
|
type PRcvrable interface {
|
|
PRcvr(int) string
|
|
}
|
|
|
|
type VRcvrable interface {
|
|
VRcvr(int) string
|
|
}
|
|
|
|
var zero = 0
|
|
|
|
func makeclos(pa *astruct) func(int) string {
|
|
i := 0
|
|
return func(x int) string {
|
|
i++
|
|
return fmt.Sprintf("%d + %d + %d = %d", i, pa.X, x, i+pa.X+x)
|
|
}
|
|
}
|
|
|
|
var ga astruct
|
|
var globalPA2 *a2struct
|
|
|
|
func escapeArg(pa2 *a2struct) {
|
|
globalPA2 = pa2
|
|
}
|
|
|
|
func square(x int) int {
|
|
return x * x
|
|
}
|
|
|
|
func intcallpanic(a int) int {
|
|
if a == 0 {
|
|
panic("panic requested")
|
|
}
|
|
return a
|
|
}
|
|
|
|
func onetwothree(n int) []int {
|
|
return []int{n + 1, n + 2, n + 3}
|
|
}
|
|
|
|
func curriedAdd(n int) func(int) int {
|
|
return func(m int) int {
|
|
return n + m
|
|
}
|
|
}
|
|
|
|
func getAStruct(n int) astruct {
|
|
return astruct{X: n}
|
|
}
|
|
|
|
func getAStructPtr(n int) *astruct {
|
|
return &astruct{X: n}
|
|
}
|
|
|
|
func getVRcvrableFromAStruct(n int) VRcvrable {
|
|
return astruct{X: n}
|
|
}
|
|
|
|
func getPRcvrableFromAStructPtr(n int) PRcvrable {
|
|
return &astruct{X: n}
|
|
}
|
|
|
|
func getVRcvrableFromAStructPtr(n int) VRcvrable {
|
|
return &astruct{X: n}
|
|
}
|
|
|
|
func noreturncall(n int) {
|
|
return
|
|
}
|
|
|
|
type Base struct {
|
|
y int
|
|
}
|
|
|
|
type Derived struct {
|
|
x int
|
|
Base
|
|
}
|
|
|
|
func (b *Base) Method() int {
|
|
return b.y
|
|
}
|
|
|
|
type X int
|
|
|
|
func (_ X) CallMe() {
|
|
println("foo")
|
|
}
|
|
|
|
type X2 int
|
|
|
|
func (_ X2) CallMe(i int) int {
|
|
return i * i
|
|
}
|
|
|
|
func regabistacktest(s1, s2, s3, s4, s5 string, n uint8) (string, string, string, string, string, uint8) {
|
|
return s1 + s2, s2 + s3, s3 + s4, s4 + s5, s5 + s1, 2 * n
|
|
}
|
|
|
|
func regabistacktest2(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10 int) (int, int, int, int, int, int, int, int, int, int) {
|
|
return n1 + n2, n2 + n3, n3 + n4, n4 + n5, n5 + n6, n6 + n7, n7 + n8, n8 + n9, n9 + n10, n10 + n1
|
|
}
|
|
|
|
func main() {
|
|
one, two := 1, 2
|
|
intslice := []int{1, 2, 3}
|
|
stringslice := []string{"one", "two", "three"}
|
|
comma := ","
|
|
a := astruct{X: 3}
|
|
pa := &astruct{X: 6}
|
|
a2 := a2struct{Y: 7}
|
|
var pa2 *astruct
|
|
var str string = "old string value"
|
|
longstrs := []string{"very long string 0123456789a0123456789b0123456789c0123456789d0123456789e0123456789f0123456789g012345678h90123456789i0123456789j0123456789"}
|
|
var vable_a VRcvrable = a
|
|
var vable_pa VRcvrable = pa
|
|
var pable_pa PRcvrable = pa
|
|
var x X = 2
|
|
var x2 X2 = 2
|
|
|
|
fn2clos := makeclos(pa)
|
|
fn2glob := call1
|
|
fn2valmeth := pa.VRcvr
|
|
fn2ptrmeth := pa.PRcvr
|
|
var fn2nil func()
|
|
|
|
d := &Derived{3, Base{4}}
|
|
|
|
runtime.Breakpoint() // breakpoint here
|
|
call1(one, two)
|
|
fn2clos(2)
|
|
strings.LastIndexByte(stringslice[1], 'w')
|
|
d.Method()
|
|
d.Base.Method()
|
|
x.CallMe()
|
|
fmt.Println(one, two, zero, call, call0, call2, callexit, callpanic, callbreak, callstacktrace, stringsJoin, intslice, stringslice, comma, a.VRcvr, a.PRcvr, pa, vable_a, vable_pa, pable_pa, fn2clos, fn2glob, fn2valmeth, fn2ptrmeth, fn2nil, ga, escapeArg, a2, square, intcallpanic, onetwothree, curriedAdd, getAStruct, getAStructPtr, getVRcvrableFromAStruct, getPRcvrableFromAStructPtr, getVRcvrableFromAStructPtr, pa2, noreturncall, str, d, x, x2.CallMe(5), longstrs, regabistacktest, regabistacktest2)
|
|
}
|