From a0f5d5f8830e578892c47f7704e6a2616273aac1 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Mon, 10 Sep 2018 15:36:59 +0200 Subject: [PATCH] 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 Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/dwarf.go | 29 ++++--- src/cmd/link/internal/ld/dwarf_test.go | 114 +++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 12 deletions(-) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 959fc8290c..2164fa80a0 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -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 } diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 157bebbb41..5d2aadf589 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -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) + } + } +}