cmd/link: fix DWARF refs so that they always point to the typedef entry

For types defined as:

type typename struct { ... }

the linker produces two DIEs: (1) a DW_TAG_structure_type DIE and (2) a
DW_TAG_typedef_type DIE having (1) as its type attribute.

All subsequent references to 'typename' should use the
DW_TAG_typedef_type DIE, not the DW_TAG_structure_type. Mostly this is
true but sometimes one reference will use the DW_TAG_structure_type
directly. In particular, this happens to the 'first' reference to the
type in question (where 'first' means whatever happens first in the way
the linker scans its symbols).

This isn't only true of struct types: pointer types, array types, etc.
can also be affected.

This fix solves the problem by always returning the typedef DIE in
newtype, when one is created.

Fixes #27614

Change-Id: Ia65b4a1d8c2b752e33a4ebdb74ccd92faa69526e
Reviewed-on: https://go-review.googlesource.com/134555
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 959fc82..2164fa8 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -340,19 +340,19 @@
 	return s
 }
 
-func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) {
+func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie {
 	// Only emit typedefs for real names.
 	if strings.HasPrefix(name, "map[") {
-		return
+		return nil
 	}
 	if strings.HasPrefix(name, "struct {") {
-		return
+		return nil
 	}
 	if strings.HasPrefix(name, "chan ") {
-		return
+		return nil
 	}
 	if name[0] == '[' || name[0] == '*' {
-		return
+		return nil
 	}
 	if def == nil {
 		Errorf(nil, "dwarf: bad def in dotypedef")
@@ -370,6 +370,8 @@
 	die := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
 
 	newrefattr(die, dwarf.DW_AT_type, s)
+
+	return die
 }
 
 // Define gotype, for composite ones recurse into constituents.
@@ -399,7 +401,7 @@
 	kind := decodetypeKind(ctxt.Arch, gotype)
 	bytesize := decodetypeSize(ctxt.Arch, gotype)
 
-	var die *dwarf.DWDie
+	var die, typedefdie *dwarf.DWDie
 	switch kind {
 	case objabi.KindBool:
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
@@ -439,7 +441,7 @@
 
 	case objabi.KindArray:
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
-		dotypedef(ctxt, &dwtypes, name, die)
+		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 		s := decodetypeArrayElem(ctxt.Arch, gotype)
 		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
@@ -461,7 +463,7 @@
 	case objabi.KindFunc:
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-		dotypedef(ctxt, &dwtypes, name, die)
+		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
 		nfields := decodetypeFuncInCount(ctxt.Arch, gotype)
 		for i := 0; i < nfields; i++ {
 			s := decodetypeFuncInType(ctxt.Arch, gotype, i)
@@ -481,7 +483,7 @@
 
 	case objabi.KindInterface:
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
-		dotypedef(ctxt, &dwtypes, name, die)
+		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
 		nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype))
 		var s *sym.Symbol
 		if nfields == 0 {
@@ -503,13 +505,13 @@
 
 	case objabi.KindPtr:
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0)
-		dotypedef(ctxt, &dwtypes, name, die)
+		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
 		s := decodetypePtrElem(ctxt.Arch, gotype)
 		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
 
 	case objabi.KindSlice:
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
-		dotypedef(ctxt, &dwtypes, name, die)
+		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 		s := decodetypeArrayElem(ctxt.Arch, gotype)
 		elem := defgotype(ctxt, s)
@@ -521,7 +523,7 @@
 
 	case objabi.KindStruct:
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
-		dotypedef(ctxt, &dwtypes, name, die)
+		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 		nfields := decodetypeStructFieldCount(ctxt.Arch, gotype)
 		for i := 0; i < nfields; i++ {
@@ -557,6 +559,9 @@
 		prototypedies[gotype.Name] = die
 	}
 
+	if typedefdie != nil {
+		return typedefdie
+	}
 	return die
 }
 
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index 157bebb..5d2aadf 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -948,3 +948,117 @@
 		t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
 	}
 }
+
+func TestIssue27614(t *testing.T) {
+	// Type references in debug_info should always use the DW_TAG_typedef_type
+	// for the type, when that's generated.
+
+	testenv.MustHaveGoBuild(t)
+
+	if runtime.GOOS == "plan9" {
+		t.Skip("skipping on plan9; no DWARF symbol table in executables")
+	}
+
+	dir, err := ioutil.TempDir("", "go-build")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	const prog = `package main
+
+import "fmt"
+
+type astruct struct {
+	X int
+}
+
+type bstruct struct {
+	X float32
+}
+
+var globalptr *astruct
+var globalvar astruct
+var bvar0, bvar1, bvar2 bstruct
+
+func main() {
+	fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
+}
+`
+
+	f := gobuild(t, dir, prog, NoOpt)
+
+	defer f.Close()
+
+	data, err := f.DWARF()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rdr := data.Reader()
+
+	var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
+	var globalptrDIE, globalvarDIE *dwarf.Entry
+	var bvarDIE [3]*dwarf.Entry
+
+	for {
+		e, err := rdr.Next()
+		if err != nil {
+			t.Fatal(err)
+		}
+		if e == nil {
+			break
+		}
+
+		name, _ := e.Val(dwarf.AttrName).(string)
+
+		switch e.Tag {
+		case dwarf.TagTypedef:
+			switch name {
+			case "main.astruct":
+				astructTypeDIE = e
+			case "main.bstruct":
+				bstructTypeDIE = e
+			}
+		case dwarf.TagPointerType:
+			if name == "*main.astruct" {
+				ptrastructTypeDIE = e
+			}
+		case dwarf.TagVariable:
+			switch name {
+			case "main.globalptr":
+				globalptrDIE = e
+			case "main.globalvar":
+				globalvarDIE = e
+			default:
+				const bvarprefix = "main.bvar"
+				if strings.HasPrefix(name, bvarprefix) {
+					i, _ := strconv.Atoi(name[len(bvarprefix):])
+					bvarDIE[i] = e
+				}
+			}
+		}
+	}
+
+	typedieof := func(e *dwarf.Entry) dwarf.Offset {
+		return e.Val(dwarf.AttrType).(dwarf.Offset)
+	}
+
+	if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
+		t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+	}
+
+	if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
+		t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
+	}
+
+	if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
+		t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+	}
+
+	for i := range bvarDIE {
+		if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
+			t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
+		}
+	}
+}