Skip to content

cmd/compile: fix loclist for heap return vars without optimizations #74398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions src/cmd/compile/internal/dwarfgen/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,6 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
tag = dwarf.DW_TAG_formal_parameter
}
if n.Esc() == ir.EscHeap {
// The variable in question has been promoted to the heap.
// Its address is in n.Heapaddr.
// TODO(thanm): generate a better location expression
}
inlIndex := 0
if base.Flag.GenDwarfInl > 1 {
if n.InlFormal() || n.InlLocal() {
Expand All @@ -263,7 +258,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
}
}
declpos := base.Ctxt.InnermostPos(n.Pos())
vars = append(vars, &dwarf.Var{
dvar := &dwarf.Var{
Name: n.Sym().Name,
IsReturnValue: isReturnValue,
Tag: tag,
Expand All @@ -277,8 +272,19 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
ChildIndex: -1,
DictIndex: n.DictIndex,
ClosureOffset: closureOffset(n, closureVars),
})
// Record go type of to insure that it gets emitted by the linker.
}
if n.Esc() == ir.EscHeap {
if n.Heapaddr == nil {
panic("invalid heap allocated var without Heapaddr")
}
debug := fn.DebugInfo.(*ssa.FuncDebug)
list := createHeapDerefLocationList(n, fnsym, debug.EntryID, ssa.FuncEnd.ID)
dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
}
}
vars = append(vars, dvar)
// Record go type to ensure that it gets emitted by the linker.
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
}

Expand Down Expand Up @@ -550,6 +556,29 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars
return dvar
}

// createHeapDerefLocationList creates a location list for a heap-escaped variable
// that describes "dereference pointer at stack offset"
func createHeapDerefLocationList(n *ir.Name, fnsym *obj.LSym, entryID, prologEndID ssa.ID) []byte {
// Get the stack offset where the heap pointer is stored
heapPtrOffset := n.Heapaddr.FrameOffset()
if base.Ctxt.Arch.FixedFrameSize == 0 {
heapPtrOffset -= int64(types.PtrSize)
}
if buildcfg.FramePointerEnabled {
heapPtrOffset -= int64(types.PtrSize)
}

// Create a location expression: DW_OP_fbreg <offset> DW_OP_deref
var locExpr []byte
var sizeIdx int
locExpr, sizeIdx = ssa.SetupLocList(base.Ctxt, entryID, locExpr, ssa.BlockStart.ID, ssa.FuncEnd.ID)
locExpr = append(locExpr, dwarf.DW_OP_fbreg)
locExpr = dwarf.AppendSleb128(locExpr, heapPtrOffset)
locExpr = append(locExpr, dwarf.DW_OP_deref)
base.Ctxt.Arch.ByteOrder.PutUint16(locExpr[sizeIdx:], uint16(len(locExpr)-sizeIdx-2))
return locExpr
}

// RecordFlags records the specified command-line flags to be placed
// in the DWARF info.
func RecordFlags(flags ...string) {
Expand Down
16 changes: 9 additions & 7 deletions src/cmd/compile/internal/ssa/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ type FuncDebug struct {
RegOutputParams []*ir.Name
// Variable declarations that were removed during optimization
OptDcl []*ir.Name
// The ssa.Func.EntryID value, used to build location lists for
// return values promoted to heap in later DWARF generation.
EntryID ID

// Filled in by the user. Translates Block and Value ID to PC.
//
Expand Down Expand Up @@ -1645,13 +1648,13 @@ func readPtr(ctxt *obj.Link, buf []byte) uint64 {

}

// setupLocList creates the initial portion of a location list for a
// SetupLocList creates the initial portion of a location list for a
// user variable. It emits the encoded start/end of the range and a
// placeholder for the size. Return value is the new list plus the
// slot in the list holding the size (to be updated later).
func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) {
start, startOK := encodeValue(ctxt, f.Entry.ID, st)
end, endOK := encodeValue(ctxt, f.Entry.ID, en)
func SetupLocList(ctxt *obj.Link, entryID ID, list []byte, st, en ID) ([]byte, int) {
start, startOK := encodeValue(ctxt, entryID, st)
end, endOK := encodeValue(ctxt, entryID, en)
if !startOK || !endOK {
// This could happen if someone writes a function that uses
// >65K values on a 32-bit platform. Hopefully a degraded debugging
Expand Down Expand Up @@ -1800,7 +1803,6 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
// appropriate for the ".closureptr" compiler-synthesized variable
// needed by the debugger for range func bodies.
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {

needCloCtx := f.CloSlot != nil
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)

Expand Down Expand Up @@ -1911,7 +1913,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
// Param is arriving in one or more registers. We need a 2-element
// location expression for it. First entry in location list
// will correspond to lifetime in input registers.
list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx],
list, sizeIdx := SetupLocList(ctxt, f.Entry.ID, rval.LocationLists[pidx],
BlockStart.ID, afterPrologVal)
if list == nil {
pidx++
Expand Down Expand Up @@ -1961,7 +1963,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta

// Second entry in the location list will be the stack home
// of the param, once it has been spilled. Emit that now.
list, sizeIdx = setupLocList(ctxt, f, list,
list, sizeIdx = SetupLocList(ctxt, f.Entry.ID, list,
afterPrologVal, FuncEnd.ID)
if list == nil {
pidx++
Expand Down
3 changes: 3 additions & 0 deletions src/cmd/compile/internal/ssagen/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -6960,6 +6960,9 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
if base.Ctxt.Flag_locationlists {
var debugInfo *ssa.FuncDebug
debugInfo = e.curfn.DebugInfo.(*ssa.FuncDebug)
// Save off entry ID in case we need it later for DWARF generation
// for return values promoted to the heap.
debugInfo.EntryID = f.Entry.ID
if e.curfn.ABI == obj.ABIInternal && base.Flag.N != 0 {
ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset, debugInfo)
} else {
Expand Down
95 changes: 95 additions & 0 deletions src/cmd/link/dwarf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,98 @@ func TestDWARFiOS(t *testing.T) {
testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=ios", "GOARCH=arm64")
})
}

func TestDWARFLocationList(t *testing.T) {
testenv.MustHaveCGO(t)
testenv.MustHaveGoBuild(t)

if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) {
t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH)
}

t.Parallel()

tmpDir := t.TempDir()
exe := filepath.Join(tmpDir, "issue65405.exe")
dir := "./testdata/dwarf/issue65405"

cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-toolexec", os.Args[0], "-gcflags=all=-N -l", "-o", exe, dir)
cmd.Env = append(os.Environ(), "CGO_CFLAGS=")
cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
}

f, err := objfile.Open(exe)
if err != nil {
t.Fatal(err)
}
defer f.Close()

d, err := f.DWARF()
if err != nil {
t.Fatal(err)
}

// Find the net.sendFile function and check its return parameter location list
reader := d.Reader()
found := false

for {
entry, err := reader.Next()
if err != nil {
t.Fatal(err)
}
if entry == nil {
break
}

// Look for the net.sendFile subprogram
if entry.Tag == dwarf.TagSubprogram {
name, ok := entry.Val(dwarf.AttrName).(string)
if ok && name == "net.sendFile" {
found = true

for {
paramEntry, err := reader.Next()
if err != nil {
t.Fatal(err)
}
if paramEntry == nil || paramEntry.Tag == 0 {
break
}

if paramEntry.Tag == dwarf.TagFormalParameter {
paramName, hasName := paramEntry.Val(dwarf.AttrName).(string)
if hasName && paramName == "handled" {
// Check if this parameter has a location attribute
if loc := paramEntry.Val(dwarf.AttrLocation); loc != nil {
switch locData := loc.(type) {
case []byte:
if len(locData) == 0 {
t.Errorf("net.sendFile return parameter 'handled' has empty location list")
}
case int64:
// Location list offset - this means it has a location list
if locData == 0 {
t.Errorf("net.sendFile return parameter 'handled' has zero location list offset")
}
default:
t.Errorf("net.sendFile return parameter 'handled' has unexpected location type %T: %v", locData, locData)
}
} else {
t.Errorf("net.sendFile return parameter 'handled' has no location attribute")
}
return
}
}
}
}
}
}

if !found {
t.Fatal("could not find net.sendFile function in DWARF info")
}
}
8 changes: 8 additions & 0 deletions src/cmd/link/testdata/dwarf/issue65405/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

import "net/http"

func main() {
http.Handle("/", http.StripPrefix("/static/", http.FileServer(http.Dir("./output"))))
http.ListenAndServe(":8000", nil)
}