package proc import ( "bytes" "debug/dwarf" "errors" "fmt" "go/ast" "go/constant" "go/parser" "go/printer" "go/token" "reflect" "runtime/debug" "sort" "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" "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 ) // 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 } // 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) } 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 } } if scope.Fn.rangeParentName() != "" && (strings.HasPrefix(name, "~") || entry.Val(godwarf.AttrGoClosureOffset) != nil) { // 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) } 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.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 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": 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 { 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.Mem)) gvar.Children[0].Name = "goid" stack.push(gvar) } case *evalop.PushFrameoff: stack.push(newConstant(constant.MakeInt64(scope.frameOffset), 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.Mem)) } else { stack.push(newConstant(constant.MakeInt64(0), scope.Mem)) } case *evalop.PushThreadID: stack.push(newConstant(constant.MakeInt64(int64(scope.threadID)), scope.Mem)) case *evalop.PushConst: stack.push(newConstant(op.Value, 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.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.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.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 = 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, 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) 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.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 (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 exprToString(t ast.Expr) string { var buf bytes.Buffer printer.Fprint(&buf, token.NewFileSet(), t) return buf.String() } 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", 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 := resolveTypedef(op.DwarfType) converr := fmt.Errorf("can not convert %q to %s", exprToString(op.Node.Args[0]), typ.String()) // compatible underlying types if typeCastCompatibleTypes(argv.RealType, typ) { if ptyp, isptr := typ.(*godwarf.PtrType); argv.Kind == reflect.Ptr && 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 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(resolveTypedef(ttyp1.Type), 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.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", 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(0) 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.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", 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 } 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", 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 . 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 } rv, err := xv.findMethod(op.Name) if err != nil { stack.err = err return } if rv != nil { stack.push(rv) return } stack.pushErr(xv.structMember(op.Name)) } // Evaluates expressions .() 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", 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(), 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 [] (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.Flags&VariableCPtr == 0 { xev = xev.maybeDereference() } cantindex := fmt.Errorf("expression %q (%s) does not support indexing", 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", 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 [:] // 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", 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)", exprToString(op.Node.X), xev.TypeString()) return } } // Evaluates a pointer dereference expression: * 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", 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 & 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", exprToString(op.Node.X)) return } stack.push(xev.pointerToVariable()) } 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: - and + 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(), 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.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.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(), exprToString(node.X)) return } if yv.Value == nil { stack.err = fmt.Errorf("operator %s can not be applied to %q", node.Op.String(), 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.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 = 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, 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 } // 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:] //TODO(aarzilli): support generic functions? if fns := v.bi.LookupFunc()[fmt.Sprintf("%s.%s.%s", pkg, receiver, mname)]; len(fns) == 1 { r, err := functionToVariable(fns[0], 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 fns := v.bi.LookupFunc()[fmt.Sprintf("%s.(*%s).%s", pkg, receiver, mname)]; len(fns) == 1 { r, err := functionToVariable(fns[0], 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 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 }