mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-01 03:42:59 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			3293 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			3293 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package proc
 | |
| 
 | |
| import (
 | |
| 	"debug/dwarf"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/constant"
 | |
| 	"go/parser"
 | |
| 	"go/token"
 | |
| 	"reflect"
 | |
| 	"runtime/debug"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/go-delve/delve/pkg/astutil"
 | |
| 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | |
| 	"github.com/go-delve/delve/pkg/dwarf/op"
 | |
| 	"github.com/go-delve/delve/pkg/dwarf/reader"
 | |
| 	"github.com/go-delve/delve/pkg/goversion"
 | |
| 	"github.com/go-delve/delve/pkg/logflags"
 | |
| 	"github.com/go-delve/delve/pkg/proc/evalop"
 | |
| )
 | |
| 
 | |
| var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
 | |
| 
 | |
| const (
 | |
| 	goDictionaryName = ".dict"
 | |
| 	goClosurePtr     = ".closureptr"
 | |
| )
 | |
| 
 | |
| // EvalScope is the scope for variable evaluation. Contains the thread,
 | |
| // current location (PC), and canonical frame address.
 | |
| type EvalScope struct {
 | |
| 	Location
 | |
| 	Regs     op.DwarfRegisters
 | |
| 	Mem      MemoryReadWriter // Target's memory
 | |
| 	g        *G
 | |
| 	threadID int
 | |
| 	BinInfo  *BinaryInfo
 | |
| 	target   *Target
 | |
| 	loadCfg  *LoadConfig
 | |
| 
 | |
| 	frameOffset int64
 | |
| 
 | |
| 	// When the following pointer is not nil this EvalScope was created
 | |
| 	// by EvalExpressionWithCalls and function call injection are allowed.
 | |
| 	// See the top comment in fncall.go for a description of how the call
 | |
| 	// injection protocol is handled.
 | |
| 	callCtx *callContext
 | |
| 
 | |
| 	dictAddr uint64 // dictionary address for instantiated generic functions
 | |
| 
 | |
| 	enclosingRangeScopes []*EvalScope
 | |
| 	rangeFrames          []Stackframe
 | |
| }
 | |
| 
 | |
| type localsFlags uint8
 | |
| 
 | |
| const (
 | |
| 	// If localsTrustArgOrder is set function arguments that don't have an
 | |
| 	// address will have one assigned by looking at their position in the argument
 | |
| 	// list.
 | |
| 	localsTrustArgOrder localsFlags = 1 << iota
 | |
| 
 | |
| 	// If localsNoDeclLineCheck the declaration line isn't checked at
 | |
| 	// all to determine if the variable is in scope.
 | |
| 	localsNoDeclLineCheck
 | |
| 
 | |
| 	// If localsOnlyRangeBodyClosures is set simpleLocals only returns
 | |
| 	// variables containing the range body closure.
 | |
| 	localsOnlyRangeBodyClosures
 | |
| 
 | |
| 	// If localsIsRangeBody is set DW_AT_formal_parameter variables will be
 | |
| 	// considered local variables.
 | |
| 	localsIsRangeBody
 | |
| 
 | |
| 	// If localsFakeFunctionEntryScope is set the current scope was generated by fakeFunctionEntryScope
 | |
| 	localsFakeFunctionEntryScope
 | |
| )
 | |
| 
 | |
| // ConvertEvalScope returns a new EvalScope in the context of the
 | |
| // specified goroutine ID and stack frame.
 | |
| // If deferCall is > 0 the eval scope will be relative to the specified deferred call.
 | |
| func ConvertEvalScope(dbp *Target, gid int64, frame, deferCall int) (*EvalScope, error) {
 | |
| 	if _, err := dbp.Valid(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	ct := dbp.CurrentThread()
 | |
| 	threadID := ct.ThreadID()
 | |
| 	g, err := FindGoroutine(dbp, gid)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var opts StacktraceOptions
 | |
| 	if deferCall > 0 {
 | |
| 		opts = StacktraceReadDefers
 | |
| 	}
 | |
| 
 | |
| 	var locs []Stackframe
 | |
| 	if g != nil {
 | |
| 		if g.Thread != nil {
 | |
| 			threadID = g.Thread.ThreadID()
 | |
| 		}
 | |
| 		locs, err = GoroutineStacktrace(dbp, g, frame+1, opts)
 | |
| 	} else {
 | |
| 		locs, err = ThreadStacktrace(dbp, ct, frame+1)
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if frame >= len(locs) {
 | |
| 		return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
 | |
| 	}
 | |
| 
 | |
| 	if deferCall > 0 {
 | |
| 		if deferCall-1 >= len(locs[frame].Defers) {
 | |
| 			return nil, fmt.Errorf("Frame %d only has %d deferred calls", frame, len(locs[frame].Defers))
 | |
| 		}
 | |
| 
 | |
| 		d := locs[frame].Defers[deferCall-1]
 | |
| 		if d.Unreadable != nil {
 | |
| 			return nil, d.Unreadable
 | |
| 		}
 | |
| 
 | |
| 		return d.EvalScope(dbp, ct)
 | |
| 	}
 | |
| 
 | |
| 	return FrameToScope(dbp, dbp.Memory(), g, threadID, locs[frame:]...), nil
 | |
| }
 | |
| 
 | |
| // FrameToScope returns a new EvalScope for frames[0].
 | |
| // If frames has at least two elements all memory between
 | |
| // frames[0].Regs.SP() and frames[1].Regs.CFA will be cached.
 | |
| // Otherwise all memory between frames[0].Regs.SP() and frames[0].Regs.CFA
 | |
| // will be cached.
 | |
| func FrameToScope(t *Target, thread MemoryReadWriter, g *G, threadID int, frames ...Stackframe) *EvalScope {
 | |
| 	// Creates a cacheMem that will preload the entire stack frame the first
 | |
| 	// time any local variable is read.
 | |
| 	// Remember that the stack grows downward in memory.
 | |
| 	minaddr := frames[0].Regs.SP()
 | |
| 	var maxaddr uint64
 | |
| 	if len(frames) > 1 && frames[0].SystemStack == frames[1].SystemStack {
 | |
| 		maxaddr = uint64(frames[1].Regs.CFA)
 | |
| 	} else {
 | |
| 		maxaddr = uint64(frames[0].Regs.CFA)
 | |
| 	}
 | |
| 	if maxaddr > minaddr && maxaddr-minaddr < maxFramePrefetchSize {
 | |
| 		thread = cacheMemory(thread, minaddr, int(maxaddr-minaddr))
 | |
| 	}
 | |
| 
 | |
| 	s := &EvalScope{Location: frames[0].Call, Regs: frames[0].Regs, Mem: thread, g: g, BinInfo: t.BinInfo(), target: t, frameOffset: frames[0].FrameOffset(), threadID: threadID}
 | |
| 	s.PC = frames[0].lastpc
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| // ThreadScope returns an EvalScope for the given thread.
 | |
| func ThreadScope(t *Target, thread Thread) (*EvalScope, error) {
 | |
| 	locations, err := ThreadStacktrace(t, thread, 1)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if len(locations) < 1 {
 | |
| 		return nil, errors.New("could not decode first frame")
 | |
| 	}
 | |
| 	return FrameToScope(t, thread.ProcessMemory(), nil, thread.ThreadID(), locations...), nil
 | |
| }
 | |
| 
 | |
| // GoroutineScope returns an EvalScope for the goroutine running on the given thread.
 | |
| func GoroutineScope(t *Target, thread Thread) (*EvalScope, error) {
 | |
| 	locations, err := ThreadStacktrace(t, thread, 1)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if len(locations) < 1 {
 | |
| 		return nil, errors.New("could not decode first frame")
 | |
| 	}
 | |
| 	g, err := GetG(thread)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	threadID := 0
 | |
| 	if g.Thread != nil {
 | |
| 		threadID = g.Thread.ThreadID()
 | |
| 	}
 | |
| 	return FrameToScope(t, thread.ProcessMemory(), g, threadID, locations...), nil
 | |
| }
 | |
| 
 | |
| func (scope *EvalScope) evalopFlags() evalop.Flags {
 | |
| 	flags := evalop.Flags(0)
 | |
| 	if scope.BinInfo.hasDebugPinner() {
 | |
| 		flags |= evalop.HasDebugPinner
 | |
| 	}
 | |
| 	return flags
 | |
| }
 | |
| 
 | |
| // EvalExpression returns the value of the given expression.
 | |
| func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
 | |
| 	ops, err := evalop.Compile(scopeToEvalLookup{scope}, expr, scope.evalopFlags())
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	stack := &evalStack{}
 | |
| 
 | |
| 	scope.loadCfg = &cfg
 | |
| 	stack.eval(scope, ops)
 | |
| 	ev, err := stack.result(&cfg)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	ev.loadValue(cfg)
 | |
| 	if ev.Name == "" {
 | |
| 		ev.Name = expr
 | |
| 	}
 | |
| 	return ev, nil
 | |
| }
 | |
| 
 | |
| type scopeToEvalLookup struct {
 | |
| 	*EvalScope
 | |
| }
 | |
| 
 | |
| func (s scopeToEvalLookup) FindTypeExpr(expr ast.Expr) (godwarf.Type, error) {
 | |
| 	return s.BinInfo.findTypeExpr(expr)
 | |
| }
 | |
| 
 | |
| func (scope scopeToEvalLookup) HasBuiltin(name string) bool {
 | |
| 	return supportedBuiltins[name] != nil
 | |
| }
 | |
| 
 | |
| func (scope scopeToEvalLookup) PtrSize() int {
 | |
| 	return scope.BinInfo.Arch.ptrSize
 | |
| }
 | |
| 
 | |
| // ChanGoroutines returns the list of goroutines waiting to receive from or
 | |
| // send to the channel.
 | |
| func (scope *EvalScope) ChanGoroutines(expr string, start, count int) ([]int64, error) {
 | |
| 	t, err := parser.ParseExpr(expr)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	v, err := scope.evalAST(t)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if v.Kind != reflect.Chan {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	structMemberMulti := func(v *Variable, names ...string) *Variable {
 | |
| 		for _, name := range names {
 | |
| 			var err error
 | |
| 			v, err = v.structMember(name)
 | |
| 			if err != nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 		}
 | |
| 		return v
 | |
| 	}
 | |
| 
 | |
| 	waitqFirst := func(qname string) *Variable {
 | |
| 		qvar := structMemberMulti(v, qname, "first")
 | |
| 		if qvar == nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 		return qvar.maybeDereference()
 | |
| 	}
 | |
| 
 | |
| 	var goids []int64
 | |
| 
 | |
| 	waitqToGoIDSlice := func(qvar *Variable) error {
 | |
| 		if qvar == nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 		for {
 | |
| 			if qvar.Addr == 0 {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if len(goids) > count {
 | |
| 				return nil
 | |
| 			}
 | |
| 			goidVar := structMemberMulti(qvar, "g", "goid")
 | |
| 			if goidVar == nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			goidVar.loadValue(loadSingleValue)
 | |
| 			if goidVar.Unreadable != nil {
 | |
| 				return goidVar.Unreadable
 | |
| 			}
 | |
| 			goid, _ := constant.Int64Val(goidVar.Value)
 | |
| 			if start > 0 {
 | |
| 				start--
 | |
| 			} else {
 | |
| 				goids = append(goids, goid)
 | |
| 			}
 | |
| 
 | |
| 			nextVar, err := qvar.structMember("next")
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			qvar = nextVar.maybeDereference()
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	recvqVar := waitqFirst("recvq")
 | |
| 	err = waitqToGoIDSlice(recvqVar)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	sendqVar := waitqFirst("sendq")
 | |
| 	err = waitqToGoIDSlice(sendqVar)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return goids, nil
 | |
| }
 | |
| 
 | |
| // Locals returns all variables in 'scope' named wantedName, or all of them
 | |
| // if wantedName is "".
 | |
| // If scope is the scope for a range-over-func closure body it will merge in
 | |
| // the scopes of the enclosing functions.
 | |
| func (scope *EvalScope) Locals(flags localsFlags, wantedName string) ([]*Variable, error) {
 | |
| 	var scopes [][]*Variable
 | |
| 	filter := func(vars []*Variable) []*Variable {
 | |
| 		if wantedName == "" || vars == nil {
 | |
| 			return vars
 | |
| 		}
 | |
| 		vars2 := []*Variable{}
 | |
| 		for _, v := range vars {
 | |
| 			if v.Name == wantedName {
 | |
| 				vars2 = append(vars2, v)
 | |
| 			}
 | |
| 		}
 | |
| 		return vars2
 | |
| 	}
 | |
| 
 | |
| 	rangeBodyFlags := localsFlags(0)
 | |
| 	if scope.Fn != nil && scope.Fn.rangeParentName() != "" {
 | |
| 		rangeBodyFlags = localsFlags(localsIsRangeBody)
 | |
| 	}
 | |
| 
 | |
| 	vars0, err := scope.simpleLocals(flags|rangeBodyFlags, wantedName)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	vars0 = filter(vars0)
 | |
| 	if scope.Fn.extra(scope.BinInfo).rangeParent == nil || scope.target == nil || scope.g == nil {
 | |
| 		return vars0, nil
 | |
| 	}
 | |
| 	if wantedName != "" && len(vars0) > 0 {
 | |
| 		return vars0, nil
 | |
| 	}
 | |
| 
 | |
| 	scopes = append(scopes, vars0)
 | |
| 
 | |
| 	if scope.rangeFrames == nil {
 | |
| 		err := scope.setupRangeFrames()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 	for i, scope2 := range scope.enclosingRangeScopes {
 | |
| 		if scope2 == nil {
 | |
| 			scope2 = FrameToScope(scope.target, scope.target.Memory(), scope.g, scope.threadID, scope.rangeFrames[2*i:]...)
 | |
| 			scope.enclosingRangeScopes[i] = scope2
 | |
| 		}
 | |
| 		rangeBodyFlags := localsFlags(localsIsRangeBody)
 | |
| 		if i == len(scope.enclosingRangeScopes)-1 {
 | |
| 			rangeBodyFlags = 0
 | |
| 		}
 | |
| 		vars, err := scope2.simpleLocals(flags|rangeBodyFlags, wantedName)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		vars = filter(vars)
 | |
| 		scopes = append(scopes, vars)
 | |
| 		if wantedName != "" && len(vars) > 0 {
 | |
| 			return vars, nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	vars := []*Variable{}
 | |
| 	for i := len(scopes) - 1; i >= 0; i-- {
 | |
| 		vars = append(vars, scopes[i]...)
 | |
| 	}
 | |
| 
 | |
| 	// Apply shadowning
 | |
| 	lvn := map[string]*Variable{}
 | |
| 	for _, v := range vars {
 | |
| 		if otherv := lvn[v.Name]; otherv != nil {
 | |
| 			otherv.Flags |= VariableShadowed
 | |
| 		}
 | |
| 		lvn[v.Name] = v
 | |
| 	}
 | |
| 	return vars, nil
 | |
| }
 | |
| 
 | |
| func (scope *EvalScope) setupRangeFrames() error {
 | |
| 	var err error
 | |
| 	scope.rangeFrames, err = rangeFuncStackTrace(scope.target, scope.g)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if len(scope.rangeFrames) > 0 {
 | |
| 		scope.rangeFrames = scope.rangeFrames[2:] // skip the first frame and its return frame
 | |
| 	}
 | |
| 	scope.enclosingRangeScopes = make([]*EvalScope, len(scope.rangeFrames)/2)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // simpleLocals returns all local variables in 'scope'.
 | |
| // This function does not try to merge the scopes of range-over-func closure
 | |
| // bodies with their enclosing function, for that use (*EvalScope).Locals or
 | |
| // (*EvalScope).FindLocal instead.
 | |
| // If wantedName is specified only variables called wantedName or "&"+wantedName are returned.
 | |
| func (scope *EvalScope) simpleLocals(flags localsFlags, wantedName string) ([]*Variable, error) {
 | |
| 	if scope.Fn == nil {
 | |
| 		return nil, errors.New("unable to find function context")
 | |
| 	}
 | |
| 
 | |
| 	if scope.image().Stripped() {
 | |
| 		return nil, errors.New("unable to find locals: no debug information present in binary")
 | |
| 	}
 | |
| 
 | |
| 	trustArgOrder := (flags&localsTrustArgOrder != 0) && scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 12) && scope.Fn != nil && (scope.PC == scope.Fn.Entry)
 | |
| 
 | |
| 	dwarfTree, err := scope.image().getDwarfTree(scope.Fn.offset)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	variablesFlags := reader.VariablesOnlyVisible | reader.VariablesSkipInlinedSubroutines
 | |
| 	if flags&localsNoDeclLineCheck != 0 {
 | |
| 		variablesFlags = reader.VariablesNoDeclLineCheck
 | |
| 	}
 | |
| 	if scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 15) {
 | |
| 		variablesFlags |= reader.VariablesTrustDeclLine
 | |
| 	}
 | |
| 
 | |
| 	varEntries := reader.Variables(dwarfTree, scope.PC, scope.Line, variablesFlags)
 | |
| 
 | |
| 	// look for dictionary entry
 | |
| 	if scope.dictAddr == 0 {
 | |
| 		scope.dictAddr = readLocalPtrVar(dwarfTree, goDictionaryName, scope.target, scope.BinInfo, scope.image(), scope.Regs, scope.Mem)
 | |
| 	}
 | |
| 
 | |
| 	var closureStruct *Variable
 | |
| 
 | |
| 	vars := make([]*Variable, 0, len(varEntries))
 | |
| 	depths := make([]int, 0, len(varEntries))
 | |
| 	for _, entry := range varEntries {
 | |
| 		name, _ := entry.Val(dwarf.AttrName).(string)
 | |
| 		switch {
 | |
| 		case wantedName != "":
 | |
| 			if name != wantedName && name != "&"+wantedName {
 | |
| 				continue
 | |
| 			}
 | |
| 		case flags&localsOnlyRangeBodyClosures != 0:
 | |
| 			if !strings.HasPrefix(name, "#yield") && !strings.HasPrefix(name, "&#yield") {
 | |
| 				continue
 | |
| 			}
 | |
| 		default:
 | |
| 			if name == goDictionaryName || name == goClosurePtr || strings.HasPrefix(name, "#yield") || strings.HasPrefix(name, "&#yield") {
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 		isCapturedVar := entry.Val(godwarf.AttrGoClosureOffset) != nil
 | |
| 		if scope.Fn.rangeParentName() != "" && (strings.HasPrefix(name, "~") || isCapturedVar) {
 | |
| 			// Skip unnamed parameters and closure variables for range-over-func closure bodies
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		val, err := extractVarInfoFromEntry(scope.target, scope.BinInfo, scope.image(), scope.Regs, scope.Mem, entry.Tree, scope.dictAddr)
 | |
| 		if err != nil {
 | |
| 			// skip variables that we can't parse yet
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if trustArgOrder && ((val.Unreadable != nil && val.Addr == 0) || val.Flags&VariableFakeAddress != 0) && entry.Tag == dwarf.TagFormalParameter {
 | |
| 			addr := afterLastArgAddr(vars)
 | |
| 			if addr == 0 {
 | |
| 				addr = uint64(scope.Regs.CFA)
 | |
| 			}
 | |
| 			addr = uint64(alignAddr(int64(addr), val.DwarfType.Align()))
 | |
| 			val = newVariable(val.Name, addr, val.DwarfType, scope.BinInfo, scope.Mem)
 | |
| 		}
 | |
| 
 | |
| 		if isCapturedVar && int(val.DeclLine) == scope.Line && scope.Fn != nil && entry.Tag == dwarf.TagVariable && (flags&localsFakeFunctionEntryScope == 0) {
 | |
| 			// For variables captured by closures if we are "early" in the function
 | |
| 			// read  the value from the closure struct instead of their location.
 | |
| 			// First check that we are actually between the entry point of the
 | |
| 			// function and the end of the prologue in case the code has not been
 | |
| 			// gofmt'd.
 | |
| 			firstPCAfterPrologue := uint64(0)
 | |
| 			if scope.Fn.cu.lineInfo != nil {
 | |
| 				fn := scope.Fn
 | |
| 				firstPCAfterPrologue, _, _, _ = fn.cu.lineInfo.PrologueEndPC(fn.Entry, fn.End)
 | |
| 			}
 | |
| 			if firstPCAfterPrologue < scope.Fn.Entry {
 | |
| 				firstPCAfterPrologue = scope.Fn.Entry
 | |
| 			}
 | |
| 			if scope.PC >= scope.Fn.Entry && scope.PC <= firstPCAfterPrologue {
 | |
| 				cst := scope.Fn.extra(scope.BinInfo).closureStructType
 | |
| 				if closureStruct == nil {
 | |
| 					closureStruct = newVariable(scope.Fn.Name, scope.Regs.Uint64Val(scope.BinInfo.Arch.ContextRegNum), cst, scope.BinInfo, scope.Mem)
 | |
| 				}
 | |
| 				for i := range cst.Field {
 | |
| 					if cst.Field[i].Name == name {
 | |
| 						val2, _ := closureStruct.toField(cst.Field[i])
 | |
| 						if val2 != nil && val2.Unreadable == nil {
 | |
| 							val2.Name = name
 | |
| 							val = val2
 | |
| 						}
 | |
| 						break
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		vars = append(vars, val)
 | |
| 		depth := entry.Depth
 | |
| 		if (flags&localsIsRangeBody == 0) && (entry.Tag == dwarf.TagFormalParameter) {
 | |
| 			if depth <= 1 {
 | |
| 				depth = 0
 | |
| 			}
 | |
| 			isret, _ := entry.Val(dwarf.AttrVarParam).(bool)
 | |
| 			if isret {
 | |
| 				val.Flags |= VariableReturnArgument
 | |
| 			} else {
 | |
| 				val.Flags |= VariableArgument
 | |
| 			}
 | |
| 		}
 | |
| 		depths = append(depths, depth)
 | |
| 	}
 | |
| 
 | |
| 	if len(vars) == 0 {
 | |
| 		return vars, nil
 | |
| 	}
 | |
| 
 | |
| 	sort.Stable(&variablesByDepthAndDeclLine{vars, depths})
 | |
| 
 | |
| 	lvn := map[string]*Variable{} // lvn[n] is the last variable we saw named n
 | |
| 
 | |
| 	for i, v := range vars {
 | |
| 		if name := v.Name; len(name) > 1 && name[0] == '&' {
 | |
| 			locationExpr := v.LocationExpr
 | |
| 			declLine := v.DeclLine
 | |
| 			v = v.maybeDereference()
 | |
| 			if v.Addr == 0 && v.Unreadable == nil {
 | |
| 				v.Unreadable = errors.New("no address for escaped variable")
 | |
| 			}
 | |
| 			v.Name = name[1:]
 | |
| 			v.Flags |= VariableEscaped
 | |
| 			// See https://github.com/go-delve/delve/issues/2049 for details
 | |
| 			if locationExpr != nil {
 | |
| 				locationExpr.isEscaped = true
 | |
| 				v.LocationExpr = locationExpr
 | |
| 			}
 | |
| 			v.DeclLine = declLine
 | |
| 			vars[i] = v
 | |
| 		}
 | |
| 		if otherv := lvn[v.Name]; otherv != nil {
 | |
| 			otherv.Flags |= VariableShadowed
 | |
| 		}
 | |
| 		lvn[v.Name] = v
 | |
| 	}
 | |
| 
 | |
| 	return vars, nil
 | |
| }
 | |
| 
 | |
| func afterLastArgAddr(vars []*Variable) uint64 {
 | |
| 	for i := len(vars) - 1; i >= 0; i-- {
 | |
| 		v := vars[i]
 | |
| 		if (v.Flags&VariableArgument != 0) || (v.Flags&VariableReturnArgument != 0) {
 | |
| 			return v.Addr + uint64(v.DwarfType.Size())
 | |
| 		}
 | |
| 	}
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| // readLocalPtrVar reads the value of the local pointer variable vname. This
 | |
| // is a low level helper function, it does not support nested scopes, range
 | |
| // resolution across range bodies, type parameters, &c...
 | |
| func readLocalPtrVar(dwarfTree *godwarf.Tree, vname string, tgt *Target, bi *BinaryInfo, image *Image, regs op.DwarfRegisters, mem MemoryReadWriter) uint64 {
 | |
| 	for _, entry := range dwarfTree.Children {
 | |
| 		name, _ := entry.Val(dwarf.AttrName).(string)
 | |
| 		if name == vname {
 | |
| 			v, err := extractVarInfoFromEntry(tgt, bi, image, regs, mem, entry, 0)
 | |
| 			if err != nil {
 | |
| 				logflags.DebuggerLogger().Errorf("could not load %s variable: %v", name, err)
 | |
| 			} else if v.Unreadable != nil {
 | |
| 				logflags.DebuggerLogger().Errorf("could not load %s variable: %v", name, v.Unreadable)
 | |
| 			} else {
 | |
| 				r, err := readUintRaw(v.mem, v.Addr, int64(bi.Arch.PtrSize()))
 | |
| 				if err != nil {
 | |
| 					logflags.DebuggerLogger().Errorf("could not load %s variable: %v", name, err)
 | |
| 				}
 | |
| 				return r
 | |
| 			}
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| // setValue writes the value of srcv to dstv.
 | |
| //   - If srcv is a numerical literal constant and srcv is of a compatible type
 | |
| //     the necessary type conversion is performed.
 | |
| //   - If srcv is nil and dstv is of a nil'able type then dstv is nilled.
 | |
| //   - If srcv is the empty string and dstv is a string then dstv is set to the
 | |
| //     empty string.
 | |
| //   - If dstv is an "interface {}" and srcv is either an interface (possibly
 | |
| //     non-empty) or a pointer shaped type (map, channel, pointer or struct
 | |
| //     containing a single pointer field) the type conversion to "interface {}"
 | |
| //     is performed.
 | |
| //   - If srcv and dstv have the same type and are both addressable then the
 | |
| //     contents of srcv are copied byte-by-byte into dstv
 | |
| func (scope *EvalScope) setValue(dstv, srcv *Variable, srcExpr string) error {
 | |
| 	srcv.loadValue(loadSingleValue)
 | |
| 
 | |
| 	typerr := srcv.isType(dstv.RealType, dstv.Kind)
 | |
| 	if _, isTypeConvErr := typerr.(*typeConvErr); isTypeConvErr {
 | |
| 		// attempt iface -> eface and ptr-shaped -> eface conversions.
 | |
| 		return convertToEface(srcv, dstv)
 | |
| 	}
 | |
| 	if typerr != nil {
 | |
| 		return typerr
 | |
| 	}
 | |
| 
 | |
| 	if srcv.Unreadable != nil {
 | |
| 		//lint:ignore ST1005 backwards compatibility
 | |
| 		return fmt.Errorf("Expression %q is unreadable: %v", srcExpr, srcv.Unreadable)
 | |
| 	}
 | |
| 
 | |
| 	// Numerical types
 | |
| 	switch dstv.Kind {
 | |
| 	case reflect.Float32, reflect.Float64:
 | |
| 		f, _ := constant.Float64Val(srcv.Value)
 | |
| 		return dstv.writeFloatRaw(f, dstv.RealType.Size())
 | |
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 		n, _ := constant.Int64Val(srcv.Value)
 | |
| 		return dstv.writeUint(uint64(n), dstv.RealType.Size())
 | |
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | |
| 		n, _ := constant.Uint64Val(srcv.Value)
 | |
| 		return dstv.writeUint(n, dstv.RealType.Size())
 | |
| 	case reflect.Bool:
 | |
| 		return dstv.writeBool(constant.BoolVal(srcv.Value))
 | |
| 	case reflect.Complex64, reflect.Complex128:
 | |
| 		real, _ := constant.Float64Val(constant.Real(srcv.Value))
 | |
| 		imag, _ := constant.Float64Val(constant.Imag(srcv.Value))
 | |
| 		return dstv.writeComplex(real, imag, dstv.RealType.Size())
 | |
| 	case reflect.Func:
 | |
| 		if dstv.RealType.Size() == 0 {
 | |
| 			if dstv.Name != "" {
 | |
| 				return fmt.Errorf("can not assign to %s", dstv.Name)
 | |
| 			}
 | |
| 			return errors.New("can not assign to function expression")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// nilling nillable variables
 | |
| 	if srcv == nilVariable {
 | |
| 		return dstv.writeZero()
 | |
| 	}
 | |
| 
 | |
| 	if srcv.Kind == reflect.String {
 | |
| 		if srcv.Base == 0 && srcv.Len > 0 && srcv.Flags&VariableConstant != 0 {
 | |
| 			return errFuncCallNotAllowedStrAlloc
 | |
| 		}
 | |
| 		return dstv.writeString(uint64(srcv.Len), srcv.Base)
 | |
| 	}
 | |
| 
 | |
| 	// slice assignment (this is not handled by the writeCopy below so that
 | |
| 	// results of a reslice operation can be used here).
 | |
| 	if srcv.Kind == reflect.Slice {
 | |
| 		return dstv.writeSlice(srcv.Len, srcv.Cap, srcv.Base)
 | |
| 	}
 | |
| 
 | |
| 	// allow any integer to be converted to any pointer
 | |
| 	if t, isptr := dstv.RealType.(*godwarf.PtrType); isptr {
 | |
| 		return dstv.writeUint(srcv.Children[0].Addr, t.ByteSize)
 | |
| 	}
 | |
| 
 | |
| 	// byte-by-byte copying for everything else, but the source must be addressable
 | |
| 	if srcv.Addr != 0 {
 | |
| 		return dstv.writeCopy(srcv)
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Errorf("can not set variables of type %s (not implemented)", dstv.Kind.String())
 | |
| }
 | |
| 
 | |
| // SetVariable sets the value of the named variable
 | |
| func (scope *EvalScope) SetVariable(name, value string) error {
 | |
| 	ops, err := evalop.CompileSet(scopeToEvalLookup{scope}, name, value, scope.evalopFlags())
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	stack := &evalStack{}
 | |
| 	stack.eval(scope, ops)
 | |
| 	_, err = stack.result(nil)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // LocalVariables returns all local variables from the current function scope.
 | |
| func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
 | |
| 	vars, err := scope.Locals(0, "")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	vars = filterVariables(vars, func(v *Variable) bool {
 | |
| 		return (v.Flags & (VariableArgument | VariableReturnArgument)) == 0
 | |
| 	})
 | |
| 	cfg.MaxMapBuckets = maxMapBucketsFactor * cfg.MaxArrayValues
 | |
| 	loadValues(vars, cfg)
 | |
| 	return vars, nil
 | |
| }
 | |
| 
 | |
| // FunctionArguments returns the name, value, and type of all current function arguments.
 | |
| func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
 | |
| 	vars, err := scope.Locals(0, "")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	vars = filterVariables(vars, func(v *Variable) bool {
 | |
| 		return (v.Flags & (VariableArgument | VariableReturnArgument)) != 0
 | |
| 	})
 | |
| 	cfg.MaxMapBuckets = maxMapBucketsFactor * cfg.MaxArrayValues
 | |
| 	loadValues(vars, cfg)
 | |
| 	return vars, nil
 | |
| }
 | |
| 
 | |
| func filterVariables(vars []*Variable, pred func(v *Variable) bool) []*Variable {
 | |
| 	r := make([]*Variable, 0, len(vars))
 | |
| 	for i := range vars {
 | |
| 		if pred(vars[i]) {
 | |
| 			r = append(r, vars[i])
 | |
| 		}
 | |
| 	}
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| func regsReplaceStaticBase(regs op.DwarfRegisters, image *Image) op.DwarfRegisters {
 | |
| 	regs.StaticBase = image.StaticBase
 | |
| 	return regs
 | |
| }
 | |
| 
 | |
| // PackageVariables returns the name, value, and type of all package variables in the application.
 | |
| func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
 | |
| 	pkgvars := make([]packageVar, len(scope.BinInfo.packageVars))
 | |
| 	copy(pkgvars, scope.BinInfo.packageVars)
 | |
| 	sort.Slice(pkgvars, func(i, j int) bool {
 | |
| 		if pkgvars[i].cu.image.addr == pkgvars[j].cu.image.addr {
 | |
| 			return pkgvars[i].offset < pkgvars[j].offset
 | |
| 		}
 | |
| 		return pkgvars[i].cu.image.addr < pkgvars[j].cu.image.addr
 | |
| 	})
 | |
| 	vars := make([]*Variable, 0, len(scope.BinInfo.packageVars))
 | |
| 	for _, pkgvar := range pkgvars {
 | |
| 		reader := pkgvar.cu.image.dwarfReader
 | |
| 		reader.Seek(pkgvar.offset)
 | |
| 		entry, err := reader.Next()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		// Ignore errors trying to extract values
 | |
| 		val, err := extractVarInfoFromEntry(scope.target, scope.BinInfo, pkgvar.cu.image, regsReplaceStaticBase(scope.Regs, pkgvar.cu.image), scope.Mem, godwarf.EntryToTree(entry), 0)
 | |
| 		if val != nil && val.Kind == reflect.Invalid {
 | |
| 			continue
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		val.loadValue(cfg)
 | |
| 		vars = append(vars, val)
 | |
| 	}
 | |
| 
 | |
| 	return vars, nil
 | |
| }
 | |
| 
 | |
| func (scope *EvalScope) findGlobal(pkgName, varName string) (*Variable, error) {
 | |
| 	for _, pkgPath := range scope.BinInfo.PackageMap[pkgName] {
 | |
| 		v, err := scope.findGlobalInternal(pkgPath + "." + varName)
 | |
| 		if err != nil || v != nil {
 | |
| 			return v, err
 | |
| 		}
 | |
| 	}
 | |
| 	v, err := scope.findGlobalInternal(pkgName + "." + varName)
 | |
| 	if err != nil || v != nil {
 | |
| 		return v, err
 | |
| 	}
 | |
| 	return nil, &errCouldNotFindSymbol{fmt.Sprintf("%s.%s", pkgName, varName)}
 | |
| }
 | |
| 
 | |
| type errCouldNotFindSymbol struct {
 | |
| 	name string
 | |
| }
 | |
| 
 | |
| func (e *errCouldNotFindSymbol) Error() string {
 | |
| 	return fmt.Sprintf("could not find symbol %s", e.name)
 | |
| }
 | |
| 
 | |
| func isSymbolNotFound(e error) bool {
 | |
| 	var e2 *errCouldNotFindSymbol
 | |
| 	return errors.As(e, &e2)
 | |
| }
 | |
| 
 | |
| func (scope *EvalScope) findGlobalInternal(name string) (*Variable, error) {
 | |
| 	for _, pkgvar := range scope.BinInfo.packageVars {
 | |
| 		if pkgvar.name == name || strings.HasSuffix(pkgvar.name, "/"+name) {
 | |
| 			reader := pkgvar.cu.image.dwarfReader
 | |
| 			reader.Seek(pkgvar.offset)
 | |
| 			entry, err := reader.Next()
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			return extractVarInfoFromEntry(scope.target, scope.BinInfo, pkgvar.cu.image, regsReplaceStaticBase(scope.Regs, pkgvar.cu.image), scope.Mem, godwarf.EntryToTree(entry), 0)
 | |
| 		}
 | |
| 	}
 | |
| 	for _, fn := range scope.BinInfo.Functions {
 | |
| 		if fn.Name == name || strings.HasSuffix(fn.Name, "/"+name) {
 | |
| 			//TODO(aarzilli): convert function entry into a function type?
 | |
| 			r := newVariable(fn.Name, fn.Entry, &godwarf.FuncType{}, scope.BinInfo, scope.Mem)
 | |
| 			r.Value = constant.MakeString(fn.Name)
 | |
| 			r.Base = fn.Entry
 | |
| 			r.loaded = true
 | |
| 			if fn.Entry == 0 {
 | |
| 				r.Unreadable = fmt.Errorf("function %s is inlined", fn.Name)
 | |
| 			}
 | |
| 			return r, nil
 | |
| 		}
 | |
| 	}
 | |
| 	for dwref, ctyp := range scope.BinInfo.consts {
 | |
| 		for _, cval := range ctyp.values {
 | |
| 			if cval.fullName == name || strings.HasSuffix(cval.fullName, "/"+name) {
 | |
| 				t, err := scope.BinInfo.Images[dwref.imageIndex].Type(dwref.offset)
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 				v := newVariable(name, 0x0, t, scope.BinInfo, scope.Mem)
 | |
| 				switch v.Kind {
 | |
| 				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 					v.Value = constant.MakeInt64(cval.value)
 | |
| 				case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | |
| 					v.Value = constant.MakeUint64(uint64(cval.value))
 | |
| 				default:
 | |
| 					return nil, fmt.Errorf("unsupported constant kind %v", v.Kind)
 | |
| 				}
 | |
| 				v.Flags |= VariableConstant
 | |
| 				v.loaded = true
 | |
| 				return v, nil
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil, nil
 | |
| }
 | |
| 
 | |
| // image returns the image containing the current function.
 | |
| func (scope *EvalScope) image() *Image {
 | |
| 	return scope.BinInfo.funcToImage(scope.Fn)
 | |
| }
 | |
| 
 | |
| // evalStack stores the stack machine used to evaluate a program made of
 | |
| // evalop.Ops.
 | |
| // When an opcode sets callInjectionContinue execution of the program will be suspended
 | |
| // and the call injection protocol will be executed instead.
 | |
| type evalStack struct {
 | |
| 	stack                 []*Variable          // current stack of Variable values
 | |
| 	fncalls               []*functionCallState // stack of call injections currently being executed
 | |
| 	ops                   []evalop.Op          // program being executed
 | |
| 	opidx                 int                  // program counter for the stack program
 | |
| 	callInjectionContinue bool                 // when set program execution suspends and the call injection protocol is executed instead
 | |
| 	err                   error
 | |
| 
 | |
| 	spoff, bpoff, fboff int64
 | |
| 	scope               *EvalScope
 | |
| 	curthread           Thread
 | |
| 	lastRetiredFncall   *functionCallState
 | |
| 	debugPinner         *Variable
 | |
| }
 | |
| 
 | |
| func (s *evalStack) push(v *Variable) {
 | |
| 	if v == nil {
 | |
| 		panic(errors.New("internal debugger error, nil pushed onto variables stack"))
 | |
| 	}
 | |
| 	s.stack = append(s.stack, v)
 | |
| }
 | |
| 
 | |
| func (s *evalStack) pop() *Variable {
 | |
| 	v := s.stack[len(s.stack)-1]
 | |
| 	s.stack = s.stack[:len(s.stack)-1]
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func (s *evalStack) peek() *Variable {
 | |
| 	return s.stack[len(s.stack)-1]
 | |
| }
 | |
| 
 | |
| func (s *evalStack) fncallPush(fncall *functionCallState) {
 | |
| 	s.fncalls = append(s.fncalls, fncall)
 | |
| }
 | |
| 
 | |
| func (s *evalStack) fncallPop() *functionCallState {
 | |
| 	fncall := s.fncalls[len(s.fncalls)-1]
 | |
| 	s.fncalls = s.fncalls[:len(s.fncalls)-1]
 | |
| 	return fncall
 | |
| }
 | |
| 
 | |
| func (s *evalStack) fncallPeek() *functionCallState {
 | |
| 	return s.fncalls[len(s.fncalls)-1]
 | |
| }
 | |
| 
 | |
| func (s *evalStack) pushErr(v *Variable, err error) {
 | |
| 	s.err = err
 | |
| 	s.stack = append(s.stack, v)
 | |
| }
 | |
| 
 | |
| // eval evaluates ops. When it returns if callInjectionContinue is set the
 | |
| // target program should be resumed to execute the call injection protocol.
 | |
| // Otherwise the result of the evaluation can be retrieved using
 | |
| // stack.result.
 | |
| func (stack *evalStack) eval(scope *EvalScope, ops []evalop.Op) {
 | |
| 	if logflags.FnCall() {
 | |
| 		fncallLog("eval program:\n%s", evalop.Listing(nil, ops))
 | |
| 	}
 | |
| 
 | |
| 	stack.ops = ops
 | |
| 	stack.scope = scope
 | |
| 
 | |
| 	if scope.g != nil {
 | |
| 		stack.spoff = int64(scope.Regs.Uint64Val(scope.Regs.SPRegNum)) - int64(scope.g.stack.hi)
 | |
| 		stack.bpoff = int64(scope.Regs.Uint64Val(scope.Regs.BPRegNum)) - int64(scope.g.stack.hi)
 | |
| 		stack.fboff = scope.Regs.FrameBase - int64(scope.g.stack.hi)
 | |
| 	}
 | |
| 
 | |
| 	if scope.g != nil && scope.g.Thread != nil {
 | |
| 		stack.curthread = scope.g.Thread
 | |
| 	}
 | |
| 
 | |
| 	stack.run()
 | |
| }
 | |
| 
 | |
| // resume resumes evaluation of stack.ops. When it returns if
 | |
| // callInjectionContinue is set the target program should be resumed to
 | |
| // execute the call injection protocol. Otherwise the result of the
 | |
| // evaluation can be retrieved using stack.result.
 | |
| func (stack *evalStack) resume(g *G) {
 | |
| 	stack.callInjectionContinue = false
 | |
| 	scope := stack.scope
 | |
| 	// Go 1.15 will move call injection execution to a different goroutine,
 | |
| 	// but we want to keep evaluation on the original goroutine.
 | |
| 	if g.ID == scope.g.ID {
 | |
| 		scope.g = g
 | |
| 	} else {
 | |
| 		// We are in Go 1.15 and we switched to a new goroutine, the original
 | |
| 		// goroutine is now parked and therefore does not have a thread
 | |
| 		// associated.
 | |
| 		scope.g.Thread = nil
 | |
| 		scope.g.Status = Gwaiting
 | |
| 		scope.callCtx.injectionThread = g.Thread
 | |
| 	}
 | |
| 
 | |
| 	// adjust the value of registers inside scope
 | |
| 	pcreg, bpreg, spreg := scope.Regs.Reg(scope.Regs.PCRegNum), scope.Regs.Reg(scope.Regs.BPRegNum), scope.Regs.Reg(scope.Regs.SPRegNum)
 | |
| 	scope.Regs.ClearRegisters()
 | |
| 	scope.Regs.AddReg(scope.Regs.PCRegNum, pcreg)
 | |
| 	scope.Regs.AddReg(scope.Regs.BPRegNum, bpreg)
 | |
| 	scope.Regs.AddReg(scope.Regs.SPRegNum, spreg)
 | |
| 	scope.Regs.Reg(scope.Regs.SPRegNum).Uint64Val = uint64(stack.spoff + int64(scope.g.stack.hi))
 | |
| 	scope.Regs.Reg(scope.Regs.BPRegNum).Uint64Val = uint64(stack.bpoff + int64(scope.g.stack.hi))
 | |
| 	scope.Regs.FrameBase = stack.fboff + int64(scope.g.stack.hi)
 | |
| 	scope.Regs.CFA = scope.frameOffset + int64(scope.g.stack.hi)
 | |
| 	stack.curthread = g.Thread
 | |
| 	scope.rangeFrames = nil
 | |
| 	scope.enclosingRangeScopes = nil
 | |
| 
 | |
| 	finished := funcCallStep(scope, stack, g.Thread)
 | |
| 	if finished {
 | |
| 		funcCallFinish(scope, stack)
 | |
| 	}
 | |
| 	if stack.err == nil && len(stack.fncalls) > 0 {
 | |
| 		if fncall := stack.fncallPeek(); fncall.err != nil {
 | |
| 			stack.err = fncall.err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if stack.callInjectionContinue {
 | |
| 		// not done with call injection, stay in this mode
 | |
| 		stack.scope.callCtx.injectionThread = nil
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// call injection protocol suspended or concluded, resume normal opcode execution
 | |
| 	if len(stack.fncalls) == 0 && g.Thread != nil {
 | |
| 		// The function call protocol concluded, recover the thread's registers so
 | |
| 		// we can use them to evaluate variables.
 | |
| 		so := scope.image()
 | |
| 		if regs, err := g.Thread.Registers(); err == nil {
 | |
| 			cfa := scope.Regs.CFA
 | |
| 			frameBase := scope.Regs.FrameBase
 | |
| 			dwarfRegs := *(scope.BinInfo.Arch.RegistersToDwarfRegisters(so.StaticBase, regs))
 | |
| 			dwarfRegs.ChangeFunc = g.Thread.SetReg
 | |
| 			scope.Regs = dwarfRegs
 | |
| 			scope.Regs.CFA = cfa
 | |
| 			scope.Regs.FrameBase = frameBase
 | |
| 		}
 | |
| 	}
 | |
| 	stack.run()
 | |
| }
 | |
| 
 | |
| func (stack *evalStack) run() {
 | |
| 	scope, curthread := stack.scope, stack.curthread
 | |
| 	for stack.opidx < len(stack.ops) && stack.err == nil {
 | |
| 		stack.callInjectionContinue = false
 | |
| 		stack.executeOp()
 | |
| 		// If the instruction we just executed requests the call injection
 | |
| 		// protocol by setting callInjectionContinue we switch to it.
 | |
| 		if stack.callInjectionContinue && stack.err == nil {
 | |
| 			scope.callCtx.injectionThread = nil
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if stack.err == nil && len(stack.fncalls) > 0 {
 | |
| 		stack.err = fmt.Errorf("internal debugger error: eval program finished without error but %d call injections still active", len(stack.fncalls))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// If there is an error we must undo all currently executing call
 | |
| 	// injections before returning.
 | |
| 
 | |
| 	if len(stack.fncalls) > 0 {
 | |
| 		fncallLog("undoing calls (%v)", stack.err)
 | |
| 		fncall := stack.fncallPeek()
 | |
| 		if fncall == stack.lastRetiredFncall {
 | |
| 			stack.err = fmt.Errorf("internal debugger error: could not undo injected call during error recovery, original error: %v", stack.err)
 | |
| 			return
 | |
| 		}
 | |
| 		if fncall.undoInjection != nil {
 | |
| 			if fncall.undoInjection.doComplete2 {
 | |
| 				// doComplete2 is set if CallInjectionComplete{DoPinning: true} has been
 | |
| 				// executed but CallInjectionComplete2 hasn't.
 | |
| 				regs, err := curthread.Registers()
 | |
| 				if err == nil {
 | |
| 					callInjectionComplete2(scope, scope.BinInfo, fncall, regs, curthread)
 | |
| 				}
 | |
| 			} else {
 | |
| 				// undoInjection is set if evalop.CallInjectionSetTarget has been
 | |
| 				// executed but evalop.CallInjectionComplete hasn't, we must undo the callOP
 | |
| 				// call in evalop.CallInjectionSetTarget before continuing.
 | |
| 				switch scope.BinInfo.Arch.Name {
 | |
| 				case "amd64":
 | |
| 					regs, _ := curthread.Registers()
 | |
| 					setSP(curthread, regs.SP()+uint64(scope.BinInfo.Arch.PtrSize()))
 | |
| 					setPC(curthread, fncall.undoInjection.oldpc)
 | |
| 				case "arm64", "ppc64le", "loong64":
 | |
| 					setLR(curthread, fncall.undoInjection.oldlr)
 | |
| 					setPC(curthread, fncall.undoInjection.oldpc)
 | |
| 				default:
 | |
| 					panic("not implemented")
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		stack.lastRetiredFncall = fncall
 | |
| 		// Resume target to undo one call
 | |
| 		stack.callInjectionContinue = true
 | |
| 		scope.callCtx.injectionThread = nil
 | |
| 		return
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (stack *evalStack) result(cfg *LoadConfig) (*Variable, error) {
 | |
| 	var r *Variable
 | |
| 	switch len(stack.stack) {
 | |
| 	case 0:
 | |
| 		// ok
 | |
| 	case 1:
 | |
| 		r = stack.peek()
 | |
| 	default:
 | |
| 		if stack.err == nil {
 | |
| 			stack.err = fmt.Errorf("internal debugger error: wrong stack size at end %d", len(stack.stack))
 | |
| 		}
 | |
| 	}
 | |
| 	if r != nil && cfg != nil && stack.err == nil {
 | |
| 		r.loadValue(*cfg)
 | |
| 	}
 | |
| 	return r, stack.err
 | |
| }
 | |
| 
 | |
| // executeOp executes the opcode at stack.ops[stack.opidx] and increments stack.opidx.
 | |
| func (stack *evalStack) executeOp() {
 | |
| 	scope, ops, curthread := stack.scope, stack.ops, stack.curthread
 | |
| 	defer func() {
 | |
| 		err := recover()
 | |
| 		if err != nil {
 | |
| 			logflags.Bug.Inc()
 | |
| 			stack.err = fmt.Errorf("internal debugger error: %v (recovered)\n%s", err, string(debug.Stack()))
 | |
| 		}
 | |
| 	}()
 | |
| 	switch op := ops[stack.opidx].(type) {
 | |
| 	case *evalop.PushCurg:
 | |
| 		if scope.g != nil {
 | |
| 			stack.push(scope.g.variable.clone())
 | |
| 		} else {
 | |
| 			typ, err := scope.BinInfo.findType("runtime.g")
 | |
| 			if err != nil {
 | |
| 				stack.err = fmt.Errorf("could not find runtime.g: %v", err)
 | |
| 				return
 | |
| 			}
 | |
| 			gvar := newVariable("curg", fakeAddressUnresolv, typ, scope.BinInfo, scope.Mem)
 | |
| 			gvar.loaded = true
 | |
| 			gvar.Flags = VariableFakeAddress
 | |
| 			gvar.Children = append(gvar.Children, *newConstant(constant.MakeInt64(0), scope.BinInfo, scope.Mem))
 | |
| 			gvar.Children[0].Name = "goid"
 | |
| 			stack.push(gvar)
 | |
| 		}
 | |
| 
 | |
| 	case *evalop.PushFrameoff:
 | |
| 		stack.push(newConstant(constant.MakeInt64(scope.frameOffset), scope.BinInfo, scope.Mem))
 | |
| 
 | |
| 	case *evalop.PushRangeParentOffset:
 | |
| 		if scope.rangeFrames == nil {
 | |
| 			stack.err = scope.setupRangeFrames()
 | |
| 		}
 | |
| 		if len(scope.rangeFrames) > 0 {
 | |
| 			stack.push(newConstant(constant.MakeInt64(scope.rangeFrames[len(scope.rangeFrames)-2].FrameOffset()), scope.BinInfo, scope.Mem))
 | |
| 		} else {
 | |
| 			stack.push(newConstant(constant.MakeInt64(0), scope.BinInfo, scope.Mem))
 | |
| 		}
 | |
| 
 | |
| 	case *evalop.PushThreadID:
 | |
| 		stack.push(newConstant(constant.MakeInt64(int64(scope.threadID)), scope.BinInfo, scope.Mem))
 | |
| 
 | |
| 	case *evalop.PushConst:
 | |
| 		stack.push(newConstant(op.Value, scope.BinInfo, scope.Mem))
 | |
| 
 | |
| 	case *evalop.PushLocal:
 | |
| 		found := stack.pushLocal(scope, op.Name, op.Frame)
 | |
| 		if !found {
 | |
| 			stack.err = fmt.Errorf("could not find symbol value for %s", op.Name)
 | |
| 		}
 | |
| 
 | |
| 	case *evalop.PushNil:
 | |
| 		stack.push(nilVariable)
 | |
| 
 | |
| 	case *evalop.PushPackageVarOrSelect:
 | |
| 		v, err := scope.findGlobal(op.Name, op.Sel)
 | |
| 		if err != nil && !isSymbolNotFound(err) {
 | |
| 			stack.err = err
 | |
| 			return
 | |
| 		}
 | |
| 		if v != nil {
 | |
| 			stack.push(v)
 | |
| 		} else {
 | |
| 			if op.NameIsString {
 | |
| 				stack.err = fmt.Errorf("%q (type string) is not a struct", op.Name)
 | |
| 				return
 | |
| 			}
 | |
| 			found := stack.pushIdent(scope, op.Name)
 | |
| 			if stack.err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			if found {
 | |
| 				scope.evalStructSelector(&evalop.Select{Name: op.Sel}, stack)
 | |
| 			} else {
 | |
| 				stack.err = fmt.Errorf("could not find symbol value for %s", op.Name)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case *evalop.PushIdent:
 | |
| 		found := stack.pushIdent(scope, op.Name)
 | |
| 		if !found {
 | |
| 			stack.err = fmt.Errorf("could not find symbol value for %s", op.Name)
 | |
| 		}
 | |
| 
 | |
| 	case *evalop.PushLen:
 | |
| 		v := stack.peek()
 | |
| 		stack.push(newConstant(constant.MakeInt64(v.Len), scope.BinInfo, scope.Mem))
 | |
| 
 | |
| 	case *evalop.Select:
 | |
| 		scope.evalStructSelector(op, stack)
 | |
| 
 | |
| 	case *evalop.TypeAssert:
 | |
| 		scope.evalTypeAssert(op, stack)
 | |
| 
 | |
| 	case *evalop.PointerDeref:
 | |
| 		scope.evalPointerDeref(op, stack)
 | |
| 
 | |
| 	case *evalop.Unary:
 | |
| 		scope.evalUnary(op, stack)
 | |
| 
 | |
| 	case *evalop.AddrOf:
 | |
| 		scope.evalAddrOf(op, stack)
 | |
| 
 | |
| 	case *evalop.TypeCast:
 | |
| 		scope.evalTypeCast(op, stack)
 | |
| 
 | |
| 	case *evalop.Reslice:
 | |
| 		scope.evalReslice(op, stack)
 | |
| 
 | |
| 	case *evalop.Index:
 | |
| 		scope.evalIndex(op, stack)
 | |
| 
 | |
| 	case *evalop.Jump:
 | |
| 		scope.evalJump(op, stack)
 | |
| 
 | |
| 	case *evalop.Binary:
 | |
| 		scope.evalBinary(op, stack)
 | |
| 
 | |
| 	case *evalop.BoolToConst:
 | |
| 		x := stack.pop()
 | |
| 		if x.Kind != reflect.Bool {
 | |
| 			stack.err = errors.New("internal debugger error: expected boolean")
 | |
| 			return
 | |
| 		}
 | |
| 		x.loadValue(loadFullValue)
 | |
| 		stack.push(newConstant(x.Value, scope.BinInfo, scope.Mem))
 | |
| 
 | |
| 	case *evalop.Pop:
 | |
| 		stack.pop()
 | |
| 
 | |
| 	case *evalop.Roll:
 | |
| 		rolled := stack.stack[len(stack.stack)-op.N-1]
 | |
| 		copy(stack.stack[len(stack.stack)-op.N-1:], stack.stack[len(stack.stack)-op.N:])
 | |
| 		stack.stack[len(stack.stack)-1] = rolled
 | |
| 
 | |
| 	case *evalop.Dup:
 | |
| 		x := stack.stack[len(stack.stack)-1]
 | |
| 		stack.push(x)
 | |
| 
 | |
| 	case *evalop.BuiltinCall:
 | |
| 		vars := make([]*Variable, len(op.Args))
 | |
| 		for i := len(op.Args) - 1; i >= 0; i-- {
 | |
| 			vars[i] = stack.pop()
 | |
| 		}
 | |
| 		stack.pushErr(supportedBuiltins[op.Name](vars, op.Args))
 | |
| 
 | |
| 	case *evalop.CallInjectionStart:
 | |
| 		scope.evalCallInjectionStart(op, stack)
 | |
| 
 | |
| 	case *evalop.CallInjectionSetTarget:
 | |
| 		scope.evalCallInjectionSetTarget(op, stack, curthread)
 | |
| 
 | |
| 	case *evalop.CallInjectionCopyArg:
 | |
| 		fncall := stack.fncallPeek()
 | |
| 		actualArg := stack.pop()
 | |
| 		if actualArg.Name == "" {
 | |
| 			actualArg.Name = astutil.ExprToString(op.ArgExpr)
 | |
| 		}
 | |
| 		stack.err = funcCallCopyOneArg(scope, fncall, actualArg, &fncall.formalArgs[op.ArgNum], curthread)
 | |
| 
 | |
| 	case *evalop.CallInjectionComplete:
 | |
| 		fncall := stack.fncallPeek()
 | |
| 		fncall.doPinning = op.DoPinning
 | |
| 		if op.DoPinning {
 | |
| 			fncall.undoInjection.doComplete2 = true
 | |
| 		} else {
 | |
| 			fncall.undoInjection = nil
 | |
| 		}
 | |
| 		stack.callInjectionContinue = true
 | |
| 
 | |
| 	case *evalop.CallInjectionComplete2:
 | |
| 		fncall := stack.fncallPeek()
 | |
| 		if len(fncall.addrsToPin) != 0 {
 | |
| 			stack.err = fmt.Errorf("internal debugger error: CallInjectionComplete2 called when there still are addresses to pin")
 | |
| 		}
 | |
| 		fncall.undoInjection = nil
 | |
| 		regs, err := curthread.Registers()
 | |
| 		if err == nil {
 | |
| 			callInjectionComplete2(scope, scope.BinInfo, fncall, regs, curthread)
 | |
| 			stack.callInjectionContinue = true
 | |
| 		} else {
 | |
| 			stack.err = err
 | |
| 		}
 | |
| 
 | |
| 	case *evalop.CallInjectionStartSpecial:
 | |
| 		stack.callInjectionContinue = scope.callInjectionStartSpecial(stack, op, curthread)
 | |
| 
 | |
| 	case *evalop.ConvertAllocToString:
 | |
| 		scope.convertAllocToString(stack)
 | |
| 
 | |
| 	case *evalop.SetValue:
 | |
| 		lhv := stack.pop()
 | |
| 		rhv := stack.pop()
 | |
| 		stack.err = scope.setValue(lhv, rhv, astutil.ExprToString(op.Rhe))
 | |
| 
 | |
| 	case *evalop.PushPinAddress:
 | |
| 		debugPinCount++
 | |
| 		fncall := stack.fncallPeek()
 | |
| 		addrToPin := fncall.addrsToPin[len(fncall.addrsToPin)-1]
 | |
| 		fncall.addrsToPin = fncall.addrsToPin[:len(fncall.addrsToPin)-1]
 | |
| 		typ, err := scope.BinInfo.findType("unsafe.Pointer")
 | |
| 		if ptyp, ok := typ.(*godwarf.PtrType); err == nil && ok {
 | |
| 			v := newVariable("", 0, typ, scope.BinInfo, scope.Mem)
 | |
| 			v.Children = []Variable{*(newVariable("", uint64(addrToPin), ptyp.Type, scope.BinInfo, scope.Mem))}
 | |
| 			stack.push(v)
 | |
| 		} else {
 | |
| 			stack.err = fmt.Errorf("can not pin address: %v", err)
 | |
| 		}
 | |
| 
 | |
| 	case *evalop.SetDebugPinner:
 | |
| 		stack.debugPinner = stack.pop()
 | |
| 
 | |
| 	case *evalop.PushDebugPinner:
 | |
| 		stack.push(stack.debugPinner)
 | |
| 
 | |
| 	case *evalop.PushBreakpointHitCount:
 | |
| 		stack.push(newVariable(evalop.BreakpointHitCountVarNameQualified, fakeAddressUnresolv, godwarf.FakeSliceType(godwarf.FakeBasicType("uint", 64)), scope.BinInfo, scope.Mem))
 | |
| 
 | |
| 	case *evalop.PushRuntimeType:
 | |
| 		typeAddr, _, _, err := dwarfToRuntimeType(scope.BinInfo, scope.Mem, op.Type)
 | |
| 		if err != nil {
 | |
| 			stack.err = err
 | |
| 			break
 | |
| 		}
 | |
| 		rttyp, err := scope.BinInfo.findType(scope.BinInfo.runtimeTypeTypename())
 | |
| 		if err != nil {
 | |
| 			stack.err = err
 | |
| 			break
 | |
| 		}
 | |
| 		v := newVariable("", typeAddr, rttyp, scope.BinInfo, scope.Mem)
 | |
| 		stack.push(v.pointerToVariable())
 | |
| 
 | |
| 	case *evalop.PushNewFakeVariable:
 | |
| 		stack.pushNewFakeVariable(scope, op.Type)
 | |
| 
 | |
| 	default:
 | |
| 		stack.err = fmt.Errorf("internal debugger error: unknown eval opcode: %#v", op)
 | |
| 	}
 | |
| 
 | |
| 	stack.opidx++
 | |
| }
 | |
| 
 | |
| func (stack *evalStack) pushLocal(scope *EvalScope, name string, frame int64) (found bool) {
 | |
| 	var vars []*Variable
 | |
| 	var err error
 | |
| 	if frame != 0 {
 | |
| 		frameScope, err2 := ConvertEvalScope(scope.target, -1, int(frame), 0)
 | |
| 		if err2 != nil {
 | |
| 			stack.err = err2
 | |
| 			return
 | |
| 		}
 | |
| 		vars, err = frameScope.Locals(0, name)
 | |
| 	} else {
 | |
| 		vars, err = scope.Locals(0, name)
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		stack.err = err
 | |
| 		return
 | |
| 	}
 | |
| 	found = false
 | |
| 	for i := range vars {
 | |
| 		if vars[i].Name == name && vars[i].Flags&VariableShadowed == 0 {
 | |
| 			stack.push(vars[i])
 | |
| 			found = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	return found
 | |
| }
 | |
| 
 | |
| func (stack *evalStack) pushIdent(scope *EvalScope, name string) (found bool) {
 | |
| 	found = stack.pushLocal(scope, name, 0)
 | |
| 	if found || stack.err != nil {
 | |
| 		return found
 | |
| 	}
 | |
| 	v, err := scope.findGlobal(scope.Fn.PackageName(), name)
 | |
| 	if err != nil && !isSymbolNotFound(err) {
 | |
| 		stack.err = err
 | |
| 		return false
 | |
| 	}
 | |
| 	if v != nil {
 | |
| 		v.Name = name
 | |
| 		stack.push(v)
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	switch name {
 | |
| 	case "true", "false":
 | |
| 		stack.push(newConstant(constant.MakeBool(name == "true"), scope.BinInfo, scope.Mem))
 | |
| 		return true
 | |
| 	case "nil":
 | |
| 		stack.push(nilVariable)
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	regname := validRegisterName(name)
 | |
| 	if regname == "" {
 | |
| 		return false
 | |
| 	}
 | |
| 	regnum, ok := scope.BinInfo.Arch.RegisterNameToDwarf(regname)
 | |
| 	if !ok {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	reg := scope.Regs.Reg(uint64(regnum))
 | |
| 	if reg == nil {
 | |
| 		return
 | |
| 	}
 | |
| 	reg.FillBytes()
 | |
| 
 | |
| 	var typ godwarf.Type
 | |
| 	if len(reg.Bytes) <= 8 {
 | |
| 		typ = godwarf.FakeBasicType("uint", 64)
 | |
| 	} else {
 | |
| 		var err error
 | |
| 		typ, err = scope.BinInfo.findType("string")
 | |
| 		if err != nil {
 | |
| 			stack.err = err
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	v = newVariable(regname, 0, typ, scope.BinInfo, scope.Mem)
 | |
| 	if v.Kind == reflect.String {
 | |
| 		v.Len = int64(len(reg.Bytes) * 2)
 | |
| 		v.Base = fakeAddressUnresolv
 | |
| 	}
 | |
| 	v.Addr = fakeAddressUnresolv
 | |
| 	v.Flags = VariableCPURegister
 | |
| 	v.reg = reg
 | |
| 	stack.push(v)
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func (stack *evalStack) pushNewFakeVariable(scope *EvalScope, typ godwarf.Type) {
 | |
| 	cm, err := CreateCompositeMemory(scope.Mem, scope.BinInfo.Arch, *new(op.DwarfRegisters), []op.Piece{{Kind: op.ImmPiece, Bytes: make([]byte, typ.Size()), Size: int(typ.Size())}}, typ.Size())
 | |
| 	if err != nil {
 | |
| 		stack.err = err
 | |
| 		return
 | |
| 	}
 | |
| 	v := newVariable("", cm.base, typ, scope.BinInfo, cm)
 | |
| 	v.Flags = VariableFakeAddress
 | |
| 	stack.push(v)
 | |
| }
 | |
| 
 | |
| func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
 | |
| 	ops, err := evalop.CompileAST(scopeToEvalLookup{scope}, t, scope.evalopFlags())
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	stack := &evalStack{}
 | |
| 	stack.eval(scope, ops)
 | |
| 	return stack.result(nil)
 | |
| }
 | |
| 
 | |
| func (scope *EvalScope) evalJump(op *evalop.Jump, stack *evalStack) {
 | |
| 	var x *Variable
 | |
| 
 | |
| 	switch op.When {
 | |
| 	case evalop.JumpIfTrue, evalop.JumpIfFalse, evalop.JumpIfAllocStringChecksFail:
 | |
| 		x = stack.peek()
 | |
| 		if op.Pop {
 | |
| 			stack.pop()
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var v bool
 | |
| 	switch op.When {
 | |
| 	case evalop.JumpIfTrue:
 | |
| 		v = true
 | |
| 	case evalop.JumpIfFalse:
 | |
| 		v = false
 | |
| 	case evalop.JumpIfAllocStringChecksFail:
 | |
| 		stringChecksFailed := x.Kind != reflect.String || x.Addr != 0 || x.Flags&VariableConstant == 0 || x.Len <= 0
 | |
| 		nilCallCtx := scope.callCtx == nil // do not complain here, setValue will if no other errors happen
 | |
| 		if stringChecksFailed || nilCallCtx {
 | |
| 			stack.opidx = op.Target - 1
 | |
| 			return
 | |
| 		}
 | |
| 		return
 | |
| 	case evalop.JumpAlways:
 | |
| 		stack.opidx = op.Target - 1
 | |
| 		return
 | |
| 	case evalop.JumpIfPinningDone:
 | |
| 		fncall := stack.fncallPeek()
 | |
| 		if len(fncall.addrsToPin) == 0 {
 | |
| 			stack.opidx = op.Target - 1
 | |
| 		}
 | |
| 		return
 | |
| 	default:
 | |
| 		panic("internal error, bad jump condition")
 | |
| 	}
 | |
| 
 | |
| 	if x.Kind != reflect.Bool {
 | |
| 		if op.Node != nil {
 | |
| 			stack.err = fmt.Errorf("expression %q should be boolean not %s", astutil.ExprToString(op.Node), x.Kind)
 | |
| 		} else {
 | |
| 			stack.err = errors.New("internal debugger error: expected boolean")
 | |
| 		}
 | |
| 		return
 | |
| 	}
 | |
| 	x.loadValue(loadFullValue)
 | |
| 	if x.Unreadable != nil {
 | |
| 		stack.err = x.Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 	if constant.BoolVal(x.Value) == v {
 | |
| 		stack.opidx = op.Target - 1
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Eval type cast expressions
 | |
| func (scope *EvalScope) evalTypeCast(op *evalop.TypeCast, stack *evalStack) {
 | |
| 	argv := stack.pop()
 | |
| 
 | |
| 	typ := godwarf.ResolveTypedef(op.DwarfType)
 | |
| 
 | |
| 	converr := fmt.Errorf("can not convert %q to %s", astutil.ExprToString(op.Node.Args[0]), typ.String())
 | |
| 
 | |
| 	// compatible underlying types
 | |
| 	if typeCastCompatibleTypes(argv.RealType, typ) {
 | |
| 		ptyp, isptr := typ.(*godwarf.PtrType)
 | |
| 		_, isvoid := argv.DwarfType.(*godwarf.VoidType)
 | |
| 		if (argv.Kind == reflect.Ptr || isvoid) && argv.loaded && len(argv.Children) > 0 && isptr {
 | |
| 			cv := argv.Children[0]
 | |
| 			argv.Children[0] = *newVariable(cv.Name, cv.Addr, ptyp.Type, cv.bi, cv.mem)
 | |
| 			argv.Children[0].OnlyAddr = true
 | |
| 		}
 | |
| 		argv.RealType = typ
 | |
| 		argv.DwarfType = op.DwarfType
 | |
| 		if isptr {
 | |
| 			argv.Kind = reflect.Ptr // could be converting from unsafe.Pointer
 | |
| 		}
 | |
| 		stack.push(argv)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	v := newVariable("", 0, op.DwarfType, scope.BinInfo, scope.Mem)
 | |
| 	v.loaded = true
 | |
| 
 | |
| 	switch ttyp := typ.(type) {
 | |
| 	case *godwarf.PtrType:
 | |
| 		switch argv.Kind {
 | |
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 			// ok
 | |
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | |
| 			// ok
 | |
| 		default:
 | |
| 			stack.err = converr
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		argv.loadValue(loadSingleValue)
 | |
| 		if argv.Unreadable != nil {
 | |
| 			stack.err = argv.Unreadable
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		n, _ := constant.Int64Val(argv.Value)
 | |
| 
 | |
| 		mem := scope.Mem
 | |
| 		if scope.target != nil {
 | |
| 			if mem2 := scope.target.findFakeMemory(uint64(n)); mem2 != nil {
 | |
| 				mem = mem2
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		v.Children = []Variable{*(newVariable("", uint64(n), ttyp.Type, scope.BinInfo, mem))}
 | |
| 		v.Children[0].OnlyAddr = true
 | |
| 		stack.push(v)
 | |
| 		return
 | |
| 
 | |
| 	case *godwarf.UintType:
 | |
| 		argv.loadValue(loadSingleValue)
 | |
| 		if argv.Unreadable != nil {
 | |
| 			stack.err = argv.Unreadable
 | |
| 			return
 | |
| 		}
 | |
| 		switch argv.Kind {
 | |
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 			n, _ := constant.Int64Val(argv.Value)
 | |
| 			v.Value = constant.MakeUint64(convertInt(uint64(n), false, ttyp.Size()))
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | |
| 			n, _ := constant.Uint64Val(argv.Value)
 | |
| 			v.Value = constant.MakeUint64(convertInt(n, false, ttyp.Size()))
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		case reflect.Float32, reflect.Float64:
 | |
| 			x, _ := constant.Float64Val(argv.Value)
 | |
| 			v.Value = constant.MakeUint64(uint64(x))
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		case reflect.Ptr:
 | |
| 			v.Value = constant.MakeUint64(argv.Children[0].Addr)
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		}
 | |
| 	case *godwarf.IntType:
 | |
| 		argv.loadValue(loadSingleValue)
 | |
| 		if argv.Unreadable != nil {
 | |
| 			stack.err = argv.Unreadable
 | |
| 			return
 | |
| 		}
 | |
| 		switch argv.Kind {
 | |
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 			n, _ := constant.Int64Val(argv.Value)
 | |
| 			v.Value = constant.MakeInt64(int64(convertInt(uint64(n), true, ttyp.Size())))
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | |
| 			n, _ := constant.Uint64Val(argv.Value)
 | |
| 			v.Value = constant.MakeInt64(int64(convertInt(n, true, ttyp.Size())))
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		case reflect.Float32, reflect.Float64:
 | |
| 			x, _ := constant.Float64Val(argv.Value)
 | |
| 			v.Value = constant.MakeInt64(int64(x))
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		}
 | |
| 	case *godwarf.FloatType:
 | |
| 		argv.loadValue(loadSingleValue)
 | |
| 		if argv.Unreadable != nil {
 | |
| 			stack.err = argv.Unreadable
 | |
| 			return
 | |
| 		}
 | |
| 		switch argv.Kind {
 | |
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 			fallthrough
 | |
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | |
| 			fallthrough
 | |
| 		case reflect.Float32, reflect.Float64:
 | |
| 			v.Value = argv.Value
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		}
 | |
| 	case *godwarf.ComplexType:
 | |
| 		argv.loadValue(loadSingleValue)
 | |
| 		if argv.Unreadable != nil {
 | |
| 			stack.err = argv.Unreadable
 | |
| 			return
 | |
| 		}
 | |
| 		switch argv.Kind {
 | |
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 			fallthrough
 | |
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | |
| 			fallthrough
 | |
| 		case reflect.Float32, reflect.Float64:
 | |
| 			v.Value = argv.Value
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	cfg := loadFullValue
 | |
| 	if scope.loadCfg != nil {
 | |
| 		cfg = *scope.loadCfg
 | |
| 	}
 | |
| 
 | |
| 	switch ttyp := typ.(type) {
 | |
| 	case *godwarf.SliceType:
 | |
| 		switch ttyp.ElemType.Common().ReflectKind {
 | |
| 		case reflect.Uint8:
 | |
| 			// string -> []uint8
 | |
| 			if argv.Kind != reflect.String {
 | |
| 				stack.err = converr
 | |
| 				return
 | |
| 			}
 | |
| 			cfg.MaxStringLen = cfg.MaxArrayValues
 | |
| 			argv.loadValue(cfg)
 | |
| 			if argv.Unreadable != nil {
 | |
| 				stack.err = argv.Unreadable
 | |
| 				return
 | |
| 			}
 | |
| 			for i, ch := range []byte(constant.StringVal(argv.Value)) {
 | |
| 				e := newVariable("", argv.Addr+uint64(i), typ.(*godwarf.SliceType).ElemType, scope.BinInfo, argv.mem)
 | |
| 				e.loaded = true
 | |
| 				e.Value = constant.MakeInt64(int64(ch))
 | |
| 				v.Children = append(v.Children, *e)
 | |
| 			}
 | |
| 			v.Len = argv.Len
 | |
| 			v.Cap = v.Len
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 
 | |
| 		case reflect.Int32:
 | |
| 			// string -> []rune
 | |
| 			if argv.Kind != reflect.String {
 | |
| 				stack.err = converr
 | |
| 				return
 | |
| 			}
 | |
| 			argv.loadValue(cfg)
 | |
| 			if argv.Unreadable != nil {
 | |
| 				stack.err = argv.Unreadable
 | |
| 				return
 | |
| 			}
 | |
| 			for i, ch := range constant.StringVal(argv.Value) {
 | |
| 				e := newVariable("", argv.Addr+uint64(i), typ.(*godwarf.SliceType).ElemType, scope.BinInfo, argv.mem)
 | |
| 				e.loaded = true
 | |
| 				e.Value = constant.MakeInt64(int64(ch))
 | |
| 				v.Children = append(v.Children, *e)
 | |
| 			}
 | |
| 			v.Len = int64(len(v.Children))
 | |
| 			v.Cap = v.Len
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 	case *godwarf.StringType:
 | |
| 		switch argv.Kind {
 | |
| 		case reflect.String:
 | |
| 			// string -> string
 | |
| 			argv.DwarfType = v.DwarfType
 | |
| 			argv.RealType = v.RealType
 | |
| 			stack.push(argv)
 | |
| 			return
 | |
| 		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
 | |
| 			// integer -> string
 | |
| 			argv.loadValue(cfg)
 | |
| 			if argv.Unreadable != nil {
 | |
| 				stack.err = argv.Unreadable
 | |
| 				return
 | |
| 			}
 | |
| 			b, _ := constant.Int64Val(argv.Value)
 | |
| 			s := string(rune(b))
 | |
| 			v.Value = constant.MakeString(s)
 | |
| 			v.Len = int64(len(s))
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		case reflect.Slice, reflect.Array:
 | |
| 			var elem godwarf.Type
 | |
| 			if argv.Kind == reflect.Slice {
 | |
| 				elem = argv.RealType.(*godwarf.SliceType).ElemType
 | |
| 			} else {
 | |
| 				elem = argv.RealType.(*godwarf.ArrayType).Type
 | |
| 			}
 | |
| 			switch elemType := elem.(type) {
 | |
| 			case *godwarf.UintType:
 | |
| 				// []uint8 -> string
 | |
| 				if elemType.Name != "uint8" && elemType.Name != "byte" {
 | |
| 					stack.err = converr
 | |
| 					return
 | |
| 				}
 | |
| 				cfg.MaxArrayValues = cfg.MaxStringLen
 | |
| 				argv.loadValue(cfg)
 | |
| 				if argv.Unreadable != nil {
 | |
| 					stack.err = argv.Unreadable
 | |
| 					return
 | |
| 				}
 | |
| 				bytes := make([]byte, len(argv.Children))
 | |
| 				for i := range argv.Children {
 | |
| 					n, _ := constant.Int64Val(argv.Children[i].Value)
 | |
| 					bytes[i] = byte(n)
 | |
| 				}
 | |
| 				v.Value = constant.MakeString(string(bytes))
 | |
| 				v.Len = argv.Len
 | |
| 
 | |
| 			case *godwarf.IntType:
 | |
| 				// []rune -> string
 | |
| 				if elemType.Name != "int32" && elemType.Name != "rune" {
 | |
| 					stack.err = converr
 | |
| 					return
 | |
| 				}
 | |
| 				cfg.MaxArrayValues = cfg.MaxStringLen
 | |
| 				argv.loadValue(cfg)
 | |
| 				if argv.Unreadable != nil {
 | |
| 					stack.err = argv.Unreadable
 | |
| 					return
 | |
| 				}
 | |
| 				runes := make([]rune, len(argv.Children))
 | |
| 				for i := range argv.Children {
 | |
| 					n, _ := constant.Int64Val(argv.Children[i].Value)
 | |
| 					runes[i] = rune(n)
 | |
| 				}
 | |
| 				v.Value = constant.MakeString(string(runes))
 | |
| 				// The following line is wrong but the only way to get the correct value
 | |
| 				// would be to decode the entire slice.
 | |
| 				v.Len = int64(len(constant.StringVal(v.Value)))
 | |
| 
 | |
| 			default:
 | |
| 				stack.err = converr
 | |
| 				return
 | |
| 			}
 | |
| 			stack.push(v)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	stack.err = converr
 | |
| }
 | |
| 
 | |
| // typeCastCompatibleTypes returns true if typ1 and typ2 are compatible for
 | |
| // a type cast where only the type of the variable is changed.
 | |
| func typeCastCompatibleTypes(typ1, typ2 godwarf.Type) bool {
 | |
| 	if typ1 == nil || typ2 == nil || typ1.Common().Size() != typ2.Common().Size() || typ1.Common().Align() != typ2.Common().Align() {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	if typ1.String() == typ2.String() {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	switch ttyp1 := typ1.(type) {
 | |
| 	case *godwarf.PtrType:
 | |
| 		if ttyp2, ok := typ2.(*godwarf.PtrType); ok {
 | |
| 			_, isvoid1 := ttyp1.Type.(*godwarf.VoidType)
 | |
| 			_, isvoid2 := ttyp2.Type.(*godwarf.VoidType)
 | |
| 			if isvoid1 || isvoid2 {
 | |
| 				return true
 | |
| 			}
 | |
| 			// pointer types are compatible if their element types are compatible
 | |
| 			return typeCastCompatibleTypes(godwarf.ResolveTypedef(ttyp1.Type), godwarf.ResolveTypedef(ttyp2.Type))
 | |
| 		}
 | |
| 	case *godwarf.StringType:
 | |
| 		if _, ok := typ2.(*godwarf.StringType); ok {
 | |
| 			return true
 | |
| 		}
 | |
| 	case *godwarf.StructType:
 | |
| 		if ttyp2, ok := typ2.(*godwarf.StructType); ok {
 | |
| 			// struct types are compatible if they have the same fields
 | |
| 			if len(ttyp1.Field) != len(ttyp2.Field) {
 | |
| 				return false
 | |
| 			}
 | |
| 			for i := range ttyp1.Field {
 | |
| 				if *ttyp1.Field[i] != *ttyp2.Field[i] {
 | |
| 					return false
 | |
| 				}
 | |
| 			}
 | |
| 			return true
 | |
| 		}
 | |
| 	case *godwarf.SliceType:
 | |
| 		if ttyp2, ok := typ2.(*godwarf.SliceType); ok {
 | |
| 			return ttyp1.ElemType.String() == ttyp2.ElemType.String()
 | |
| 		}
 | |
| 	case *godwarf.ComplexType:
 | |
| 		if _, ok := typ2.(*godwarf.ComplexType); ok {
 | |
| 			// size and alignment already checked above
 | |
| 			return true
 | |
| 		}
 | |
| 	case *godwarf.FloatType:
 | |
| 		if _, ok := typ2.(*godwarf.FloatType); ok {
 | |
| 			// size and alignment already checked above
 | |
| 			return true
 | |
| 		}
 | |
| 	case *godwarf.IntType:
 | |
| 		if _, ok := typ2.(*godwarf.IntType); ok {
 | |
| 			// size and alignment already checked above
 | |
| 			return true
 | |
| 		}
 | |
| 	case *godwarf.UintType:
 | |
| 		if _, ok := typ2.(*godwarf.UintType); ok {
 | |
| 			// size and alignment already checked above
 | |
| 			return true
 | |
| 		}
 | |
| 	case *godwarf.BoolType:
 | |
| 		if _, ok := typ2.(*godwarf.BoolType); ok {
 | |
| 			// size and alignment already checked above
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func convertInt(n uint64, signed bool, size int64) uint64 {
 | |
| 	bits := uint64(size) * 8
 | |
| 	mask := uint64((1 << bits) - 1)
 | |
| 	r := n & mask
 | |
| 	if signed && (r>>(bits-1)) != 0 {
 | |
| 		// sign extension
 | |
| 		r |= ^uint64(0) &^ mask
 | |
| 	}
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| var supportedBuiltins = map[string]func([]*Variable, []ast.Expr) (*Variable, error){
 | |
| 	"cap":     capBuiltin,
 | |
| 	"len":     lenBuiltin,
 | |
| 	"complex": complexBuiltin,
 | |
| 	"imag":    imagBuiltin,
 | |
| 	"real":    realBuiltin,
 | |
| 	"min":     minBuiltin,
 | |
| 	"max":     maxBuiltin,
 | |
| }
 | |
| 
 | |
| func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	if len(args) != 1 {
 | |
| 		return nil, fmt.Errorf("wrong number of arguments to cap: %d", len(args))
 | |
| 	}
 | |
| 
 | |
| 	arg := args[0]
 | |
| 	invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for cap", astutil.ExprToString(nodeargs[0]), arg.TypeString())
 | |
| 
 | |
| 	switch arg.Kind {
 | |
| 	case reflect.Ptr:
 | |
| 		arg = arg.maybeDereference()
 | |
| 		if arg.Kind != reflect.Array {
 | |
| 			return nil, invalidArgErr
 | |
| 		}
 | |
| 		fallthrough
 | |
| 	case reflect.Array:
 | |
| 		return newConstant(constant.MakeInt64(arg.Len), arg.bi, arg.mem), nil
 | |
| 	case reflect.Slice:
 | |
| 		return newConstant(constant.MakeInt64(arg.Cap), arg.bi, arg.mem), nil
 | |
| 	case reflect.Chan:
 | |
| 		arg.loadValue(loadFullValue)
 | |
| 		if arg.Unreadable != nil {
 | |
| 			return nil, arg.Unreadable
 | |
| 		}
 | |
| 		if arg.Base == 0 {
 | |
| 			return newConstant(constant.MakeInt64(0), arg.bi, arg.mem), nil
 | |
| 		}
 | |
| 		return newConstant(arg.Children[1].Value, arg.bi, arg.mem), nil
 | |
| 	default:
 | |
| 		return nil, invalidArgErr
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func lenBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	if len(args) != 1 {
 | |
| 		return nil, fmt.Errorf("wrong number of arguments to len: %d", len(args))
 | |
| 	}
 | |
| 	arg := args[0]
 | |
| 	invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for len", astutil.ExprToString(nodeargs[0]), arg.TypeString())
 | |
| 
 | |
| 	switch arg.Kind {
 | |
| 	case reflect.Ptr:
 | |
| 		arg = arg.maybeDereference()
 | |
| 		if arg.Kind != reflect.Array {
 | |
| 			return nil, invalidArgErr
 | |
| 		}
 | |
| 		fallthrough
 | |
| 	case reflect.Array, reflect.Slice, reflect.String:
 | |
| 		if arg.Unreadable != nil {
 | |
| 			return nil, arg.Unreadable
 | |
| 		}
 | |
| 		return newConstant(constant.MakeInt64(arg.Len), arg.bi, arg.mem), nil
 | |
| 	case reflect.Chan:
 | |
| 		arg.loadValue(loadFullValue)
 | |
| 		if arg.Unreadable != nil {
 | |
| 			return nil, arg.Unreadable
 | |
| 		}
 | |
| 		if arg.Base == 0 {
 | |
| 			return newConstant(constant.MakeInt64(0), arg.bi, arg.mem), nil
 | |
| 		}
 | |
| 		return newConstant(arg.Children[0].Value, arg.bi, arg.mem), nil
 | |
| 	case reflect.Map:
 | |
| 		it := arg.mapIterator(0)
 | |
| 		if arg.Unreadable != nil {
 | |
| 			return nil, arg.Unreadable
 | |
| 		}
 | |
| 		if it == nil {
 | |
| 			return newConstant(constant.MakeInt64(0), arg.bi, arg.mem), nil
 | |
| 		}
 | |
| 		return newConstant(constant.MakeInt64(arg.Len), arg.bi, arg.mem), nil
 | |
| 	default:
 | |
| 		return nil, invalidArgErr
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	if len(args) != 2 {
 | |
| 		return nil, fmt.Errorf("wrong number of arguments to complex: %d", len(args))
 | |
| 	}
 | |
| 
 | |
| 	realev := args[0]
 | |
| 	imagev := args[1]
 | |
| 
 | |
| 	realev.loadValue(loadSingleValue)
 | |
| 	imagev.loadValue(loadSingleValue)
 | |
| 
 | |
| 	if realev.Unreadable != nil {
 | |
| 		return nil, realev.Unreadable
 | |
| 	}
 | |
| 
 | |
| 	if imagev.Unreadable != nil {
 | |
| 		return nil, imagev.Unreadable
 | |
| 	}
 | |
| 
 | |
| 	if realev.Value == nil || ((realev.Value.Kind() != constant.Int) && (realev.Value.Kind() != constant.Float)) {
 | |
| 		return nil, fmt.Errorf("invalid argument 1 %s (type %s) to complex", astutil.ExprToString(nodeargs[0]), realev.TypeString())
 | |
| 	}
 | |
| 
 | |
| 	if imagev.Value == nil || ((imagev.Value.Kind() != constant.Int) && (imagev.Value.Kind() != constant.Float)) {
 | |
| 		return nil, fmt.Errorf("invalid argument 2 %s (type %s) to complex", astutil.ExprToString(nodeargs[1]), imagev.TypeString())
 | |
| 	}
 | |
| 
 | |
| 	sz := int64(0)
 | |
| 	if realev.RealType != nil {
 | |
| 		sz = realev.RealType.(*godwarf.FloatType).Size()
 | |
| 	}
 | |
| 	if imagev.RealType != nil {
 | |
| 		isz := imagev.RealType.(*godwarf.FloatType).Size()
 | |
| 		if isz > sz {
 | |
| 			sz = isz
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if sz == 0 {
 | |
| 		sz = 128
 | |
| 	}
 | |
| 
 | |
| 	typ := godwarf.FakeBasicType("complex", int(sz))
 | |
| 
 | |
| 	r := realev.newVariable("", 0, typ, nil)
 | |
| 	r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value))
 | |
| 	return r, nil
 | |
| }
 | |
| 
 | |
| func imagBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	if len(args) != 1 {
 | |
| 		return nil, fmt.Errorf("wrong number of arguments to imag: %d", len(args))
 | |
| 	}
 | |
| 
 | |
| 	arg := args[0]
 | |
| 	arg.loadValue(loadSingleValue)
 | |
| 
 | |
| 	if arg.Unreadable != nil {
 | |
| 		return nil, arg.Unreadable
 | |
| 	}
 | |
| 
 | |
| 	if arg.Kind != reflect.Complex64 && arg.Kind != reflect.Complex128 {
 | |
| 		return nil, fmt.Errorf("invalid argument %s (type %s) to imag", astutil.ExprToString(nodeargs[0]), arg.TypeString())
 | |
| 	}
 | |
| 
 | |
| 	return newConstant(constant.Imag(arg.Value), arg.bi, arg.mem), nil
 | |
| }
 | |
| 
 | |
| func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	if len(args) != 1 {
 | |
| 		return nil, fmt.Errorf("wrong number of arguments to real: %d", len(args))
 | |
| 	}
 | |
| 
 | |
| 	arg := args[0]
 | |
| 	arg.loadValue(loadSingleValue)
 | |
| 
 | |
| 	if arg.Unreadable != nil {
 | |
| 		return nil, arg.Unreadable
 | |
| 	}
 | |
| 
 | |
| 	if arg.Value == nil || ((arg.Value.Kind() != constant.Int) && (arg.Value.Kind() != constant.Float) && (arg.Value.Kind() != constant.Complex)) {
 | |
| 		return nil, fmt.Errorf("invalid argument %s (type %s) to real", astutil.ExprToString(nodeargs[0]), arg.TypeString())
 | |
| 	}
 | |
| 
 | |
| 	return newConstant(constant.Real(arg.Value), arg.bi, arg.mem), nil
 | |
| }
 | |
| 
 | |
| func minBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	return minmaxBuiltin("min", token.LSS, args, nodeargs)
 | |
| }
 | |
| 
 | |
| func maxBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	return minmaxBuiltin("max", token.GTR, args, nodeargs)
 | |
| }
 | |
| 
 | |
| func minmaxBuiltin(name string, op token.Token, args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | |
| 	var best *Variable
 | |
| 
 | |
| 	for i := range args {
 | |
| 		if args[i].Kind == reflect.String {
 | |
| 			args[i].loadValue(loadFullValueLongerStrings)
 | |
| 		} else {
 | |
| 			args[i].loadValue(loadFullValue)
 | |
| 		}
 | |
| 
 | |
| 		if args[i].Unreadable != nil {
 | |
| 			return nil, fmt.Errorf("could not load %q: %v", astutil.ExprToString(nodeargs[i]), args[i].Unreadable)
 | |
| 		}
 | |
| 		if args[i].FloatSpecial != 0 {
 | |
| 			return nil, errOperationOnSpecialFloat
 | |
| 		}
 | |
| 
 | |
| 		if best == nil {
 | |
| 			best = args[i]
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		_, err := negotiateType(op, args[i], best)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		v, err := compareOp(op, args[i], best)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		if v {
 | |
| 			best = args[i]
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if best == nil {
 | |
| 		return nil, fmt.Errorf("not enough arguments to %s", name)
 | |
| 	}
 | |
| 	return best, nil
 | |
| }
 | |
| 
 | |
| // Evaluates expressions <subexpr>.<field name> where subexpr is not a package name
 | |
| func (scope *EvalScope) evalStructSelector(op *evalop.Select, stack *evalStack) {
 | |
| 	xv := stack.pop()
 | |
| 	// Prevent abuse, attempting to call "nil.member" directly.
 | |
| 	if xv.Addr == 0 && xv.Name == "nil" {
 | |
| 		stack.err = fmt.Errorf("%s (type %s) is not a struct", xv.Name, xv.TypeString())
 | |
| 		return
 | |
| 	}
 | |
| 	// Prevent abuse, attempting to call "\"fake\".member" directly.
 | |
| 	if xv.Addr == 0 && xv.Name == "" && xv.DwarfType == nil && xv.RealType == nil {
 | |
| 		stack.err = fmt.Errorf("%s (type %s) is not a struct", xv.Value, xv.TypeString())
 | |
| 		return
 | |
| 	}
 | |
| 	// Special type conversions for CPU register variables (REGNAME.int8, etc)
 | |
| 	if xv.Flags&VariableCPURegister != 0 && !xv.loaded {
 | |
| 		stack.pushErr(xv.registerVariableTypeConv(op.Name))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	stack.pushErr(xv.findStructMemberOrMethod(op.Name, true))
 | |
| }
 | |
| 
 | |
| // Evaluates expressions <subexpr>.(<type>)
 | |
| func (scope *EvalScope) evalTypeAssert(op *evalop.TypeAssert, stack *evalStack) {
 | |
| 	xv := stack.pop()
 | |
| 	if xv.Kind != reflect.Interface {
 | |
| 		stack.err = fmt.Errorf("expression %q not an interface", astutil.ExprToString(op.Node.X))
 | |
| 		return
 | |
| 	}
 | |
| 	xv.loadInterface(0, false, loadFullValue)
 | |
| 	if xv.Unreadable != nil {
 | |
| 		stack.err = xv.Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 	if xv.Children[0].Unreadable != nil {
 | |
| 		stack.err = xv.Children[0].Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 	if xv.Children[0].Addr == 0 {
 | |
| 		stack.err = fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), astutil.ExprToString(op.Node.Type))
 | |
| 		return
 | |
| 	}
 | |
| 	typ := op.DwarfType
 | |
| 	if typ != nil && xv.Children[0].DwarfType.Common().Name != typ.Common().Name {
 | |
| 		stack.err = fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
 | |
| 		return
 | |
| 	}
 | |
| 	// loadInterface will set OnlyAddr for the data member since here we are
 | |
| 	// passing false to loadData, however returning the variable with OnlyAddr
 | |
| 	// set here would be wrong since, once the expression evaluation
 | |
| 	// terminates, the value of this variable will be loaded.
 | |
| 	xv.Children[0].OnlyAddr = false
 | |
| 	stack.push(&xv.Children[0])
 | |
| }
 | |
| 
 | |
| // Evaluates expressions <subexpr>[<subexpr>] (subscript access to arrays, slices and maps)
 | |
| func (scope *EvalScope) evalIndex(op *evalop.Index, stack *evalStack) {
 | |
| 	idxev := stack.pop()
 | |
| 	xev := stack.pop()
 | |
| 	if xev.Unreadable != nil {
 | |
| 		stack.err = xev.Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if xev.Name == evalop.BreakpointHitCountVarNameQualified {
 | |
| 		if idxev.Kind == reflect.String {
 | |
| 			s := constant.StringVal(idxev.Value)
 | |
| 			thc, err := totalHitCountByName(scope.target.Breakpoints().Logical, s)
 | |
| 			if err == nil {
 | |
| 				stack.push(newConstant(constant.MakeUint64(thc), scope.BinInfo, scope.Mem))
 | |
| 			}
 | |
| 			stack.err = err
 | |
| 			return
 | |
| 		}
 | |
| 		n, err := idxev.asInt()
 | |
| 		if err != nil {
 | |
| 			n2, err := idxev.asUint()
 | |
| 			if err != nil {
 | |
| 				stack.err = fmt.Errorf("can not index %s with %s", xev.Name, astutil.ExprToString(op.Node.Index))
 | |
| 				return
 | |
| 			}
 | |
| 			n = int64(n2)
 | |
| 		}
 | |
| 		thc, err := totalHitCountByID(scope.target.Breakpoints().Logical, int(n))
 | |
| 		if err == nil {
 | |
| 			stack.push(newConstant(constant.MakeUint64(thc), scope.BinInfo, scope.Mem))
 | |
| 		}
 | |
| 		stack.err = err
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if xev.Flags&VariableCPtr == 0 {
 | |
| 		xev = xev.maybeDereference()
 | |
| 	}
 | |
| 
 | |
| 	cantindex := fmt.Errorf("expression %q (%s) does not support indexing", astutil.ExprToString(op.Node.X), xev.TypeString())
 | |
| 
 | |
| 	switch xev.Kind {
 | |
| 	case reflect.Ptr:
 | |
| 		if xev == nilVariable {
 | |
| 			stack.err = cantindex
 | |
| 			return
 | |
| 		}
 | |
| 		if xev.Flags&VariableCPtr == 0 {
 | |
| 			_, isarrptr := xev.RealType.(*godwarf.PtrType).Type.(*godwarf.ArrayType)
 | |
| 			if !isarrptr {
 | |
| 				stack.err = cantindex
 | |
| 				return
 | |
| 			}
 | |
| 			xev = xev.maybeDereference()
 | |
| 		}
 | |
| 		fallthrough
 | |
| 
 | |
| 	case reflect.Slice, reflect.Array, reflect.String:
 | |
| 		if xev.Base == 0 {
 | |
| 			stack.err = fmt.Errorf("can not index %q", astutil.ExprToString(op.Node.X))
 | |
| 			return
 | |
| 		}
 | |
| 		n, err := idxev.asInt()
 | |
| 		if err != nil {
 | |
| 			stack.err = err
 | |
| 			return
 | |
| 		}
 | |
| 		stack.pushErr(xev.sliceAccess(int(n)))
 | |
| 		return
 | |
| 
 | |
| 	case reflect.Map:
 | |
| 		idxev.loadValue(loadFullValue)
 | |
| 		if idxev.Unreadable != nil {
 | |
| 			stack.err = idxev.Unreadable
 | |
| 			return
 | |
| 		}
 | |
| 		stack.pushErr(xev.mapAccess(idxev))
 | |
| 		return
 | |
| 	default:
 | |
| 		stack.err = cantindex
 | |
| 		return
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Evaluates expressions <subexpr>[<subexpr>:<subexpr>]
 | |
| // HACK: slicing a map expression with [0:0] will return the whole map
 | |
| func (scope *EvalScope) evalReslice(op *evalop.Reslice, stack *evalStack) {
 | |
| 	low, err := stack.pop().asInt()
 | |
| 	if err != nil {
 | |
| 		stack.err = err
 | |
| 		return
 | |
| 	}
 | |
| 	var high int64
 | |
| 	if op.HasHigh {
 | |
| 		high, err = stack.pop().asInt()
 | |
| 		if err != nil {
 | |
| 			stack.err = err
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	xev := stack.pop()
 | |
| 	if xev.Unreadable != nil {
 | |
| 		stack.err = xev.Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 	if !op.HasHigh {
 | |
| 		high = xev.Len
 | |
| 	}
 | |
| 
 | |
| 	switch xev.Kind {
 | |
| 	case reflect.Slice, reflect.Array, reflect.String:
 | |
| 		if xev.Base == 0 {
 | |
| 			stack.err = fmt.Errorf("can not slice %q", astutil.ExprToString(op.Node.X))
 | |
| 			return
 | |
| 		}
 | |
| 		stack.pushErr(xev.reslice(low, high, op.TrustLen))
 | |
| 		return
 | |
| 	case reflect.Map:
 | |
| 		if op.Node.High != nil {
 | |
| 			stack.err = errors.New("second slice argument must be empty for maps")
 | |
| 			return
 | |
| 		}
 | |
| 		xev.mapSkip += int(low)
 | |
| 		xev.mapIterator(0) // reads map length
 | |
| 		if int64(xev.mapSkip) >= xev.Len {
 | |
| 			stack.err = errors.New("map index out of bounds")
 | |
| 			return
 | |
| 		}
 | |
| 		stack.push(xev)
 | |
| 		return
 | |
| 	case reflect.Ptr:
 | |
| 		if xev.Flags&VariableCPtr != 0 {
 | |
| 			stack.pushErr(xev.reslice(low, high, op.TrustLen))
 | |
| 			return
 | |
| 		}
 | |
| 		fallthrough
 | |
| 	default:
 | |
| 		stack.err = fmt.Errorf("can not slice %q (type %s)", astutil.ExprToString(op.Node.X), xev.TypeString())
 | |
| 		return
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Evaluates a pointer dereference expression: *<subexpr>
 | |
| func (scope *EvalScope) evalPointerDeref(op *evalop.PointerDeref, stack *evalStack) {
 | |
| 	xev := stack.pop()
 | |
| 
 | |
| 	if xev.Kind != reflect.Ptr {
 | |
| 		stack.err = fmt.Errorf("expression %q (%s) can not be dereferenced", astutil.ExprToString(op.Node.X), xev.TypeString())
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if xev == nilVariable {
 | |
| 		stack.err = errors.New("nil can not be dereferenced")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if len(xev.Children) == 1 {
 | |
| 		// this branch is here to support pointers constructed with typecasts from ints
 | |
| 		xev.Children[0].OnlyAddr = false
 | |
| 		stack.push(&(xev.Children[0]))
 | |
| 		return
 | |
| 	}
 | |
| 	xev.loadPtr()
 | |
| 	if xev.Unreadable != nil {
 | |
| 		val, ok := constant.Uint64Val(xev.Value)
 | |
| 		if ok && val == 0 {
 | |
| 			stack.err = fmt.Errorf("couldn't read pointer: %w", xev.Unreadable)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	rv := &xev.Children[0]
 | |
| 	if rv.Addr == 0 {
 | |
| 		stack.err = errors.New("nil pointer dereference")
 | |
| 		return
 | |
| 	}
 | |
| 	stack.push(rv)
 | |
| }
 | |
| 
 | |
| // Evaluates expressions &<subexpr>
 | |
| func (scope *EvalScope) evalAddrOf(op *evalop.AddrOf, stack *evalStack) {
 | |
| 	xev := stack.pop()
 | |
| 	if xev.Addr == 0 || xev.DwarfType == nil {
 | |
| 		stack.err = fmt.Errorf("can not take address of %q", astutil.ExprToString(op.Node.X))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	stack.push(xev.pointerToVariable())
 | |
| }
 | |
| 
 | |
| func (v *Variable) pointerToVariable() *Variable {
 | |
| 	v.OnlyAddr = true
 | |
| 
 | |
| 	rv := v.newVariable("", 0, godwarf.FakePointerType(v.DwarfType, int64(v.bi.Arch.PtrSize())), v.mem)
 | |
| 	rv.Children = []Variable{*v}
 | |
| 	rv.loaded = true
 | |
| 
 | |
| 	return rv
 | |
| }
 | |
| 
 | |
| func constantUnaryOp(op token.Token, y constant.Value) (r constant.Value, err error) {
 | |
| 	defer func() {
 | |
| 		if ierr := recover(); ierr != nil {
 | |
| 			err = fmt.Errorf("%v", ierr)
 | |
| 		}
 | |
| 	}()
 | |
| 	r = constant.UnaryOp(op, y, 0)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func constantBinaryOp(op token.Token, x, y constant.Value) (r constant.Value, err error) {
 | |
| 	defer func() {
 | |
| 		if ierr := recover(); ierr != nil {
 | |
| 			err = fmt.Errorf("%v", ierr)
 | |
| 		}
 | |
| 	}()
 | |
| 	switch op {
 | |
| 	case token.SHL, token.SHR:
 | |
| 		n, _ := constant.Uint64Val(y)
 | |
| 		r = constant.Shift(x, op, uint(n))
 | |
| 	default:
 | |
| 		r = constant.BinaryOp(x, op, y)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func constantCompare(op token.Token, x, y constant.Value) (r bool, err error) {
 | |
| 	defer func() {
 | |
| 		if ierr := recover(); ierr != nil {
 | |
| 			err = fmt.Errorf("%v", ierr)
 | |
| 		}
 | |
| 	}()
 | |
| 	r = constant.Compare(x, op, y)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Evaluates expressions: -<subexpr> and +<subexpr>
 | |
| func (scope *EvalScope) evalUnary(op *evalop.Unary, stack *evalStack) {
 | |
| 	xv := stack.pop()
 | |
| 
 | |
| 	xv.loadValue(loadSingleValue)
 | |
| 	if xv.Unreadable != nil {
 | |
| 		stack.err = xv.Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 	if xv.FloatSpecial != 0 {
 | |
| 		stack.err = errOperationOnSpecialFloat
 | |
| 		return
 | |
| 	}
 | |
| 	if xv.Value == nil {
 | |
| 		stack.err = fmt.Errorf("operator %s can not be applied to %q", op.Node.Op.String(), astutil.ExprToString(op.Node.X))
 | |
| 		return
 | |
| 	}
 | |
| 	rc, err := constantUnaryOp(op.Node.Op, xv.Value)
 | |
| 	if err != nil {
 | |
| 		stack.err = err
 | |
| 		return
 | |
| 	}
 | |
| 	if xv.DwarfType != nil {
 | |
| 		r := xv.newVariable("", 0, xv.DwarfType, scope.Mem)
 | |
| 		r.Value = rc
 | |
| 		stack.push(r)
 | |
| 		return
 | |
| 	}
 | |
| 	stack.push(newConstant(rc, xv.bi, xv.mem))
 | |
| }
 | |
| 
 | |
| func negotiateType(op token.Token, xv, yv *Variable) (godwarf.Type, error) {
 | |
| 	if xv == nilVariable {
 | |
| 		return nil, negotiateTypeNil(op, yv)
 | |
| 	}
 | |
| 
 | |
| 	if yv == nilVariable {
 | |
| 		return nil, negotiateTypeNil(op, xv)
 | |
| 	}
 | |
| 
 | |
| 	if op == token.SHR || op == token.SHL {
 | |
| 		if xv.Value == nil || xv.Value.Kind() != constant.Int {
 | |
| 			return nil, fmt.Errorf("shift of type %s", xv.Kind)
 | |
| 		}
 | |
| 
 | |
| 		switch yv.Kind {
 | |
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | |
| 			// ok
 | |
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 			if constant.Sign(yv.Value) < 0 {
 | |
| 				return nil, errors.New("shift count must not be negative")
 | |
| 			}
 | |
| 		default:
 | |
| 			return nil, fmt.Errorf("shift count type %s, must be unsigned integer", yv.Kind.String())
 | |
| 		}
 | |
| 
 | |
| 		return xv.DwarfType, nil
 | |
| 	}
 | |
| 
 | |
| 	if xv.DwarfType == nil && yv.DwarfType == nil {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	if xv.DwarfType != nil && yv.DwarfType != nil {
 | |
| 		if xv.DwarfType.String() != yv.DwarfType.String() {
 | |
| 			return nil, fmt.Errorf("mismatched types %q and %q", xv.DwarfType.String(), yv.DwarfType.String())
 | |
| 		}
 | |
| 		return xv.DwarfType, nil
 | |
| 	} else if xv.DwarfType != nil && yv.DwarfType == nil {
 | |
| 		if err := yv.isType(xv.DwarfType, xv.Kind); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return xv.DwarfType, nil
 | |
| 	} else if xv.DwarfType == nil && yv.DwarfType != nil {
 | |
| 		if err := xv.isType(yv.DwarfType, yv.Kind); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return yv.DwarfType, nil
 | |
| 	}
 | |
| 
 | |
| 	panic("unreachable")
 | |
| }
 | |
| 
 | |
| func negotiateTypeNil(op token.Token, v *Variable) error {
 | |
| 	if op != token.EQL && op != token.NEQ {
 | |
| 		return fmt.Errorf("operator %s can not be applied to \"nil\"", op.String())
 | |
| 	}
 | |
| 	switch v.Kind {
 | |
| 	case reflect.Ptr, reflect.UnsafePointer, reflect.Chan, reflect.Map, reflect.Interface, reflect.Slice, reflect.Func:
 | |
| 		return nil
 | |
| 	default:
 | |
| 		return fmt.Errorf("can not compare %s to nil", v.Kind.String())
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (scope *EvalScope) evalBinary(binop *evalop.Binary, stack *evalStack) {
 | |
| 	node := binop.Node
 | |
| 
 | |
| 	yv := stack.pop()
 | |
| 	xv := stack.pop()
 | |
| 
 | |
| 	if xv.Kind != reflect.String { // delay loading strings until we use them
 | |
| 		xv.loadValue(loadFullValue)
 | |
| 	}
 | |
| 	if xv.Unreadable != nil {
 | |
| 		stack.err = xv.Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 	if yv.Kind != reflect.String { // delay loading strings until we use them
 | |
| 		yv.loadValue(loadFullValue)
 | |
| 	}
 | |
| 	if yv.Unreadable != nil {
 | |
| 		stack.err = yv.Unreadable
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
 | |
| 		stack.err = errOperationOnSpecialFloat
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	typ, err := negotiateType(node.Op, xv, yv)
 | |
| 	if err != nil {
 | |
| 		stack.err = err
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	op := node.Op
 | |
| 	if op == token.QUO {
 | |
| 		if typ != nil {
 | |
| 			_, isint := typ.(*godwarf.IntType)
 | |
| 			_, isuint := typ.(*godwarf.UintType)
 | |
| 			if isint || isuint {
 | |
| 				// forces integer division if the result type is integer
 | |
| 				op = token.QUO_ASSIGN
 | |
| 			}
 | |
| 		} else {
 | |
| 			if xv.Value != nil && yv.Value != nil && xv.Value.Kind() == constant.Int && yv.Value.Kind() == constant.Int {
 | |
| 				// See issue #3793 and the specification at https://go.dev/ref/spec#Constant_expressions
 | |
| 				// in particular:
 | |
| 				//
 | |
| 				// "If the untyped operands of a binary operation (other than a shift)
 | |
| 				// are of different kinds, the result is of the operand's kind that
 | |
| 				// appears later in this list: integer, rune, floating-point, complex"
 | |
| 				//
 | |
| 				// However the go/constant package says that to get an integer result
 | |
| 				// from a division token.QUO_ASSIGN must be used.
 | |
| 				op = token.QUO_ASSIGN
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	switch op {
 | |
| 	case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ:
 | |
| 		v, err := compareOp(op, xv, yv)
 | |
| 		if err != nil {
 | |
| 			stack.err = err
 | |
| 			return
 | |
| 		}
 | |
| 		stack.push(newConstant(constant.MakeBool(v), xv.bi, xv.mem))
 | |
| 
 | |
| 	default:
 | |
| 		if xv.Kind == reflect.String {
 | |
| 			xv.loadValue(loadFullValueLongerStrings)
 | |
| 		}
 | |
| 		if yv.Kind == reflect.String {
 | |
| 			yv.loadValue(loadFullValueLongerStrings)
 | |
| 		}
 | |
| 		if xv.Value == nil {
 | |
| 			stack.err = fmt.Errorf("operator %s can not be applied to %q", node.Op.String(), astutil.ExprToString(node.X))
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if yv.Value == nil {
 | |
| 			stack.err = fmt.Errorf("operator %s can not be applied to %q", node.Op.String(), astutil.ExprToString(node.Y))
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		rc, err := constantBinaryOp(op, xv.Value, yv.Value)
 | |
| 		if err != nil {
 | |
| 			stack.err = err
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if typ == nil {
 | |
| 			stack.push(newConstant(rc, xv.bi, xv.mem))
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		r := xv.newVariable("", 0, typ, scope.Mem)
 | |
| 		r.Value = rc
 | |
| 		switch r.Kind {
 | |
| 		case reflect.String:
 | |
| 			r.Len = xv.Len + yv.Len
 | |
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 			n, _ := constant.Int64Val(r.Value)
 | |
| 			r.Value = constant.MakeInt64(int64(convertInt(uint64(n), true, typ.Size())))
 | |
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | |
| 			n, _ := constant.Uint64Val(r.Value)
 | |
| 			r.Value = constant.MakeUint64(convertInt(n, false, typ.Size()))
 | |
| 		}
 | |
| 		stack.push(r)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Compares xv to yv using operator op
 | |
| // Both xv and yv must be loaded and have a compatible type (as determined by negotiateType)
 | |
| func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
 | |
| 	switch xv.Kind {
 | |
| 	case reflect.Bool:
 | |
| 		fallthrough
 | |
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 		fallthrough
 | |
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | |
| 		fallthrough
 | |
| 	case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
 | |
| 		return constantCompare(op, xv.Value, yv.Value)
 | |
| 	case reflect.String:
 | |
| 		if xv.Len != yv.Len {
 | |
| 			switch op {
 | |
| 			case token.EQL:
 | |
| 				return false, nil
 | |
| 			case token.NEQ:
 | |
| 				return true, nil
 | |
| 			}
 | |
| 		}
 | |
| 		if xv.Kind == reflect.String {
 | |
| 			xv.loadValue(loadFullValueLongerStrings)
 | |
| 		}
 | |
| 		if yv.Kind == reflect.String {
 | |
| 			yv.loadValue(loadFullValueLongerStrings)
 | |
| 		}
 | |
| 		if int64(len(constant.StringVal(xv.Value))) != xv.Len || int64(len(constant.StringVal(yv.Value))) != yv.Len {
 | |
| 			return false, errors.New("string too long for comparison")
 | |
| 		}
 | |
| 		return constantCompare(op, xv.Value, yv.Value)
 | |
| 	}
 | |
| 
 | |
| 	if op != token.EQL && op != token.NEQ {
 | |
| 		return false, fmt.Errorf("operator %s not defined on %s", op.String(), xv.Kind.String())
 | |
| 	}
 | |
| 
 | |
| 	var eql bool
 | |
| 	var err error
 | |
| 
 | |
| 	if xv == nilVariable {
 | |
| 		switch op {
 | |
| 		case token.EQL:
 | |
| 			return yv.isNil(), nil
 | |
| 		case token.NEQ:
 | |
| 			return !yv.isNil(), nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if yv == nilVariable {
 | |
| 		switch op {
 | |
| 		case token.EQL:
 | |
| 			return xv.isNil(), nil
 | |
| 		case token.NEQ:
 | |
| 			return !xv.isNil(), nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	switch xv.Kind {
 | |
| 	case reflect.Ptr:
 | |
| 		eql = xv.Children[0].Addr == yv.Children[0].Addr
 | |
| 	case reflect.Array:
 | |
| 		if int64(len(xv.Children)) != xv.Len || int64(len(yv.Children)) != yv.Len {
 | |
| 			return false, errors.New("array too long for comparison")
 | |
| 		}
 | |
| 		eql, err = equalChildren(xv, yv, true)
 | |
| 	case reflect.Struct:
 | |
| 		if len(xv.Children) != len(yv.Children) {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 		if int64(len(xv.Children)) != xv.Len || int64(len(yv.Children)) != yv.Len {
 | |
| 			return false, errors.New("structure too deep for comparison")
 | |
| 		}
 | |
| 		eql, err = equalChildren(xv, yv, false)
 | |
| 	case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
 | |
| 		return false, fmt.Errorf("can not compare %s variables", xv.Kind.String())
 | |
| 	case reflect.Interface:
 | |
| 		if xv.Children[0].RealType.String() != yv.Children[0].RealType.String() {
 | |
| 			eql = false
 | |
| 		} else {
 | |
| 			eql, err = compareOp(token.EQL, &xv.Children[0], &yv.Children[0])
 | |
| 		}
 | |
| 	default:
 | |
| 		return false, fmt.Errorf("unimplemented comparison of %s variables", xv.Kind.String())
 | |
| 	}
 | |
| 
 | |
| 	if op == token.NEQ {
 | |
| 		return !eql, err
 | |
| 	}
 | |
| 	return eql, err
 | |
| }
 | |
| 
 | |
| func (v *Variable) isNil() bool {
 | |
| 	switch v.Kind {
 | |
| 	case reflect.Ptr:
 | |
| 		return v.Children[0].Addr == 0
 | |
| 	case reflect.Interface:
 | |
| 		return v.Children[0].Addr == 0 && v.Children[0].Kind == reflect.Invalid
 | |
| 	case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
 | |
| 		return v.Base == 0
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func equalChildren(xv, yv *Variable, shortcircuit bool) (bool, error) {
 | |
| 	r := true
 | |
| 	for i := range xv.Children {
 | |
| 		eql, err := compareOp(token.EQL, &xv.Children[i], &yv.Children[i])
 | |
| 		if err != nil {
 | |
| 			return false, err
 | |
| 		}
 | |
| 		r = r && eql
 | |
| 		if !r && shortcircuit {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return r, nil
 | |
| }
 | |
| 
 | |
| func (v *Variable) asInt() (int64, error) {
 | |
| 	if v.DwarfType == nil {
 | |
| 		if v.Value.Kind() != constant.Int {
 | |
| 			return 0, fmt.Errorf("can not convert constant %s to int", v.Value)
 | |
| 		}
 | |
| 	} else {
 | |
| 		v.loadValue(loadSingleValue)
 | |
| 		if v.Unreadable != nil {
 | |
| 			return 0, v.Unreadable
 | |
| 		}
 | |
| 		if _, ok := v.DwarfType.(*godwarf.IntType); !ok {
 | |
| 			return 0, fmt.Errorf("can not convert value of type %s to int", v.DwarfType.String())
 | |
| 		}
 | |
| 	}
 | |
| 	n, _ := constant.Int64Val(v.Value)
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| func (v *Variable) asUint() (uint64, error) {
 | |
| 	if v.DwarfType == nil {
 | |
| 		if v.Value.Kind() != constant.Int {
 | |
| 			return 0, fmt.Errorf("can not convert constant %s to uint", v.Value)
 | |
| 		}
 | |
| 	} else {
 | |
| 		v.loadValue(loadSingleValue)
 | |
| 		if v.Unreadable != nil {
 | |
| 			return 0, v.Unreadable
 | |
| 		}
 | |
| 		if _, ok := v.DwarfType.(*godwarf.UintType); !ok {
 | |
| 			return 0, fmt.Errorf("can not convert value of type %s to uint", v.DwarfType.String())
 | |
| 		}
 | |
| 	}
 | |
| 	n, _ := constant.Uint64Val(v.Value)
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| type typeConvErr struct {
 | |
| 	srcType, dstType godwarf.Type
 | |
| }
 | |
| 
 | |
| func (err *typeConvErr) Error() string {
 | |
| 	return fmt.Sprintf("can not convert value of type %s to %s", err.srcType.String(), err.dstType.String())
 | |
| }
 | |
| 
 | |
| func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
 | |
| 	if v.DwarfType != nil {
 | |
| 		if typ == nil || !sameType(typ, v.RealType) {
 | |
| 			return &typeConvErr{v.DwarfType, typ}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if typ == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if v == nilVariable {
 | |
| 		switch kind {
 | |
| 		case reflect.Slice, reflect.Map, reflect.Func, reflect.Ptr, reflect.Chan, reflect.Interface:
 | |
| 			return nil
 | |
| 		default:
 | |
| 			return fmt.Errorf("mismatched types nil and %s", typ.String())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	converr := fmt.Errorf("can not convert %s constant to %s", v.Value, typ.String())
 | |
| 
 | |
| 	if v.Value == nil {
 | |
| 		return converr
 | |
| 	}
 | |
| 
 | |
| 	switch typ.(type) {
 | |
| 	case *godwarf.IntType:
 | |
| 		if v.Value.Kind() != constant.Int {
 | |
| 			return converr
 | |
| 		}
 | |
| 	case *godwarf.UintType:
 | |
| 		if v.Value.Kind() != constant.Int {
 | |
| 			return converr
 | |
| 		}
 | |
| 	case *godwarf.FloatType:
 | |
| 		if (v.Value.Kind() != constant.Int) && (v.Value.Kind() != constant.Float) {
 | |
| 			return converr
 | |
| 		}
 | |
| 	case *godwarf.BoolType:
 | |
| 		if v.Value.Kind() != constant.Bool {
 | |
| 			return converr
 | |
| 		}
 | |
| 	case *godwarf.StringType:
 | |
| 		if v.Value.Kind() != constant.String {
 | |
| 			return converr
 | |
| 		}
 | |
| 	case *godwarf.ComplexType:
 | |
| 		if v.Value.Kind() != constant.Complex && v.Value.Kind() != constant.Float && v.Value.Kind() != constant.Int {
 | |
| 			return converr
 | |
| 		}
 | |
| 	default:
 | |
| 		return converr
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func sameType(t1, t2 godwarf.Type) bool {
 | |
| 	// Because of a bug in the go linker a type that refers to another type
 | |
| 	// (for example a pointer type) will usually use the typedef but rarely use
 | |
| 	// the non-typedef entry directly.
 | |
| 	// For types that we read directly from go this is fine because it's
 | |
| 	// consistent, however we also synthesize some types ourselves
 | |
| 	// (specifically pointers and slices) and we always use a reference through
 | |
| 	// a typedef.
 | |
| 	t1 = godwarf.ResolveTypedef(t1)
 | |
| 	t2 = godwarf.ResolveTypedef(t2)
 | |
| 
 | |
| 	if tt1, isptr1 := t1.(*godwarf.PtrType); isptr1 {
 | |
| 		tt2, isptr2 := t2.(*godwarf.PtrType)
 | |
| 		if !isptr2 {
 | |
| 			return false
 | |
| 		}
 | |
| 		return sameType(tt1.Type, tt2.Type)
 | |
| 	}
 | |
| 	if tt1, isslice1 := t1.(*godwarf.SliceType); isslice1 {
 | |
| 		tt2, isslice2 := t2.(*godwarf.SliceType)
 | |
| 		if !isslice2 {
 | |
| 			return false
 | |
| 		}
 | |
| 		return sameType(tt1.ElemType, tt2.ElemType)
 | |
| 	}
 | |
| 	return t1.String() == t2.String()
 | |
| }
 | |
| 
 | |
| func (v *Variable) sliceAccess(idx int) (*Variable, error) {
 | |
| 	wrong := false
 | |
| 	if v.Flags&VariableCPtr == 0 {
 | |
| 		wrong = idx < 0 || int64(idx) >= v.Len
 | |
| 	} else {
 | |
| 		wrong = idx < 0
 | |
| 	}
 | |
| 	if wrong {
 | |
| 		return nil, errors.New("index out of bounds")
 | |
| 	}
 | |
| 	if v.loaded {
 | |
| 		if v.Kind == reflect.String {
 | |
| 			s := constant.StringVal(v.Value)
 | |
| 			if idx >= len(s) {
 | |
| 				return nil, errors.New("index out of bounds")
 | |
| 			}
 | |
| 			r := v.newVariable("", v.Base+uint64(int64(idx)*v.stride), v.fieldType, v.mem)
 | |
| 			r.loaded = true
 | |
| 			r.Value = constant.MakeInt64(int64(s[idx]))
 | |
| 			return r, nil
 | |
| 		} else {
 | |
| 			if idx >= len(v.Children) {
 | |
| 				return nil, errors.New("index out of bounds")
 | |
| 			}
 | |
| 			return &v.Children[idx], nil
 | |
| 		}
 | |
| 	}
 | |
| 	mem := v.mem
 | |
| 	if v.Kind != reflect.Array {
 | |
| 		mem = DereferenceMemory(mem)
 | |
| 	}
 | |
| 	return v.newVariable("", v.Base+uint64(int64(idx)*v.stride), v.fieldType, mem), nil
 | |
| }
 | |
| 
 | |
| func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
 | |
| 	it := v.mapIterator(0)
 | |
| 	if it == nil {
 | |
| 		return nil, fmt.Errorf("can not access unreadable map: %v", v.Unreadable)
 | |
| 	}
 | |
| 
 | |
| 	lcfg := loadFullValue
 | |
| 	if idx.Kind == reflect.String && int64(len(constant.StringVal(idx.Value))) == idx.Len && idx.Len > int64(lcfg.MaxStringLen) {
 | |
| 		// If the index is a string load as much of the keys to at least match the length of the index.
 | |
| 		//TODO(aarzilli): when struct literals are implemented this needs to be
 | |
| 		//done recursively for literal struct fields.
 | |
| 		lcfg.MaxStringLen = int(idx.Len)
 | |
| 	}
 | |
| 
 | |
| 	first := true
 | |
| 	for it.next() {
 | |
| 		key := it.key()
 | |
| 		key.loadValue(lcfg)
 | |
| 		if key.Unreadable != nil {
 | |
| 			return nil, fmt.Errorf("can not access unreadable map: %v", key.Unreadable)
 | |
| 		}
 | |
| 		if first {
 | |
| 			first = false
 | |
| 			if err := idx.isType(key.RealType, key.Kind); err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 		}
 | |
| 		eql, err := compareOp(token.EQL, key, idx)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if eql {
 | |
| 			return it.value(), nil
 | |
| 		}
 | |
| 	}
 | |
| 	if v.Unreadable != nil {
 | |
| 		return nil, v.Unreadable
 | |
| 	}
 | |
| 	// go would return zero for the map value type here, we do not have the ability to create zeroes
 | |
| 	return nil, errors.New("key not found")
 | |
| }
 | |
| 
 | |
| // LoadResliced returns a new array, slice or map that starts at index start and contains
 | |
| // up to cfg.MaxArrayValues children.
 | |
| func (v *Variable) LoadResliced(start int, cfg LoadConfig) (newV *Variable, err error) {
 | |
| 	switch v.Kind {
 | |
| 	case reflect.Array, reflect.Slice:
 | |
| 		low, high := int64(start), int64(start+cfg.MaxArrayValues)
 | |
| 		if high > v.Len {
 | |
| 			high = v.Len
 | |
| 		}
 | |
| 		newV, err = v.reslice(low, high, false)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	case reflect.Map:
 | |
| 		newV = v.clone()
 | |
| 		newV.Children = nil
 | |
| 		newV.loaded = false
 | |
| 		newV.mapSkip = start
 | |
| 	default:
 | |
| 		return nil, errors.New("variable to reslice is not an array, slice, or map")
 | |
| 	}
 | |
| 	newV.loadValue(cfg)
 | |
| 	return newV, nil
 | |
| }
 | |
| 
 | |
| func (v *Variable) reslice(low int64, high int64, trustLen bool) (*Variable, error) {
 | |
| 	wrong := false
 | |
| 	cptrNeedsFakeSlice := false
 | |
| 	if v.Flags&VariableCPtr == 0 {
 | |
| 		if v.Kind == reflect.Slice {
 | |
| 			wrong = low < 0 || low > v.Cap || high < 0 || high > v.Cap
 | |
| 		} else {
 | |
| 			wrong = low < 0 || low > v.Len || high < 0 || high > v.Len
 | |
| 		}
 | |
| 	} else {
 | |
| 		wrong = low < 0 || high < 0
 | |
| 		if high == 0 {
 | |
| 			high = low
 | |
| 		}
 | |
| 		cptrNeedsFakeSlice = v.Kind != reflect.String
 | |
| 	}
 | |
| 	if wrong {
 | |
| 		return nil, errors.New("index out of bounds")
 | |
| 	}
 | |
| 
 | |
| 	base := v.Base + uint64(low*v.stride)
 | |
| 	len := high - low
 | |
| 
 | |
| 	if high-low < 0 {
 | |
| 		return nil, errors.New("index out of bounds")
 | |
| 	}
 | |
| 
 | |
| 	typ := v.DwarfType
 | |
| 	if _, isarr := v.DwarfType.(*godwarf.ArrayType); isarr || cptrNeedsFakeSlice {
 | |
| 		typ = godwarf.FakeSliceType(v.fieldType)
 | |
| 	}
 | |
| 
 | |
| 	mem := v.mem
 | |
| 	if v.Kind != reflect.Array {
 | |
| 		mem = DereferenceMemory(mem)
 | |
| 	}
 | |
| 
 | |
| 	r := v.newVariable("", 0, typ, mem)
 | |
| 	if v.Flags&VariableCPtr == 0 {
 | |
| 		r.Cap = v.Cap - low
 | |
| 	} else {
 | |
| 		r.Cap = len
 | |
| 	}
 | |
| 	r.Len = len
 | |
| 	r.Base = base
 | |
| 	r.stride = v.stride
 | |
| 	r.fieldType = v.fieldType
 | |
| 	r.Flags = v.Flags
 | |
| 	if trustLen {
 | |
| 		r.Flags |= variableTrustLen
 | |
| 	}
 | |
| 	r.reg = v.reg
 | |
| 
 | |
| 	return r, nil
 | |
| }
 | |
| 
 | |
| // findStructMemberOrMethod finds struct member or method name in the type of variable v
 | |
| func (v *Variable) findStructMemberOrMethod(name string, includeStructMember bool) (*Variable, error) {
 | |
| 	if v.Unreadable != nil {
 | |
| 		return v.clone(), nil
 | |
| 	}
 | |
| 	vname := v.Name
 | |
| 	if v.Name == "" {
 | |
| 		vname = v.DwarfType.String()
 | |
| 	}
 | |
| 	if v.loaded && (v.Flags&VariableFakeAddress) != 0 {
 | |
| 		for i := range v.Children {
 | |
| 			if v.Children[i].Name == name {
 | |
| 				return &v.Children[i], nil
 | |
| 			}
 | |
| 		}
 | |
| 		return nil, fmt.Errorf("%s has no member %s", vname, name)
 | |
| 	}
 | |
| 	closure := false
 | |
| 	switch v.Kind {
 | |
| 	case reflect.Chan:
 | |
| 		v = v.clone()
 | |
| 		v.RealType = godwarf.ResolveTypedef(&(v.RealType.(*godwarf.ChanType).TypedefType))
 | |
| 	case reflect.Interface:
 | |
| 		v.loadInterface(0, false, LoadConfig{})
 | |
| 		if len(v.Children) > 0 {
 | |
| 			v = &v.Children[0]
 | |
| 		}
 | |
| 	case reflect.Func:
 | |
| 		fn := v.bi.PCToFunc(v.Base)
 | |
| 		v.loadFunctionPtr(0, LoadConfig{MaxVariableRecurse: -1})
 | |
| 		if v.Unreadable != nil {
 | |
| 			cst := fn.extra(v.bi).closureStructType
 | |
| 			if cst == nil || cst.ByteSize == 0 {
 | |
| 				// Not a closure, normal function
 | |
| 				if _, ok := v.bi.PackageMap[vname]; ok {
 | |
| 					return nil, fmt.Errorf("package %s has no function %s", vname, name)
 | |
| 				}
 | |
| 				return nil, fmt.Errorf("%s has no member %s", vname, name)
 | |
| 			}
 | |
| 			return nil, v.Unreadable
 | |
| 		}
 | |
| 		if v.closureAddr != 0 {
 | |
| 			fn = v.bi.PCToFunc(v.Base)
 | |
| 			if fn != nil {
 | |
| 				cst := fn.extra(v.bi).closureStructType
 | |
| 				v = v.newVariable(v.Name, v.closureAddr, cst, v.mem)
 | |
| 				closure = true
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	queue := []*Variable{v}
 | |
| 	seen := map[string]struct{}{}
 | |
| 	first := true
 | |
| 
 | |
| 	for len(queue) > 0 {
 | |
| 		v := queue[0]
 | |
| 		queue = append(queue[:0], queue[1:]...)
 | |
| 		if _, isseen := seen[v.RealType.String()]; isseen {
 | |
| 			continue
 | |
| 		}
 | |
| 		seen[v.RealType.String()] = struct{}{}
 | |
| 
 | |
| 		typ := v.DwarfType
 | |
| 		ptyp, isptr := typ.(*godwarf.PtrType)
 | |
| 		if isptr {
 | |
| 			typ = ptyp.Type
 | |
| 		}
 | |
| 
 | |
| 		var pkg, receiver string
 | |
| 		typePath := typ.Common().Name
 | |
| 		dot := strings.LastIndex(typePath, ".")
 | |
| 		if dot >= 0 {
 | |
| 			pkg, receiver = typePath[:dot], typePath[dot+1:]
 | |
| 		}
 | |
| 
 | |
| 		if len(pkg) > 0 && len(receiver) > 0 {
 | |
| 			rv, err := lookupMethod(v, isptr, pkg, receiver, name)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			} else if rv != nil {
 | |
| 				return rv, nil
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// queue embedded fields for search
 | |
| 		structVar := v.maybeDereference()
 | |
| 		structVar.Name = v.Name
 | |
| 		if structVar.Unreadable != nil {
 | |
| 			return structVar, nil
 | |
| 		}
 | |
| 		switch t := structVar.RealType.(type) {
 | |
| 		case *godwarf.StructType:
 | |
| 			for _, field := range t.Field {
 | |
| 				if !includeStructMember {
 | |
| 					if !field.Embedded {
 | |
| 						continue
 | |
| 					}
 | |
| 					embeddedVar, err := structVar.toField(field)
 | |
| 					if err != nil {
 | |
| 						return nil, err
 | |
| 					}
 | |
| 					queue = append(queue, embeddedVar)
 | |
| 					continue
 | |
| 				}
 | |
| 
 | |
| 				if field.Name == name {
 | |
| 					return structVar.toField(field)
 | |
| 				}
 | |
| 				if len(queue) == 0 && field.Name == "&"+name && closure {
 | |
| 					f, err := structVar.toField(field)
 | |
| 					if err != nil {
 | |
| 						return nil, err
 | |
| 					}
 | |
| 					return f.maybeDereference(), nil
 | |
| 				}
 | |
| 				if !field.Embedded {
 | |
| 					continue
 | |
| 				}
 | |
| 				embeddedVar, err := structVar.toField(field)
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 				// Check for embedded field referenced by type name
 | |
| 				parts := strings.Split(field.Name, ".")
 | |
| 				if includeStructMember && len(parts) > 1 && parts[1] == name {
 | |
| 					return embeddedVar, nil
 | |
| 				}
 | |
| 				embeddedVar.Name = structVar.Name
 | |
| 				queue = append(queue, embeddedVar)
 | |
| 			}
 | |
| 		case *godwarf.InterfaceType:
 | |
| 			v.loadInterface(0, false, LoadConfig{})
 | |
| 			if len(v.Children) > 0 {
 | |
| 				if rv, _ := v.Children[0].findStructMemberOrMethod(name, false); rv != nil {
 | |
| 					return rv, nil
 | |
| 				}
 | |
| 			}
 | |
| 		default:
 | |
| 			if first {
 | |
| 				return nil, fmt.Errorf("%s (type %s) has no member %s", vname, structVar.TypeString(), name)
 | |
| 			}
 | |
| 		}
 | |
| 		first = false
 | |
| 	}
 | |
| 
 | |
| 	return nil, fmt.Errorf("%s has no member %s", vname, name)
 | |
| }
 | |
| 
 | |
| func lookupMethod(v *Variable, isptr bool, pkg, receiver, name string) (*Variable, error) {
 | |
| 	checks := []struct {
 | |
| 		fmt     string
 | |
| 		ptrRecv bool
 | |
| 	}{
 | |
| 		{"%s.(*%s).%s", true},
 | |
| 		{"%s.%s.%s", false},
 | |
| 	}
 | |
| 	if !isptr {
 | |
| 		checks[0], checks[1] = checks[1], checks[0]
 | |
| 	}
 | |
| 
 | |
| 	for _, check := range checks {
 | |
| 		if fns := v.bi.LookupFunc()[fmt.Sprintf(check.fmt, pkg, receiver, name)]; len(fns) == 1 {
 | |
| 			r, err := functionToVariable(fns[0], v.bi, v.mem)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			switch {
 | |
| 			case isptr == check.ptrRecv:
 | |
| 				r.Children = append(r.Children, *v)
 | |
| 			case isptr && !check.ptrRecv:
 | |
| 				r.Children = append(r.Children, *(v.maybeDereference()))
 | |
| 			case !isptr && check.ptrRecv:
 | |
| 				r.Children = append(r.Children, *(v.pointerToVariable()))
 | |
| 			}
 | |
| 			return r, nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil, nil
 | |
| }
 | |
| 
 | |
| func functionToVariable(fn *Function, bi *BinaryInfo, mem MemoryReadWriter) (*Variable, error) {
 | |
| 	typ, err := fn.fakeType(bi, true)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	v := newVariable(fn.Name, 0, typ, bi, mem)
 | |
| 	v.Value = constant.MakeString(fn.Name)
 | |
| 	v.loaded = true
 | |
| 	v.Base = fn.Entry
 | |
| 	return v, nil
 | |
| }
 | |
| 
 | |
| func fakeArrayType(n uint64, fieldType godwarf.Type) godwarf.Type {
 | |
| 	stride := alignAddr(fieldType.Common().ByteSize, fieldType.Align())
 | |
| 	return &godwarf.ArrayType{
 | |
| 		CommonType: godwarf.CommonType{
 | |
| 			ReflectKind: reflect.Array,
 | |
| 			ByteSize:    int64(n) * stride,
 | |
| 			Name:        fmt.Sprintf("[%d]%s", n, fieldType.String())},
 | |
| 		Type:          fieldType,
 | |
| 		StrideBitSize: stride * 8,
 | |
| 		Count:         int64(n)}
 | |
| }
 | |
| 
 | |
| var errMethodEvalUnsupported = errors.New("evaluating methods not supported on this version of Go")
 | |
| 
 | |
| func (fn *Function) fakeType(bi *BinaryInfo, removeReceiver bool) (*godwarf.FuncType, error) {
 | |
| 	if producer := bi.Producer(); producer == "" || !goversion.ProducerAfterOrEqual(producer, 1, 10) {
 | |
| 		// versions of Go prior to 1.10 do not distinguish between parameters and
 | |
| 		// return values, therefore we can't use a subprogram DIE to derive a
 | |
| 		// function type.
 | |
| 		return nil, errMethodEvalUnsupported
 | |
| 	}
 | |
| 	_, formalArgs, err := funcCallArgs(fn, bi, true)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Only try and remove the receiver if it is actually being passed in as a formal argument.
 | |
| 	// In the case of:
 | |
| 	//
 | |
| 	// func (_ X) Method() { ... }
 | |
| 	//
 | |
| 	// that would not be true, the receiver is not used and thus
 | |
| 	// not being passed in as a formal argument.
 | |
| 	//
 | |
| 	// TODO(derekparker) This, I think, creates a new bug where
 | |
| 	// if the receiver is not passed in as a formal argument but
 | |
| 	// there are other arguments, such as:
 | |
| 	//
 | |
| 	// func (_ X) Method(i int) { ... }
 | |
| 	//
 | |
| 	// The first argument 'i int' will be removed. We must actually detect
 | |
| 	// here if the receiver is being used. While this is a bug, it's not a
 | |
| 	// functional bug, it only affects the string representation of the fake
 | |
| 	// function type we create. It's not really easy to tell here if we use
 | |
| 	// the receiver or not. Perhaps we should not perform this manipulation at all?
 | |
| 	if removeReceiver && len(formalArgs) > 0 {
 | |
| 		formalArgs = formalArgs[1:]
 | |
| 	}
 | |
| 
 | |
| 	args := make([]string, 0, len(formalArgs))
 | |
| 	rets := make([]string, 0, len(formalArgs))
 | |
| 
 | |
| 	for _, formalArg := range formalArgs {
 | |
| 		var s string
 | |
| 		if strings.HasPrefix(formalArg.name, "~") {
 | |
| 			s = formalArg.typ.String()
 | |
| 		} else {
 | |
| 			s = fmt.Sprintf("%s %s", formalArg.name, formalArg.typ.String())
 | |
| 		}
 | |
| 		if formalArg.isret {
 | |
| 			rets = append(rets, s)
 | |
| 		} else {
 | |
| 			args = append(args, s)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	argstr := strings.Join(args, ", ")
 | |
| 	var retstr string
 | |
| 	switch len(rets) {
 | |
| 	case 0:
 | |
| 		retstr = ""
 | |
| 	case 1:
 | |
| 		retstr = " " + rets[0]
 | |
| 	default:
 | |
| 		retstr = " (" + strings.Join(rets, ", ") + ")"
 | |
| 	}
 | |
| 	return &godwarf.FuncType{
 | |
| 		CommonType: godwarf.CommonType{
 | |
| 			Name:        "func(" + argstr + ")" + retstr,
 | |
| 			ReflectKind: reflect.Func,
 | |
| 		},
 | |
| 		//TODO(aarzilli): at the moment we aren't using the ParamType and
 | |
| 		// ReturnType fields of FuncType anywhere (when this is returned to the
 | |
| 		// client it's first converted to a string and the function calling code
 | |
| 		// reads the subroutine entry because it needs to know the stack offsets).
 | |
| 		// If we start using them they should be filled here.
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func validRegisterName(s string) string {
 | |
| 	for len(s) > 0 && s[0] == '_' {
 | |
| 		s = s[1:]
 | |
| 	}
 | |
| 	for i := range s {
 | |
| 		if (s[i] < '0' || s[i] > '9') && (s[i] < 'A' || s[i] > 'Z') {
 | |
| 			return ""
 | |
| 		}
 | |
| 	}
 | |
| 	return s
 | |
| }
 | 
