
Fixes a bug where we cannot get locals (including arguments and return values) from a given scope because the line number state machine ends up in an invalid state because of this parameter being set to false.
2489 lines
69 KiB
Go
2489 lines
69 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"
|
|
"github.com/go-delve/delve/pkg/logflags"
|
|
)
|
|
|
|
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
|
|
|
|
const goDictionaryName = ".dict"
|
|
|
|
// 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
|
|
loadCfg *LoadConfig
|
|
|
|
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
|
|
|
|
dictAddr uint64 // dictionary address for instantiated generic functions
|
|
}
|
|
|
|
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 int64, 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
|
|
}
|
|
|
|
scope.loadCfg = &cfg
|
|
|
|
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)
|
|
|
|
// look for dictionary entry
|
|
if scope.dictAddr == 0 {
|
|
for _, entry := range varEntries {
|
|
name, _ := entry.Val(dwarf.AttrName).(string)
|
|
if name == goDictionaryName {
|
|
dictVar, err := extractVarInfoFromEntry(scope.target, scope.BinInfo, scope.image(), scope.Regs, scope.Mem, entry.Tree, 0)
|
|
if err != nil {
|
|
logflags.DebuggerLogger().Errorf("could not load %s variable: %v", name, err)
|
|
} else if dictVar.Unreadable != nil {
|
|
logflags.DebuggerLogger().Errorf("could not load %s variable: %v", name, dictVar.Unreadable)
|
|
} else {
|
|
scope.dictAddr, err = readUintRaw(dictVar.mem, dictVar.Addr, int64(scope.BinInfo.Arch.PtrSize()))
|
|
if err != nil {
|
|
logflags.DebuggerLogger().Errorf("could not load %s variable: %v", name, err)
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
vars := make([]*Variable, 0, len(varEntries))
|
|
depths := make([]int, 0, len(varEntries))
|
|
for _, entry := range varEntries {
|
|
if name, _ := entry.Val(dwarf.AttrName).(string); name == goDictionaryName {
|
|
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 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())
|
|
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 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())
|
|
}
|
|
|
|
// 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(localsNoDeclLineCheck)
|
|
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, 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), 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)
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
|
|
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
|
switch node := t.(type) {
|
|
case *ast.CallExpr:
|
|
return scope.evalTypeCastOrFuncCall(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
|
|
}
|
|
|
|
// evalTypeCastOrFuncCall evaluates a type cast or a function call
|
|
func (scope *EvalScope) evalTypeCastOrFuncCall(node *ast.CallExpr) (*Variable, error) {
|
|
if len(node.Args) != 1 {
|
|
// Things that have more or less than one argument are always function calls.
|
|
return evalFunctionCall(scope, node)
|
|
}
|
|
|
|
ambiguous := func() (*Variable, error) {
|
|
// Ambiguous, could be a function call or a type cast, if node.Fun can be
|
|
// evaluated then try to treat it as a function call, otherwise try the
|
|
// type cast.
|
|
_, err0 := scope.evalAST(node.Fun)
|
|
if err0 == nil {
|
|
return evalFunctionCall(scope, node)
|
|
}
|
|
v, err := scope.evalTypeCast(node)
|
|
if err == reader.ErrTypeNotFound {
|
|
return nil, fmt.Errorf("could not evaluate function or type %s: %v", exprToString(node.Fun), err0)
|
|
}
|
|
return v, err
|
|
}
|
|
|
|
fnnode := node.Fun
|
|
for {
|
|
fnnode = removeParen(fnnode)
|
|
n, _ := fnnode.(*ast.StarExpr)
|
|
if n == nil {
|
|
break
|
|
}
|
|
fnnode = n.X
|
|
}
|
|
|
|
switch n := fnnode.(type) {
|
|
case *ast.BasicLit:
|
|
// It can only be a ("type string")(x) type cast
|
|
return scope.evalTypeCast(node)
|
|
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
|
return scope.evalTypeCast(node)
|
|
case *ast.SelectorExpr:
|
|
if _, isident := n.X.(*ast.Ident); isident {
|
|
return ambiguous()
|
|
}
|
|
return evalFunctionCall(scope, node)
|
|
case *ast.Ident:
|
|
if supportedBuiltins[n.Name] {
|
|
return evalFunctionCall(scope, node)
|
|
}
|
|
return ambiguous()
|
|
case *ast.IndexExpr:
|
|
// Ambiguous, could be a parametric type
|
|
switch n.X.(type) {
|
|
case *ast.Ident, *ast.SelectorExpr:
|
|
// Do the type-cast first since evaluating node.Fun could be expensive.
|
|
v, err := scope.evalTypeCast(node)
|
|
if err == nil || err != reader.ErrTypeNotFound {
|
|
return v, err
|
|
}
|
|
return evalFunctionCall(scope, node)
|
|
default:
|
|
return evalFunctionCall(scope, node)
|
|
}
|
|
case *astIndexListExpr:
|
|
return scope.evalTypeCast(node)
|
|
default:
|
|
// All other expressions must be function calls
|
|
return evalFunctionCall(scope, node)
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
fnnode := node.Fun
|
|
|
|
// remove all enclosing parenthesis from the type name
|
|
fnnode = removeParen(fnnode)
|
|
|
|
targetTypeStr := exprToString(removeParen(node.Fun))
|
|
styp, err := scope.BinInfo.findTypeExpr(fnnode)
|
|
if err != nil {
|
|
switch targetTypeStr {
|
|
case "[]byte", "[]uint8":
|
|
styp = fakeSliceType(fakeBasicType("uint", 8))
|
|
case "[]int32", "[]rune":
|
|
styp = fakeSliceType(fakeBasicType("int", 32))
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
typ := resolveTypedef(styp)
|
|
|
|
converr := fmt.Errorf("can not convert %q to %s", exprToString(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 = styp
|
|
return argv, nil
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
argv.loadValue(loadSingleValue)
|
|
if argv.Unreadable != nil {
|
|
return nil, argv.Unreadable
|
|
}
|
|
|
|
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:
|
|
argv.loadValue(loadSingleValue)
|
|
if argv.Unreadable != nil {
|
|
return nil, argv.Unreadable
|
|
}
|
|
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:
|
|
argv.loadValue(loadSingleValue)
|
|
if argv.Unreadable != nil {
|
|
return nil, argv.Unreadable
|
|
}
|
|
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:
|
|
argv.loadValue(loadSingleValue)
|
|
if argv.Unreadable != nil {
|
|
return nil, argv.Unreadable
|
|
}
|
|
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:
|
|
argv.loadValue(loadSingleValue)
|
|
if argv.Unreadable != nil {
|
|
return nil, argv.Unreadable
|
|
}
|
|
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
|
|
}
|
|
}
|
|
|
|
cfg := loadFullValue
|
|
if scope.loadCfg != nil {
|
|
cfg = *scope.loadCfg
|
|
}
|
|
argv.loadValue(cfg)
|
|
if argv.Unreadable != nil {
|
|
return nil, argv.Unreadable
|
|
}
|
|
|
|
switch ttyp := typ.(type) {
|
|
case *godwarf.SliceType:
|
|
switch ttyp.ElemType.Common().ReflectKind {
|
|
case reflect.Uint8:
|
|
if argv.Kind != reflect.String {
|
|
return nil, converr
|
|
}
|
|
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 = int64(len(v.Children))
|
|
v.Cap = v.Len
|
|
return v, nil
|
|
|
|
case reflect.Int32:
|
|
if argv.Kind != reflect.String {
|
|
return nil, converr
|
|
}
|
|
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
|
|
return v, nil
|
|
}
|
|
|
|
case *godwarf.StringType:
|
|
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, converr
|
|
}
|
|
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, converr
|
|
}
|
|
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, converr
|
|
}
|
|
v.Len = int64(len(constant.StringVal(v.Value)))
|
|
return v, nil
|
|
}
|
|
}
|
|
|
|
return nil, 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]bool{"cap": true, "len": true, "complex": true, "imag": true, "real": true}
|
|
|
|
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 := 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
|
|
}
|
|
|
|
// 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 = fakeBasicType("uint", 64)
|
|
} 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
|
|
}
|
|
xev.loadPtr()
|
|
if xev.Unreadable != nil {
|
|
val, ok := constant.Uint64Val(xev.Value)
|
|
if ok && val == 0 {
|
|
return nil, fmt.Errorf("couldn't read pointer: %w", xev.Unreadable)
|
|
}
|
|
}
|
|
rv := &xev.Children[0]
|
|
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 {
|
|
if v.Kind == reflect.String {
|
|
s := constant.StringVal(v.Value)
|
|
if idx >= len(s) {
|
|
return nil, fmt.Errorf("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, fmt.Errorf("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()
|
|
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, 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:]
|
|
|
|
//TODO(aarzilli): support generic functions?
|
|
|
|
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 fakeBasicType(name string, bitSize int) godwarf.Type {
|
|
byteSize := bitSize / 8
|
|
szr := popcnt(uint64(byteSize^(byteSize-1))) - 1 // position of rightmost 1 bit, minus 1
|
|
|
|
basic := func(kind reflect.Kind) godwarf.BasicType {
|
|
return godwarf.BasicType{
|
|
CommonType: godwarf.CommonType{
|
|
ByteSize: int64(byteSize),
|
|
Name: fmt.Sprintf("%s%d", name, bitSize),
|
|
ReflectKind: kind,
|
|
},
|
|
BitSize: int64(bitSize),
|
|
BitOffset: 0,
|
|
}
|
|
}
|
|
|
|
switch name {
|
|
case "int":
|
|
return &godwarf.IntType{BasicType: basic(reflect.Int8 + reflect.Kind(szr))}
|
|
case "uint":
|
|
return &godwarf.UintType{BasicType: basic(reflect.Uint8 + reflect.Kind(szr))}
|
|
case "float":
|
|
return &godwarf.FloatType{BasicType: basic(reflect.Float32 + reflect.Kind(szr-2))}
|
|
case "complex":
|
|
return &godwarf.ComplexType{BasicType: basic(reflect.Complex64 + reflect.Kind(szr-3))}
|
|
default:
|
|
panic("unsupported")
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|