mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-04 14:36:47 +08:00 
			
		
		
		
	When the function we are calling is an autogenerated stub (because, for example, we are calling it through a function pointer) the declaration line of variables is meaningless and could cause us to discard valid return arguments.
		
			
				
	
	
		
			2260 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			2260 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package proc
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"debug/dwarf"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"go/ast"
 | 
						|
	"go/constant"
 | 
						|
	"go/parser"
 | 
						|
	"go/printer"
 | 
						|
	"go/scanner"
 | 
						|
	"go/token"
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"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"
 | 
						|
)
 | 
						|
 | 
						|
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
 | 
						|
 | 
						|
// 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
 | 
						|
	BinInfo *BinaryInfo
 | 
						|
	target  *Target
 | 
						|
 | 
						|
	frameOffset int64
 | 
						|
 | 
						|
	// When the following pointer is not nil this EvalScope was created
 | 
						|
	// by CallFunction and the expression evaluation is executing on a
 | 
						|
	// different goroutine from the debugger's main goroutine.
 | 
						|
	// Under this circumstance the expression evaluator can make function
 | 
						|
	// calls by setting up the runtime.debugCallV1 call and then writing a
 | 
						|
	// value to the continueRequest channel.
 | 
						|
	// When a value is written to continueRequest the debugger's main goroutine
 | 
						|
	// will call Continue, when the runtime in the target process sends us a
 | 
						|
	// request in the function call protocol the debugger's main goroutine will
 | 
						|
	// write a value to the continueCompleted channel.
 | 
						|
	// The goroutine executing the expression evaluation shall signal that the
 | 
						|
	// evaluation is complete by closing the continueRequest channel.
 | 
						|
	callCtx *callContext
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
)
 | 
						|
 | 
						|
// 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, frame, deferCall int) (*EvalScope, error) {
 | 
						|
	if _, err := dbp.Valid(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	ct := dbp.CurrentThread()
 | 
						|
	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 {
 | 
						|
		locs, err = g.Stacktrace(frame+1, opts)
 | 
						|
	} else {
 | 
						|
		locs, err = ThreadStacktrace(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, 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, 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()}
 | 
						|
	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(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, 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(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
 | 
						|
	}
 | 
						|
	return FrameToScope(t, thread.ProcessMemory(), g, locations...), nil
 | 
						|
}
 | 
						|
 | 
						|
// EvalExpression returns the value of the given expression.
 | 
						|
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
 | 
						|
	if scope.callCtx != nil {
 | 
						|
		// makes sure that the other goroutine won't wait forever if we make a mistake
 | 
						|
		defer close(scope.callCtx.continueRequest)
 | 
						|
	}
 | 
						|
	t, err := parser.ParseExpr(expr)
 | 
						|
	if eqOff, isAs := isAssignment(err); scope.callCtx != nil && isAs {
 | 
						|
		lexpr := expr[:eqOff]
 | 
						|
		rexpr := expr[eqOff+1:]
 | 
						|
		err := scope.SetVariable(lexpr, rexpr)
 | 
						|
		scope.callCtx.doReturn(nil, err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		scope.callCtx.doReturn(nil, err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	ev, err := scope.evalToplevelTypeCast(t, cfg)
 | 
						|
	if ev == nil && err == nil {
 | 
						|
		ev, err = scope.evalAST(t)
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		scope.callCtx.doReturn(nil, err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	ev.loadValue(cfg)
 | 
						|
	if ev.Name == "" {
 | 
						|
		ev.Name = expr
 | 
						|
	}
 | 
						|
	scope.callCtx.doReturn(ev, nil)
 | 
						|
	return ev, nil
 | 
						|
}
 | 
						|
 | 
						|
func isAssignment(err error) (int, bool) {
 | 
						|
	el, isScannerErr := err.(scanner.ErrorList)
 | 
						|
	if isScannerErr && el[0].Msg == "expected '==', found '='" {
 | 
						|
		return el[0].Pos.Offset, true
 | 
						|
	}
 | 
						|
	return 0, false
 | 
						|
}
 | 
						|
 | 
						|
// Locals returns all variables in 'scope'.
 | 
						|
func (scope *EvalScope) Locals(flags localsFlags) ([]*Variable, error) {
 | 
						|
	if scope.Fn == nil {
 | 
						|
		return nil, errors.New("unable to find function context")
 | 
						|
	}
 | 
						|
 | 
						|
	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
 | 
						|
	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)
 | 
						|
	vars := make([]*Variable, 0, len(varEntries))
 | 
						|
	depths := make([]int, 0, len(varEntries))
 | 
						|
	for _, entry := range varEntries {
 | 
						|
		val, err := extractVarInfoFromEntry(scope.target, scope.BinInfo, scope.image(), scope.Regs, scope.Mem, entry.Tree)
 | 
						|
		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)
 | 
						|
		}
 | 
						|
		vars = append(vars, val)
 | 
						|
		depth := entry.Depth
 | 
						|
		if 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 = fmt.Errorf("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
 | 
						|
}
 | 
						|
 | 
						|
// 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 \"%s\" 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())
 | 
						|
	}
 | 
						|
 | 
						|
	// nilling nillable variables
 | 
						|
	if srcv == nilVariable {
 | 
						|
		return dstv.writeZero()
 | 
						|
	}
 | 
						|
 | 
						|
	if srcv.Kind == reflect.String {
 | 
						|
		if err := allocString(scope, srcv); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		return dstv.writeString(uint64(srcv.Len), uint64(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(uint64(srcv.Children[0].Addr), int64(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())
 | 
						|
}
 | 
						|
 | 
						|
// EvalVariable returns the value of the given expression (backwards compatibility).
 | 
						|
func (scope *EvalScope) EvalVariable(name string, cfg LoadConfig) (*Variable, error) {
 | 
						|
	return scope.EvalExpression(name, cfg)
 | 
						|
}
 | 
						|
 | 
						|
// SetVariable sets the value of the named variable
 | 
						|
func (scope *EvalScope) SetVariable(name, value string) error {
 | 
						|
	t, err := parser.ParseExpr(name)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	xv, err := scope.evalAST(t)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if xv.Addr == 0 {
 | 
						|
		//lint:ignore ST1005 backwards compatibility
 | 
						|
		return fmt.Errorf("Can not assign to \"%s\"", name)
 | 
						|
	}
 | 
						|
 | 
						|
	if xv.Unreadable != nil {
 | 
						|
		//lint:ignore ST1005 backwards compatibility
 | 
						|
		return fmt.Errorf("Expression \"%s\" is unreadable: %v", name, xv.Unreadable)
 | 
						|
	}
 | 
						|
 | 
						|
	t, err = parser.ParseExpr(value)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	yv, err := scope.evalAST(t)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return scope.setValue(xv, yv, value)
 | 
						|
}
 | 
						|
 | 
						|
// 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))
 | 
						|
		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, fmt.Errorf("could not find symbol value for %s.%s", pkgName, varName)
 | 
						|
}
 | 
						|
 | 
						|
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))
 | 
						|
		}
 | 
						|
	}
 | 
						|
	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)
 | 
						|
}
 | 
						|
 | 
						|
// DwarfReader returns the DwarfReader containing the
 | 
						|
// Dwarf information for the target process.
 | 
						|
func (scope *EvalScope) DwarfReader() *reader.Reader {
 | 
						|
	return scope.image().DwarfReader()
 | 
						|
}
 | 
						|
 | 
						|
// PtrSize returns the size of a pointer.
 | 
						|
func (scope *EvalScope) PtrSize() int {
 | 
						|
	return scope.BinInfo.Arch.PtrSize()
 | 
						|
}
 | 
						|
 | 
						|
// evalToplevelTypeCast implements certain type casts that we only support
 | 
						|
// at the outermost levels of an expression.
 | 
						|
func (scope *EvalScope) evalToplevelTypeCast(t ast.Expr, cfg LoadConfig) (*Variable, error) {
 | 
						|
	call, _ := t.(*ast.CallExpr)
 | 
						|
	if call == nil || len(call.Args) != 1 {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
	targetTypeStr := exprToString(removeParen(call.Fun))
 | 
						|
	var targetType godwarf.Type
 | 
						|
	switch targetTypeStr {
 | 
						|
	case "[]byte", "[]uint8":
 | 
						|
		targetType = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "uint8"}, BitSize: 8, BitOffset: 0}})
 | 
						|
	case "[]int32", "[]rune":
 | 
						|
		targetType = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "int32"}, BitSize: 32, BitOffset: 0}})
 | 
						|
	case "string":
 | 
						|
		var err error
 | 
						|
		targetType, err = scope.BinInfo.findType("string")
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	argv, err := scope.evalToplevelTypeCast(call.Args[0], cfg)
 | 
						|
	if argv == nil && err == nil {
 | 
						|
		argv, err = scope.evalAST(call.Args[0])
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	argv.loadValue(cfg)
 | 
						|
	if argv.Unreadable != nil {
 | 
						|
		return nil, argv.Unreadable
 | 
						|
	}
 | 
						|
 | 
						|
	v := newVariable("", 0, targetType, scope.BinInfo, scope.Mem)
 | 
						|
	v.loaded = true
 | 
						|
 | 
						|
	converr := fmt.Errorf("can not convert %q to %s", exprToString(call.Args[0]), targetTypeStr)
 | 
						|
 | 
						|
	switch targetTypeStr {
 | 
						|
	case "[]byte", "[]uint8":
 | 
						|
		if argv.Kind != reflect.String {
 | 
						|
			return nil, converr
 | 
						|
		}
 | 
						|
		for i, ch := range []byte(constant.StringVal(argv.Value)) {
 | 
						|
			e := newVariable("", argv.Addr+uint64(i), targetType.(*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
 | 
						|
		return v, nil
 | 
						|
 | 
						|
	case "[]int32", "[]rune":
 | 
						|
		if argv.Kind != reflect.String {
 | 
						|
			return nil, converr
 | 
						|
		}
 | 
						|
		for i, ch := range constant.StringVal(argv.Value) {
 | 
						|
			e := newVariable("", argv.Addr+uint64(i), targetType.(*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
 | 
						|
		return v, nil
 | 
						|
 | 
						|
	case "string":
 | 
						|
		switch argv.Kind {
 | 
						|
		case reflect.String:
 | 
						|
			s := constant.StringVal(argv.Value)
 | 
						|
			v.Value = constant.MakeString(s)
 | 
						|
			v.Len = int64(len(s))
 | 
						|
			return v, nil
 | 
						|
		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
 | 
						|
			b, _ := constant.Int64Val(argv.Value)
 | 
						|
			s := string(rune(b))
 | 
						|
			v.Value = constant.MakeString(s)
 | 
						|
			v.Len = int64(len(s))
 | 
						|
			return v, nil
 | 
						|
		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:
 | 
						|
				if elemType.Name != "uint8" && elemType.Name != "byte" {
 | 
						|
					return nil, nil
 | 
						|
				}
 | 
						|
				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))
 | 
						|
 | 
						|
			case *godwarf.IntType:
 | 
						|
				if elemType.Name != "int32" && elemType.Name != "rune" {
 | 
						|
					return nil, nil
 | 
						|
				}
 | 
						|
				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))
 | 
						|
 | 
						|
			default:
 | 
						|
				return nil, nil
 | 
						|
			}
 | 
						|
			v.Len = int64(len(constant.StringVal(v.Value)))
 | 
						|
			return v, nil
 | 
						|
 | 
						|
		default:
 | 
						|
			return nil, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil, nil
 | 
						|
}
 | 
						|
 | 
						|
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
 | 
						|
	switch node := t.(type) {
 | 
						|
	case *ast.CallExpr:
 | 
						|
		if len(node.Args) == 1 {
 | 
						|
			v, err := scope.evalTypeCast(node)
 | 
						|
			if err == nil || err != reader.ErrTypeNotFound {
 | 
						|
				return v, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return evalFunctionCall(scope, node)
 | 
						|
 | 
						|
	case *ast.Ident:
 | 
						|
		return scope.evalIdent(node)
 | 
						|
 | 
						|
	case *ast.ParenExpr:
 | 
						|
		// otherwise just eval recursively
 | 
						|
		return scope.evalAST(node.X)
 | 
						|
 | 
						|
	case *ast.SelectorExpr: // <expression>.<identifier>
 | 
						|
		// try to interpret the selector as a package variable
 | 
						|
		if maybePkg, ok := node.X.(*ast.Ident); ok {
 | 
						|
			if maybePkg.Name == "runtime" && node.Sel.Name == "curg" {
 | 
						|
				if scope.g == nil {
 | 
						|
					typ, err := scope.BinInfo.findType("runtime.g")
 | 
						|
					if err != nil {
 | 
						|
						return nil, fmt.Errorf("blah: %v", err)
 | 
						|
					}
 | 
						|
					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.Mem))
 | 
						|
					gvar.Children[0].Name = "goid"
 | 
						|
					return gvar, nil
 | 
						|
				}
 | 
						|
				return scope.g.variable.clone(), nil
 | 
						|
			} else if maybePkg.Name == "runtime" && node.Sel.Name == "frameoff" {
 | 
						|
				return newConstant(constant.MakeInt64(scope.frameOffset), scope.Mem), nil
 | 
						|
			} else if v, err := scope.findGlobal(maybePkg.Name, node.Sel.Name); err == nil {
 | 
						|
				return v, nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// try to accept "package/path".varname syntax for package variables
 | 
						|
		if maybePkg, ok := node.X.(*ast.BasicLit); ok && maybePkg.Kind == token.STRING {
 | 
						|
			pkgpath, err := strconv.Unquote(maybePkg.Value)
 | 
						|
			if err == nil {
 | 
						|
				if v, err := scope.findGlobal(pkgpath, node.Sel.Name); err == nil {
 | 
						|
					return v, nil
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// if it's not a package variable then it must be a struct member access
 | 
						|
		return scope.evalStructSelector(node)
 | 
						|
 | 
						|
	case *ast.TypeAssertExpr: // <expression>.(<type>)
 | 
						|
		return scope.evalTypeAssert(node)
 | 
						|
 | 
						|
	case *ast.IndexExpr:
 | 
						|
		return scope.evalIndex(node)
 | 
						|
 | 
						|
	case *ast.SliceExpr:
 | 
						|
		if node.Slice3 {
 | 
						|
			return nil, fmt.Errorf("3-index slice expressions not supported")
 | 
						|
		}
 | 
						|
		return scope.evalReslice(node)
 | 
						|
 | 
						|
	case *ast.StarExpr:
 | 
						|
		// pointer dereferencing *<expression>
 | 
						|
		return scope.evalPointerDeref(node)
 | 
						|
 | 
						|
	case *ast.UnaryExpr:
 | 
						|
		// The unary operators we support are +, - and & (note that unary * is parsed as ast.StarExpr)
 | 
						|
		switch node.Op {
 | 
						|
		case token.AND:
 | 
						|
			return scope.evalAddrOf(node)
 | 
						|
 | 
						|
		default:
 | 
						|
			return scope.evalUnary(node)
 | 
						|
		}
 | 
						|
 | 
						|
	case *ast.BinaryExpr:
 | 
						|
		return scope.evalBinary(node)
 | 
						|
 | 
						|
	case *ast.BasicLit:
 | 
						|
		return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Mem), nil
 | 
						|
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("expression %T not implemented", t)
 | 
						|
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func exprToString(t ast.Expr) string {
 | 
						|
	var buf bytes.Buffer
 | 
						|
	printer.Fprint(&buf, token.NewFileSet(), t)
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
func removeParen(n ast.Expr) ast.Expr {
 | 
						|
	for {
 | 
						|
		p, ok := n.(*ast.ParenExpr)
 | 
						|
		if !ok {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		n = p.X
 | 
						|
	}
 | 
						|
	return n
 | 
						|
}
 | 
						|
 | 
						|
// Eval type cast expressions
 | 
						|
func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
						|
	argv, err := scope.evalAST(node.Args[0])
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	argv.loadValue(loadSingleValue)
 | 
						|
	if argv.Unreadable != nil {
 | 
						|
		return nil, argv.Unreadable
 | 
						|
	}
 | 
						|
 | 
						|
	fnnode := node.Fun
 | 
						|
 | 
						|
	// remove all enclosing parenthesis from the type name
 | 
						|
	fnnode = removeParen(fnnode)
 | 
						|
 | 
						|
	styp, err := scope.BinInfo.findTypeExpr(fnnode)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	typ := resolveTypedef(styp)
 | 
						|
 | 
						|
	converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String())
 | 
						|
 | 
						|
	v := newVariable("", 0, styp, 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:
 | 
						|
			return nil, converr
 | 
						|
		}
 | 
						|
 | 
						|
		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
 | 
						|
		return v, nil
 | 
						|
 | 
						|
	case *godwarf.UintType:
 | 
						|
		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()))
 | 
						|
			return v, nil
 | 
						|
		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()))
 | 
						|
			return v, nil
 | 
						|
		case reflect.Float32, reflect.Float64:
 | 
						|
			x, _ := constant.Float64Val(argv.Value)
 | 
						|
			v.Value = constant.MakeUint64(uint64(x))
 | 
						|
			return v, nil
 | 
						|
		case reflect.Ptr:
 | 
						|
			v.Value = constant.MakeUint64(uint64(argv.Children[0].Addr))
 | 
						|
			return v, nil
 | 
						|
		}
 | 
						|
	case *godwarf.IntType:
 | 
						|
		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())))
 | 
						|
			return v, nil
 | 
						|
		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())))
 | 
						|
			return v, nil
 | 
						|
		case reflect.Float32, reflect.Float64:
 | 
						|
			x, _ := constant.Float64Val(argv.Value)
 | 
						|
			v.Value = constant.MakeInt64(int64(x))
 | 
						|
			return v, nil
 | 
						|
		}
 | 
						|
	case *godwarf.FloatType:
 | 
						|
		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
 | 
						|
			return v, nil
 | 
						|
		}
 | 
						|
	case *godwarf.ComplexType:
 | 
						|
		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
 | 
						|
			return v, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil, converr
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
func (scope *EvalScope) evalBuiltinCall(node *ast.CallExpr) (*Variable, error) {
 | 
						|
	fnnode, ok := node.Fun.(*ast.Ident)
 | 
						|
	if !ok {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	callBuiltinWithArgs := func(builtin func([]*Variable, []ast.Expr) (*Variable, error)) (*Variable, error) {
 | 
						|
		args := make([]*Variable, len(node.Args))
 | 
						|
 | 
						|
		for i := range node.Args {
 | 
						|
			v, err := scope.evalAST(node.Args[i])
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			args[i] = v
 | 
						|
		}
 | 
						|
 | 
						|
		return builtin(args, node.Args)
 | 
						|
	}
 | 
						|
 | 
						|
	switch fnnode.Name {
 | 
						|
	case "cap":
 | 
						|
		return callBuiltinWithArgs(capBuiltin)
 | 
						|
	case "len":
 | 
						|
		return callBuiltinWithArgs(lenBuiltin)
 | 
						|
	case "complex":
 | 
						|
		return callBuiltinWithArgs(complexBuiltin)
 | 
						|
	case "imag":
 | 
						|
		return callBuiltinWithArgs(imagBuiltin)
 | 
						|
	case "real":
 | 
						|
		return callBuiltinWithArgs(realBuiltin)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil, nil
 | 
						|
}
 | 
						|
 | 
						|
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", 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.mem), nil
 | 
						|
	case reflect.Slice:
 | 
						|
		return newConstant(constant.MakeInt64(arg.Cap), 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.mem), nil
 | 
						|
		}
 | 
						|
		return newConstant(arg.Children[1].Value, 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", 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.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.mem), nil
 | 
						|
		}
 | 
						|
		return newConstant(arg.Children[0].Value, arg.mem), nil
 | 
						|
	case reflect.Map:
 | 
						|
		it := arg.mapIterator()
 | 
						|
		if arg.Unreadable != nil {
 | 
						|
			return nil, arg.Unreadable
 | 
						|
		}
 | 
						|
		if it == nil {
 | 
						|
			return newConstant(constant.MakeInt64(0), arg.mem), nil
 | 
						|
		}
 | 
						|
		return newConstant(constant.MakeInt64(arg.Len), 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", 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", 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.ComplexType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, BitSize: sz, BitOffset: 0}}
 | 
						|
 | 
						|
	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", exprToString(nodeargs[0]), arg.TypeString())
 | 
						|
	}
 | 
						|
 | 
						|
	return newConstant(constant.Imag(arg.Value), 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", exprToString(nodeargs[0]), arg.TypeString())
 | 
						|
	}
 | 
						|
 | 
						|
	return newConstant(constant.Real(arg.Value), arg.mem), nil
 | 
						|
}
 | 
						|
 | 
						|
// Evaluates identifier expressions
 | 
						|
func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
 | 
						|
	switch node.Name {
 | 
						|
	case "true", "false":
 | 
						|
		return newConstant(constant.MakeBool(node.Name == "true"), scope.Mem), nil
 | 
						|
	case "nil":
 | 
						|
		return nilVariable, nil
 | 
						|
	}
 | 
						|
 | 
						|
	vars, err := scope.Locals(0)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	for i := range vars {
 | 
						|
		if vars[i].Name == node.Name && vars[i].Flags&VariableShadowed == 0 {
 | 
						|
			return vars[i], nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// if it's not a local variable then it could be a package variable w/o explicit package name
 | 
						|
	if scope.Fn != nil {
 | 
						|
		if v, err := scope.findGlobal(scope.Fn.PackageName(), node.Name); err == nil {
 | 
						|
			v.Name = node.Name
 | 
						|
			return v, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// not a local variable, nor a global variable, try a CPU register
 | 
						|
	if s := validRegisterName(node.Name); s != "" {
 | 
						|
		if regnum, ok := scope.BinInfo.Arch.RegisterNameToDwarf(s); ok {
 | 
						|
			if reg := scope.Regs.Reg(uint64(regnum)); reg != nil {
 | 
						|
				reg.FillBytes()
 | 
						|
 | 
						|
				var typ godwarf.Type
 | 
						|
				if len(reg.Bytes) <= 8 {
 | 
						|
					typ = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 8, Name: "uint64"}, BitSize: 64, BitOffset: 0}}
 | 
						|
				} else {
 | 
						|
					typ, err = scope.BinInfo.findType("string")
 | 
						|
					if err != nil {
 | 
						|
						return nil, err
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				v := newVariable(node.Name, 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
 | 
						|
				return v, nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil, fmt.Errorf("could not find symbol value for %s", node.Name)
 | 
						|
}
 | 
						|
 | 
						|
// Evaluates expressions <subexpr>.<field name> where subexpr is not a package name
 | 
						|
func (scope *EvalScope) evalStructSelector(node *ast.SelectorExpr) (*Variable, error) {
 | 
						|
	xv, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Prevent abuse, attempting to call "nil.member" directly.
 | 
						|
	if xv.Addr == 0 && xv.Name == "nil" {
 | 
						|
		return nil, fmt.Errorf("%s (type %s) is not a struct", xv.Name, xv.TypeString())
 | 
						|
	}
 | 
						|
	// Prevent abuse, attempting to call "\"fake\".member" directly.
 | 
						|
	if xv.Addr == 0 && xv.Name == "" && xv.DwarfType == nil && xv.RealType == nil {
 | 
						|
		return nil, fmt.Errorf("%s (type %s) is not a struct", xv.Value, xv.TypeString())
 | 
						|
	}
 | 
						|
	// Special type conversions for CPU register variables (REGNAME.int8, etc)
 | 
						|
	if xv.Flags&VariableCPURegister != 0 && !xv.loaded {
 | 
						|
		return xv.registerVariableTypeConv(node.Sel.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	rv, err := xv.findMethod(node.Sel.Name)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if rv != nil {
 | 
						|
		return rv, nil
 | 
						|
	}
 | 
						|
	return xv.structMember(node.Sel.Name)
 | 
						|
}
 | 
						|
 | 
						|
// Evaluates expressions <subexpr>.(<type>)
 | 
						|
func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, error) {
 | 
						|
	xv, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if xv.Kind != reflect.Interface {
 | 
						|
		return nil, fmt.Errorf("expression \"%s\" not an interface", exprToString(node.X))
 | 
						|
	}
 | 
						|
	xv.loadInterface(0, false, loadFullValue)
 | 
						|
	if xv.Unreadable != nil {
 | 
						|
		return nil, xv.Unreadable
 | 
						|
	}
 | 
						|
	if xv.Children[0].Unreadable != nil {
 | 
						|
		return nil, xv.Children[0].Unreadable
 | 
						|
	}
 | 
						|
	if xv.Children[0].Addr == 0 {
 | 
						|
		return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
 | 
						|
	}
 | 
						|
	// Accept .(data) as a type assertion that always succeeds, so that users
 | 
						|
	// can access the data field of an interface without actually having to
 | 
						|
	// type the concrete type.
 | 
						|
	if idtyp, isident := node.Type.(*ast.Ident); !isident || idtyp.Name != "data" {
 | 
						|
		typ, err := scope.BinInfo.findTypeExpr(node.Type)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if xv.Children[0].DwarfType.Common().Name != typ.Common().Name {
 | 
						|
			return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// 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
 | 
						|
	return &xv.Children[0], nil
 | 
						|
}
 | 
						|
 | 
						|
// Evaluates expressions <subexpr>[<subexpr>] (subscript access to arrays, slices and maps)
 | 
						|
func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
 | 
						|
	xev, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if xev.Unreadable != nil {
 | 
						|
		return nil, xev.Unreadable
 | 
						|
	}
 | 
						|
 | 
						|
	if xev.Flags&VariableCPtr == 0 {
 | 
						|
		xev = xev.maybeDereference()
 | 
						|
	}
 | 
						|
 | 
						|
	idxev, err := scope.evalAST(node.Index)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	cantindex := fmt.Errorf("expression \"%s\" (%s) does not support indexing", exprToString(node.X), xev.TypeString())
 | 
						|
 | 
						|
	switch xev.Kind {
 | 
						|
	case reflect.Ptr:
 | 
						|
		if xev == nilVariable {
 | 
						|
			return nil, cantindex
 | 
						|
		}
 | 
						|
		if xev.Flags&VariableCPtr == 0 {
 | 
						|
			_, isarrptr := xev.RealType.(*godwarf.PtrType).Type.(*godwarf.ArrayType)
 | 
						|
			if !isarrptr {
 | 
						|
				return nil, cantindex
 | 
						|
			}
 | 
						|
			xev = xev.maybeDereference()
 | 
						|
		}
 | 
						|
		fallthrough
 | 
						|
 | 
						|
	case reflect.Slice, reflect.Array, reflect.String:
 | 
						|
		if xev.Base == 0 {
 | 
						|
			return nil, fmt.Errorf("can not index \"%s\"", exprToString(node.X))
 | 
						|
		}
 | 
						|
		n, err := idxev.asInt()
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return xev.sliceAccess(int(n))
 | 
						|
 | 
						|
	case reflect.Map:
 | 
						|
		idxev.loadValue(loadFullValue)
 | 
						|
		if idxev.Unreadable != nil {
 | 
						|
			return nil, idxev.Unreadable
 | 
						|
		}
 | 
						|
		return xev.mapAccess(idxev)
 | 
						|
	default:
 | 
						|
		return nil, cantindex
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Evaluates expressions <subexpr>[<subexpr>:<subexpr>]
 | 
						|
// HACK: slicing a map expression with [0:0] will return the whole map
 | 
						|
func (scope *EvalScope) evalReslice(node *ast.SliceExpr) (*Variable, error) {
 | 
						|
	xev, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if xev.Unreadable != nil {
 | 
						|
		return nil, xev.Unreadable
 | 
						|
	}
 | 
						|
 | 
						|
	var low, high int64
 | 
						|
 | 
						|
	if node.Low != nil {
 | 
						|
		lowv, err := scope.evalAST(node.Low)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		low, err = lowv.asInt()
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("can not convert \"%s\" to int: %v", exprToString(node.Low), err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if node.High == nil {
 | 
						|
		high = xev.Len
 | 
						|
	} else {
 | 
						|
		highv, err := scope.evalAST(node.High)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		high, err = highv.asInt()
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("can not convert \"%s\" to int: %v", exprToString(node.High), err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	switch xev.Kind {
 | 
						|
	case reflect.Slice, reflect.Array, reflect.String:
 | 
						|
		if xev.Base == 0 {
 | 
						|
			return nil, fmt.Errorf("can not slice \"%s\"", exprToString(node.X))
 | 
						|
		}
 | 
						|
		return xev.reslice(low, high)
 | 
						|
	case reflect.Map:
 | 
						|
		if node.High != nil {
 | 
						|
			return nil, fmt.Errorf("second slice argument must be empty for maps")
 | 
						|
		}
 | 
						|
		xev.mapSkip += int(low)
 | 
						|
		xev.mapIterator() // reads map length
 | 
						|
		if int64(xev.mapSkip) >= xev.Len {
 | 
						|
			return nil, fmt.Errorf("map index out of bounds")
 | 
						|
		}
 | 
						|
		return xev, nil
 | 
						|
	case reflect.Ptr:
 | 
						|
		if xev.Flags&VariableCPtr != 0 {
 | 
						|
			return xev.reslice(low, high)
 | 
						|
		}
 | 
						|
		fallthrough
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("can not slice \"%s\" (type %s)", exprToString(node.X), xev.TypeString())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Evaluates a pointer dereference expression: *<subexpr>
 | 
						|
func (scope *EvalScope) evalPointerDeref(node *ast.StarExpr) (*Variable, error) {
 | 
						|
	xev, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if xev.Kind != reflect.Ptr {
 | 
						|
		return nil, fmt.Errorf("expression \"%s\" (%s) can not be dereferenced", exprToString(node.X), xev.TypeString())
 | 
						|
	}
 | 
						|
 | 
						|
	if xev == nilVariable {
 | 
						|
		return nil, fmt.Errorf("nil can not be dereferenced")
 | 
						|
	}
 | 
						|
 | 
						|
	if len(xev.Children) == 1 {
 | 
						|
		// this branch is here to support pointers constructed with typecasts from ints
 | 
						|
		xev.Children[0].OnlyAddr = false
 | 
						|
		return &(xev.Children[0]), nil
 | 
						|
	}
 | 
						|
	rv := xev.maybeDereference()
 | 
						|
	if rv.Addr == 0 {
 | 
						|
		return nil, fmt.Errorf("nil pointer dereference")
 | 
						|
	}
 | 
						|
	return rv, nil
 | 
						|
}
 | 
						|
 | 
						|
// Evaluates expressions &<subexpr>
 | 
						|
func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
 | 
						|
	xev, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if xev.Addr == 0 || xev.DwarfType == nil {
 | 
						|
		return nil, fmt.Errorf("can not take address of \"%s\"", exprToString(node.X))
 | 
						|
	}
 | 
						|
 | 
						|
	return xev.pointerToVariable(), nil
 | 
						|
}
 | 
						|
 | 
						|
func (v *Variable) pointerToVariable() *Variable {
 | 
						|
	v.OnlyAddr = true
 | 
						|
 | 
						|
	typename := "*" + v.DwarfType.Common().Name
 | 
						|
	rv := v.newVariable("", 0, &godwarf.PtrType{CommonType: godwarf.CommonType{ByteSize: int64(v.bi.Arch.PtrSize()), Name: typename}, Type: v.DwarfType}, 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(node *ast.UnaryExpr) (*Variable, error) {
 | 
						|
	xv, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	xv.loadValue(loadSingleValue)
 | 
						|
	if xv.Unreadable != nil {
 | 
						|
		return nil, xv.Unreadable
 | 
						|
	}
 | 
						|
	if xv.FloatSpecial != 0 {
 | 
						|
		return nil, errOperationOnSpecialFloat
 | 
						|
	}
 | 
						|
	if xv.Value == nil {
 | 
						|
		return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
 | 
						|
	}
 | 
						|
	rc, err := constantUnaryOp(node.Op, xv.Value)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if xv.DwarfType != nil {
 | 
						|
		r := xv.newVariable("", 0, xv.DwarfType, scope.Mem)
 | 
						|
		r.Value = rc
 | 
						|
		return r, nil
 | 
						|
	}
 | 
						|
	return newConstant(rc, xv.mem), nil
 | 
						|
}
 | 
						|
 | 
						|
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, fmt.Errorf("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 \"%s\" and \"%s\"", 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(node *ast.BinaryExpr) (*Variable, error) {
 | 
						|
	switch node.Op {
 | 
						|
	case token.INC, token.DEC, token.ARROW:
 | 
						|
		return nil, fmt.Errorf("operator %s not supported", node.Op.String())
 | 
						|
	}
 | 
						|
 | 
						|
	xv, err := scope.evalAST(node.X)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if xv.Kind != reflect.String { // delay loading strings until we use them
 | 
						|
		xv.loadValue(loadFullValue)
 | 
						|
	}
 | 
						|
	if xv.Unreadable != nil {
 | 
						|
		return nil, xv.Unreadable
 | 
						|
	}
 | 
						|
 | 
						|
	// short circuits logical operators
 | 
						|
	switch node.Op {
 | 
						|
	case token.LAND:
 | 
						|
		if !constant.BoolVal(xv.Value) {
 | 
						|
			return newConstant(xv.Value, xv.mem), nil
 | 
						|
		}
 | 
						|
	case token.LOR:
 | 
						|
		if constant.BoolVal(xv.Value) {
 | 
						|
			return newConstant(xv.Value, xv.mem), nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	yv, err := scope.evalAST(node.Y)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if yv.Kind != reflect.String { // delay loading strings until we use them
 | 
						|
		yv.loadValue(loadFullValue)
 | 
						|
	}
 | 
						|
	if yv.Unreadable != nil {
 | 
						|
		return nil, yv.Unreadable
 | 
						|
	}
 | 
						|
 | 
						|
	if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
 | 
						|
		return nil, errOperationOnSpecialFloat
 | 
						|
	}
 | 
						|
 | 
						|
	typ, err := negotiateType(node.Op, xv, yv)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	op := node.Op
 | 
						|
	if typ != nil && (op == token.QUO) {
 | 
						|
		_, isint := typ.(*godwarf.IntType)
 | 
						|
		_, isuint := typ.(*godwarf.UintType)
 | 
						|
		if isint || isuint {
 | 
						|
			// forces integer division if the result type is integer
 | 
						|
			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 {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return newConstant(constant.MakeBool(v), xv.mem), nil
 | 
						|
 | 
						|
	default:
 | 
						|
		if xv.Kind == reflect.String {
 | 
						|
			xv.loadValue(loadFullValueLongerStrings)
 | 
						|
		}
 | 
						|
		if yv.Kind == reflect.String {
 | 
						|
			yv.loadValue(loadFullValueLongerStrings)
 | 
						|
		}
 | 
						|
		if xv.Value == nil {
 | 
						|
			return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
 | 
						|
		}
 | 
						|
 | 
						|
		if yv.Value == nil {
 | 
						|
			return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.Y))
 | 
						|
		}
 | 
						|
 | 
						|
		rc, err := constantBinaryOp(op, xv.Value, yv.Value)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		if typ == nil {
 | 
						|
			return newConstant(rc, xv.mem), nil
 | 
						|
		}
 | 
						|
 | 
						|
		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()))
 | 
						|
		}
 | 
						|
		return r, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 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, fmt.Errorf("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, fmt.Errorf("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, fmt.Errorf("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 = resolveTypedef(t1)
 | 
						|
	t2 = 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, fmt.Errorf("index out of bounds")
 | 
						|
	}
 | 
						|
	if v.loaded {
 | 
						|
		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()
 | 
						|
	if it == nil {
 | 
						|
		return nil, fmt.Errorf("can not access unreadable map: %v", v.Unreadable)
 | 
						|
	}
 | 
						|
 | 
						|
	first := true
 | 
						|
	for it.next() {
 | 
						|
		key := it.key()
 | 
						|
		key.loadValue(loadFullValue)
 | 
						|
		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, fmt.Errorf("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)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	case reflect.Map:
 | 
						|
		newV = v.clone()
 | 
						|
		newV.Children = nil
 | 
						|
		newV.loaded = false
 | 
						|
		newV.mapSkip = start
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("variable to reslice is not an array, slice, or map")
 | 
						|
	}
 | 
						|
	newV.loadValue(cfg)
 | 
						|
	return newV, nil
 | 
						|
}
 | 
						|
 | 
						|
func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
 | 
						|
	wrong := false
 | 
						|
	cptrNeedsFakeSlice := false
 | 
						|
	if v.Flags&VariableCPtr == 0 {
 | 
						|
		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, fmt.Errorf("index out of bounds")
 | 
						|
	}
 | 
						|
 | 
						|
	base := v.Base + uint64(int64(low)*v.stride)
 | 
						|
	len := high - low
 | 
						|
 | 
						|
	if high-low < 0 {
 | 
						|
		return nil, fmt.Errorf("index out of bounds")
 | 
						|
	}
 | 
						|
 | 
						|
	typ := v.DwarfType
 | 
						|
	if _, isarr := v.DwarfType.(*godwarf.ArrayType); isarr || cptrNeedsFakeSlice {
 | 
						|
		typ = fakeSliceType(v.fieldType)
 | 
						|
	}
 | 
						|
 | 
						|
	mem := v.mem
 | 
						|
	if v.Kind != reflect.Array {
 | 
						|
		mem = DereferenceMemory(mem)
 | 
						|
	}
 | 
						|
 | 
						|
	r := v.newVariable("", 0, typ, mem)
 | 
						|
	r.Cap = len
 | 
						|
	r.Len = len
 | 
						|
	r.Base = base
 | 
						|
	r.stride = v.stride
 | 
						|
	r.fieldType = v.fieldType
 | 
						|
	r.Flags = v.Flags
 | 
						|
	r.reg = v.reg
 | 
						|
 | 
						|
	return r, nil
 | 
						|
}
 | 
						|
 | 
						|
// findMethod finds method mname in the type of variable v
 | 
						|
func (v *Variable) findMethod(mname string) (*Variable, error) {
 | 
						|
	if _, isiface := v.RealType.(*godwarf.InterfaceType); isiface {
 | 
						|
		v.loadInterface(0, false, loadFullValue)
 | 
						|
		if v.Unreadable != nil {
 | 
						|
			return nil, v.Unreadable
 | 
						|
		}
 | 
						|
		return v.Children[0].findMethod(mname)
 | 
						|
	}
 | 
						|
 | 
						|
	queue := []*Variable{v}
 | 
						|
	seen := map[string]struct{}{}
 | 
						|
 | 
						|
	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
 | 
						|
		}
 | 
						|
 | 
						|
		typePath := typ.Common().Name
 | 
						|
		dot := strings.LastIndex(typePath, ".")
 | 
						|
		if dot < 0 {
 | 
						|
			// probably just a C type
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		pkg := typePath[:dot]
 | 
						|
		receiver := typePath[dot+1:]
 | 
						|
 | 
						|
		if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.%s.%s", pkg, receiver, mname)]; ok {
 | 
						|
			r, err := functionToVariable(fn, v.bi, v.mem)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			if isptr {
 | 
						|
				r.Children = append(r.Children, *(v.maybeDereference()))
 | 
						|
			} else {
 | 
						|
				r.Children = append(r.Children, *v)
 | 
						|
			}
 | 
						|
			return r, nil
 | 
						|
		}
 | 
						|
 | 
						|
		if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.(*%s).%s", pkg, receiver, mname)]; ok {
 | 
						|
			r, err := functionToVariable(fn, v.bi, v.mem)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			if isptr {
 | 
						|
				r.Children = append(r.Children, *v)
 | 
						|
			} else {
 | 
						|
				r.Children = append(r.Children, *(v.pointerToVariable()))
 | 
						|
			}
 | 
						|
			return r, 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 field.Embedded {
 | 
						|
					embeddedVar, err := structVar.toField(field)
 | 
						|
					if err != nil {
 | 
						|
						return nil, err
 | 
						|
					}
 | 
						|
					queue = append(queue, embeddedVar)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	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 fakeSliceType(fieldType godwarf.Type) godwarf.Type {
 | 
						|
	return &godwarf.SliceType{
 | 
						|
		StructType: godwarf.StructType{
 | 
						|
			CommonType: godwarf.CommonType{
 | 
						|
				ByteSize: 24,
 | 
						|
				Name:     "",
 | 
						|
			},
 | 
						|
			StructName: fmt.Sprintf("[]%s", fieldType.Common().Name),
 | 
						|
			Kind:       "struct",
 | 
						|
			Field:      nil,
 | 
						|
		},
 | 
						|
		ElemType: fieldType,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
}
 |