From 25f1b093fe02d87c879b1d8d030dc1c65aad7c78 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Wed, 20 Nov 2019 10:43:11 -0500 Subject: [PATCH] [dev.link] cmd/link: convert DWARF type generation to use loader Converts the portion of DWARF generation that deals with creation of type DIEs and constant DIEs to use the new loader interfaces. Creation of subprogram DIE and compilation unit DIE content still operates on sym.Symbols at the moment, and happens much later in the linker. The new code for type DIE generation is gated/guarded by the linker flag "-newdw", which currently defaults to true. At some point in the near future this flag should be removed, but it is handy for triage at the moment. This patch also includes shim code designed to run after loadlibfull() that walks through the DIE chains and to converts loader.Sym references back into sym.Symbol references for the remainder of the compilation, since the second phase of DWARF has not yet been converted. Change-Id: I681a00fb8a1f3c37884a79b373d86411332e07c8 Reviewed-on: https://go-review.googlesource.com/c/go/+/208230 Reviewed-by: Cherry Zhang Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/deadcode.go | 11 +- src/cmd/link/internal/ld/dwarf.go | 1851 ++++++----------- src/cmd/link/internal/ld/dwarf2.go | 1824 ++++++++++++++++ src/cmd/link/internal/ld/lib.go | 7 + src/cmd/link/internal/ld/main.go | 25 +- src/cmd/link/internal/loader/loader.go | 135 +- src/cmd/link/internal/sym/compilation_unit.go | 2 + 7 files changed, 2680 insertions(+), 1175 deletions(-) create mode 100644 src/cmd/link/internal/ld/dwarf2.go diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index ed341288f8..e725a1eab4 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -68,7 +68,10 @@ func addToTextp(ctxt *Link) { // Put reachable text symbols into Textp. // do it in postorder so that packages are laid down in dependency order - // internal first, then everything else + // internal first, then everything else. This also populates lib and + // unit Textp slices, which are needed for DWARF + // FIXME: once DWARF is completely converted to using loader.Sym, + // we can remove the *sym.Symbol Textp slices. ctxt.Library = postorder(ctxt.Library) for _, doInternal := range [2]bool{true, false} { for _, lib := range ctxt.Library { @@ -76,23 +79,21 @@ func addToTextp(ctxt *Link) { continue } libtextp := lib.Textp[:0] - for idx, s := range lib.Textp { + for _, s := range lib.Textp { if s.Attr.Reachable() { textp = append(textp, s) libtextp = append(libtextp, s) if s.Unit != nil { s.Unit.Textp = append(s.Unit.Textp, s) - s.Unit.Textp2 = append(s.Unit.Textp2, lib.Textp2[idx]) } } } - for idx, s := range lib.DupTextSyms { + for _, s := range lib.DupTextSyms { if s.Attr.Reachable() && !s.Attr.OnList() { textp = append(textp, s) libtextp = append(libtextp, s) if s.Unit != nil { s.Unit.Textp = append(s.Unit.Textp, s) - s.Unit.Textp2 = append(s.Unit.Textp2, lib.DupTextSyms2[idx]) } s.Attr |= sym.AttrOnList // dupok symbols may be defined in multiple packages. its diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 6eba39bcf7..1dbd5cc05f 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1,4 +1,4 @@ -// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -15,116 +15,193 @@ package ld import ( "cmd/internal/dwarf" - "cmd/internal/obj" "cmd/internal/objabi" - "cmd/internal/src" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "fmt" "log" - "sort" "strings" ) -type dwctxt struct { +// dwctxt2 is a wrapper intended to satisfy the method set of +// dwarf.Context, so that functions like dwarf.PutAttrs will work with +// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also +// being used as a place to store tables/maps that are useful as part +// of type conversion (this is just a convenience; it would be easy to +// split these things out into another type if need be). +type dwctxt2 struct { linkctxt *Link + ldr *loader.Loader + arch *sys.Arch + + // This maps type name string (e.g. "uintptr") to loader symbol for + // the DWARF DIE for that type (e.g. "go.info.type.uintptr") + tmap map[string]loader.Sym + + // This maps loader symbol for the DWARF DIE symbol generated for + // a type (e.g. "go.info.uintptr") to the type symbol itself + // ("type.uintptr"). + // FIXME: try converting this map (and the next one) to a single + // array indexed by loader.Sym -- this may perform better. + rtmap map[loader.Sym]loader.Sym + + // This maps Go type symbol (e.g. "type.XXX") to loader symbol for + // the typedef DIE for that type (e.g. "go.info.XXX..def") + tdmap map[loader.Sym]loader.Sym + + // Cache these type symbols, so as to avoid repeatedly looking them up + typeRuntimeEface loader.Sym + typeRuntimeIface loader.Sym + uintptrInfoSym loader.Sym +} + +func newdwctxt2(linkctxt *Link, forTypeGen bool) dwctxt2 { + d := dwctxt2{ + linkctxt: linkctxt, + ldr: linkctxt.loader, + arch: linkctxt.Arch, + tmap: make(map[string]loader.Sym), + tdmap: make(map[loader.Sym]loader.Sym), + rtmap: make(map[loader.Sym]loader.Sym), + } + d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface") + d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface") + return d } -func (c dwctxt) PtrSize() int { - return c.linkctxt.Arch.PtrSize +func (c dwctxt2) PtrSize() int { + return c.arch.PtrSize } -func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) { - ls := s.(*sym.Symbol) - ls.AddUintXX(c.linkctxt.Arch, uint64(i), size) + +func (c dwctxt2) AddInt(s dwarf.Sym, size int, i int64) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + dsu.AddUintXX(c.arch, uint64(i), size) } -func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) { - ls := s.(*sym.Symbol) - ls.AddBytes(b) + +func (c dwctxt2) AddBytes(s dwarf.Sym, b []byte) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + dsu.AddBytes(b) } -func (c dwctxt) AddString(s dwarf.Sym, v string) { - Addstring(s.(*sym.Symbol), v) + +func (c dwctxt2) AddString(s dwarf.Sym, v string) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + dsu.Addstring(v) } -func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { +func (c dwctxt2) AddAddress(s dwarf.Sym, data interface{}, value int64) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) if value != 0 { - value -= (data.(*sym.Symbol)).Value + value -= dsu.Value() } - s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) + tgtds := data.(dwSym) + dsu.AddAddrPlus(c.arch, tgtds.s, value) } -func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { +func (c dwctxt2) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) if value != 0 { - value -= (data.(*sym.Symbol)).Value + value -= dsu.Value() } - s.(*sym.Symbol).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) + tgtds := data.(dwSym) + dsu.AddCURelativeAddrPlus(c.arch, tgtds.s, value) } -func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { - ls := s.(*sym.Symbol) +func (c dwctxt2) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + tds := t.(dwSym) switch size { default: - Errorf(ls, "invalid size %d in adddwarfref\n", size) + c.linkctxt.Errorf(ds.s, "invalid size %d in adddwarfref\n", size) fallthrough - case c.linkctxt.Arch.PtrSize: - ls.AddAddr(c.linkctxt.Arch, t.(*sym.Symbol)) + case c.arch.PtrSize: + dsu.AddAddrPlus(c.arch, tds.s, 0) case 4: - ls.AddAddrPlus4(t.(*sym.Symbol), 0) + dsu.AddAddrPlus4(c.arch, tds.s, 0) } - r := &ls.R[len(ls.R)-1] + rsl := dsu.Relocs() + r := &rsl[len(rsl)-1] r.Type = objabi.R_ADDROFF r.Add = ofs } -func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { +func (c dwctxt2) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { size := 4 if isDwarf64(c.linkctxt) { size = 8 } c.AddSectionOffset(s, size, t, ofs) - ls := s.(*sym.Symbol) - ls.R[len(ls.R)-1].Type = objabi.R_DWARFSECREF + + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + rsl := dsu.Relocs() + r := &rsl[len(rsl)-1] + r.Type = objabi.R_DWARFSECREF } -func (c dwctxt) Logf(format string, args ...interface{}) { +func (c dwctxt2) Logf(format string, args ...interface{}) { c.linkctxt.Logf(format, args...) } // At the moment these interfaces are only used in the compiler. -func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) { +func (c dwctxt2) AddFileRef(s dwarf.Sym, f interface{}) { panic("should be used only in the compiler") } -func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 { +func (c dwctxt2) CurrentOffset(s dwarf.Sym) int64 { panic("should be used only in the compiler") } -func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { +func (c dwctxt2) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { panic("should be used only in the compiler") } -func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { +func (c dwctxt2) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { panic("should be used only in the compiler") } -func isDwarf64(ctxt *Link) bool { - return ctxt.HeadType == objabi.Haix +// dwSym wraps a loader.Sym; objects of this type are stored +// in the 'Sym' field of dwarf.DIE objects. +// +// FIXME: the main reason we need the loader.Loader pointer field is +// that the dwarf.Sym interface has a Len() method with no parameters. +// If we changed this method to accept a dwxtxt (from which we could +// access the loader) then we could get rid of this field and/or avoid +// using a struct. +type dwSym struct { + s loader.Sym + l *loader.Loader +} + +func (s dwSym) Len() int64 { + return int64(len(s.l.Data(s.s))) } var gdbscript string -var dwarfp []*sym.Symbol +var dwarfp2 []loader.Sym -func writeabbrev(ctxt *Link) *sym.Symbol { - s := ctxt.Syms.Lookup(".debug_abbrev", 0) - s.Type = sym.SDWARFSECT - s.AddBytes(dwarf.GetAbbrev()) - return s +func (d *dwctxt2) writeabbrev(ctxt *Link) loader.Sym { + panic("not yet implemented") } var dwtypes dwarf.DWDie +// newattr attaches a new attribute to the specified DIE. +// +// FIXME: at the moment attributes are stored in a linked list in a +// fairly space-inefficient way -- it might be better to instead look +// up all attrs in a single large table, then store indices into the +// table in the DIE. This would allow us to common up storage for +// attributes that are shared by many DIEs (ex: byte size of N). func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr { a := new(dwarf.DWAttr) a.Link = die.Attr @@ -165,7 +242,8 @@ func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr { // attribute (but it will only be written out if it is listed in the abbrev). // The compiler does create nameless DWARF DIEs (ex: concrete subprogram // instance). -func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { +// FIXME: it would be more efficient to bulk-allocate DIEs. +func (d *dwctxt2) newdie(parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { die := new(dwarf.DWDie) die.Abbrev = abbrev die.Link = parent.Child @@ -174,15 +252,22 @@ func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version in newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name) if name != "" && (abbrev <= dwarf.DW_ABRV_VARIABLE || abbrev >= dwarf.DW_ABRV_NULLTYPE) { + // Q: do we need version here? My understanding is that all these + // symbols should be version 0. if abbrev != dwarf.DW_ABRV_VARIABLE || version == 0 { if abbrev == dwarf.DW_ABRV_COMPUNIT { // Avoid collisions with "real" symbol names. - name = fmt.Sprintf(".pkg.%s.%d", name, len(ctxt.compUnits)) + name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits)) + } + ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, version) + dsu := d.ldr.MakeSymbolUpdater(ds) + dsu.SetType(sym.SDWARFINFO) + d.ldr.SetAttrNotInSymbolTable(ds, true) + d.ldr.SetAttrReachable(ds, true) + die.Sym = dwSym{s: ds, l: d.ldr} + if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL { + d.tmap[name] = ds } - s := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version) - s.Attr |= sym.AttrNotInSymbolTable - s.Type = sym.SDWARFINFO - die.Sym = s } } @@ -205,11 +290,22 @@ func walktypedef(die *dwarf.DWDie) *dwarf.DWDie { return die } -func walksymtypedef(ctxt *Link, s *sym.Symbol) *sym.Symbol { - if t := ctxt.Syms.ROLookup(s.Name+"..def", int(s.Version)); t != nil { - return t +func (d *dwctxt2) walksymtypedef(symIdx loader.Sym) loader.Sym { + + // We're being given the loader symbol for the type DIE, e.g. + // "go.info.type.uintptr". Map that first to the type symbol (e.g. + // "type.uintptr") and then to the typedef DIE for the type. + // FIXME: this seems clunky, maybe there is a better way to do this. + + if ts, ok := d.rtmap[symIdx]; ok { + if def, ok := d.tdmap[ts]; ok { + return def + } + d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts) + return 0 } - return s + d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx) + return 0 } // Find child by AT_name using hashtable if available or linear scan @@ -230,75 +326,54 @@ func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie { // Used to avoid string allocation when looking up dwarf symbols var prefixBuf = []byte(dwarf.InfoPrefix) -func find(ctxt *Link, name string) *sym.Symbol { - n := append(prefixBuf, name...) - // The string allocation below is optimized away because it is only used in a map lookup. - s := ctxt.Syms.ROLookup(string(n), 0) - prefixBuf = n[:len(dwarf.InfoPrefix)] - if s != nil && s.Type == sym.SDWARFINFO { - return s - } - return nil +// find looks up the loader symbol for the DWARF DIE generated for the +// type with the specified name. +func (d *dwctxt2) find(name string) loader.Sym { + return d.tmap[name] } -func mustFind(ctxt *Link, name string) *sym.Symbol { - r := find(ctxt, name) - if r == nil { +func (d *dwctxt2) mustFind(name string) loader.Sym { + r := d.find(name) + if r == 0 { Exitf("dwarf find: cannot find %s", name) } return r } -func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 { +func (d *dwctxt2) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) int64 { var result int64 switch size { default: - Errorf(s, "invalid size %d in adddwarfref\n", size) + d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size) fallthrough - case ctxt.Arch.PtrSize: - result = s.AddAddr(ctxt.Arch, t) + case d.arch.PtrSize: + result = sb.AddAddrPlus(d.arch, t, 0) case 4: - result = s.AddAddrPlus4(t, 0) + result = sb.AddAddrPlus4(d.arch, t, 0) } - r := &s.R[len(s.R)-1] + rsl := sb.Relocs() + r := &rsl[len(rsl)-1] r.Type = objabi.R_DWARFSECREF return result } -func newrefattr(die *dwarf.DWDie, attr uint16, ref *sym.Symbol) *dwarf.DWAttr { - if ref == nil { +func (d *dwctxt2) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr { + if ref == 0 { return nil } - return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref) + return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym{s: ref, l: d.ldr}) } -func dtolsym(s dwarf.Sym) *sym.Symbol { +func (d *dwctxt2) dtolsym(s dwarf.Sym) loader.Sym { if s == nil { - return nil + return 0 } - return s.(*sym.Symbol) + dws := s.(dwSym) + return dws.s } -func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*sym.Symbol, die *dwarf.DWDie) []*sym.Symbol { - s := dtolsym(die.Sym) - if s == nil { - s = syms[len(syms)-1] - } else { - if s.Attr.OnList() { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Attr |= sym.AttrOnList - syms = append(syms, s) - } - dwarf.Uleb128put(ctxt, s, int64(die.Abbrev)) - dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr) - if dwarf.HasChildren(die) { - for die := die.Child; die != nil; die = die.Link { - syms = putdie(linkctxt, ctxt, syms, die) - } - syms[len(syms)-1].AddUint8(0) - } - return syms +func (d *dwctxt2) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym { + panic("not yet implemented") } func reverselist(list **dwarf.DWDie) { @@ -329,42 +404,23 @@ func newmemberoffsetattr(die *dwarf.DWDie, offs int32) { // GDB doesn't like FORM_addr for AT_location, so emit a // location expression that evals to a const. -func newabslocexprattr(die *dwarf.DWDie, addr int64, sym *sym.Symbol) { - newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, sym) - // below +func (d *dwctxt2) newabslocexprattr(die *dwarf.DWDie, addr int64, symIdx loader.Sym) { + newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, dwSym{s: symIdx, l: d.ldr}) } -// Lookup predefined types -func lookupOrDiag(ctxt *Link, n string) *sym.Symbol { - s := ctxt.Syms.ROLookup(n, 0) - if s == nil || s.Size == 0 { +func (d *dwctxt2) lookupOrDiag(n string) loader.Sym { + symIdx := d.ldr.Lookup(n, 0) + if symIdx == 0 { Exitf("dwarf: missing type: %s", n) } - - return s -} - -// dwarfFuncSym looks up a DWARF metadata symbol for function symbol s. -// If the symbol does not exist, it creates it if create is true, -// or returns nil otherwise. -func dwarfFuncSym(ctxt *Link, s *sym.Symbol, meta string, create bool) *sym.Symbol { - // All function ABIs use symbol version 0 for the DWARF data. - // - // TODO(austin): It may be useful to have DWARF info for ABI - // wrappers, in which case we may want these versions to - // align. Better yet, replace these name lookups with a - // general way to attach metadata to a symbol. - ver := 0 - if s.IsFileLocal() { - ver = int(s.Version) + if len(d.ldr.Data(symIdx)) == 0 { + Exitf("dwarf: missing type (no data): %s", n) } - if create { - return ctxt.Syms.Lookup(meta+s.Name, ver) - } - return ctxt.Syms.ROLookup(meta+s.Name, ver) + + return symIdx } -func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie { +func (d *dwctxt2) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, def *dwarf.DWDie) *dwarf.DWDie { // Only emit typedefs for real names. if strings.HasPrefix(name, "map[") { return nil @@ -382,53 +438,66 @@ func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) * Errorf(nil, "dwarf: bad def in dotypedef") } - s := ctxt.Syms.Lookup(dtolsym(def.Sym).Name+"..def", 0) - s.Attr |= sym.AttrNotInSymbolTable - s.Type = sym.SDWARFINFO - def.Sym = s + // Create a new loader symbol for the typedef. We no longer + // do lookups of typedef symbols by name, so this is going + // to be an anonymous symbol (we want this for perf reasons). + tds := d.ldr.CreateExtSym("") + tdsu := d.ldr.MakeSymbolUpdater(tds) + tdsu.SetType(sym.SDWARFINFO) + def.Sym = dwSym{s: tds, l: d.ldr} + d.ldr.SetAttrNotInSymbolTable(tds, true) + d.ldr.SetAttrReachable(tds, true) // The typedef entry must be created after the def, // so that future lookups will find the typedef instead // of the real definition. This hooks the typedef into any // circular definition loops, so that gdb can understand them. - die := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0) + die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name, 0) - newrefattr(die, dwarf.DW_AT_type, s) + d.newrefattr(die, dwarf.DW_AT_type, tds) return die } // Define gotype, for composite ones recurse into constituents. -func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol { - if gotype == nil { - return mustFind(ctxt, "") +func (d *dwctxt2) defgotype(gotype loader.Sym) loader.Sym { + if gotype == 0 { + return d.mustFind("") } - if !strings.HasPrefix(gotype.Name, "type.") { - Errorf(gotype, "dwarf: type name doesn't start with \"type.\"") - return mustFind(ctxt, "") + // If we already have a tdmap entry for the gotype, return it. + if ds, ok := d.tdmap[gotype]; ok { + return ds } - name := gotype.Name[5:] // could also decode from Type.string - - sdie := find(ctxt, name) + sn := d.ldr.SymName(gotype) + if !strings.HasPrefix(sn, "type.") { + d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type.\"") + return d.mustFind("") + } + name := sn[5:] // could also decode from Type.string - if sdie != nil { + sdie := d.find(name) + if sdie != 0 { return sdie } - return newtype(ctxt, gotype).Sym.(*sym.Symbol) + gtdwSym := d.newtype(gotype) + d.tdmap[gotype] = gtdwSym.Sym.(dwSym).s + return gtdwSym.Sym.(dwSym).s } -func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { - name := gotype.Name[5:] // could also decode from Type.string - kind := decodetypeKind(ctxt.Arch, gotype.P) - bytesize := decodetypeSize(ctxt.Arch, gotype.P) +func (d *dwctxt2) newtype(gotype loader.Sym) *dwarf.DWDie { + sn := d.ldr.SymName(gotype) + name := sn[5:] // could also decode from Type.string + tdata := d.ldr.Data(gotype) + kind := decodetypeKind(d.arch, tdata) + bytesize := decodetypeSize(d.arch, tdata) var die, typedefdie *dwarf.DWDie switch kind { case objabi.KindBool: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) @@ -437,7 +506,7 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { objabi.KindInt16, objabi.KindInt32, objabi.KindInt64: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) @@ -447,118 +516,126 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { objabi.KindUint32, objabi.KindUint64, objabi.KindUintptr: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindFloat32, objabi.KindFloat64: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindComplex64, objabi.KindComplex128: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindArray: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, 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)) - fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) + s := decodetypeArrayElem2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) // use actual length not upper bound; correct for 0-length arrays. - newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen2(d.ldr, d.arch, gotype), 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) case objabi.KindChan: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) - s := decodetypeChanElem(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) + s := decodetypeChanElem2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) // Save elem type for synthesizechantypes. We could synthesize here // but that would change the order of DIEs we output. - newrefattr(die, dwarf.DW_AT_type, s) + d.newrefattr(die, dwarf.DW_AT_type, s) case objabi.KindFunc: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) - nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + data := d.ldr.Data(gotype) + // FIXME: add caching or reuse reloc slice. + relocs := d.ldr.Relocs(gotype) + rslice := relocs.ReadAll(nil) + nfields := decodetypeFuncInCount(d.arch, data) for i := 0; i < nfields; i++ { - s := decodetypeFuncInType(ctxt.Arch, gotype, i) - fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) - newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) + s := decodetypeFuncInType2(d.ldr, d.arch, gotype, rslice, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) } - if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) { - newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) + if decodetypeFuncDotdotdot(d.arch, data) { + d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) } - nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P) + nfields = decodetypeFuncOutCount(d.arch, data) for i := 0; i < nfields; i++ { - s := decodetypeFuncOutType(ctxt.Arch, gotype, i) - fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) - newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, defgotype(ctxt, s))) + s := decodetypeFuncOutType2(d.ldr, d.arch, gotype, rslice, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s))) } case objabi.KindInterface: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) - nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P)) - var s *sym.Symbol + die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + data := d.ldr.Data(gotype) + nfields := int(decodetypeIfaceMethodCount(d.arch, data)) + var s loader.Sym if nfields == 0 { - s = lookupOrDiag(ctxt, "type.runtime.eface") + s = d.typeRuntimeEface } else { - s = lookupOrDiag(ctxt, "type.runtime.iface") + s = d.typeRuntimeIface } - newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindMap: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) - s := decodetypeMapKey(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_go_key, defgotype(ctxt, s)) - s = decodetypeMapValue(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) + s := decodetypeMapKey2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s)) + s = decodetypeMapValue2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) // Save gotype for use in synthesizemaptypes. We could synthesize here, // but that would change the order of the DIEs. - newrefattr(die, dwarf.DW_AT_type, gotype) + d.newrefattr(die, dwarf.DW_AT_type, gotype) case objabi.KindPtr: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) - s := decodetypePtrElem(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + s := decodetypePtrElem2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindSlice: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - s := decodetypeArrayElem(ctxt.Arch, gotype) - elem := defgotype(ctxt, s) - newrefattr(die, dwarf.DW_AT_go_elem, elem) + s := decodetypeArrayElem2(d.ldr, d.arch, gotype) + elem := d.defgotype(s) + d.newrefattr(die, dwarf.DW_AT_go_elem, elem) case objabi.KindString: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindStruct: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - nfields := decodetypeStructFieldCount(ctxt.Arch, gotype) + nfields := decodetypeStructFieldCount2(d.ldr, d.arch, gotype) for i := 0; i < nfields; i++ { - f := decodetypeStructFieldName(ctxt.Arch, gotype, i) - s := decodetypeStructFieldType(ctxt.Arch, gotype, i) + f := decodetypeStructFieldName2(d.ldr, d.arch, gotype, i) + s := decodetypeStructFieldType2(d.ldr, d.arch, gotype, i) if f == "" { - f = s.Name[5:] // skip "type." + sn := d.ldr.SymName(s) + f = sn[5:] // skip "type." } - fld := newdie(ctxt, die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) - newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) - offsetAnon := decodetypeStructFieldOffsAnon(ctxt.Arch, gotype, i) + fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) + offsetAnon := decodetypeStructFieldOffsAnon2(d.ldr, d.arch, gotype, i) newmemberoffsetattr(fld, int32(offsetAnon>>1)) if offsetAnon&1 != 0 { // is embedded field newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) @@ -566,21 +643,33 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { } case objabi.KindUnsafePointer: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) default: - Errorf(gotype, "dwarf: definition of unknown kind %d", kind) - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) - newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "")) + d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) + d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("")) } newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) - if gotype.Attr.Reachable() { - newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + + if d.ldr.AttrReachable(gotype) { + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym{s: gotype, l: d.ldr}) } - if _, ok := prototypedies[gotype.Name]; ok { - prototypedies[gotype.Name] = die + // Sanity check. + if _, ok := d.rtmap[gotype]; ok { + log.Fatalf("internal error: rtmap entry already installed\n") + } + + ds := die.Sym.(dwSym) + if typedefdie != nil { + ds = typedefdie.Sym.(dwSym) + } + d.rtmap[ds.s] = gotype + + if _, ok := prototypedies[sn]; ok { + prototypedies[sn] = die } if typedefdie != nil { @@ -589,55 +678,67 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { return die } -func nameFromDIESym(dwtype *sym.Symbol) string { - return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def") +func (d *dwctxt2) nameFromDIESym(dwtypeDIESym loader.Sym) string { + sn := d.ldr.SymName(dwtypeDIESym) + return sn[len(dwarf.InfoPrefix):] } -// Find or construct *T given T. -func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol { - ptrname := "*" + nameFromDIESym(dwtype) - if die := find(ctxt, ptrname); die != nil { +func (d *dwctxt2) defptrto(dwtype loader.Sym) loader.Sym { + + // FIXME: it would be nice if the compiler attached an aux symbol + // ref from the element type to the pointer type -- it would be + // more efficient to do it this way as opposed to via name lookups. + + ptrname := "*" + d.nameFromDIESym(dwtype) + if die := d.find(ptrname); die != 0 { return die } - pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) - newrefattr(pdie, dwarf.DW_AT_type, dwtype) + pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) + d.newrefattr(pdie, dwarf.DW_AT_type, dwtype) // The DWARF info synthesizes pointer types that don't exist at the // language level, like *hash<...> and *bucket<...>, and the data // pointers of slices. Link to the ones we can find. - gotype := ctxt.Syms.ROLookup("type."+ptrname, 0) - if gotype != nil && gotype.Attr.Reachable() { - newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + gts := d.ldr.Lookup("type."+ptrname, 0) + if gts != 0 && d.ldr.AttrReachable(gts) { + newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym{s: gts, l: d.ldr}) + } + + if gts != 0 { + ds := pdie.Sym.(dwSym) + d.rtmap[ds.s] = gts + d.tdmap[gts] = ds.s } - return dtolsym(pdie.Sym) + + return d.dtolsym(pdie.Sym) } // Copies src's children into dst. Copies attributes by value. // DWAttr.data is copied as pointer only. If except is one of // the top-level children, it will not be copied. -func copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { +func (d *dwctxt2) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { for src = src.Child; src != nil; src = src.Link { if src == except { continue } - c := newdie(ctxt, dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) + c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) for a := src.Attr; a != nil; a = a.Link { newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) } - copychildrenexcept(ctxt, c, src, nil) + d.copychildrenexcept(ctxt, c, src, nil) } reverselist(&dst.Child) } -func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { - copychildrenexcept(ctxt, dst, src, nil) +func (d *dwctxt2) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { + d.copychildrenexcept(ctxt, dst, src, nil) } // Search children (assumed to have TAG_member) for the one named // field and set its AT_type to dwtype -func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) { +func (d *dwctxt2) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) { child := findchild(structdie, field) if child == nil { Exitf("dwarf substitutetype: %s does not have member %s", @@ -647,23 +748,26 @@ func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) { a := getattr(child, dwarf.DW_AT_type) if a != nil { - a.Data = dwtype + a.Data = dwSym{s: dwtype, l: d.ldr} } else { - newrefattr(child, dwarf.DW_AT_type, dwtype) + d.newrefattr(child, dwarf.DW_AT_type, dwtype) } } -func findprotodie(ctxt *Link, name string) *dwarf.DWDie { +func (d *dwctxt2) findprotodie(ctxt *Link, name string) *dwarf.DWDie { die, ok := prototypedies[name] if ok && die == nil { - defgotype(ctxt, lookupOrDiag(ctxt, name)) + d.defgotype(d.lookupOrDiag(name)) die = prototypedies[name] } + if die == nil { + log.Fatalf("internal error: DIE generation failed for %s\n", name) + } return die } -func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { - prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF")) +func (d *dwctxt2) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.stringStructDWARF")) if prototype == nil { return } @@ -672,12 +776,12 @@ func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE { continue } - copychildren(ctxt, die, prototype) + d.copychildren(ctxt, die, prototype) } } -func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { - prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice")) +func (d *dwctxt2) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.slice")) if prototype == nil { return } @@ -686,9 +790,9 @@ func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_SLICETYPE { continue } - copychildren(ctxt, die, prototype) - elem := getattr(die, dwarf.DW_AT_go_elem).Data.(*sym.Symbol) - substitutetype(die, "array", defptrto(ctxt, elem)) + d.copychildren(ctxt, die, prototype) + elem := getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym).s + d.substitutetype(die, "array", d.defptrto(elem)) } } @@ -706,21 +810,21 @@ const ( BucketSize = 8 ) -func mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *sym.Symbol { +func (d *dwctxt2) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym { name := mkinternaltypename(typename, keyname, valname) symname := dwarf.InfoPrefix + name - s := ctxt.Syms.ROLookup(symname, 0) - if s != nil && s.Type == sym.SDWARFINFO { + s := d.ldr.Lookup(symname, 0) + if s != 0 && d.ldr.SymType(s) == sym.SDWARFINFO { return s } - die := newdie(ctxt, &dwtypes, abbrev, name, 0) + die := d.newdie(&dwtypes, abbrev, name, 0) f(die) - return dtolsym(die.Sym) + return d.dtolsym(die.Sym) } -func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { - hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap")) - bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap")) +func (d *dwctxt2) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { + hash := walktypedef(d.findprotodie(ctxt, "type.runtime.hmap")) + bucket := walktypedef(d.findprotodie(ctxt, "type.runtime.bmap")) if hash == nil { return @@ -730,92 +834,94 @@ func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_MAPTYPE { continue } - gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) - keytype := decodetypeMapKey(ctxt.Arch, gotype) - valtype := decodetypeMapValue(ctxt.Arch, gotype) - keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P) - keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype)) + gotype := getattr(die, dwarf.DW_AT_type).Data.(dwSym).s + keytype := decodetypeMapKey2(d.ldr, d.arch, gotype) + valtype := decodetypeMapValue2(d.ldr, d.arch, gotype) + keydata := d.ldr.Data(keytype) + valdata := d.ldr.Data(valtype) + keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata) + keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype)) // compute size info like hashmap.c does. indirectKey, indirectVal := false, false if keysize > MaxKeySize { - keysize = int64(ctxt.Arch.PtrSize) + keysize = int64(d.arch.PtrSize) indirectKey = true } if valsize > MaxValSize { - valsize = int64(ctxt.Arch.PtrSize) + valsize = int64(d.arch.PtrSize) indirectVal = true } // Construct type to represent an array of BucketSize keys - keyname := nameFromDIESym(keytype) - dwhks := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { + keyname := d.nameFromDIESym(keytype) + dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0) t := keytype if indirectKey { - t = defptrto(ctxt, keytype) + t = d.defptrto(keytype) } - newrefattr(dwhk, dwarf.DW_AT_type, t) + d.newrefattr(dwhk, dwarf.DW_AT_type, t) fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) // Construct type to represent an array of BucketSize values - valname := nameFromDIESym(valtype) - dwhvs := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { + valname := d.nameFromDIESym(valtype) + dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0) t := valtype if indirectVal { - t = defptrto(ctxt, valtype) + t = d.defptrto(valtype) } - newrefattr(dwhv, dwarf.DW_AT_type, t) + d.newrefattr(dwhv, dwarf.DW_AT_type, t) fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) // Construct bucket - dwhbs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { + dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { // Copy over all fields except the field "data" from the generic // bucket. "data" will be replaced with keys/values below. - copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) + d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0) - newrefattr(fld, dwarf.DW_AT_type, dwhks) + d.newrefattr(fld, dwarf.DW_AT_type, dwhks) newmemberoffsetattr(fld, BucketSize) fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0) - newrefattr(fld, dwarf.DW_AT_type, dwhvs) + d.newrefattr(fld, dwarf.DW_AT_type, dwhvs) newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0) - newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym))) + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym))) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) - if ctxt.Arch.RegSize > ctxt.Arch.PtrSize { + if d.arch.RegSize > d.arch.PtrSize { fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) - newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(ctxt.Arch.PtrSize)) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize)) } - newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(ctxt.Arch.RegSize), 0) + newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0) }) // Construct hash - dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { - copychildren(ctxt, dwh, hash) - substitutetype(dwh, "buckets", defptrto(ctxt, dwhbs)) - substitutetype(dwh, "oldbuckets", defptrto(ctxt, dwhbs)) + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hash) + d.substitutetype(dwh, "buckets", d.defptrto(dwhbs)) + d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs)) newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil) }) // make map type a pointer to hash - newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) } } -func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { - sudog := walktypedef(findprotodie(ctxt, "type.runtime.sudog")) - waitq := walktypedef(findprotodie(ctxt, "type.runtime.waitq")) - hchan := walktypedef(findprotodie(ctxt, "type.runtime.hchan")) +func (d *dwctxt2) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { + sudog := walktypedef(d.findprotodie(ctxt, "type.runtime.sudog")) + waitq := walktypedef(d.findprotodie(ctxt, "type.runtime.waitq")) + hchan := walktypedef(d.findprotodie(ctxt, "type.runtime.hchan")) if sudog == nil || waitq == nil || hchan == nil { return } @@ -826,143 +932,77 @@ func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_CHANTYPE { continue } - elemgotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) - elemname := elemgotype.Name[5:] - elemtype := walksymtypedef(ctxt, defgotype(ctxt, elemgotype)) + elemgotype := getattr(die, dwarf.DW_AT_type).Data.(dwSym).s + tname := d.ldr.SymName(elemgotype) + elemname := tname[5:] + elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname))) // sudog - dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { - copychildren(ctxt, dws, sudog) - substitutetype(dws, "elem", defptrto(ctxt, elemtype)) + dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { + d.copychildren(ctxt, dws, sudog) + d.substitutetype(dws, "elem", d.defptrto(elemtype)) newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil) }) // waitq - dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { + dwws := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { - copychildren(ctxt, dww, waitq) - substitutetype(dww, "first", defptrto(ctxt, dwss)) - substitutetype(dww, "last", defptrto(ctxt, dwss)) + d.copychildren(ctxt, dww, waitq) + d.substitutetype(dww, "first", d.defptrto(dwss)) + d.substitutetype(dww, "last", d.defptrto(dwss)) newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil) }) // hchan - dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { - copychildren(ctxt, dwh, hchan) - substitutetype(dwh, "recvq", dwws) - substitutetype(dwh, "sendq", dwws) + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hchan) + d.substitutetype(dwh, "recvq", dwws) + d.substitutetype(dwh, "sendq", dwws) newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil) }) - newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) } } -func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *sym.Symbol) { +func (d *dwctxt2) dwarfDefineGlobal(ctxt *Link, symIdx loader.Sym, str string, v int64, gotype loader.Sym) { // Find a suitable CU DIE to include the global. // One would think it's as simple as just looking at the unit, but that might // not have any reachable code. So, we go to the runtime's CU if our unit // isn't otherwise reachable. - var unit *sym.CompilationUnit - if s.Unit != nil { - unit = s.Unit - } else { + unit := d.ldr.SymUnit(symIdx) + if unit == nil { unit = ctxt.runtimeCU } - dv := newdie(ctxt, unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version)) - newabslocexprattr(dv, v, s) - if !s.IsFileLocal() { + ver := d.ldr.SymVersion(symIdx) + dv := d.newdie(unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(ver)) + d.newabslocexprattr(dv, v, symIdx) + if d.ldr.SymVersion(symIdx) < sym.SymVerStatic { newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0) } - dt := defgotype(ctxt, gotype) - newrefattr(dv, dwarf.DW_AT_type, dt) -} - -// For use with pass.c::genasmsym -func defdwsymb(ctxt *Link, s *sym.Symbol, str string, t SymbolType, v int64, gotype *sym.Symbol) { - if strings.HasPrefix(str, "go.string.") { - return - } - if strings.HasPrefix(str, "runtime.gcbits.") { - return - } - - switch t { - case DataSym, BSSSym: - switch s.Type { - case sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: - // ok - case sym.SRODATA: - if gotype != nil { - defgotype(ctxt, gotype) - } - return - default: - return - } - if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) { - return - } - dwarfDefineGlobal(ctxt, s, str, v, gotype) - - case AutoSym, ParamSym, DeletedAutoSym: - defgotype(ctxt, gotype) - } + dt := d.defgotype(gotype) + d.newrefattr(dv, dwarf.DW_AT_type, dt) } // createUnitLength creates the initial length field with value v and update // offset of unit_length if needed. -func createUnitLength(ctxt *Link, s *sym.Symbol, v uint64) { - if isDwarf64(ctxt) { - s.AddUint32(ctxt.Arch, 0xFFFFFFFF) - } - addDwarfAddrField(ctxt, s, v) +func (d *dwctxt2) createUnitLength(ctxt *Link, symIdx loader.Sym, v uint64) { + panic("not yet implemented") } // addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits. -func addDwarfAddrField(ctxt *Link, s *sym.Symbol, v uint64) { - if isDwarf64(ctxt) { - s.AddUint(ctxt.Arch, v) - } else { - s.AddUint32(ctxt.Arch, uint32(v)) - } +func (d *dwctxt2) addDwarfAddrField(ctxt *Link, symIdx loader.Sym, v uint64) { + panic("not yet implemented") } // addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits. -func addDwarfAddrRef(ctxt *Link, s *sym.Symbol, t *sym.Symbol) { - if isDwarf64(ctxt) { - adddwarfref(ctxt, s, t, 8) - } else { - adddwarfref(ctxt, s, t, 4) - } +func (d *dwctxt2) addDwarfAddrRef(ctxt *Link, symIdx loader.Sym, t *sym.Symbol) { + panic("not yet implemented") } // calcCompUnitRanges calculates the PC ranges of the compilation units. -func calcCompUnitRanges(ctxt *Link) { - var prevUnit *sym.CompilationUnit - for _, s := range ctxt.Textp { - if s.FuncInfo == nil { - continue - } - // Skip linker-created functions (ex: runtime.addmoduledata), since they - // don't have DWARF to begin with. - if s.Unit == nil { - continue - } - unit := s.Unit - // Update PC ranges. - // - // We don't simply compare the end of the previous - // symbol with the start of the next because there's - // often a little padding between them. Instead, we - // only create boundaries between symbols from - // different units. - if prevUnit != unit { - unit.PCs = append(unit.PCs, dwarf.Range{Start: s.Value - unit.Textp[0].Value}) - prevUnit = unit - } - unit.PCs[len(unit.PCs)-1].End = s.Value - unit.Textp[0].Value + s.Size - } +func (d *dwctxt2) calcCompUnitRanges(ctxt *Link) { + panic("not yet implemented") } func movetomodule(ctxt *Link, parent *dwarf.DWDie) { @@ -978,21 +1018,8 @@ func movetomodule(ctxt *Link, parent *dwarf.DWDie) { } // If the pcln table contains runtime/proc.go, use that to set gdbscript path. -func finddebugruntimepath(s *sym.Symbol) { - if gdbscript != "" { - return - } - - for i := range s.FuncInfo.File { - f := s.FuncInfo.File[i] - // We can't use something that may be dead-code - // eliminated from a binary here. proc.go contains - // main and the scheduler, so it's not going anywhere. - if i := strings.Index(f.Name, "runtime/proc.go"); i >= 0 { - gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" - break - } - } +func (d *dwctxt2) finddebugruntimepath(symIdx loader.Sym) { + panic("not yet implemented") } /* @@ -1020,202 +1047,41 @@ func getCompilationDir() string { return "." } -func importInfoSymbol(ctxt *Link, dsym *sym.Symbol) { - dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable - dsym.Type = sym.SDWARFINFO - for i := range dsym.R { - r := &dsym.R[i] // Copying sym.Reloc has measurable impact on performance - if r.Type == objabi.R_DWARFSECREF && r.Sym.Size == 0 { - n := nameFromDIESym(r.Sym) - defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) - } - } -} - -func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) { - - var dwarfctxt dwarf.Context = dwctxt{ctxt} - is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles. - - unitstart := int64(-1) - headerstart := int64(-1) - headerend := int64(-1) - - newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, ls.Size, ls) - - // Write .debug_line Line Number Program Header (sec 6.2.4) - // Fields marked with (*) must be changed for 64-bit dwarf - unitLengthOffset := ls.Size - createUnitLength(ctxt, ls, 0) // unit_length (*), filled in at end - unitstart = ls.Size - ls.AddUint16(ctxt.Arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05 - headerLengthOffset := ls.Size - addDwarfAddrField(ctxt, ls, 0) // header_length (*), filled in at end - headerstart = ls.Size - - // cpos == unitstart + 4 + 2 + 4 - ls.AddUint8(1) // minimum_instruction_length - ls.AddUint8(is_stmt) // default_is_stmt - ls.AddUint8(LINE_BASE & 0xFF) // line_base - ls.AddUint8(LINE_RANGE) // line_range - ls.AddUint8(OPCODE_BASE) // opcode_base - ls.AddUint8(0) // standard_opcode_lengths[1] - ls.AddUint8(1) // standard_opcode_lengths[2] - ls.AddUint8(1) // standard_opcode_lengths[3] - ls.AddUint8(1) // standard_opcode_lengths[4] - ls.AddUint8(1) // standard_opcode_lengths[5] - ls.AddUint8(0) // standard_opcode_lengths[6] - ls.AddUint8(0) // standard_opcode_lengths[7] - ls.AddUint8(0) // standard_opcode_lengths[8] - ls.AddUint8(1) // standard_opcode_lengths[9] - ls.AddUint8(0) // standard_opcode_lengths[10] - ls.AddUint8(0) // include_directories (empty) - - // Copy over the file table. - fileNums := make(map[string]int) - for i, name := range unit.DWARFFileTable { - if len(name) != 0 { - if strings.HasPrefix(name, src.FileSymPrefix) { - name = name[len(src.FileSymPrefix):] - } - name = expandGoroot(name) - } else { - // Can't have empty filenames, and having a unique filename is quite useful - // for debugging. - name = fmt.Sprintf("_%d", i) - } - fileNums[name] = i + 1 - dwarfctxt.AddString(ls, name) - ls.AddUint8(0) - ls.AddUint8(0) - ls.AddUint8(0) - } - // Grab files for inlined functions. - // TODO: With difficulty, this could be moved into the compiler. - for _, s := range unit.Textp { - dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true) - for ri := 0; ri < len(dsym.R); ri++ { - r := &dsym.R[ri] - if r.Type != objabi.R_DWARFFILEREF { - continue - } - name := r.Sym.Name - if _, ok := fileNums[name]; ok { - continue - } - fileNums[name] = len(fileNums) + 1 - dwarfctxt.AddString(ls, name) - ls.AddUint8(0) - ls.AddUint8(0) - ls.AddUint8(0) - } +func (d *dwctxt2) importInfoSymbol(ctxt *Link, dsym loader.Sym) { + d.ldr.SetAttrReachable(dsym, true) + d.ldr.SetAttrNotInSymbolTable(dsym, true) + if d.ldr.SymType(dsym) != sym.SDWARFINFO { + log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String()) } - - // 4 zeros: the string termination + 3 fields. - ls.AddUint8(0) - // terminate file_names. - headerend = ls.Size - - // Output the state machine for each function remaining. - var lastAddr int64 - for _, s := range unit.Textp { - finddebugruntimepath(s) - - // Set the PC. - ls.AddUint8(0) - dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize)) - ls.AddUint8(dwarf.DW_LNE_set_address) - addr := ls.AddAddr(ctxt.Arch, s) - // Make sure the units are sorted. - if addr < lastAddr { - Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr) + drelocs := d.ldr.Relocs(dsym) + rslice := drelocs.ReadSyms(nil) + for i := 0; i < len(rslice); i++ { + r := &rslice[i] + if r.Type != objabi.R_DWARFSECREF { + continue } - lastAddr = addr - - // Output the line table. - // TODO: Now that we have all the debug information in separate - // symbols, it would make sense to use a rope, and concatenate them all - // together rather then the append() below. This would allow us to have - // the compiler emit the DW_LNE_set_address and a rope data structure - // to concat them all together in the output. - lines := dwarfFuncSym(ctxt, s, dwarf.DebugLinesPrefix, false) - if lines != nil { - ls.P = append(ls.P, lines.P...) + // If there is an entry for the symbol in our rtmap, then it + // means we've processed the type already, and can skip this one. + if _, ok := d.rtmap[r.Sym]; ok { + // type already generated + continue } + // FIXME: is there a way we could avoid materializing the + // symbol name here? + sn := d.ldr.SymName(r.Sym) + tn := sn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type."+tn, 0) + d.defgotype(ts) } +} - ls.AddUint8(0) // start extended opcode - dwarf.Uleb128put(dwarfctxt, ls, 1) - ls.AddUint8(dwarf.DW_LNE_end_sequence) - - if ctxt.HeadType == objabi.Haix { - saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(ls.Size-unitLengthOffset)) - } - if isDwarf64(ctxt) { - ls.SetUint(ctxt.Arch, unitLengthOffset+4, uint64(ls.Size-unitstart)) // +4 because of 0xFFFFFFFF - ls.SetUint(ctxt.Arch, headerLengthOffset, uint64(headerend-headerstart)) - } else { - ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart)) - ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart)) - } - - // Process any R_DWARFFILEREF relocations, since we now know the - // line table file indices for this compilation unit. Note that - // this loop visits only subprogram DIEs: if the compiler is - // changed to generate DW_AT_decl_file attributes for other - // DIE flavors (ex: variables) then those DIEs would need to - // be included below. - missing := make(map[int]interface{}) - s := unit.Textp[0] - for _, f := range unit.FuncDIEs { - for ri := range f.R { - r := &f.R[ri] - if r.Type != objabi.R_DWARFFILEREF { - continue - } - idx, ok := fileNums[r.Sym.Name] - if ok { - if int(int32(idx)) != idx { - Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow") - } - if r.Siz != 4 { - Errorf(f, "bad R_DWARFFILEREF relocation: has size %d, expected 4", r.Siz) - } - if r.Off < 0 || r.Off+4 > int32(len(f.P)) { - Errorf(f, "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d", r.Off, len(s.P)) - continue - } - if r.Add != 0 { - Errorf(f, "bad R_DWARFFILEREF relocation: addend not zero") - } - r.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable - r.Add = int64(idx) // record the index in r.Add, we'll apply it in the reloc phase. - } else { - _, found := missing[int(r.Sym.Value)] - if !found { - Errorf(f, "R_DWARFFILEREF relocation file missing: %v idx %d", r.Sym, r.Sym.Value) - missing[int(r.Sym.Value)] = nil - } - } - } - } +func (d *dwctxt2) writelines(ctxt *Link, unit *sym.CompilationUnit, ls loader.Sym) { + panic("not yet implemented") } // writepcranges generates the DW_AT_ranges table for compilation unit cu. -func writepcranges(ctxt *Link, unit *sym.CompilationUnit, base *sym.Symbol, pcs []dwarf.Range, ranges *sym.Symbol) { - var dwarfctxt dwarf.Context = dwctxt{ctxt} - - unitLengthOffset := ranges.Size - - // Create PC ranges for this CU. - newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, ranges.Size, ranges) - newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, base.Value, base) - dwarf.PutBasedRanges(dwarfctxt, ranges, pcs) - - if ctxt.HeadType == objabi.Haix { - addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(ranges.Size-unitLengthOffset)) - } - +func (d *dwctxt2) writepcranges(ctxt *Link, unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, ranges loader.Sym) { + panic("not yet implemented") } /* @@ -1246,237 +1112,27 @@ func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte { return b } -func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { - var dwarfctxt dwarf.Context = dwctxt{ctxt} - fs := ctxt.Syms.Lookup(".debug_frame", 0) - fs.Type = sym.SDWARFSECT - syms = append(syms, fs) - - // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64 - lengthFieldSize := int64(4) - if isDwarf64(ctxt) { - lengthFieldSize += 8 - } - - // Emit the CIE, Section 6.4.1 - cieReserve := uint32(16) - if haslinkregister(ctxt) { - cieReserve = 32 - } - if isDwarf64(ctxt) { - cieReserve += 4 // 4 bytes added for cid - } - createUnitLength(ctxt, fs, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize - addDwarfAddrField(ctxt, fs, ^uint64(0)) // cid - fs.AddUint8(3) // dwarf version (appendix F) - fs.AddUint8(0) // augmentation "" - dwarf.Uleb128put(dwarfctxt, fs, 1) // code_alignment_factor - dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // return_address_register - - fs.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... - if haslinkregister(ctxt) { - dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset. - - fs.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) - - fs.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... - dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...is CFA+0. - } else { - dwarf.Uleb128put(dwarfctxt, fs, int64(ctxt.Arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). - - fs.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // ...of the return address... - dwarf.Uleb128put(dwarfctxt, fs, int64(-ctxt.Arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. - } - - pad := int64(cieReserve) + lengthFieldSize - fs.Size - - if pad < 0 { - Exitf("dwarf: cieReserve too small by %d bytes.", -pad) - } - - fs.AddBytes(zeros[:pad]) - - var deltaBuf []byte - pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) - for _, s := range ctxt.Textp { - if s.FuncInfo == nil { - continue - } - - // Emit a FDE, Section 6.4.1. - // First build the section contents into a byte buffer. - deltaBuf = deltaBuf[:0] - if haslinkregister(ctxt) && s.Attr.TopFrame() { - // Mark the link register as having an undefined value. - // This stops call stack unwinders progressing any further. - // TODO: similar mark on non-LR architectures. - deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined) - deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) - } - for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() { - nextpc := pcsp.NextPC - - // pciterinit goes up to the end of the function, - // but DWARF expects us to stop just before the end. - if int64(nextpc) == s.Size { - nextpc-- - if nextpc < pcsp.PC { - continue - } - } - - spdelta := int64(pcsp.Value) - if !haslinkregister(ctxt) { - // Return address has been pushed onto stack. - spdelta += int64(ctxt.Arch.PtrSize) - } - - if haslinkregister(ctxt) && !s.Attr.TopFrame() { - // TODO(bryanpkc): This is imprecise. In general, the instruction - // that stores the return address to the stack frame is not the - // same one that allocates the frame. - if pcsp.Value > 0 { - // The return address is preserved at (CFA-frame_size) - // after a stack frame has been allocated. - deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf) - deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) - deltaBuf = dwarf.AppendSleb128(deltaBuf, -spdelta/dataAlignmentFactor) - } else { - // The return address is restored into the link register - // when a stack frame has been de-allocated. - deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value) - deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) - } - } - - deltaBuf = appendPCDeltaCFA(ctxt.Arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) - } - pad := int(Rnd(int64(len(deltaBuf)), int64(ctxt.Arch.PtrSize))) - len(deltaBuf) - deltaBuf = append(deltaBuf, zeros[:pad]...) - - // Emit the FDE header, Section 6.4.1. - // 4 bytes: length, must be multiple of thearch.ptrsize - // 4/8 bytes: Pointer to the CIE above, at offset 0 - // ptrsize: initial location - // ptrsize: address range - - fdeLength := uint64(4 + 2*ctxt.Arch.PtrSize + len(deltaBuf)) - if isDwarf64(ctxt) { - fdeLength += 4 // 4 bytes added for CIE pointer - } - createUnitLength(ctxt, fs, fdeLength) - - if ctxt.LinkMode == LinkExternal { - addDwarfAddrRef(ctxt, fs, fs) - } else { - addDwarfAddrField(ctxt, fs, 0) // CIE offset - } - fs.AddAddr(ctxt.Arch, s) - fs.AddUintXX(ctxt.Arch, uint64(s.Size), ctxt.Arch.PtrSize) // address range - fs.AddBytes(deltaBuf) - - if ctxt.HeadType == objabi.Haix { - addDwsectCUSize(".debug_frame", s.File, fdeLength+uint64(lengthFieldSize)) - } - } - return syms +func (d *dwctxt2) writeframes([]loader.Sym) []loader.Sym { + panic("not yet implemented") } /* * Walk DWarfDebugInfoEntries, and emit .debug_info */ + const ( COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 ) -func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abbrevsym *sym.Symbol, pubNames, pubTypes *pubWriter) []*sym.Symbol { - infosec := ctxt.Syms.Lookup(".debug_info", 0) - infosec.Type = sym.SDWARFINFO - infosec.Attr |= sym.AttrReachable - syms = append(syms, infosec) - - var dwarfctxt dwarf.Context = dwctxt{ctxt} - - for _, u := range units { - compunit := u.DWInfo - s := dtolsym(compunit.Sym) - - if len(u.Textp) == 0 && u.DWInfo.Child == nil { - continue - } - - pubNames.beginCompUnit(compunit) - pubTypes.beginCompUnit(compunit) - - // Write .debug_info Compilation Unit Header (sec 7.5.1) - // Fields marked with (*) must be changed for 64-bit dwarf - // This must match COMPUNITHEADERSIZE above. - createUnitLength(ctxt, s, 0) // unit_length (*), will be filled in later. - s.AddUint16(ctxt.Arch, 4) // dwarf version (appendix F) - - // debug_abbrev_offset (*) - addDwarfAddrRef(ctxt, s, abbrevsym) - - s.AddUint8(uint8(ctxt.Arch.PtrSize)) // address_size - - dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev)) - dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr) - - cu := []*sym.Symbol{s} - cu = append(cu, u.AbsFnDIEs...) - cu = append(cu, u.FuncDIEs...) - if u.Consts != nil { - cu = append(cu, u.Consts) - } - var cusize int64 - for _, child := range cu { - cusize += child.Size - } - - for die := compunit.Child; die != nil; die = die.Link { - l := len(cu) - lastSymSz := cu[l-1].Size - cu = putdie(ctxt, dwarfctxt, cu, die) - if ispubname(die) { - pubNames.add(die, cusize) - } - if ispubtype(die) { - pubTypes.add(die, cusize) - } - if lastSymSz != cu[l-1].Size { - // putdie will sometimes append directly to the last symbol of the list - cusize = cusize - lastSymSz + cu[l-1].Size - } - for _, child := range cu[l:] { - cusize += child.Size - } - } - cu[len(cu)-1].AddUint8(0) // closes compilation unit DIE - cusize++ - - // Save size for AIX symbol table. - if ctxt.HeadType == objabi.Haix { - saveDwsectCUSize(".debug_info", getPkgFromCUSym(s), uint64(cusize)) - } - if isDwarf64(ctxt) { - cusize -= 12 // exclude the length field. - s.SetUint(ctxt.Arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF - } else { - cusize -= 4 // exclude the length field. - s.SetUint32(ctxt.Arch, 0, uint32(cusize)) - } - pubNames.endCompUnit(compunit, uint32(cusize)+4) - pubTypes.endCompUnit(compunit, uint32(cusize)+4) - syms = append(syms, cu...) - } - return syms +func (d *dwctxt2) writeinfo(ctxt *Link, syms []loader.Sym, units []*sym.CompilationUnit, abbrevsym loader.Sym, pubNames, pubTypes *pubWriter) []loader.Sym { + panic("not yet implemented") } +/* + * Emit .debug_pubnames/_types. _info must have been written before, + * because we need die->offs and infoo/infosize; + */ + /* * Emit .debug_pubnames/_types. _info must have been written before, * because we need die->offs and infoo/infosize; @@ -1495,84 +1151,12 @@ func ispubtype(die *dwarf.DWDie) bool { return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE } -type pubWriter struct { - ctxt *Link - s *sym.Symbol - sname string - - sectionstart int64 - culengthOff int64 -} - -func newPubWriter(ctxt *Link, sname string) *pubWriter { - s := ctxt.Syms.Lookup(sname, 0) - s.Type = sym.SDWARFSECT - return &pubWriter{ctxt: ctxt, s: s, sname: sname} +func (d *dwctxt2) writegdbscript(syms []loader.Sym) []loader.Sym { + panic("not yet implemented") } -func (pw *pubWriter) beginCompUnit(compunit *dwarf.DWDie) { - pw.sectionstart = pw.s.Size - - // Write .debug_pubnames/types Header (sec 6.1.1) - createUnitLength(pw.ctxt, pw.s, 0) // unit_length (*), will be filled in later. - pw.s.AddUint16(pw.ctxt.Arch, 2) // dwarf version (appendix F) - addDwarfAddrRef(pw.ctxt, pw.s, dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header) - pw.culengthOff = pw.s.Size - addDwarfAddrField(pw.ctxt, pw.s, uint64(0)) // debug_info_length, will be filled in later. - -} - -func (pw *pubWriter) add(die *dwarf.DWDie, offset int64) { - dwa := getattr(die, dwarf.DW_AT_name) - name := dwa.Data.(string) - if die.Sym == nil { - fmt.Println("Missing sym for ", name) - } - addDwarfAddrField(pw.ctxt, pw.s, uint64(offset)) - Addstring(pw.s, name) -} - -func (pw *pubWriter) endCompUnit(compunit *dwarf.DWDie, culength uint32) { - addDwarfAddrField(pw.ctxt, pw.s, 0) // Null offset - - // On AIX, save the current size of this compilation unit. - if pw.ctxt.HeadType == objabi.Haix { - saveDwsectCUSize(pw.sname, getPkgFromCUSym(dtolsym(compunit.Sym)), uint64(pw.s.Size-pw.sectionstart)) - } - if isDwarf64(pw.ctxt) { - pw.s.SetUint(pw.ctxt.Arch, pw.sectionstart+4, uint64(pw.s.Size-pw.sectionstart)-12) // exclude the length field. - pw.s.SetUint(pw.ctxt.Arch, pw.culengthOff, uint64(culength)) - } else { - pw.s.SetUint32(pw.ctxt.Arch, pw.sectionstart, uint32(pw.s.Size-pw.sectionstart)-4) // exclude the length field. - pw.s.SetUint32(pw.ctxt.Arch, pw.culengthOff, culength) - } -} - -func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { - // TODO (aix): make it available - if ctxt.HeadType == objabi.Haix { - return syms - } - if ctxt.LinkMode == LinkExternal && ctxt.HeadType == objabi.Hwindows && ctxt.BuildMode == BuildModeCArchive { - // gcc on Windows places .debug_gdb_scripts in the wrong location, which - // causes the program not to run. See https://golang.org/issue/20183 - // Non c-archives can avoid this issue via a linker script - // (see fix near writeGDBLinkerScript). - // c-archive users would need to specify the linker script manually. - // For UX it's better not to deal with this. - return syms - } - - if gdbscript != "" { - s := ctxt.Syms.Lookup(".debug_gdb_scripts", 0) - s.Type = sym.SDWARFSECT - syms = append(syms, s) - s.AddUint8(1) // magic 1 byte? - Addstring(s, gdbscript) - } - - return syms -} +// FIXME: might be worth looking replacing this map with a function +// that switches based on symbol instead. var prototypedies map[string]*dwarf.DWDie @@ -1606,37 +1190,60 @@ func dwarfEnabled(ctxt *Link) bool { return true } +// mkBuiltinType populates the dwctxt2 sym lookup maps for the +// newly created builtin type DIE 'typeDie'. +func (d *dwctxt2) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie { + // create type DIE + die := d.newdie(&dwtypes, abrv, tname, 0) + + // Look up type symbol. + gotype := d.lookupOrDiag("type." + tname) + + // Map from die sym to type sym + ds := die.Sym.(dwSym) + d.rtmap[ds.s] = gotype + + // Map from type to def sym + d.tdmap[gotype] = ds.s + + return die +} + // dwarfGenerateDebugInfo generated debug info entries for all types, // variables and functions in the program. // Along with dwarfGenerateDebugSyms they are the two main entry points into // dwarf generation: dwarfGenerateDebugInfo does all the work that should be -// done before symbol names are mangled while dwarfgeneratedebugsyms does +// done before symbol names are mangled while dwarfGenerateDebugSyms does // all the work that can only be done after addresses have been assigned to // text symbols. -func dwarfGenerateDebugInfo(ctxt *Link) { +func dwarfGenerateDebugInfo2(ctxt *Link) { if !dwarfEnabled(ctxt) { return } + d := newdwctxt2(ctxt, true) + if ctxt.HeadType == objabi.Haix { // Initial map used to store package size for each DWARF section. dwsectCUSize = make(map[string]uint64) } - // Forctxt.Diagnostic messages. + // For ctxt.Diagnostic messages. newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") - // Some types that must exist to define other ones. - newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "", 0) - - newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0) - newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0) + // Unspecified type. There are no references to this in the symbol table. + d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "", 0) - die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size + // Some types that must exist to define other ones (uintptr in particular + // is needed for array size) + d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") + die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr") newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) - newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(ctxt.Arch.PtrSize), 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0) newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0) - newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, lookupOrDiag(ctxt, "type.uintptr")) + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym{s: d.lookupOrDiag("type.uintptr"), l: d.ldr}) + + d.uintptrInfoSym = d.mustFind("uintptr") // Prototypes needed for type synthesis. prototypedies = map[string]*dwarf.DWDie{ @@ -1662,23 +1269,24 @@ func dwarfGenerateDebugInfo(ctxt *Link) { "type.runtime.interfacetype", "type.runtime.itab", "type.runtime.imethod"} { - defgotype(ctxt, lookupOrDiag(ctxt, typ)) + d.defgotype(d.lookupOrDiag(typ)) } // fake root DIE for compile unit DIEs var dwroot dwarf.DWDie flagVariants := make(map[string]bool) + var relocs []loader.Reloc for _, lib := range ctxt.Library { - consts := ctxt.Syms.ROLookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) + + consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) for _, unit := range lib.Units { // We drop the constants into the first CU. - if consts != nil { - importInfoSymbol(ctxt, consts) - unit.Consts = consts - consts = nil + if consts != 0 { + unit.Consts2 = sym.LoaderSym(consts) + d.importInfoSymbol(ctxt, consts) + consts = 0 } - ctxt.compUnits = append(ctxt.compUnits, unit) // We need at least one runtime unit. @@ -1686,7 +1294,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) { ctxt.runtimeCU = unit } - unit.DWInfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0) + unit.DWInfo = d.newdie(&dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0) newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0) // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. compDir := getCompilationDir() @@ -1694,17 +1302,21 @@ func dwarfGenerateDebugInfo(ctxt *Link) { // the linker directory. If we move CU construction into the // compiler, this should happen naturally. newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir) - producerExtra := ctxt.Syms.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0) + + var peData []byte + if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 { + peData = d.ldr.Data(producerExtra) + } producer := "Go cmd/compile " + objabi.Version - if len(producerExtra.P) > 0 { + if len(peData) > 0 { // We put a semicolon before the flags to clearly // separate them from the version, which can be long // and have lots of weird things in it in development // versions. We promise not to put a semicolon in the // version, so it should be safe for readers to scan // forward to the semicolon. - producer += "; " + string(producerExtra.P) - flagVariants[string(producerExtra.P)] = true + producer += "; " + string(peData) + flagVariants[string(peData)] = true } else { flagVariants[""] = true } @@ -1712,12 +1324,13 @@ func dwarfGenerateDebugInfo(ctxt *Link) { newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer) var pkgname string - if s := ctxt.Syms.ROLookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); s != nil { - pkgname = string(s.P) + if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 { + pnsData := d.ldr.Data(pnSymIdx) + pkgname = string(pnsData) } newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname) - if len(unit.Textp) == 0 { + if len(unit.Textp2) == 0 { unit.DWInfo.Abbrev = dwarf.DW_ABRV_COMPUNIT_TEXTLESS } @@ -1725,36 +1338,44 @@ func dwarfGenerateDebugInfo(ctxt *Link) { // referenced types, create the file table for debug_line, find all // referenced abstract functions. // Collect all debug_range symbols in unit.rangeSyms - for _, s := range unit.Textp { // textp has been dead-code-eliminated already. - dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, false) - dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable - dsym.Type = sym.SDWARFINFO - unit.FuncDIEs = append(unit.FuncDIEs, dsym) - - rangeSym := dwarfFuncSym(ctxt, s, dwarf.RangePrefix, false) - if rangeSym != nil && rangeSym.Size > 0 { - rangeSym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable - rangeSym.Type = sym.SDWARFRANGE + for _, s := range unit.Textp2 { // textp2 has been dead-code-eliminated already. + fnSym := loader.Sym(s) + infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym) + d.ldr.SetAttrNotInSymbolTable(infosym, true) + d.ldr.SetAttrReachable(infosym, true) + unit.FuncDIEs2 = append(unit.FuncDIEs2, sym.LoaderSym(infosym)) + if rangesym != 0 { + rs := len(d.ldr.Data(rangesym)) + d.ldr.SetAttrNotInSymbolTable(rangesym, true) + d.ldr.SetAttrReachable(rangesym, true) if ctxt.HeadType == objabi.Haix { - addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rangeSym.Size)) + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rs)) } - unit.RangeSyms = append(unit.RangeSyms, rangeSym) + unit.RangeSyms2 = append(unit.RangeSyms2, sym.LoaderSym(rangesym)) } - for ri := 0; ri < len(dsym.R); ri++ { - r := &dsym.R[ri] + drelocs := d.ldr.Relocs(infosym) + relocs = drelocs.ReadSyms(relocs) + for ri := 0; ri < drelocs.Count; ri++ { + r := &relocs[ri] if r.Type == objabi.R_DWARFSECREF { rsym := r.Sym - if strings.HasPrefix(rsym.Name, dwarf.InfoPrefix) && strings.HasSuffix(rsym.Name, dwarf.AbstractFuncSuffix) && !rsym.Attr.OnList() { + // NB: there should be a better way to do this that doesn't involve materializing the symbol name and doing string prefix+suffix checks. + rsn := d.ldr.SymName(rsym) + if strings.HasPrefix(rsn, dwarf.InfoPrefix) && strings.HasSuffix(rsn, dwarf.AbstractFuncSuffix) && !d.ldr.AttrOnList(rsym) { // abstract function - rsym.Attr |= sym.AttrOnList - unit.AbsFnDIEs = append(unit.AbsFnDIEs, rsym) - importInfoSymbol(ctxt, rsym) - } else if rsym.Size == 0 { - // a type we do not have a DIE for - n := nameFromDIESym(rsym) - defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) + d.ldr.SetAttrOnList(rsym, true) + unit.AbsFnDIEs2 = append(unit.AbsFnDIEs2, sym.LoaderSym(rsym)) + d.importInfoSymbol(ctxt, rsym) + continue } + if _, ok := d.rtmap[rsym]; ok { + // type already generated + continue + } + tn := rsn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type."+tn, 0) + d.defgotype(ts) } } } @@ -1769,19 +1390,56 @@ func dwarfGenerateDebugInfo(ctxt *Link) { } // Create DIEs for global variables and the types they use. - genasmsym(ctxt, defdwsymb) + // FIXME: ideally this should be done in the compiler, since + // for globals there isn't any abiguity about which package + // a global belongs to. + for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ { + if !d.ldr.AttrReachable(idx) || + d.ldr.AttrNotInSymbolTable(idx) || + d.ldr.SymVersion(idx) >= sym.SymVerStatic { + continue + } + t := d.ldr.SymType(idx) + switch t { + case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: + // ok + default: + continue + } + // Skip things with no type + if d.ldr.SymGoType(idx) == 0 { + continue + } + + sn := d.ldr.SymName(idx) + if ctxt.LinkMode != LinkExternal && isStaticTemp(sn) { + continue + } + if sn == "" { + // skip aux symbols + continue + } + + // Create DIE for global. + sv := d.ldr.SymValue(idx) + gt := d.ldr.SymGoType(idx) + d.dwarfDefineGlobal(ctxt, idx, sn, sv, gt) + } // Create DIEs for variable types indirectly referenced by function // autos (which may not appear directly as param/var DIEs). for _, lib := range ctxt.Library { for _, unit := range lib.Units { - lists := [][]*sym.Symbol{unit.AbsFnDIEs, unit.FuncDIEs} + lists := [][]sym.LoaderSym{unit.AbsFnDIEs2, unit.FuncDIEs2} for _, list := range lists { for _, s := range list { - for i := 0; i < len(s.R); i++ { - r := &s.R[i] + symIdx := loader.Sym(s) + srelocs := d.ldr.Relocs(symIdx) + relocs = srelocs.ReadSyms(relocs) + for i := 0; i < len(relocs); i++ { + r := &relocs[i] if r.Type == objabi.R_USETYPE { - defgotype(ctxt, r.Sym) + d.defgotype(r.Sym) } } } @@ -1789,256 +1447,123 @@ func dwarfGenerateDebugInfo(ctxt *Link) { } } - synthesizestringtypes(ctxt, dwtypes.Child) - synthesizeslicetypes(ctxt, dwtypes.Child) - synthesizemaptypes(ctxt, dwtypes.Child) - synthesizechantypes(ctxt, dwtypes.Child) + d.synthesizestringtypes(ctxt, dwtypes.Child) + d.synthesizeslicetypes(ctxt, dwtypes.Child) + d.synthesizemaptypes(ctxt, dwtypes.Child) + d.synthesizechantypes(ctxt, dwtypes.Child) + + // NB: at this stage we have all the DIE objects constructed, but + // they have loader.Sym attributes and not sym.Symbol attributes. + // At the point when loadlibfull runs we will need to visit + // every DIE constructed and convert the symbols. } -// dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc, -// debug_pubnames and debug_pubtypes. It also writes out the debug_info -// section using symbols generated in dwarfGenerateDebugInfo. -func dwarfGenerateDebugSyms(ctxt *Link) { - if !dwarfEnabled(ctxt) { - return +// dwarfConvertSymbols is invoked around the time that loader.LoadFull +// runs (converting all loader.Sym's into sym.Symbols); it walks +// through dwarf DIE objects and rewrites loader.Sym refs to +// sym.Symbol there as well. This is obviously a temporary function. +func dwarfConvertSymbols(ctxt *Link) { + convdies := make(map[*dwarf.DWDie]bool) + for _, lib := range ctxt.Library { + for _, unit := range lib.Units { + convertSymbolsInDIE(ctxt, unit.DWInfo, convdies) + } } + convertSymbolsInDIE(ctxt, &dwtypes, convdies) - abbrev := writeabbrev(ctxt) - syms := []*sym.Symbol{abbrev} - - calcCompUnitRanges(ctxt) - sort.Sort(compilationUnitByStartPC(ctxt.compUnits)) - - // Write per-package line and range tables and start their CU DIEs. - debugLine := ctxt.Syms.Lookup(".debug_line", 0) - debugLine.Type = sym.SDWARFSECT - debugRanges := ctxt.Syms.Lookup(".debug_ranges", 0) - debugRanges.Type = sym.SDWARFRANGE - debugRanges.Attr |= sym.AttrReachable - syms = append(syms, debugLine) - for _, u := range ctxt.compUnits { - reversetree(&u.DWInfo.Child) - if u.DWInfo.Abbrev == dwarf.DW_ABRV_COMPUNIT_TEXTLESS { - continue - } - writelines(ctxt, u, debugLine) - writepcranges(ctxt, u, u.Textp[0], u.PCs, debugRanges) - } - - // newdie adds DIEs to the *beginning* of the parent's DIE list. - // Now that we're done creating DIEs, reverse the trees so DIEs - // appear in the order they were created. - reversetree(&dwtypes.Child) - movetomodule(ctxt, &dwtypes) - - pubNames := newPubWriter(ctxt, ".debug_pubnames") - pubTypes := newPubWriter(ctxt, ".debug_pubtypes") - - // Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT - infosyms := writeinfo(ctxt, nil, ctxt.compUnits, abbrev, pubNames, pubTypes) - - syms = writeframes(ctxt, syms) - syms = append(syms, pubNames.s, pubTypes.s) - syms = writegdbscript(ctxt, syms) - // Now we're done writing SDWARFSECT symbols, so we can write - // other SDWARF* symbols. - syms = append(syms, infosyms...) - syms = collectlocs(ctxt, syms, ctxt.compUnits) - syms = append(syms, debugRanges) - for _, unit := range ctxt.compUnits { - syms = append(syms, unit.RangeSyms...) - } - dwarfp = syms -} - -func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) []*sym.Symbol { - empty := true - for _, u := range units { - for _, fn := range u.FuncDIEs { - for i := range fn.R { - reloc := &fn.R[i] // Copying sym.Reloc has measurable impact on performance - if reloc.Type == objabi.R_DWARFSECREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) { - reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable - syms = append(syms, reloc.Sym) - empty = false - // One location list entry per function, but many relocations to it. Don't duplicate. - break + // Convert over the unit function DIE and abstract function DIE lists. + for _, lib := range ctxt.Library { + for _, unit := range lib.Units { + for _, fd := range unit.FuncDIEs2 { + ds := ctxt.loader.Syms[fd] + if ds == nil { + panic("bad") } + unit.FuncDIEs = append(unit.FuncDIEs, ds) + } + for _, fd := range unit.RangeSyms2 { + ds := ctxt.loader.Syms[fd] + if ds == nil { + panic("bad") + } + unit.RangeSyms = append(unit.RangeSyms, ds) + } + for _, fd := range unit.AbsFnDIEs2 { + ds := ctxt.loader.Syms[fd] + if ds == nil { + panic("bad") + } + unit.AbsFnDIEs = append(unit.AbsFnDIEs, ds) + } + if unit.Consts2 != 0 { + ds := ctxt.loader.Syms[unit.Consts2] + if ds == nil { + panic("bad") + } + unit.Consts = ds } - } - } - // Don't emit .debug_loc if it's empty -- it makes the ARM linker mad. - if !empty { - locsym := ctxt.Syms.Lookup(".debug_loc", 0) - locsym.Type = sym.SDWARFLOC - locsym.Attr |= sym.AttrReachable - syms = append(syms, locsym) - } - return syms -} - -// Read a pointer-sized uint from the beginning of buf. -func readPtr(ctxt *Link, buf []byte) uint64 { - switch ctxt.Arch.PtrSize { - case 4: - return uint64(ctxt.Arch.ByteOrder.Uint32(buf)) - case 8: - return ctxt.Arch.ByteOrder.Uint64(buf) - default: - panic("unexpected pointer size") - } -} - -/* - * Elf. - */ -func dwarfaddshstrings(ctxt *Link, shstrtab *sym.Symbol) { - if *FlagW { // disable dwarf - return - } - - secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"} - for _, sec := range secs { - Addstring(shstrtab, ".debug_"+sec) - if ctxt.LinkMode == LinkExternal { - Addstring(shstrtab, elfRelType+".debug_"+sec) - } else { - Addstring(shstrtab, ".zdebug_"+sec) } } } -// Add section symbols for DWARF debug info. This is called before -// dwarfaddelfheaders. -func dwarfaddelfsectionsyms(ctxt *Link) { - if *FlagW { // disable dwarf +func convertSymbolsInDIE(ctxt *Link, die *dwarf.DWDie, convdies map[*dwarf.DWDie]bool) { + if die == nil { return } - if ctxt.LinkMode != LinkExternal { + if convdies[die] { return } - - s := ctxt.Syms.Lookup(".debug_info", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_abbrev", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_line", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_frame", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_loc", 0) - if s.Sect != nil { - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - } - s = ctxt.Syms.Lookup(".debug_ranges", 0) - if s.Sect != nil { - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - } -} - -// dwarfcompress compresses the DWARF sections. Relocations are applied -// on the fly. After this, dwarfp will contain a different (new) set of -// symbols, and sections may have been replaced. -func dwarfcompress(ctxt *Link) { - supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin - if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal { - return - } - - var start int - var newDwarfp []*sym.Symbol - Segdwarf.Sections = Segdwarf.Sections[:0] - for i, s := range dwarfp { - // Find the boundaries between sections and compress - // the whole section once we've found the last of its - // symbols. - if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect { - s1 := compressSyms(ctxt, dwarfp[start:i+1]) - if s1 == nil { - // Compression didn't help. - newDwarfp = append(newDwarfp, dwarfp[start:i+1]...) - Segdwarf.Sections = append(Segdwarf.Sections, s.Sect) - } else { - compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):] - sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04) - sect.Length = uint64(len(s1)) - newSym := ctxt.Syms.Lookup(compressedSegName, 0) - newSym.P = s1 - newSym.Size = int64(len(s1)) - newSym.Sect = sect - newDwarfp = append(newDwarfp, newSym) - } - start = i + 1 - } - } - dwarfp = newDwarfp - ctxt.relocbuf = nil // no longer needed, don't hold it live - - // Re-compute the locations of the compressed DWARF symbols - // and sections, since the layout of these within the file is - // based on Section.Vaddr and Symbol.Value. - pos := Segdwarf.Vaddr - var prevSect *sym.Section - for _, s := range dwarfp { - s.Value = int64(pos) - if s.Sect != prevSect { - s.Sect.Vaddr = uint64(s.Value) - prevSect = s.Sect + convdies[die] = true + if die.Sym != nil { + symIdx, ok := die.Sym.(dwSym) + if !ok { + panic("bad die sym field") } - if s.Sub != nil { - log.Fatalf("%s: unexpected sub-symbols", s) + ls := symIdx.s + if ls == 0 { + panic("zero loader sym for die") } - pos += uint64(s.Size) - if ctxt.HeadType == objabi.Hwindows { - pos = uint64(Rnd(int64(pos), PEFILEALIGN)) + die.Sym = ctxt.loader.Syms[symIdx.s] + } + for a := die.Attr; a != nil; a = a.Link { + if attrSym, ok := a.Data.(dwSym); ok { + a.Data = ctxt.loader.Syms[attrSym.s] } - } - Segdwarf.Length = pos - Segdwarf.Vaddr + convertSymbolsInDIE(ctxt, die.Child, convdies) + convertSymbolsInDIE(ctxt, die.Link, convdies) } -type compilationUnitByStartPC []*sym.CompilationUnit - -func (v compilationUnitByStartPC) Len() int { return len(v) } -func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] } - -func (v compilationUnitByStartPC) Less(i, j int) bool { - switch { - case len(v[i].Textp) == 0 && len(v[j].Textp) == 0: - return v[i].Lib.Pkg < v[j].Lib.Pkg - case len(v[i].Textp) != 0 && len(v[j].Textp) == 0: - return true - case len(v[i].Textp) == 0 && len(v[j].Textp) != 0: - return false - default: - return v[i].Textp[0].Value < v[j].Textp[0].Value +// dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc, +// debug_pubnames and debug_pubtypes. It also writes out the debug_info +// section using symbols generated in dwarfGenerateDebugInfo2. +func (d *dwctxt2) dwarfGenerateDebugSyms(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return } + panic("not yet implemented") } -// On AIX, the symbol table needs to know where are the compilation units parts -// for a specific package in each .dw section. -// dwsectCUSize map will save the size of a compilation unit for -// the corresponding .dw section. -// This size can later be retrieved with the index "sectionName.pkgName". -var dwsectCUSize map[string]uint64 - -// getDwsectCUSize retrieves the corresponding package size inside the current section. -func getDwsectCUSize(sname string, pkgname string) uint64 { - return dwsectCUSize[sname+"."+pkgname] +func (d *dwctxt2) collectlocs(ctxt *Link, syms []loader.Sym, units []*sym.CompilationUnit) []loader.Sym { + panic("not yet implemented") } -func saveDwsectCUSize(sname string, pkgname string, size uint64) { - dwsectCUSize[sname+"."+pkgname] = size +/* + * Elf. + */ +func (d *dwctxt2) dwarfaddshstrings(ctxt *Link, shstrtab loader.Sym) { + panic("not yet implemented") } -func addDwsectCUSize(sname string, pkgname string, size uint64) { - dwsectCUSize[sname+"."+pkgname] += size +// Add section symbols for DWARF debug info. This is called before +// dwarfaddelfheaders. +func (d *dwctxt2) dwarfaddelfsectionsyms(ctxt *Link) { + panic("not yet implemented") } -// getPkgFromCUSym returns the package name for the compilation unit -// represented by s. -// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get -// the package name. -func getPkgFromCUSym(s *sym.Symbol) string { - return strings.TrimPrefix(s.Name, dwarf.InfoPrefix+".pkg.") +// dwarfcompress compresses the DWARF sections. Relocations are applied +// on the fly. After this, dwarfp will contain a different (new) set of +// symbols, and sections may have been replaced. +func (d *dwctxt2) dwarfcompress(ctxt *Link) { + panic("not yet implemented") } diff --git a/src/cmd/link/internal/ld/dwarf2.go b/src/cmd/link/internal/ld/dwarf2.go new file mode 100644 index 0000000000..61cf7e7a76 --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf2.go @@ -0,0 +1,1824 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type + +package ld + +import ( + "cmd/internal/dwarf" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" + "cmd/link/internal/sym" + "fmt" + "log" + "sort" + "strings" +) + +type dwctxt struct { + linkctxt *Link +} + +func (c dwctxt) PtrSize() int { + return c.linkctxt.Arch.PtrSize +} +func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) { + ls := s.(*sym.Symbol) + ls.AddUintXX(c.linkctxt.Arch, uint64(i), size) +} +func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) { + ls := s.(*sym.Symbol) + ls.AddBytes(b) +} +func (c dwctxt) AddString(s dwarf.Sym, v string) { + Addstring(s.(*sym.Symbol), v) +} + +func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { + if value != 0 { + value -= (data.(*sym.Symbol)).Value + } + s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) +} + +func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + if value != 0 { + value -= (data.(*sym.Symbol)).Value + } + s.(*sym.Symbol).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) +} + +func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { + ls := s.(*sym.Symbol) + switch size { + default: + Errorf(ls, "invalid size %d in adddwarfref\n", size) + fallthrough + case c.linkctxt.Arch.PtrSize: + ls.AddAddr(c.linkctxt.Arch, t.(*sym.Symbol)) + case 4: + ls.AddAddrPlus4(t.(*sym.Symbol), 0) + } + r := &ls.R[len(ls.R)-1] + r.Type = objabi.R_ADDROFF + r.Add = ofs +} + +func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { + size := 4 + if isDwarf64(c.linkctxt) { + size = 8 + } + + c.AddSectionOffset(s, size, t, ofs) + ls := s.(*sym.Symbol) + ls.R[len(ls.R)-1].Type = objabi.R_DWARFSECREF +} + +func (c dwctxt) Logf(format string, args ...interface{}) { + c.linkctxt.Logf(format, args...) +} + +// At the moment these interfaces are only used in the compiler. + +func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) { + panic("should be used only in the compiler") +} + +func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { + panic("should be used only in the compiler") +} + +func isDwarf64(ctxt *Link) bool { + return ctxt.HeadType == objabi.Haix +} + +var dwarfp []*sym.Symbol + +func writeabbrev(ctxt *Link) *sym.Symbol { + s := ctxt.Syms.Lookup(".debug_abbrev", 0) + s.Type = sym.SDWARFSECT + s.AddBytes(dwarf.GetAbbrev()) + return s +} + +// Every DIE manufactured by the linker has at least an AT_name +// attribute (but it will only be written out if it is listed in the abbrev). +// The compiler does create nameless DWARF DIEs (ex: concrete subprogram +// instance). +func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { + die := new(dwarf.DWDie) + die.Abbrev = abbrev + die.Link = parent.Child + parent.Child = die + + newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name) + + if name != "" && (abbrev <= dwarf.DW_ABRV_VARIABLE || abbrev >= dwarf.DW_ABRV_NULLTYPE) { + if abbrev != dwarf.DW_ABRV_VARIABLE || version == 0 { + if abbrev == dwarf.DW_ABRV_COMPUNIT { + // Avoid collisions with "real" symbol names. + name = fmt.Sprintf(".pkg.%s.%d", name, len(ctxt.compUnits)) + } + s := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version) + s.Attr |= sym.AttrNotInSymbolTable + s.Type = sym.SDWARFINFO + die.Sym = s + } + } + + return die +} + +func walksymtypedef(ctxt *Link, s *sym.Symbol) *sym.Symbol { + if t := ctxt.Syms.ROLookup(s.Name+"..def", int(s.Version)); t != nil { + return t + } + return s +} + +func find(ctxt *Link, name string) *sym.Symbol { + n := append(prefixBuf, name...) + // The string allocation below is optimized away because it is only used in a map lookup. + s := ctxt.Syms.ROLookup(string(n), 0) + prefixBuf = n[:len(dwarf.InfoPrefix)] + if s != nil && s.Type == sym.SDWARFINFO { + return s + } + return nil +} + +func mustFind(ctxt *Link, name string) *sym.Symbol { + r := find(ctxt, name) + if r == nil { + Exitf("dwarf find: cannot find %s", name) + } + return r +} + +func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 { + var result int64 + switch size { + default: + Errorf(s, "invalid size %d in adddwarfref\n", size) + fallthrough + case ctxt.Arch.PtrSize: + result = s.AddAddr(ctxt.Arch, t) + case 4: + result = s.AddAddrPlus4(t, 0) + } + r := &s.R[len(s.R)-1] + r.Type = objabi.R_DWARFSECREF + return result +} + +func newrefattr(die *dwarf.DWDie, attr uint16, ref *sym.Symbol) *dwarf.DWAttr { + if ref == nil { + return nil + } + return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref) +} + +func dtolsym(s dwarf.Sym) *sym.Symbol { + if s == nil { + return nil + } + return s.(*sym.Symbol) +} + +func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*sym.Symbol, die *dwarf.DWDie) []*sym.Symbol { + s := dtolsym(die.Sym) + if s == nil { + s = syms[len(syms)-1] + } else { + if s.Attr.OnList() { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + syms = append(syms, s) + } + dwarf.Uleb128put(ctxt, s, int64(die.Abbrev)) + dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr) + if dwarf.HasChildren(die) { + for die := die.Child; die != nil; die = die.Link { + syms = putdie(linkctxt, ctxt, syms, die) + } + syms[len(syms)-1].AddUint8(0) + } + return syms +} + +// GDB doesn't like FORM_addr for AT_location, so emit a +// location expression that evals to a const. +func newabslocexprattr(die *dwarf.DWDie, addr int64, sym *sym.Symbol) { + newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, sym) + // below +} + +// Lookup predefined types +func lookupOrDiag(ctxt *Link, n string) *sym.Symbol { + s := ctxt.Syms.ROLookup(n, 0) + if s == nil || s.Size == 0 { + Exitf("dwarf: missing type: %s", n) + } + + return s +} + +// dwarfFuncSym looks up a DWARF metadata symbol for function symbol s. +// If the symbol does not exist, it creates it if create is true, +// or returns nil otherwise. +func dwarfFuncSym(ctxt *Link, s *sym.Symbol, meta string, create bool) *sym.Symbol { + // All function ABIs use symbol version 0 for the DWARF data. + // + // TODO(austin): It may be useful to have DWARF info for ABI + // wrappers, in which case we may want these versions to + // align. Better yet, replace these name lookups with a + // general way to attach metadata to a symbol. + ver := 0 + if s.IsFileLocal() { + ver = int(s.Version) + } + if create { + return ctxt.Syms.Lookup(meta+s.Name, ver) + } + return ctxt.Syms.ROLookup(meta+s.Name, ver) +} + +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 nil + } + if strings.HasPrefix(name, "struct {") { + return nil + } + if strings.HasPrefix(name, "chan ") { + return nil + } + if name[0] == '[' || name[0] == '*' { + return nil + } + if def == nil { + Errorf(nil, "dwarf: bad def in dotypedef") + } + + s := ctxt.Syms.Lookup(dtolsym(def.Sym).Name+"..def", 0) + s.Attr |= sym.AttrNotInSymbolTable + s.Type = sym.SDWARFINFO + def.Sym = s + + // The typedef entry must be created after the def, + // so that future lookups will find the typedef instead + // of the real definition. This hooks the typedef into any + // circular definition loops, so that gdb can understand them. + 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. +func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol { + if gotype == nil { + return mustFind(ctxt, "") + } + + if *FlagNewDw { + panic("should not be called on this path") + } + + if !strings.HasPrefix(gotype.Name, "type.") { + Errorf(gotype, "dwarf: type name doesn't start with \"type.\"") + return mustFind(ctxt, "") + } + + name := gotype.Name[5:] // could also decode from Type.string + + sdie := find(ctxt, name) + + if sdie != nil { + return sdie + } + + return newtype(ctxt, gotype).Sym.(*sym.Symbol) +} + +func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { + name := gotype.Name[5:] // could also decode from Type.string + kind := decodetypeKind(ctxt.Arch, gotype.P) + bytesize := decodetypeSize(ctxt.Arch, gotype.P) + + if *FlagNewDw { + panic("should not be called on this path") + } + + var die, typedefdie *dwarf.DWDie + switch kind { + case objabi.KindBool: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindInt, + objabi.KindInt8, + objabi.KindInt16, + objabi.KindInt32, + objabi.KindInt64: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindUint, + objabi.KindUint8, + objabi.KindUint16, + objabi.KindUint32, + objabi.KindUint64, + objabi.KindUintptr: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindFloat32, + objabi.KindFloat64: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindComplex64, + objabi.KindComplex128: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindArray: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) + 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)) + fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) + + // use actual length not upper bound; correct for 0-length arrays. + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0) + + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + + case objabi.KindChan: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) + s := decodetypeChanElem(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + // Save elem type for synthesizechantypes. We could synthesize here + // but that would change the order of DIEs we output. + newrefattr(die, dwarf.DW_AT_type, s) + + 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) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P) + for i := 0; i < nfields; i++ { + s := decodetypeFuncInType(ctxt.Arch, gotype, i) + fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) + newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) + } + + if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) { + newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) + } + nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P) + for i := 0; i < nfields; i++ { + s := decodetypeFuncOutType(ctxt.Arch, gotype, i) + fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) + newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, defgotype(ctxt, s))) + } + + case objabi.KindInterface: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P)) + var s *sym.Symbol + if nfields == 0 { + s = lookupOrDiag(ctxt, "type.runtime.eface") + } else { + s = lookupOrDiag(ctxt, "type.runtime.iface") + } + newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + + case objabi.KindMap: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) + s := decodetypeMapKey(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_go_key, defgotype(ctxt, s)) + s = decodetypeMapValue(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + // Save gotype for use in synthesizemaptypes. We could synthesize here, + // but that would change the order of the DIEs. + newrefattr(die, dwarf.DW_AT_type, gotype) + + case objabi.KindPtr: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) + 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) + 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) + newrefattr(die, dwarf.DW_AT_go_elem, elem) + + case objabi.KindString: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindStruct: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) + 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++ { + f := decodetypeStructFieldName(ctxt.Arch, gotype, i) + s := decodetypeStructFieldType(ctxt.Arch, gotype, i) + if f == "" { + f = s.Name[5:] // skip "type." + } + fld := newdie(ctxt, die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) + newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) + offsetAnon := decodetypeStructFieldOffsAnon(ctxt.Arch, gotype, i) + newmemberoffsetattr(fld, int32(offsetAnon>>1)) + if offsetAnon&1 != 0 { // is embedded field + newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) + } + } + + case objabi.KindUnsafePointer: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) + + default: + Errorf(gotype, "dwarf: definition of unknown kind %d", kind) + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) + newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "")) + } + + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) + if gotype.Attr.Reachable() { + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + } + + if _, ok := prototypedies[gotype.Name]; ok { + prototypedies[gotype.Name] = die + } + + if typedefdie != nil { + return typedefdie + } + return die +} + +func nameFromDIESym(dwtype *sym.Symbol) string { + return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def") +} + +// Find or construct *T given T. +func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol { + ptrname := "*" + nameFromDIESym(dwtype) + if die := find(ctxt, ptrname); die != nil { + return die + } + pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) + newrefattr(pdie, dwarf.DW_AT_type, dwtype) + + // The DWARF info synthesizes pointer types that don't exist at the + // language level, like *hash<...> and *bucket<...>, and the data + // pointers of slices. Link to the ones we can find. + gotype := ctxt.Syms.ROLookup("type."+ptrname, 0) + if gotype != nil && gotype.Attr.Reachable() { + newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + } + return dtolsym(pdie.Sym) +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. If except is one of +// the top-level children, it will not be copied. +func copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { + for src = src.Child; src != nil; src = src.Link { + if src == except { + continue + } + c := newdie(ctxt, dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) + for a := src.Attr; a != nil; a = a.Link { + newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) + } + copychildrenexcept(ctxt, c, src, nil) + } + + reverselist(&dst.Child) +} + +func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { + copychildrenexcept(ctxt, dst, src, nil) +} + +// Search children (assumed to have TAG_member) for the one named +// field and set its AT_type to dwtype +func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) { + child := findchild(structdie, field) + if child == nil { + Exitf("dwarf substitutetype: %s does not have member %s", + getattr(structdie, dwarf.DW_AT_name).Data, field) + return + } + + a := getattr(child, dwarf.DW_AT_type) + if a != nil { + a.Data = dwtype + } else { + newrefattr(child, dwarf.DW_AT_type, dwtype) + } +} + +func findprotodie(ctxt *Link, name string) *dwarf.DWDie { + die, ok := prototypedies[name] + if ok && die == nil { + defgotype(ctxt, lookupOrDiag(ctxt, name)) + die = prototypedies[name] + } + return die +} + +func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE { + continue + } + copychildren(ctxt, die, prototype) + } +} + +func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_SLICETYPE { + continue + } + copychildren(ctxt, die, prototype) + elem := getattr(die, dwarf.DW_AT_go_elem).Data.(*sym.Symbol) + substitutetype(die, "array", defptrto(ctxt, elem)) + } +} + +// synthesizemaptypes is way too closely married to runtime/hashmap.c + +func mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *sym.Symbol { + name := mkinternaltypename(typename, keyname, valname) + symname := dwarf.InfoPrefix + name + s := ctxt.Syms.ROLookup(symname, 0) + if s != nil && s.Type == sym.SDWARFINFO { + return s + } + die := newdie(ctxt, &dwtypes, abbrev, name, 0) + f(die) + return dtolsym(die.Sym) +} + +func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { + hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap")) + bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap")) + + if hash == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_MAPTYPE { + continue + } + gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) + keytype := decodetypeMapKey(ctxt.Arch, gotype) + valtype := decodetypeMapValue(ctxt.Arch, gotype) + keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P) + keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype)) + + // compute size info like hashmap.c does. + indirectKey, indirectVal := false, false + if keysize > MaxKeySize { + keysize = int64(ctxt.Arch.PtrSize) + indirectKey = true + } + if valsize > MaxValSize { + valsize = int64(ctxt.Arch.PtrSize) + indirectVal = true + } + + // Construct type to represent an array of BucketSize keys + keyname := nameFromDIESym(keytype) + dwhks := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { + newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0) + t := keytype + if indirectKey { + t = defptrto(ctxt, keytype) + } + newrefattr(dwhk, dwarf.DW_AT_type, t) + fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + }) + + // Construct type to represent an array of BucketSize values + valname := nameFromDIESym(valtype) + dwhvs := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { + newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0) + t := valtype + if indirectVal { + t = defptrto(ctxt, valtype) + } + newrefattr(dwhv, dwarf.DW_AT_type, t) + fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + }) + + // Construct bucket + dwhbs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { + // Copy over all fields except the field "data" from the generic + // bucket. "data" will be replaced with keys/values below. + copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) + + fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0) + newrefattr(fld, dwarf.DW_AT_type, dwhks) + newmemberoffsetattr(fld, BucketSize) + fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0) + newrefattr(fld, dwarf.DW_AT_type, dwhvs) + newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) + fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0) + newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym))) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) + if ctxt.Arch.RegSize > ctxt.Arch.PtrSize { + fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0) + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(ctxt.Arch.PtrSize)) + } + + newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(ctxt.Arch.RegSize), 0) + }) + + // Construct hash + dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { + copychildren(ctxt, dwh, hash) + substitutetype(dwh, "buckets", defptrto(ctxt, dwhbs)) + substitutetype(dwh, "oldbuckets", defptrto(ctxt, dwhbs)) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil) + }) + + // make map type a pointer to hash + newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + } +} + +func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { + sudog := walktypedef(findprotodie(ctxt, "type.runtime.sudog")) + waitq := walktypedef(findprotodie(ctxt, "type.runtime.waitq")) + hchan := walktypedef(findprotodie(ctxt, "type.runtime.hchan")) + if sudog == nil || waitq == nil || hchan == nil { + return + } + + sudogsize := int(getattr(sudog, dwarf.DW_AT_byte_size).Value) + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_CHANTYPE { + continue + } + elemgotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) + elemname := elemgotype.Name[5:] + elemtype := walksymtypedef(ctxt, defgotype(ctxt, elemgotype)) + + // sudog + dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { + copychildren(ctxt, dws, sudog) + substitutetype(dws, "elem", defptrto(ctxt, elemtype)) + newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil) + }) + + // waitq + dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { + + copychildren(ctxt, dww, waitq) + substitutetype(dww, "first", defptrto(ctxt, dwss)) + substitutetype(dww, "last", defptrto(ctxt, dwss)) + newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil) + }) + + // hchan + dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { + copychildren(ctxt, dwh, hchan) + substitutetype(dwh, "recvq", dwws) + substitutetype(dwh, "sendq", dwws) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil) + }) + + newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + } +} + +func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *sym.Symbol) { + // Find a suitable CU DIE to include the global. + // One would think it's as simple as just looking at the unit, but that might + // not have any reachable code. So, we go to the runtime's CU if our unit + // isn't otherwise reachable. + var unit *sym.CompilationUnit + if s.Unit != nil { + unit = s.Unit + } else { + unit = ctxt.runtimeCU + } + dv := newdie(ctxt, unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version)) + newabslocexprattr(dv, v, s) + if !s.IsFileLocal() { + newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0) + } + dt := defgotype(ctxt, gotype) + newrefattr(dv, dwarf.DW_AT_type, dt) +} + +// For use with pass.c::genasmsym +func defdwsymb(ctxt *Link, s *sym.Symbol, str string, t SymbolType, v int64, gotype *sym.Symbol) { + if strings.HasPrefix(str, "go.string.") { + return + } + if strings.HasPrefix(str, "runtime.gcbits.") { + return + } + + switch t { + case DataSym, BSSSym: + switch s.Type { + case sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: + // ok + case sym.SRODATA: + if gotype != nil { + defgotype(ctxt, gotype) + } + return + default: + return + } + if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) { + return + } + dwarfDefineGlobal(ctxt, s, str, v, gotype) + + case AutoSym, ParamSym, DeletedAutoSym: + defgotype(ctxt, gotype) + } +} + +// createUnitLength creates the initial length field with value v and update +// offset of unit_length if needed. +func createUnitLength(ctxt *Link, s *sym.Symbol, v uint64) { + if isDwarf64(ctxt) { + s.AddUint32(ctxt.Arch, 0xFFFFFFFF) + } + addDwarfAddrField(ctxt, s, v) +} + +// addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits. +func addDwarfAddrField(ctxt *Link, s *sym.Symbol, v uint64) { + if isDwarf64(ctxt) { + s.AddUint(ctxt.Arch, v) + } else { + s.AddUint32(ctxt.Arch, uint32(v)) + } +} + +// addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits. +func addDwarfAddrRef(ctxt *Link, s *sym.Symbol, t *sym.Symbol) { + if isDwarf64(ctxt) { + adddwarfref(ctxt, s, t, 8) + } else { + adddwarfref(ctxt, s, t, 4) + } +} + +// calcCompUnitRanges calculates the PC ranges of the compilation units. +func calcCompUnitRanges(ctxt *Link) { + var prevUnit *sym.CompilationUnit + for _, s := range ctxt.Textp { + if s.FuncInfo == nil { + continue + } + // Skip linker-created functions (ex: runtime.addmoduledata), since they + // don't have DWARF to begin with. + if s.Unit == nil { + continue + } + unit := s.Unit + // Update PC ranges. + // + // We don't simply compare the end of the previous + // symbol with the start of the next because there's + // often a little padding between them. Instead, we + // only create boundaries between symbols from + // different units. + if prevUnit != unit { + unit.PCs = append(unit.PCs, dwarf.Range{Start: s.Value - unit.Textp[0].Value}) + prevUnit = unit + } + unit.PCs[len(unit.PCs)-1].End = s.Value - unit.Textp[0].Value + s.Size + } +} + +// If the pcln table contains runtime/proc.go, use that to set gdbscript path. +func finddebugruntimepath(s *sym.Symbol) { + if gdbscript != "" { + return + } + + for i := range s.FuncInfo.File { + f := s.FuncInfo.File[i] + // We can't use something that may be dead-code + // eliminated from a binary here. proc.go contains + // main and the scheduler, so it's not going anywhere. + if i := strings.Index(f.Name, "runtime/proc.go"); i >= 0 { + gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" + break + } + } +} + +func importInfoSymbol(ctxt *Link, dsym *sym.Symbol) { + dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable + dsym.Type = sym.SDWARFINFO + for i := range dsym.R { + r := &dsym.R[i] // Copying sym.Reloc has measurable impact on performance + if r.Type == objabi.R_DWARFSECREF && r.Sym.Size == 0 { + n := nameFromDIESym(r.Sym) + defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) + } + } +} + +func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) { + + var dwarfctxt dwarf.Context = dwctxt{ctxt} + is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles. + + unitstart := int64(-1) + headerstart := int64(-1) + headerend := int64(-1) + + newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, ls.Size, ls) + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + unitLengthOffset := ls.Size + createUnitLength(ctxt, ls, 0) // unit_length (*), filled in at end + unitstart = ls.Size + ls.AddUint16(ctxt.Arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05 + headerLengthOffset := ls.Size + addDwarfAddrField(ctxt, ls, 0) // header_length (*), filled in at end + headerstart = ls.Size + + // cpos == unitstart + 4 + 2 + 4 + ls.AddUint8(1) // minimum_instruction_length + ls.AddUint8(is_stmt) // default_is_stmt + ls.AddUint8(LINE_BASE & 0xFF) // line_base + ls.AddUint8(LINE_RANGE) // line_range + ls.AddUint8(OPCODE_BASE) // opcode_base + ls.AddUint8(0) // standard_opcode_lengths[1] + ls.AddUint8(1) // standard_opcode_lengths[2] + ls.AddUint8(1) // standard_opcode_lengths[3] + ls.AddUint8(1) // standard_opcode_lengths[4] + ls.AddUint8(1) // standard_opcode_lengths[5] + ls.AddUint8(0) // standard_opcode_lengths[6] + ls.AddUint8(0) // standard_opcode_lengths[7] + ls.AddUint8(0) // standard_opcode_lengths[8] + ls.AddUint8(1) // standard_opcode_lengths[9] + ls.AddUint8(0) // standard_opcode_lengths[10] + ls.AddUint8(0) // include_directories (empty) + + // Copy over the file table. + fileNums := make(map[string]int) + for i, name := range unit.DWARFFileTable { + if len(name) != 0 { + if strings.HasPrefix(name, src.FileSymPrefix) { + name = name[len(src.FileSymPrefix):] + } + name = expandGoroot(name) + } else { + // Can't have empty filenames, and having a unique filename is quite useful + // for debugging. + name = fmt.Sprintf("_%d", i) + } + fileNums[name] = i + 1 + dwarfctxt.AddString(ls, name) + ls.AddUint8(0) + ls.AddUint8(0) + ls.AddUint8(0) + } + // Grab files for inlined functions. + // TODO: With difficulty, this could be moved into the compiler. + for _, s := range unit.Textp { + dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true) + for ri := 0; ri < len(dsym.R); ri++ { + r := &dsym.R[ri] + if r.Type != objabi.R_DWARFFILEREF { + continue + } + name := r.Sym.Name + if _, ok := fileNums[name]; ok { + continue + } + fileNums[name] = len(fileNums) + 1 + dwarfctxt.AddString(ls, name) + ls.AddUint8(0) + ls.AddUint8(0) + ls.AddUint8(0) + } + } + + // 4 zeros: the string termination + 3 fields. + ls.AddUint8(0) + // terminate file_names. + headerend = ls.Size + + // Output the state machine for each function remaining. + var lastAddr int64 + for _, s := range unit.Textp { + finddebugruntimepath(s) + + // Set the PC. + ls.AddUint8(0) + dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize)) + ls.AddUint8(dwarf.DW_LNE_set_address) + addr := ls.AddAddr(ctxt.Arch, s) + // Make sure the units are sorted. + if addr < lastAddr { + Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr) + } + lastAddr = addr + + // Output the line table. + // TODO: Now that we have all the debug information in separate + // symbols, it would make sense to use a rope, and concatenate them all + // together rather then the append() below. This would allow us to have + // the compiler emit the DW_LNE_set_address and a rope data structure + // to concat them all together in the output. + lines := dwarfFuncSym(ctxt, s, dwarf.DebugLinesPrefix, false) + if lines != nil { + ls.P = append(ls.P, lines.P...) + } + } + + ls.AddUint8(0) // start extended opcode + dwarf.Uleb128put(dwarfctxt, ls, 1) + ls.AddUint8(dwarf.DW_LNE_end_sequence) + + if ctxt.HeadType == objabi.Haix { + saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(ls.Size-unitLengthOffset)) + } + if isDwarf64(ctxt) { + ls.SetUint(ctxt.Arch, unitLengthOffset+4, uint64(ls.Size-unitstart)) // +4 because of 0xFFFFFFFF + ls.SetUint(ctxt.Arch, headerLengthOffset, uint64(headerend-headerstart)) + } else { + ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart)) + ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart)) + } + + // Process any R_DWARFFILEREF relocations, since we now know the + // line table file indices for this compilation unit. Note that + // this loop visits only subprogram DIEs: if the compiler is + // changed to generate DW_AT_decl_file attributes for other + // DIE flavors (ex: variables) then those DIEs would need to + // be included below. + missing := make(map[int]interface{}) + s := unit.Textp[0] + for _, f := range unit.FuncDIEs { + for ri := range f.R { + r := &f.R[ri] + if r.Type != objabi.R_DWARFFILEREF { + continue + } + idx, ok := fileNums[r.Sym.Name] + if ok { + if int(int32(idx)) != idx { + Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow") + } + if r.Siz != 4 { + Errorf(f, "bad R_DWARFFILEREF relocation: has size %d, expected 4", r.Siz) + } + if r.Off < 0 || r.Off+4 > int32(len(f.P)) { + Errorf(f, "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d", r.Off, len(s.P)) + continue + } + if r.Add != 0 { + Errorf(f, "bad R_DWARFFILEREF relocation: addend not zero") + } + r.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable + r.Add = int64(idx) // record the index in r.Add, we'll apply it in the reloc phase. + } else { + _, found := missing[int(r.Sym.Value)] + if !found { + Errorf(f, "R_DWARFFILEREF relocation file missing: %v idx %d", r.Sym, r.Sym.Value) + missing[int(r.Sym.Value)] = nil + } + } + } + } +} + +// writepcranges generates the DW_AT_ranges table for compilation unit cu. +func writepcranges(ctxt *Link, unit *sym.CompilationUnit, base *sym.Symbol, pcs []dwarf.Range, ranges *sym.Symbol) { + var dwarfctxt dwarf.Context = dwctxt{ctxt} + + unitLengthOffset := ranges.Size + + // Create PC ranges for this CU. + newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, ranges.Size, ranges) + newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, base.Value, base) + dwarf.PutBasedRanges(dwarfctxt, ranges, pcs) + + if ctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(ranges.Size-unitLengthOffset)) + } + +} + +func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { + var dwarfctxt dwarf.Context = dwctxt{ctxt} + fs := ctxt.Syms.Lookup(".debug_frame", 0) + fs.Type = sym.SDWARFSECT + syms = append(syms, fs) + + // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64 + lengthFieldSize := int64(4) + if isDwarf64(ctxt) { + lengthFieldSize += 8 + } + + // Emit the CIE, Section 6.4.1 + cieReserve := uint32(16) + if haslinkregister(ctxt) { + cieReserve = 32 + } + if isDwarf64(ctxt) { + cieReserve += 4 // 4 bytes added for cid + } + createUnitLength(ctxt, fs, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize + addDwarfAddrField(ctxt, fs, ^uint64(0)) // cid + fs.AddUint8(3) // dwarf version (appendix F) + fs.AddUint8(0) // augmentation "" + dwarf.Uleb128put(dwarfctxt, fs, 1) // code_alignment_factor + dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // return_address_register + + fs.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... + if haslinkregister(ctxt) { + dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset. + + fs.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) + + fs.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... + dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...is CFA+0. + } else { + dwarf.Uleb128put(dwarfctxt, fs, int64(ctxt.Arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). + + fs.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // ...of the return address... + dwarf.Uleb128put(dwarfctxt, fs, int64(-ctxt.Arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. + } + + pad := int64(cieReserve) + lengthFieldSize - fs.Size + + if pad < 0 { + Exitf("dwarf: cieReserve too small by %d bytes.", -pad) + } + + fs.AddBytes(zeros[:pad]) + + var deltaBuf []byte + pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) + for _, s := range ctxt.Textp { + if s.FuncInfo == nil { + continue + } + + // Emit a FDE, Section 6.4.1. + // First build the section contents into a byte buffer. + deltaBuf = deltaBuf[:0] + if haslinkregister(ctxt) && s.Attr.TopFrame() { + // Mark the link register as having an undefined value. + // This stops call stack unwinders progressing any further. + // TODO: similar mark on non-LR architectures. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() { + nextpc := pcsp.NextPC + + // pciterinit goes up to the end of the function, + // but DWARF expects us to stop just before the end. + if int64(nextpc) == s.Size { + nextpc-- + if nextpc < pcsp.PC { + continue + } + } + + spdelta := int64(pcsp.Value) + if !haslinkregister(ctxt) { + // Return address has been pushed onto stack. + spdelta += int64(ctxt.Arch.PtrSize) + } + + if haslinkregister(ctxt) && !s.Attr.TopFrame() { + // TODO(bryanpkc): This is imprecise. In general, the instruction + // that stores the return address to the stack frame is not the + // same one that allocates the frame. + if pcsp.Value > 0 { + // The return address is preserved at (CFA-frame_size) + // after a stack frame has been allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + deltaBuf = dwarf.AppendSleb128(deltaBuf, -spdelta/dataAlignmentFactor) + } else { + // The return address is restored into the link register + // when a stack frame has been de-allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + } + + deltaBuf = appendPCDeltaCFA(ctxt.Arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) + } + pad := int(Rnd(int64(len(deltaBuf)), int64(ctxt.Arch.PtrSize))) - len(deltaBuf) + deltaBuf = append(deltaBuf, zeros[:pad]...) + + // Emit the FDE header, Section 6.4.1. + // 4 bytes: length, must be multiple of thearch.ptrsize + // 4/8 bytes: Pointer to the CIE above, at offset 0 + // ptrsize: initial location + // ptrsize: address range + + fdeLength := uint64(4 + 2*ctxt.Arch.PtrSize + len(deltaBuf)) + if isDwarf64(ctxt) { + fdeLength += 4 // 4 bytes added for CIE pointer + } + createUnitLength(ctxt, fs, fdeLength) + + if ctxt.LinkMode == LinkExternal { + addDwarfAddrRef(ctxt, fs, fs) + } else { + addDwarfAddrField(ctxt, fs, 0) // CIE offset + } + fs.AddAddr(ctxt.Arch, s) + fs.AddUintXX(ctxt.Arch, uint64(s.Size), ctxt.Arch.PtrSize) // address range + fs.AddBytes(deltaBuf) + + if ctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_frame", s.File, fdeLength+uint64(lengthFieldSize)) + } + } + return syms +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ + +func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abbrevsym *sym.Symbol, pubNames, pubTypes *pubWriter) []*sym.Symbol { + infosec := ctxt.Syms.Lookup(".debug_info", 0) + infosec.Type = sym.SDWARFINFO + infosec.Attr |= sym.AttrReachable + syms = append(syms, infosec) + + var dwarfctxt dwarf.Context = dwctxt{ctxt} + + for _, u := range units { + compunit := u.DWInfo + s := dtolsym(compunit.Sym) + + if len(u.Textp) == 0 && u.DWInfo.Child == nil { + continue + } + + pubNames.beginCompUnit(compunit) + pubTypes.beginCompUnit(compunit) + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + createUnitLength(ctxt, s, 0) // unit_length (*), will be filled in later. + s.AddUint16(ctxt.Arch, 4) // dwarf version (appendix F) + + // debug_abbrev_offset (*) + addDwarfAddrRef(ctxt, s, abbrevsym) + + s.AddUint8(uint8(ctxt.Arch.PtrSize)) // address_size + + dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev)) + dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr) + + cu := []*sym.Symbol{s} + cu = append(cu, u.AbsFnDIEs...) + cu = append(cu, u.FuncDIEs...) + if u.Consts != nil { + cu = append(cu, u.Consts) + } + var cusize int64 + for _, child := range cu { + cusize += child.Size + } + + for die := compunit.Child; die != nil; die = die.Link { + l := len(cu) + lastSymSz := cu[l-1].Size + cu = putdie(ctxt, dwarfctxt, cu, die) + if ispubname(die) { + pubNames.add(die, cusize) + } + if ispubtype(die) { + pubTypes.add(die, cusize) + } + if lastSymSz != cu[l-1].Size { + // putdie will sometimes append directly to the last symbol of the list + cusize = cusize - lastSymSz + cu[l-1].Size + } + for _, child := range cu[l:] { + cusize += child.Size + } + } + cu[len(cu)-1].AddUint8(0) // closes compilation unit DIE + cusize++ + + // Save size for AIX symbol table. + if ctxt.HeadType == objabi.Haix { + saveDwsectCUSize(".debug_info", getPkgFromCUSym(s), uint64(cusize)) + } + if isDwarf64(ctxt) { + cusize -= 12 // exclude the length field. + s.SetUint(ctxt.Arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF + } else { + cusize -= 4 // exclude the length field. + s.SetUint32(ctxt.Arch, 0, uint32(cusize)) + } + pubNames.endCompUnit(compunit, uint32(cusize)+4) + pubTypes.endCompUnit(compunit, uint32(cusize)+4) + syms = append(syms, cu...) + } + return syms +} + +type pubWriter struct { + ctxt *Link + s *sym.Symbol + sname string + + sectionstart int64 + culengthOff int64 +} + +func newPubWriter(ctxt *Link, sname string) *pubWriter { + s := ctxt.Syms.Lookup(sname, 0) + s.Type = sym.SDWARFSECT + return &pubWriter{ctxt: ctxt, s: s, sname: sname} +} + +func (pw *pubWriter) beginCompUnit(compunit *dwarf.DWDie) { + pw.sectionstart = pw.s.Size + + // Write .debug_pubnames/types Header (sec 6.1.1) + createUnitLength(pw.ctxt, pw.s, 0) // unit_length (*), will be filled in later. + pw.s.AddUint16(pw.ctxt.Arch, 2) // dwarf version (appendix F) + addDwarfAddrRef(pw.ctxt, pw.s, dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header) + pw.culengthOff = pw.s.Size + addDwarfAddrField(pw.ctxt, pw.s, uint64(0)) // debug_info_length, will be filled in later. + +} + +func (pw *pubWriter) add(die *dwarf.DWDie, offset int64) { + dwa := getattr(die, dwarf.DW_AT_name) + name := dwa.Data.(string) + if die.Sym == nil { + fmt.Println("Missing sym for ", name) + } + addDwarfAddrField(pw.ctxt, pw.s, uint64(offset)) + Addstring(pw.s, name) +} + +func (pw *pubWriter) endCompUnit(compunit *dwarf.DWDie, culength uint32) { + addDwarfAddrField(pw.ctxt, pw.s, 0) // Null offset + + // On AIX, save the current size of this compilation unit. + if pw.ctxt.HeadType == objabi.Haix { + saveDwsectCUSize(pw.sname, getPkgFromCUSym(dtolsym(compunit.Sym)), uint64(pw.s.Size-pw.sectionstart)) + } + if isDwarf64(pw.ctxt) { + pw.s.SetUint(pw.ctxt.Arch, pw.sectionstart+4, uint64(pw.s.Size-pw.sectionstart)-12) // exclude the length field. + pw.s.SetUint(pw.ctxt.Arch, pw.culengthOff, uint64(culength)) + } else { + pw.s.SetUint32(pw.ctxt.Arch, pw.sectionstart, uint32(pw.s.Size-pw.sectionstart)-4) // exclude the length field. + pw.s.SetUint32(pw.ctxt.Arch, pw.culengthOff, culength) + } +} + +func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { + // TODO (aix): make it available + if ctxt.HeadType == objabi.Haix { + return syms + } + if ctxt.LinkMode == LinkExternal && ctxt.HeadType == objabi.Hwindows && ctxt.BuildMode == BuildModeCArchive { + // gcc on Windows places .debug_gdb_scripts in the wrong location, which + // causes the program not to run. See https://golang.org/issue/20183 + // Non c-archives can avoid this issue via a linker script + // (see fix near writeGDBLinkerScript). + // c-archive users would need to specify the linker script manually. + // For UX it's better not to deal with this. + return syms + } + + if gdbscript != "" { + s := ctxt.Syms.Lookup(".debug_gdb_scripts", 0) + s.Type = sym.SDWARFSECT + syms = append(syms, s) + s.AddUint8(1) // magic 1 byte? + Addstring(s, gdbscript) + } + + return syms +} + +// dwarfGenerateDebugInfo generated debug info entries for all types, +// variables and functions in the program. +// Along with dwarfGenerateDebugSyms they are the two main entry points into +// dwarf generation: dwarfGenerateDebugInfo does all the work that should be +// done before symbol names are mangled while dwarfgeneratedebugsyms does +// all the work that can only be done after addresses have been assigned to +// text symbols. +func dwarfGenerateDebugInfo(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + if *FlagNewDw { + dwarfGenerateDebugInfo2(ctxt) + return + } + + if ctxt.HeadType == objabi.Haix { + // Initial map used to store package size for each DWARF section. + dwsectCUSize = make(map[string]uint64) + } + + // Forctxt.Diagnostic messages. + newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") + + // Some types that must exist to define other ones. + newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "", 0) + + newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0) + newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0) + + die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(ctxt.Arch.PtrSize), 0) + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0) + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, lookupOrDiag(ctxt, "type.uintptr")) + + // Prototypes needed for type synthesis. + prototypedies = map[string]*dwarf.DWDie{ + "type.runtime.stringStructDWARF": nil, + "type.runtime.slice": nil, + "type.runtime.hmap": nil, + "type.runtime.bmap": nil, + "type.runtime.sudog": nil, + "type.runtime.waitq": nil, + "type.runtime.hchan": nil, + } + + // Needed by the prettyprinter code for interface inspection. + for _, typ := range []string{ + "type.runtime._type", + "type.runtime.arraytype", + "type.runtime.chantype", + "type.runtime.functype", + "type.runtime.maptype", + "type.runtime.ptrtype", + "type.runtime.slicetype", + "type.runtime.structtype", + "type.runtime.interfacetype", + "type.runtime.itab", + "type.runtime.imethod"} { + defgotype(ctxt, lookupOrDiag(ctxt, typ)) + } + + // fake root DIE for compile unit DIEs + var dwroot dwarf.DWDie + flagVariants := make(map[string]bool) + + for _, lib := range ctxt.Library { + consts := ctxt.Syms.ROLookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) + for _, unit := range lib.Units { + // We drop the constants into the first CU. + if consts != nil { + importInfoSymbol(ctxt, consts) + unit.Consts = consts + consts = nil + } + + ctxt.compUnits = append(ctxt.compUnits, unit) + + // We need at least one runtime unit. + if unit.Lib.Pkg == "runtime" { + ctxt.runtimeCU = unit + } + + unit.DWInfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0) + newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0) + // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. + compDir := getCompilationDir() + // TODO: Make this be the actual compilation directory, not + // the linker directory. If we move CU construction into the + // compiler, this should happen naturally. + newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir) + producerExtra := ctxt.Syms.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0) + producer := "Go cmd/compile " + objabi.Version + if len(producerExtra.P) > 0 { + // We put a semicolon before the flags to clearly + // separate them from the version, which can be long + // and have lots of weird things in it in development + // versions. We promise not to put a semicolon in the + // version, so it should be safe for readers to scan + // forward to the semicolon. + producer += "; " + string(producerExtra.P) + flagVariants[string(producerExtra.P)] = true + } else { + flagVariants[""] = true + } + + newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer) + + var pkgname string + if s := ctxt.Syms.ROLookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); s != nil { + pkgname = string(s.P) + } + newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname) + + if len(unit.Textp) == 0 { + unit.DWInfo.Abbrev = dwarf.DW_ABRV_COMPUNIT_TEXTLESS + } + + // Scan all functions in this compilation unit, create DIEs for all + // referenced types, create the file table for debug_line, find all + // referenced abstract functions. + // Collect all debug_range symbols in unit.rangeSyms + for _, s := range unit.Textp { // textp has been dead-code-eliminated already. + dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, false) + dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable + dsym.Type = sym.SDWARFINFO + unit.FuncDIEs = append(unit.FuncDIEs, dsym) + + rangeSym := dwarfFuncSym(ctxt, s, dwarf.RangePrefix, false) + if rangeSym != nil && rangeSym.Size > 0 { + rangeSym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable + rangeSym.Type = sym.SDWARFRANGE + if ctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rangeSym.Size)) + } + unit.RangeSyms = append(unit.RangeSyms, rangeSym) + } + + for ri := 0; ri < len(dsym.R); ri++ { + r := &dsym.R[ri] + if r.Type == objabi.R_DWARFSECREF { + rsym := r.Sym + if strings.HasPrefix(rsym.Name, dwarf.InfoPrefix) && strings.HasSuffix(rsym.Name, dwarf.AbstractFuncSuffix) && !rsym.Attr.OnList() { + // abstract function + rsym.Attr |= sym.AttrOnList + unit.AbsFnDIEs = append(unit.AbsFnDIEs, rsym) + importInfoSymbol(ctxt, rsym) + } else if rsym.Size == 0 { + // a type we do not have a DIE for + n := nameFromDIESym(rsym) + defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) + } + } + } + } + } + } + + // Fix for 31034: if the objects feeding into this link were compiled + // with different sets of flags, then don't issue an error if + // the -strictdups checks fail. + if checkStrictDups > 1 && len(flagVariants) > 1 { + checkStrictDups = 1 + } + + // Create DIEs for global variables and the types they use. + genasmsym(ctxt, defdwsymb) + + // Create DIEs for variable types indirectly referenced by function + // autos (which may not appear directly as param/var DIEs). + for _, lib := range ctxt.Library { + for _, unit := range lib.Units { + lists := [][]*sym.Symbol{unit.AbsFnDIEs, unit.FuncDIEs} + for _, list := range lists { + for _, s := range list { + for i := 0; i < len(s.R); i++ { + r := &s.R[i] + if r.Type == objabi.R_USETYPE { + defgotype(ctxt, r.Sym) + } + } + } + } + } + } + + synthesizestringtypes(ctxt, dwtypes.Child) + synthesizeslicetypes(ctxt, dwtypes.Child) + synthesizemaptypes(ctxt, dwtypes.Child) + synthesizechantypes(ctxt, dwtypes.Child) +} + +// dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc, +// debug_pubnames and debug_pubtypes. It also writes out the debug_info +// section using symbols generated in dwarfGenerateDebugInfo. +func dwarfGenerateDebugSyms(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + + abbrev := writeabbrev(ctxt) + syms := []*sym.Symbol{abbrev} + + calcCompUnitRanges(ctxt) + sort.Sort(compilationUnitByStartPC(ctxt.compUnits)) + + // Write per-package line and range tables and start their CU DIEs. + debugLine := ctxt.Syms.Lookup(".debug_line", 0) + debugLine.Type = sym.SDWARFSECT + debugRanges := ctxt.Syms.Lookup(".debug_ranges", 0) + debugRanges.Type = sym.SDWARFRANGE + debugRanges.Attr |= sym.AttrReachable + syms = append(syms, debugLine) + for _, u := range ctxt.compUnits { + reversetree(&u.DWInfo.Child) + if u.DWInfo.Abbrev == dwarf.DW_ABRV_COMPUNIT_TEXTLESS { + continue + } + writelines(ctxt, u, debugLine) + writepcranges(ctxt, u, u.Textp[0], u.PCs, debugRanges) + } + + // newdie adds DIEs to the *beginning* of the parent's DIE list. + // Now that we're done creating DIEs, reverse the trees so DIEs + // appear in the order they were created. + reversetree(&dwtypes.Child) + movetomodule(ctxt, &dwtypes) + + pubNames := newPubWriter(ctxt, ".debug_pubnames") + pubTypes := newPubWriter(ctxt, ".debug_pubtypes") + + // Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT + infosyms := writeinfo(ctxt, nil, ctxt.compUnits, abbrev, pubNames, pubTypes) + + syms = writeframes(ctxt, syms) + syms = append(syms, pubNames.s, pubTypes.s) + syms = writegdbscript(ctxt, syms) + // Now we're done writing SDWARFSECT symbols, so we can write + // other SDWARF* symbols. + syms = append(syms, infosyms...) + syms = collectlocs(ctxt, syms, ctxt.compUnits) + syms = append(syms, debugRanges) + for _, unit := range ctxt.compUnits { + syms = append(syms, unit.RangeSyms...) + } + dwarfp = syms +} + +func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) []*sym.Symbol { + empty := true + for _, u := range units { + for _, fn := range u.FuncDIEs { + for i := range fn.R { + reloc := &fn.R[i] // Copying sym.Reloc has measurable impact on performance + if reloc.Type == objabi.R_DWARFSECREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) { + reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable + syms = append(syms, reloc.Sym) + empty = false + // One location list entry per function, but many relocations to it. Don't duplicate. + break + } + } + } + } + // Don't emit .debug_loc if it's empty -- it makes the ARM linker mad. + if !empty { + locsym := ctxt.Syms.Lookup(".debug_loc", 0) + locsym.Type = sym.SDWARFLOC + locsym.Attr |= sym.AttrReachable + syms = append(syms, locsym) + } + return syms +} + +// Read a pointer-sized uint from the beginning of buf. +func readPtr(ctxt *Link, buf []byte) uint64 { + switch ctxt.Arch.PtrSize { + case 4: + return uint64(ctxt.Arch.ByteOrder.Uint32(buf)) + case 8: + return ctxt.Arch.ByteOrder.Uint64(buf) + default: + panic("unexpected pointer size") + } +} + +/* + * Elf. + */ +func dwarfaddshstrings(ctxt *Link, shstrtab *sym.Symbol) { + if *FlagW { // disable dwarf + return + } + + secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"} + for _, sec := range secs { + Addstring(shstrtab, ".debug_"+sec) + if ctxt.LinkMode == LinkExternal { + Addstring(shstrtab, elfRelType+".debug_"+sec) + } else { + Addstring(shstrtab, ".zdebug_"+sec) + } + } +} + +// Add section symbols for DWARF debug info. This is called before +// dwarfaddelfheaders. +func dwarfaddelfsectionsyms(ctxt *Link) { + if *FlagW { // disable dwarf + return + } + if ctxt.LinkMode != LinkExternal { + return + } + + s := ctxt.Syms.Lookup(".debug_info", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_abbrev", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_line", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_frame", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_loc", 0) + if s.Sect != nil { + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + } + s = ctxt.Syms.Lookup(".debug_ranges", 0) + if s.Sect != nil { + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + } +} + +// dwarfcompress compresses the DWARF sections. Relocations are applied +// on the fly. After this, dwarfp will contain a different (new) set of +// symbols, and sections may have been replaced. +func dwarfcompress(ctxt *Link) { + supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin + if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal { + return + } + + var start int + var newDwarfp []*sym.Symbol + Segdwarf.Sections = Segdwarf.Sections[:0] + for i, s := range dwarfp { + // Find the boundaries between sections and compress + // the whole section once we've found the last of its + // symbols. + if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect { + s1 := compressSyms(ctxt, dwarfp[start:i+1]) + if s1 == nil { + // Compression didn't help. + newDwarfp = append(newDwarfp, dwarfp[start:i+1]...) + Segdwarf.Sections = append(Segdwarf.Sections, s.Sect) + } else { + compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):] + sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04) + sect.Length = uint64(len(s1)) + newSym := ctxt.Syms.Lookup(compressedSegName, 0) + newSym.P = s1 + newSym.Size = int64(len(s1)) + newSym.Sect = sect + newDwarfp = append(newDwarfp, newSym) + } + start = i + 1 + } + } + dwarfp = newDwarfp + ctxt.relocbuf = nil // no longer needed, don't hold it live + + // Re-compute the locations of the compressed DWARF symbols + // and sections, since the layout of these within the file is + // based on Section.Vaddr and Symbol.Value. + pos := Segdwarf.Vaddr + var prevSect *sym.Section + for _, s := range dwarfp { + s.Value = int64(pos) + if s.Sect != prevSect { + s.Sect.Vaddr = uint64(s.Value) + prevSect = s.Sect + } + if s.Sub != nil { + log.Fatalf("%s: unexpected sub-symbols", s) + } + pos += uint64(s.Size) + if ctxt.HeadType == objabi.Hwindows { + pos = uint64(Rnd(int64(pos), PEFILEALIGN)) + } + + } + Segdwarf.Length = pos - Segdwarf.Vaddr +} + +type compilationUnitByStartPC []*sym.CompilationUnit + +func (v compilationUnitByStartPC) Len() int { return len(v) } +func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] } + +func (v compilationUnitByStartPC) Less(i, j int) bool { + switch { + case len(v[i].Textp) == 0 && len(v[j].Textp) == 0: + return v[i].Lib.Pkg < v[j].Lib.Pkg + case len(v[i].Textp) != 0 && len(v[j].Textp) == 0: + return true + case len(v[i].Textp) == 0 && len(v[j].Textp) != 0: + return false + default: + return v[i].Textp[0].Value < v[j].Textp[0].Value + } +} + +// On AIX, the symbol table needs to know where are the compilation units parts +// for a specific package in each .dw section. +// dwsectCUSize map will save the size of a compilation unit for +// the corresponding .dw section. +// This size can later be retrieved with the index "sectionName.pkgName". +var dwsectCUSize map[string]uint64 + +// getDwsectCUSize retrieves the corresponding package size inside the current section. +func getDwsectCUSize(sname string, pkgname string) uint64 { + return dwsectCUSize[sname+"."+pkgname] +} + +func saveDwsectCUSize(sname string, pkgname string, size uint64) { + dwsectCUSize[sname+"."+pkgname] = size +} + +func addDwsectCUSize(sname string, pkgname string, size uint64) { + dwsectCUSize[sname+"."+pkgname] += size +} + +// getPkgFromCUSym returns the package name for the compilation unit +// represented by s. +// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get +// the package name. +func getPkgFromCUSym(s *sym.Symbol) string { + return strings.TrimPrefix(s.Name, dwarf.InfoPrefix+".pkg.") +} diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 29c4a0cbbd..a059ed2e5c 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2620,6 +2620,13 @@ func (ctxt *Link) loadlibfull() { // Pull the symbols out. ctxt.loader.ExtractSymbols(ctxt.Syms, ctxt.Reachparent) + // If -newdw is in effect, then we generated dwarf DIE objects + // with embedded loader.Sym refs as opposed to sym.Symbol refs. + // Call a helper to rewrite the former to the latter in all DIEs + if *FlagNewDw { + dwarfConvertSymbols(ctxt) + } + setupdynexp(ctxt) // Drop the cgodata reference. diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 6b29bcb314..d791950202 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -88,6 +88,7 @@ var ( flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") + FlagNewDw = flag.Bool("newdw", true, "DWARF gen with new loader") FlagRound = flag.Int("R", -1, "set address rounding `quantum`") FlagTextAddr = flag.Int64("T", -1, "set text segment `address`") @@ -241,11 +242,31 @@ func Main(arch *sys.Arch, theArch Arch) { fieldtrack(ctxt.Arch, ctxt.loader) } + if *FlagNewDw { + bench.Start("dwarfGenerateDebugInfo") + + // DWARF-gen requires that the unit Textp2 slices be populated, + // so that it can walk the functions in each unit. Call into + // the loader to do this (requires that we collect the set of + // internal libraries first). NB: might be simpler if we moved + // isRuntimeDepPkg to cmd/internal and then did the test + // in loader.AssignTextSymbolOrder. + ctxt.Library = postorder(ctxt.Library) + intlibs := []bool{} + for _, lib := range ctxt.Library { + intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg)) + } + ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs) + dwarfGenerateDebugInfo(ctxt) + } + bench.Start("loadlibfull") ctxt.loadlibfull() // XXX do it here for now - bench.Start("dwarfGenerateDebugInfo") - dwarfGenerateDebugInfo(ctxt) + if !*FlagNewDw { + bench.Start("dwarfGenerateDebugInfo") + dwarfGenerateDebugInfo(ctxt) + } bench.Start("mangleTypeSym") ctxt.mangleTypeSym() diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 04bd2e8a94..8675637af0 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -227,7 +227,7 @@ type Loader struct { } const ( - pkgDef = iota + pkgDef = iota nonPkgDef nonPkgRef ) @@ -1239,6 +1239,56 @@ func (l *Loader) AuxSym(i Sym, j int) Sym { return l.resolve(r, a.Sym) } +// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF +// symbols associated with a given function symbol. Prior to the +// introduction of the loader, this was done purely using name +// lookups, e.f. for function with name XYZ we would then look up +// go.info.XYZ, etc. +// FIXME: once all of dwarfgen is converted over to the loader, +// it would save some space to make these aux symbols nameless. +func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) { + if l.SymType(fnSymIdx) != sym.STEXT { + log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String()) + } + if l.IsExternal(fnSymIdx) { + // Current expectation is that any external function will + // not have auxsyms. + return + } + naux := l.NAux(fnSymIdx) + if naux == 0 { + return + } + r, li := l.toLocal(fnSymIdx) + for i := 0; i < naux; i++ { + a := goobj2.Aux{} + a.Read(r.Reader, r.AuxOff(li, i)) + switch a.Type { + case goobj2.AuxDwarfInfo: + auxDwarfInfo = l.resolve(r, a.Sym) + if l.SymType(auxDwarfInfo) != sym.SDWARFINFO { + panic("aux dwarf info sym with wrong type") + } + case goobj2.AuxDwarfLoc: + auxDwarfLoc = l.resolve(r, a.Sym) + if l.SymType(auxDwarfLoc) != sym.SDWARFLOC { + panic("aux dwarf loc sym with wrong type") + } + case goobj2.AuxDwarfRanges: + auxDwarfRanges = l.resolve(r, a.Sym) + if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE { + panic("aux dwarf ranges sym with wrong type") + } + case goobj2.AuxDwarfLines: + auxDwarfLines = l.resolve(r, a.Sym) + if l.SymType(auxDwarfLines) != sym.SDWARFLINES { + panic("aux dwarf lines sym with wrong type") + } + } + } + return +} + // ReadAuxSyms reads the aux symbol ids for the specified symbol into the // slice passed as a parameter. If the slice capacity is not large enough, a new // larger slice will be allocated. Final slice is returned. @@ -1545,6 +1595,7 @@ func (l *Loader) preloadSyms(r *oReader, kind int) { } } if strings.HasPrefix(name, "go.string.") || + strings.HasPrefix(name, "gclocals·") || strings.HasPrefix(name, "runtime.gcbits.") { l.SetAttrNotInSymbolTable(gi, true) } @@ -2019,7 +2070,6 @@ func loadObjFull(l *Loader, r *oReader) { s := l.Syms[gi] if s.Type == sym.STEXT { lib.DupTextSyms = append(lib.DupTextSyms, s) - lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi)) } } continue @@ -2211,12 +2261,10 @@ func loadObjFull(l *Loader, r *oReader) { } s.Attr.Set(sym.AttrOnList, true) lib.Textp = append(lib.Textp, s) - lib.Textp2 = append(lib.Textp2, sym.LoaderSym(isym)) } else { // there may be a dup in another package // put into a temp list and add to text later lib.DupTextSyms = append(lib.DupTextSyms, s) - lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(isym)) } } } @@ -2322,6 +2370,83 @@ func (l *Loader) UndefinedRelocTargets(limit int) []Sym { return result } +// AssignTextSymbolOrder populates the Textp2 slices within each +// library and compilation unit, insuring that packages are laid down +// in dependency order (internal first, then everything else). +func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool) { + + // Library Textp2 lists should be empty at this point. + for _, lib := range libs { + if len(lib.Textp2) != 0 { + panic("expected empty Textp2 slice for library") + } + if len(lib.DupTextSyms2) != 0 { + panic("expected empty DupTextSyms2 slice for library") + } + } + + // Used to record which dupok symbol we've assigned to a unit. + // Can't use the onlist attribute here because it will need to + // clear for the later assignment of the sym.Symbol to a unit. + // NB: we can convert to using onList once we no longer have to + // call the regular addToTextp. + assignedToUnit := makeBitmap(l.NSym() + 1) + + // Walk through all text symbols from Go object files and append + // them to their corresponding library's textp2 list. + for _, o := range l.objs[1:] { + r := o.r + lib := r.unit.Lib + for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ { + gi := l.toGlobal(r, i) + osym := goobj2.Sym{} + osym.ReadWithoutName(r.Reader, r.SymOff(i)) + st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)] + if st != sym.STEXT { + continue + } + // check for dupok + if r2, i2 := l.toLocal(gi); r2 != r || i2 != i { + if l.attrReachable.has(gi) { + // A dupok symbol is resolved to another package. + // We still need to record its presence in the + // current package, as the trampoline pass expects + // packages are laid out in dependency order. + lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi)) + } + continue // symbol in different object + } + + lib.Textp2 = append(lib.Textp2, sym.LoaderSym(gi)) + } + } + + // Now redo the assignment of text symbols to libs/units. + for _, doInternal := range [2]bool{true, false} { + for idx, lib := range libs { + if intlibs[idx] != doInternal { + continue + } + libtextp2 := []sym.LoaderSym{} + tpls := [2][]sym.LoaderSym{lib.Textp2, lib.DupTextSyms2} + for _, textp2 := range tpls { + for _, s := range textp2 { + sym := Sym(s) + if l.attrReachable.has(sym) && !assignedToUnit.has(sym) { + libtextp2 = append(libtextp2, s) + unit := l.SymUnit(sym) + if unit != nil { + unit.Textp2 = append(unit.Textp2, s) + assignedToUnit.set(sym) + } + } + } + } + lib.Textp2 = libtextp2 + } + } +} + // For debugging. func (l *Loader) Dump() { fmt.Println("objs") @@ -2333,7 +2458,7 @@ func (l *Loader) Dump() { fmt.Println("extStart:", l.extStart) fmt.Println("Nsyms:", len(l.objSyms)) fmt.Println("syms") - for i := Sym(1); i <= Sym(len(l.objSyms)); i++ { + for i := Sym(1); i < Sym(len(l.objSyms)); i++ { pi := interface{}("") if l.IsExternal(i) { pi = fmt.Sprintf("", l.extIndex(i)) diff --git a/src/cmd/link/internal/sym/compilation_unit.go b/src/cmd/link/internal/sym/compilation_unit.go index f3933d8535..b8b6845e6e 100644 --- a/src/cmd/link/internal/sym/compilation_unit.go +++ b/src/cmd/link/internal/sym/compilation_unit.go @@ -6,6 +6,8 @@ package sym import "cmd/internal/dwarf" +// LoaderSym holds a loader.Sym value. We can't refer to this +// type from the sym package since loader imports sym. type LoaderSym int // CompilationUnit is an abstraction used by DWARF to represent a chunk of -- 2.48.1