From 103f37497f2927eeb510789f63fe2a0319b6a49a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Fri, 3 Feb 2023 09:59:55 -0800 Subject: [PATCH] cmd/compile: ensure first instruction in a function is not inlined People are using this to get the name of the function from a function type: runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() Unfortunately, this technique falls down when the first instruction of the function is from an inlined callee. Then the expression above gets you the name of the inlined function instead of the function itself. To fix this, ensure that the first instruction is never from an inlinee. Normally functions have prologs so those are already fine. In just the cases where a function is a leaf with no local variables, and an instruction from an inlinee appears first in the prog list, add a nop at the start of the function to hold a non-inlined position. Consider the nop a "mini-prolog" for leaf functions. Fixes #58300 Change-Id: Ie37092f4ac3167fe8e5ef4a2207b14abc1786897 Reviewed-on: https://go-review.googlesource.com/c/go/+/465076 Run-TryBot: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Heschi Kreinick Reviewed-by: David Chase --- src/cmd/compile/internal/ssagen/ssa.go | 44 ++++++++++++++++++++++++++ test/fixedbugs/issue58300.go | 29 +++++++++++++++++ test/fixedbugs/issue58300.out | 2 ++ 3 files changed, 75 insertions(+) create mode 100644 test/fixedbugs/issue58300.go create mode 100644 test/fixedbugs/issue58300.out diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 48f813a48f..f31cf29925 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -7120,6 +7120,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) { } if inlMarks != nil { + hasCall := false + // We have some inline marks. Try to find other instructions we're // going to emit anyway, and use those instructions instead of the // inline marks. @@ -7137,6 +7139,9 @@ func genssa(f *ssa.Func, pp *objw.Progs) { // whether they will be zero-sized or not yet. continue } + if p.As == obj.ACALL || p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { + hasCall = true + } pos := p.Pos.AtColumn1() s := inlMarksByPos[pos] if len(s) == 0 { @@ -7162,6 +7167,45 @@ func genssa(f *ssa.Func, pp *objw.Progs) { pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[p]) } } + + if e.stksize == 0 && !hasCall { + // Frameless leaf function. It doesn't need any preamble, + // so make sure its first instruction isn't from an inlined callee. + // If it is, add a nop at the start of the function with a position + // equal to the start of the function. + // This ensures that runtime.FuncForPC(uintptr(reflect.ValueOf(fn).Pointer())).Name() + // returns the right answer. See issue 58300. + for p := pp.Text; p != nil; p = p.Link { + if p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT { + continue + } + if base.Ctxt.PosTable.Pos(p.Pos).Base().InliningIndex() >= 0 { + // Make a real (not 0-sized) nop. + nop := Arch.Ginsnop(pp) + nop.Pos = e.curfn.Pos().WithIsStmt() + + // Unfortunately, Ginsnop puts the instruction at the + // end of the list. Move it up to just before p. + + // Unlink from the current list. + for x := pp.Text; x != nil; x = x.Link { + if x.Link == nop { + x.Link = nop.Link + break + } + } + // Splice in right before p. + for x := pp.Text; x != nil; x = x.Link { + if x.Link == p { + nop.Link = p + x.Link = nop + break + } + } + } + break + } + } } if base.Ctxt.Flag_locationlists { diff --git a/test/fixedbugs/issue58300.go b/test/fixedbugs/issue58300.go new file mode 100644 index 0000000000..fff3d211f3 --- /dev/null +++ b/test/fixedbugs/issue58300.go @@ -0,0 +1,29 @@ +// run + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "reflect" + "runtime" +) + +func f(n int) int { + return n % 2 +} + +func g(n int) int { + return f(n) +} + +func name(fn any) (res string) { + return runtime.FuncForPC(uintptr(reflect.ValueOf(fn).Pointer())).Name() +} + +func main() { + println(name(f)) + println(name(g)) +} diff --git a/test/fixedbugs/issue58300.out b/test/fixedbugs/issue58300.out new file mode 100644 index 0000000000..f347287480 --- /dev/null +++ b/test/fixedbugs/issue58300.out @@ -0,0 +1,2 @@ +main.f +main.g -- 2.50.0