diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index ed341288f87..e725a1eab46 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 6eba39bcf78..1dbd5cc05f7 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 (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 +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), } - s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) + d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface") + d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface") + return d } -func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { +func (c dwctxt2) PtrSize() int { + return c.arch.PtrSize +} + +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 dwctxt2) AddBytes(s dwarf.Sym, b []byte) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + dsu.AddBytes(b) +} + +func (c dwctxt2) AddString(s dwarf.Sym, v string) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + dsu.Addstring(v) +} + +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).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) + tgtds := data.(dwSym) + dsu.AddAddrPlus(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) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + ds := s.(dwSym) + dsu := ds.l.MakeSymbolUpdater(ds.s) + if value != 0 { + value -= dsu.Value() + } + tgtds := data.(dwSym) + dsu.AddCURelativeAddrPlus(c.arch, tgtds.s, value) +} + +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) } + if len(d.ldr.Data(symIdx)) == 0 { + Exitf("dwarf: missing type (no data): %s", n) + } - return s + return symIdx } -// 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 { +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 + 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 - sdie := find(ctxt, name) - - 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}) } - return dtolsym(pdie.Sym) + + if gts != 0 { + ds := pdie.Sym.(dwSym) + d.rtmap[ds.s] = gts + d.tdmap[gts] = ds.s + } + + 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 (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()) + } + 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 } + // 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) } } -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 - } - } - } - } +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 (d *dwctxt2) writegdbscript(syms []loader.Sym) []loader.Sym { + panic("not yet implemented") } -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 -} +// 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) + // Unspecified type. There are no references to this in the symbol table. + d.newdie(&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 + // 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. +} + +// 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) + + // 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 + } + } + } +} + +func convertSymbolsInDIE(ctxt *Link, die *dwarf.DWDie, convdies map[*dwarf.DWDie]bool) { + if die == nil { + return + } + if convdies[die] { + return + } + convdies[die] = true + if die.Sym != nil { + symIdx, ok := die.Sym.(dwSym) + if !ok { + panic("bad die sym field") + } + ls := symIdx.s + if ls == 0 { + panic("zero loader sym for die") + } + 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] + } + } + convertSymbolsInDIE(ctxt, die.Child, convdies) + convertSymbolsInDIE(ctxt, die.Link, convdies) } // 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) { +// section using symbols generated in dwarfGenerateDebugInfo2. +func (d *dwctxt2) 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 + panic("not yet implemented") } -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") - } +func (d *dwctxt2) collectlocs(ctxt *Link, syms []loader.Sym, units []*sym.CompilationUnit) []loader.Sym { + panic("not yet implemented") } /* * 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) - } - } +func (d *dwctxt2) dwarfaddshstrings(ctxt *Link, shstrtab loader.Sym) { + panic("not yet implemented") } // 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) - } +func (d *dwctxt2) dwarfaddelfsectionsyms(ctxt *Link) { + panic("not yet implemented") } // 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.") +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 00000000000..61cf7e7a76c --- /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 29c4a0cbbd5..a059ed2e5c5 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 6b29bcb3146..d791950202e 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 04bd2e8a942..8675637af08 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 f3933d85358..b8b6845e6ef 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