Commit a0f5d5f8 authored by Alessandro Arzilli's avatar Alessandro Arzilli Committed by Than McIntosh

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: 's avatarThan McIntosh <thanm@google.com>
parent 19ac6a82
......@@ -340,19 +340,19 @@ func lookupOrDiag(ctxt *Link, n string) *sym.Symbol {
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 @@ func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) {
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 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
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 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
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 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
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 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
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 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
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 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
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 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
prototypedies[gotype.Name] = die
}
if typedefdie != nil {
return typedefdie
}
return die
}
......
......@@ -948,3 +948,117 @@ func main() {
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)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment