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() {
}
}
declpos := base.Ctxt.InnermostPos(n.Pos())
- vars = append(vars, &dwarf.Var{
+ dvar := &dwarf.Var{
Name: n.Sym().Name,
IsReturnValue: isReturnValue,
Tag: tag,
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 {
+ base.Fatalf("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()))
}
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) {
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.
//
}
-// 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
// 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)
// 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++
// 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++
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 {
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()
+
+ 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 {
+ fnName, ok := entry.Val(dwarf.AttrName).(string)
+ if !ok {
+ continue
+ }
+ if strings.Contains(fnName, ".eq") || // Ignore autogenerated equality funcs
+ strings.HasPrefix(fnName, "internal/") || // Ignore internal/runtime package TODO(deparker): Fix these too (likely same issue as other ignored packages below).
+ strings.HasPrefix(fnName, "runtime.") || // Ignore runtime package which contain funcs implemented in assembly or exposed through linkname which seems to not generate location lists correctly (most likely linkname causing this). TODO(deparker) Fix these too.
+ strings.HasPrefix(fnName, "reflect.") || // Ignore reflect package. TODO(deparker) Fix these too.
+ strings.HasPrefix(fnName, "time.") { // Ignore funcs in time package which are exposed through linkname and seem to not generate location lists correctly TODO(deparker) Fix these too.
+ continue
+ }
+ if fnName == "syscall.compileCallback" || fnName == "maps.clone" {
+ continue // Ignore for now, possibly caused by linkname usage. TODO(deparker) Fix this too.
+ }
+ if runtime.GOOS == "windows" && strings.HasPrefix(fnName, "syscall.") {
+ continue // Ignore, caused by linkname usage. TODO(deparker) Fix these too.
+ }
+
+ 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 {
+ continue
+ }
+ if paramName[0] == '~' {
+ continue
+ }
+ // 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("%s return parameter %q has empty location list", fnName, paramName)
+ return
+ }
+ case int64:
+ // Location list offset - this means it has a location list
+ if locData == 0 {
+ t.Errorf("%s return parameter %q has zero location list offset", fnName, paramName)
+ return
+ }
+ default:
+ t.Errorf("%s return parameter %q has unexpected location type %T: %v", fnName, paramName, locData, locData)
+ }
+ } else {
+ t.Errorf("%s return parameter %q has no location attribute", fnName, paramName)
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+package main
+
+import "net/http"
+
+func main() {
+ http.Handle("/", http.StripPrefix("/static/", http.FileServer(http.Dir("./output"))))
+ http.ListenAndServe(":8000", nil)
+}