diff --git a/pkg/dwarf/reader/variables.go b/pkg/dwarf/reader/variables.go index b7871a0e..d39f9d68 100644 --- a/pkg/dwarf/reader/variables.go +++ b/pkg/dwarf/reader/variables.go @@ -23,6 +23,7 @@ const ( VariablesOnlyVisible VariablesFlags = 1 << iota VariablesSkipInlinedSubroutines VariablesTrustDeclLine + VariablesNoDeclLineCheck ) // Variables returns a list of variables contained inside 'root'. @@ -60,7 +61,7 @@ func variablesInternal(v []Variable, root *godwarf.Tree, depth int, pc uint64, l // are defined. o = 1 } - if declLine, ok := root.Val(dwarf.AttrDeclLine).(int64); !ok || line >= int(declLine)+o { + if declLine, ok := root.Val(dwarf.AttrDeclLine).(int64); (flags&VariablesNoDeclLineCheck != 0) || !ok || line >= int(declLine)+o { return append(v, Variable{root, depth}) } return v diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go index c8a7ea49..b8434c4f 100644 --- a/pkg/proc/breakpoints.go +++ b/pkg/proc/breakpoints.go @@ -899,7 +899,7 @@ func (rbpi *returnBreakpointInfo) Collect(t *Target, thread Thread) []*Variable return returnInfoError("could not read function entry", err, thread.ProcessMemory()) } - vars, err := scope.Locals() + vars, err := scope.Locals(0) if err != nil { return returnInfoError("could not evaluate return variables", err, thread.ProcessMemory()) } diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go index 9e968777..edf81804 100644 --- a/pkg/proc/eval.go +++ b/pkg/proc/eval.go @@ -49,13 +49,21 @@ type EvalScope struct { // The goroutine executing the expression evaluation shall signal that the // evaluation is complete by closing the continueRequest channel. callCtx *callContext - - // If trustArgOrder is true function arguments that don't have an address - // will have one assigned by looking at their position in the argument - // list. - trustArgOrder bool } +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. @@ -201,12 +209,12 @@ func isAssignment(err error) (int, bool) { } // Locals returns all variables in 'scope'. -func (scope *EvalScope) Locals() ([]*Variable, error) { +func (scope *EvalScope) Locals(flags localsFlags) ([]*Variable, error) { if scope.Fn == nil { return nil, errors.New("unable to find function context") } - trustArgOrder := scope.trustArgOrder && scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 12) && scope.Fn != nil && (scope.PC == scope.Fn.Entry) + 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 { @@ -214,6 +222,9 @@ func (scope *EvalScope) Locals() ([]*Variable, error) { } variablesFlags := reader.VariablesOnlyVisible + if flags&localsNoDeclLineCheck != 0 { + variablesFlags = reader.VariablesNoDeclLineCheck + } if scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 15) { variablesFlags |= reader.VariablesTrustDeclLine } @@ -417,7 +428,7 @@ func (scope *EvalScope) SetVariable(name, value string) error { // LocalVariables returns all local variables from the current function scope. func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) { - vars, err := scope.Locals() + vars, err := scope.Locals(0) if err != nil { return nil, err } @@ -431,7 +442,7 @@ func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) { // FunctionArguments returns the name, value, and type of all current function arguments. func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) { - vars, err := scope.Locals() + vars, err := scope.Locals(0) if err != nil { return nil, err } @@ -1134,7 +1145,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) { return nilVariable, nil } - vars, err := scope.Locals() + vars, err := scope.Locals(0) if err != nil { return nil, err } diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go index b5294385..6226edcc 100644 --- a/pkg/proc/fncall.go +++ b/pkg/proc/fncall.go @@ -883,9 +883,13 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread // pretend we are still inside the function we called fakeFunctionEntryScope(retScope, fncall.fn, int64(regs.SP()), regs.SP()-uint64(bi.Arch.PtrSize())) - retScope.trustArgOrder = !bi.regabi + var flags localsFlags + flags |= localsNoDeclLineCheck // if the function we are calling is an autogenerated stub then declaration lines have no meaning + if !bi.regabi { + flags |= localsTrustArgOrder + } - fncall.retvars, err = retScope.Locals() + fncall.retvars, err = retScope.Locals(flags) if err != nil { fncall.err = fmt.Errorf("could not get return values: %v", err) break diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 7cb392a0..c7d097b3 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -3544,7 +3544,7 @@ func testDeclLineCount(t *testing.T, p *proc.Target, lineno int, tgtvars []strin assertLineNumber(p, t, lineno, "Program did not continue to correct next location") scope, err := proc.GoroutineScope(p, p.CurrentThread()) assertNoError(err, t, fmt.Sprintf("GoroutineScope (:%d)", lineno)) - vars, err := scope.Locals() + vars, err := scope.Locals(0) assertNoError(err, t, fmt.Sprintf("Locals (:%d)", lineno)) if len(vars) != len(tgtvars) { t.Fatalf("wrong number of variables %d (:%d)", len(vars), lineno)