// 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. // 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/internal/sys" "cmd/link/internal/loader" "cmd/link/internal/sym" "fmt" "internal/abi" "internal/buildcfg" "log" "path" "runtime" "sort" "strings" "sync" ) // dwctxt 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 dwctxt 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 // Used at various points in that parallel portion of DWARF gen to // protect against conflicting updates to globals (such as "gdbscript") dwmu *sync.Mutex } // dwSym wraps a loader.Sym; this type is meant to obey the interface // rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and // DwAttr objects contain references to symbols via this type. type dwSym loader.Sym func (c dwctxt) PtrSize() int { return c.arch.PtrSize } func (c dwctxt) Size(s dwarf.Sym) int64 { return int64(len(c.ldr.Data(loader.Sym(s.(dwSym))))) } func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) { ds := loader.Sym(s.(dwSym)) dsu := c.ldr.MakeSymbolUpdater(ds) dsu.AddUintXX(c.arch, uint64(i), size) } func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) { ds := loader.Sym(s.(dwSym)) dsu := c.ldr.MakeSymbolUpdater(ds) dsu.AddBytes(b) } func (c dwctxt) AddString(s dwarf.Sym, v string) { ds := loader.Sym(s.(dwSym)) dsu := c.ldr.MakeSymbolUpdater(ds) dsu.Addstring(v) } func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { ds := loader.Sym(s.(dwSym)) dsu := c.ldr.MakeSymbolUpdater(ds) if value != 0 { value -= dsu.Value() } tgtds := loader.Sym(data.(dwSym)) dsu.AddAddrPlus(c.arch, tgtds, value) } func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { ds := loader.Sym(s.(dwSym)) dsu := c.ldr.MakeSymbolUpdater(ds) if value != 0 { value -= dsu.Value() } tgtds := loader.Sym(data.(dwSym)) dsu.AddCURelativeAddrPlus(c.arch, tgtds, value) } func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { ds := loader.Sym(s.(dwSym)) dsu := c.ldr.MakeSymbolUpdater(ds) tds := loader.Sym(t.(dwSym)) switch size { default: c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) case c.arch.PtrSize, 4: } dsu.AddSymRef(c.arch, tds, ofs, objabi.R_ADDROFF, size) } func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { size := 4 if isDwarf64(c.linkctxt) { size = 8 } ds := loader.Sym(s.(dwSym)) dsu := c.ldr.MakeSymbolUpdater(ds) tds := loader.Sym(t.(dwSym)) switch size { default: c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) case c.arch.PtrSize, 4: } dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size) } 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) 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 } // https://sourceware.org/gdb/onlinedocs/gdb/dotdebug_005fgdb_005fscripts-section.html // Each entry inside .debug_gdb_scripts section begins with a non-null prefix // byte that specifies the kind of entry. The following entries are supported: const ( GdbScriptPythonFileId = 1 GdbScriptSchemeFileId = 3 GdbScriptPythonTextId = 4 GdbScriptSchemeTextId = 6 ) var gdbscript string // dwarfSecInfo holds information about a DWARF output section, // specifically a section symbol and a list of symbols contained in // that section. On the syms list, the first symbol will always be the // section symbol, then any remaining symbols (if any) will be // sub-symbols in that section. Note that for some sections (eg: // .debug_abbrev), the section symbol is all there is (all content is // contained in it). For other sections (eg: .debug_info), the section // symbol is empty and all the content is in the sub-symbols. Finally // there are some sections (eg: .debug_ranges) where it is a mix (both // the section symbol and the sub-symbols have content) type dwarfSecInfo struct { syms []loader.Sym } // secSym returns the section symbol for the section. func (dsi *dwarfSecInfo) secSym() loader.Sym { if len(dsi.syms) == 0 { return 0 } return dsi.syms[0] } // subSyms returns a list of sub-symbols for the section. func (dsi *dwarfSecInfo) subSyms() []loader.Sym { if len(dsi.syms) == 0 { return []loader.Sym{} } return dsi.syms[1:] } // dwarfp stores the collected DWARF symbols created during // dwarf generation. var dwarfp []dwarfSecInfo func (d *dwctxt) writeabbrev() dwarfSecInfo { abrvs := d.ldr.CreateSymForUpdate(".debug_abbrev", 0) abrvs.SetType(sym.SDWARFSECT) abrvs.AddBytes(dwarf.GetAbbrev()) return dwarfSecInfo{syms: []loader.Sym{abrvs.Sym()}} } 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{}) { a := new(dwarf.DWAttr) a.Link = die.Attr die.Attr = a a.Atr = attr a.Cls = uint8(cls) a.Value = value a.Data = data } // Each DIE (except the root ones) has at least 1 attribute: its // name. getattr moves the desired one to the front so // frequently searched ones are found faster. func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr { if die.Attr.Atr == attr { return die.Attr } a := die.Attr b := a.Link for b != nil { if b.Atr == attr { a.Link = b.Link b.Link = die.Attr die.Attr = b return b } a = b b = b.Link } return nil } // 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). // FIXME: it would be more efficient to bulk-allocate DIEs. func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string) *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) // Sanity check: all DIEs created in the linker should be named. if name == "" { panic("nameless DWARF DIE") } var st sym.SymKind switch abbrev { case dwarf.DW_ABRV_FUNCTYPEPARAM, dwarf.DW_ABRV_DOTDOTDOT, dwarf.DW_ABRV_STRUCTFIELD, dwarf.DW_ABRV_ARRAYRANGE: // There are no relocations against these dies, and their names // are not unique, so don't create a symbol. return die case dwarf.DW_ABRV_COMPUNIT, dwarf.DW_ABRV_COMPUNIT_TEXTLESS: // Avoid collisions with "real" symbol names. name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits)) st = sym.SDWARFCUINFO case dwarf.DW_ABRV_VARIABLE: st = sym.SDWARFVAR default: // Everything else is assigned a type of SDWARFTYPE. that // this also includes loose ends such as STRUCT_FIELD. st = sym.SDWARFTYPE } ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, 0) dsu := d.ldr.MakeSymbolUpdater(ds) dsu.SetType(st) d.ldr.SetAttrNotInSymbolTable(ds, true) d.ldr.SetAttrReachable(ds, true) die.Sym = dwSym(ds) if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL { d.tmap[name] = ds } return die } func walktypedef(die *dwarf.DWDie) *dwarf.DWDie { if die == nil { return nil } // Resolve typedef if present. if die.Abbrev == dwarf.DW_ABRV_TYPEDECL { for attr := die.Attr; attr != nil; attr = attr.Link { if attr.Atr == dwarf.DW_AT_type && attr.Cls == dwarf.DW_CLS_REFERENCE && attr.Data != nil { return attr.Data.(*dwarf.DWDie) } } } return die } func (d *dwctxt) 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 } 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 // if not. func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie { var prev *dwarf.DWDie for ; die != prev; prev, die = die, walktypedef(die) { for a := die.Child; a != nil; a = a.Link { if name == getattr(a, dwarf.DW_AT_name).Data { return a } } continue } return nil } // find looks up the loader symbol for the DWARF DIE generated for the // type with the specified name. func (d *dwctxt) find(name string) loader.Sym { return d.tmap[name] } func (d *dwctxt) mustFind(name string) loader.Sym { r := d.find(name) if r == 0 { Exitf("dwarf find: cannot find %s", name) } return r } func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) { switch size { default: d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size) case d.arch.PtrSize, 4: } sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size) } func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) { if ref == 0 { return } newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref)) } func (d *dwctxt) dtolsym(s dwarf.Sym) loader.Sym { if s == nil { return 0 } dws := loader.Sym(s.(dwSym)) return dws } func (d *dwctxt) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym { s := d.dtolsym(die.Sym) if s == 0 { s = syms[len(syms)-1] } else { syms = append(syms, s) } sDwsym := dwSym(s) dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev)) dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr) if dwarf.HasChildren(die) { for die := die.Child; die != nil; die = die.Link { syms = d.putdie(syms, die) } dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1]) dsu.AddUint8(0) } return syms } func reverselist(list **dwarf.DWDie) { curr := *list var prev *dwarf.DWDie for curr != nil { next := curr.Link curr.Link = prev prev = curr curr = next } *list = prev } func reversetree(list **dwarf.DWDie) { reverselist(list) for die := *list; die != nil; die = die.Link { if dwarf.HasChildren(die) { reversetree(&die.Child) } } } func newmemberoffsetattr(die *dwarf.DWDie, offs int32) { newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_CONSTANT, int64(offs), nil) } func (d *dwctxt) 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 symIdx } func (d *dwctxt) dotypedef(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 } // cmd/compile uses "noalg.struct {...}" as type name when hash and eq algorithm generation of // this struct type is suppressed. if strings.HasPrefix(name, "noalg.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") } // 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("", 0) tdsu := d.ldr.MakeSymbolUpdater(tds) tdsu.SetType(sym.SDWARFTYPE) def.Sym = dwSym(tds) 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 := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name) d.newrefattr(die, dwarf.DW_AT_type, tds) return die } // Define gotype, for composite ones recurse into constituents. func (d *dwctxt) defgotype(gotype loader.Sym) loader.Sym { if gotype == 0 { return d.mustFind("") } // If we already have a tdmap entry for the gotype, return it. if ds, ok := d.tdmap[gotype]; ok { return ds } 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 := d.find(name) if sdie != 0 { return sdie } gtdwSym := d.newtype(gotype) d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym)) return loader.Sym(gtdwSym.Sym.(dwSym)) } func (d *dwctxt) 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) if len(tdata) == 0 { d.linkctxt.Errorf(gotype, "missing type") } kind := decodetypeKind(d.arch, tdata) bytesize := decodetypeSize(d.arch, tdata) var die, typedefdie *dwarf.DWDie switch kind { case objabi.KindBool: die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) 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 = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) 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 = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) 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 = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) 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 = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) 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 = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name) typedefdie = d.dotypedef(&dwtypes, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) s := decodetypeArrayElem(d.ldr, d.arch, gotype) d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range") // use actual length not upper bound; correct for 0-length arrays. newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(d.ldr, d.arch, gotype), 0) d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) case objabi.KindChan: die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name) s := decodetypeChanElem(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. d.newrefattr(die, dwarf.DW_AT_type, s) case objabi.KindFunc: die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) typedefdie = d.dotypedef(&dwtypes, name, die) data := d.ldr.Data(gotype) // FIXME: add caching or reuse reloc slice. relocs := d.ldr.Relocs(gotype) nfields := decodetypeFuncInCount(d.arch, data) for i := 0; i < nfields; i++ { s := decodetypeFuncInType(d.ldr, d.arch, gotype, &relocs, i) sn := d.ldr.SymName(s) fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:]) d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) } if decodetypeFuncDotdotdot(d.arch, data) { d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...") } nfields = decodetypeFuncOutCount(d.arch, data) for i := 0; i < nfields; i++ { s := decodetypeFuncOutType(d.ldr, d.arch, gotype, &relocs, i) sn := d.ldr.SymName(s) fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:]) d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s))) } case objabi.KindInterface: die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name) typedefdie = d.dotypedef(&dwtypes, name, die) data := d.ldr.Data(gotype) nfields := int(decodetypeIfaceMethodCount(d.arch, data)) var s loader.Sym if nfields == 0 { s = d.typeRuntimeEface } else { s = d.typeRuntimeIface } d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindMap: die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name) s := decodetypeMapKey(d.ldr, d.arch, gotype) d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s)) s = decodetypeMapValue(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. d.newrefattr(die, dwarf.DW_AT_type, gotype) case objabi.KindPtr: die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name) typedefdie = d.dotypedef(&dwtypes, name, die) s := decodetypePtrElem(d.ldr, d.arch, gotype) d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindSlice: die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name) typedefdie = d.dotypedef(&dwtypes, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) s := decodetypeArrayElem(d.ldr, d.arch, gotype) elem := d.defgotype(s) d.newrefattr(die, dwarf.DW_AT_go_elem, elem) case objabi.KindString: die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindStruct: die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name) typedefdie = d.dotypedef(&dwtypes, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype) for i := 0; i < nfields; i++ { f := decodetypeStructFieldName(d.ldr, d.arch, gotype, i) s := decodetypeStructFieldType(d.ldr, d.arch, gotype, i) if f == "" { sn := d.ldr.SymName(s) f = sn[5:] // skip "type:" } fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f) d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) offset := decodetypeStructFieldOffset(d.ldr, d.arch, gotype, i) newmemberoffsetattr(fld, int32(offset)) if decodetypeStructFieldEmbedded(d.ldr, d.arch, gotype, i) { newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) } } case objabi.KindUnsafePointer: die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name) default: d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind) die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name) d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("")) } newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) if d.ldr.AttrReachable(gotype) { newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype)) } // Sanity check. if _, ok := d.rtmap[gotype]; ok { log.Fatalf("internal error: rtmap entry already installed\n") } ds := loader.Sym(die.Sym.(dwSym)) if typedefdie != nil { ds = loader.Sym(typedefdie.Sym.(dwSym)) } d.rtmap[ds] = gotype if _, ok := prototypedies[sn]; ok { prototypedies[sn] = die } if typedefdie != nil { return typedefdie } return die } func (d *dwctxt) nameFromDIESym(dwtypeDIESym loader.Sym) string { sn := d.ldr.SymName(dwtypeDIESym) return sn[len(dwarf.InfoPrefix):] } func (d *dwctxt) 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 := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname) 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. gts := d.ldr.Lookup("type:"+ptrname, 0) if gts != 0 && d.ldr.AttrReachable(gts) { newattr(pdie, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(objabi.KindPtr), 0) newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts)) } if gts != 0 { ds := loader.Sym(pdie.Sym.(dwSym)) d.rtmap[ds] = gts d.tdmap[gts] = ds } 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 (d *dwctxt) 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 := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string)) for a := src.Attr; a != nil; a = a.Link { newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) } d.copychildrenexcept(ctxt, c, src, nil) } reverselist(&dst.Child) } func (d *dwctxt) 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 (d *dwctxt) 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", getattr(structdie, dwarf.DW_AT_name).Data, field) return } a := getattr(child, dwarf.DW_AT_type) if a != nil { a.Data = dwSym(dwtype) } else { d.newrefattr(child, dwarf.DW_AT_type, dwtype) } } func (d *dwctxt) findprotodie(ctxt *Link, name string) *dwarf.DWDie { die, ok := prototypedies[name] if ok && die == nil { 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 (d *dwctxt) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.stringStructDWARF")) if prototype == nil { return } for ; die != nil; die = die.Link { if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE { continue } d.copychildren(ctxt, die, prototype) } } func (d *dwctxt) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.slice")) if prototype == nil { return } for ; die != nil; die = die.Link { if die.Abbrev != dwarf.DW_ABRV_SLICETYPE { continue } d.copychildren(ctxt, die, prototype) elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym)) d.substitutetype(die, "array", d.defptrto(elem)) } } func mkinternaltypename(base string, arg1 string, arg2 string) string { if arg2 == "" { return fmt.Sprintf("%s<%s>", base, arg1) } return fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) } // synthesizemaptypes is way too closely married to runtime/hashmap.c const ( MaxKeySize = abi.MapMaxKeyBytes MaxValSize = abi.MapMaxElemBytes BucketSize = abi.MapBucketCount ) func (d *dwctxt) 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 := d.ldr.Lookup(symname, 0) if s != 0 && d.ldr.SymType(s) == sym.SDWARFTYPE { return s } die := d.newdie(&dwtypes, abbrev, name) f(die) return d.dtolsym(die.Sym) } func (d *dwctxt) 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 } for ; die != nil; die = die.Link { if die.Abbrev != dwarf.DW_ABRV_MAPTYPE { continue } gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) keytype := decodetypeMapKey(d.ldr, d.arch, gotype) valtype := decodetypeMapValue(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(d.arch.PtrSize) indirectKey = true } if valsize > MaxValSize { valsize = int64(d.arch.PtrSize) indirectVal = true } // Construct type to represent an array of BucketSize keys 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 = d.defptrto(keytype) } d.newrefattr(dwhk, dwarf.DW_AT_type, t) fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size") newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) // Construct type to represent an array of BucketSize values 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 = d.defptrto(valtype) } d.newrefattr(dwhv, dwarf.DW_AT_type, t) fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size") newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) // Construct bucket 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. d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys") d.newrefattr(fld, dwarf.DW_AT_type, dwhks) newmemberoffsetattr(fld, BucketSize) fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values") d.newrefattr(fld, dwarf.DW_AT_type, dwhvs) newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow") d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym))) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) if d.arch.RegSize > d.arch.PtrSize { fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad") 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(d.arch.RegSize), 0) }) // Construct hash 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 d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) } } func (d *dwctxt) 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 } 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 := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) tname := d.ldr.SymName(elemgotype) elemname := tname[5:] elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname))) // sudog 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 := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { 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 := 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) }) d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) } } // createUnitLength creates the initial length field with value v and update // offset of unit_length if needed. func (d *dwctxt) createUnitLength(su *loader.SymbolBuilder, v uint64) { if isDwarf64(d.linkctxt) { su.AddUint32(d.arch, 0xFFFFFFFF) } d.addDwarfAddrField(su, v) } // addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits. func (d *dwctxt) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) { if isDwarf64(d.linkctxt) { sb.AddUint(d.arch, v) } else { sb.AddUint32(d.arch, uint32(v)) } } // addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits. func (d *dwctxt) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) { if isDwarf64(d.linkctxt) { d.adddwarfref(sb, t, 8) } else { d.adddwarfref(sb, t, 4) } } // calcCompUnitRanges calculates the PC ranges of the compilation units. func (d *dwctxt) calcCompUnitRanges() { var prevUnit *sym.CompilationUnit for _, s := range d.linkctxt.Textp { sym := loader.Sym(s) fi := d.ldr.FuncInfo(sym) if !fi.Valid() { continue } // Skip linker-created functions (ex: runtime.addmoduledata), since they // don't have DWARF to begin with. unit := d.ldr.SymUnit(sym) if unit == nil { continue } // 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. sval := d.ldr.SymValue(sym) u0val := d.ldr.SymValue(loader.Sym(unit.Textp[0])) if prevUnit != unit { unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val}) prevUnit = unit } unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym))) } } func movetomodule(ctxt *Link, parent *dwarf.DWDie) { die := ctxt.runtimeCU.DWInfo.Child if die == nil { ctxt.runtimeCU.DWInfo.Child = parent.Child return } for die.Link != nil { die = die.Link } die.Link = parent.Child } /* * Generate a sequence of opcodes that is as short as possible. * See section 6.2.5 */ const ( LINE_BASE = -4 LINE_RANGE = 10 PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE OPCODE_BASE = 11 ) /* * Walk prog table, emit line program and build DIE tree. */ func getCompilationDir() string { // OSX requires this be set to something, but it's not easy to choose // a value. Linking takes place in a temporary directory, so there's // no point including it here. Paths in the file table are usually // absolute, in which case debuggers will ignore this value. -trimpath // produces relative paths, but we don't know where they start, so // all we can do here is try not to make things worse. return "." } func (d *dwctxt) importInfoSymbol(dsym loader.Sym) { d.ldr.SetAttrReachable(dsym, true) d.ldr.SetAttrNotInSymbolTable(dsym, true) dst := d.ldr.SymType(dsym) if dst != sym.SDWARFCONST && dst != sym.SDWARFABSFCN { log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String()) } relocs := d.ldr.Relocs(dsym) for i := 0; i < relocs.Count(); i++ { r := relocs.At(i) if r.Type() != objabi.R_DWARFSECREF { continue } rsym := r.Sym() // 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[rsym]; ok { // type already generated continue } // FIXME: is there a way we could avoid materializing the // symbol name here? sn := d.ldr.SymName(rsym) tn := sn[len(dwarf.InfoPrefix):] ts := d.ldr.Lookup("type:"+tn, 0) d.defgotype(ts) } } func expandFile(fname string) string { fname = strings.TrimPrefix(fname, src.FileSymPrefix) return expandGoroot(fname) } // writeDirFileTables emits the portion of the DWARF line table // prologue containing the include directories and file names, // described in section 6.2.4 of the DWARF 4 standard. It walks the // filepaths for the unit to discover any common directories, which // are emitted to the directory table first, then the file table is // emitted after that. func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.SymbolBuilder) { type fileDir struct { base string dir int } dirNums := make(map[string]int) dirs := []string{""} files := []fileDir{} // Preprocess files to collect directories. This assumes that the // file table is already de-duped. for i, name := range unit.FileTable { name := expandFile(name) if len(name) == 0 { // Can't have empty filenames, and having a unique // filename is quite useful for debugging. name = fmt.Sprintf("_%d", i) } // Note the use of "path" here and not "filepath". The compiler // hard-codes to use "/" in DWARF paths (even for Windows), so we // want to maintain that here. file := path.Base(name) dir := path.Dir(name) dirIdx, ok := dirNums[dir] if !ok && dir != "." { dirIdx = len(dirNums) + 1 dirNums[dir] = dirIdx dirs = append(dirs, dir) } files = append(files, fileDir{base: file, dir: dirIdx}) // 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(name, "runtime/proc.go"); i >= 0 && unit.Lib.Pkg == "runtime" { d.dwmu.Lock() if gdbscript == "" { k := strings.Index(name, "runtime/proc.go") gdbscript = name[:k] + "runtime/runtime-gdb.py" } d.dwmu.Unlock() } } // Emit directory section. This is a series of nul terminated // strings, followed by a single zero byte. lsDwsym := dwSym(lsu.Sym()) for k := 1; k < len(dirs); k++ { d.AddString(lsDwsym, dirs[k]) } lsu.AddUint8(0) // terminator // Emit file section. for k := 0; k < len(files); k++ { d.AddString(lsDwsym, files[k].base) dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir)) lsu.AddUint8(0) // mtime lsu.AddUint8(0) // length } lsu.AddUint8(0) // terminator } // writelines collects up and chains together the symbols needed to // form the DWARF line table for the specified compilation unit, // returning a list of symbols. The returned list will include an // initial symbol containing the line table header and prologue (with // file table), then a series of compiler-emitted line table symbols // (one per live function), and finally an epilog symbol containing an // end-of-sequence operator. The prologue and epilog symbols are passed // in (having been created earlier); here we add content to them. func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []loader.Sym { is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles. unitstart := int64(-1) headerstart := int64(-1) headerend := int64(-1) syms := make([]loader.Sym, 0, len(unit.Textp)+2) syms = append(syms, lineProlog) lsu := d.ldr.MakeSymbolUpdater(lineProlog) lsDwsym := dwSym(lineProlog) newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, lsDwsym) // Write .debug_line Line Number Program Header (sec 6.2.4) // Fields marked with (*) must be changed for 64-bit dwarf unitLengthOffset := lsu.Size() d.createUnitLength(lsu, 0) // unit_length (*), filled in at end unitstart = lsu.Size() lsu.AddUint16(d.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 := lsu.Size() d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end headerstart = lsu.Size() // cpos == unitstart + 4 + 2 + 4 lsu.AddUint8(1) // minimum_instruction_length lsu.AddUint8(is_stmt) // default_is_stmt lsu.AddUint8(LINE_BASE & 0xFF) // line_base lsu.AddUint8(LINE_RANGE) // line_range lsu.AddUint8(OPCODE_BASE) // opcode_base lsu.AddUint8(0) // standard_opcode_lengths[1] lsu.AddUint8(1) // standard_opcode_lengths[2] lsu.AddUint8(1) // standard_opcode_lengths[3] lsu.AddUint8(1) // standard_opcode_lengths[4] lsu.AddUint8(1) // standard_opcode_lengths[5] lsu.AddUint8(0) // standard_opcode_lengths[6] lsu.AddUint8(0) // standard_opcode_lengths[7] lsu.AddUint8(0) // standard_opcode_lengths[8] lsu.AddUint8(1) // standard_opcode_lengths[9] lsu.AddUint8(0) // standard_opcode_lengths[10] // Call helper to emit dir and file sections. d.writeDirFileTables(unit, lsu) // capture length at end of file names. headerend = lsu.Size() unitlen := lsu.Size() - unitstart // Output the state machine for each function remaining. for _, s := range unit.Textp { fnSym := loader.Sym(s) _, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym) // Chain the line symbol onto the list. if lines != 0 { syms = append(syms, lines) unitlen += int64(len(d.ldr.Data(lines))) } } if d.linkctxt.HeadType == objabi.Haix { addDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(unitlen)) } if isDwarf64(d.linkctxt) { lsu.SetUint(d.arch, unitLengthOffset+4, uint64(unitlen)) // +4 because of 0xFFFFFFFF lsu.SetUint(d.arch, headerLengthOffset, uint64(headerend-headerstart)) } else { lsu.SetUint32(d.arch, unitLengthOffset, uint32(unitlen)) lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart)) } return syms } // writepcranges generates the DW_AT_ranges table for compilation unit // "unit", and returns a collection of ranges symbols (one for the // compilation unit DIE itself and the remainder from functions in the unit). func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, rangeProlog loader.Sym) []loader.Sym { syms := make([]loader.Sym, 0, len(unit.RangeSyms)+1) syms = append(syms, rangeProlog) rsu := d.ldr.MakeSymbolUpdater(rangeProlog) rDwSym := dwSym(rangeProlog) // Create PC ranges for the compilation unit DIE. newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym) newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base)) dwarf.PutBasedRanges(d, rDwSym, pcs) // Collect up the ranges for functions in the unit. rsize := uint64(rsu.Size()) for _, ls := range unit.RangeSyms { s := loader.Sym(ls) syms = append(syms, s) rsize += uint64(d.ldr.SymSize(s)) } if d.linkctxt.HeadType == objabi.Haix { addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, rsize) } return syms } /* * Emit .debug_frame */ const ( dataAlignmentFactor = -4 ) // appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice. func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte { b = append(b, dwarf.DW_CFA_def_cfa_offset_sf) b = dwarf.AppendSleb128(b, cfa/dataAlignmentFactor) switch { case deltapc < 0x40: b = append(b, uint8(dwarf.DW_CFA_advance_loc+deltapc)) case deltapc < 0x100: b = append(b, dwarf.DW_CFA_advance_loc1) b = append(b, uint8(deltapc)) case deltapc < 0x10000: b = append(b, dwarf.DW_CFA_advance_loc2, 0, 0) arch.ByteOrder.PutUint16(b[len(b)-2:], uint16(deltapc)) default: b = append(b, dwarf.DW_CFA_advance_loc4, 0, 0, 0, 0) arch.ByteOrder.PutUint32(b[len(b)-4:], uint32(deltapc)) } return b } func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo { fsd := dwSym(fs) fsu := d.ldr.MakeSymbolUpdater(fs) fsu.SetType(sym.SDWARFSECT) isdw64 := isDwarf64(d.linkctxt) haslr := d.linkctxt.Arch.HasLR // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64 lengthFieldSize := int64(4) if isdw64 { lengthFieldSize += 8 } // Emit the CIE, Section 6.4.1 cieReserve := uint32(16) if haslr { cieReserve = 32 } if isdw64 { cieReserve += 4 // 4 bytes added for cid } d.createUnitLength(fsu, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize d.addDwarfAddrField(fsu, ^uint64(0)) // cid fsu.AddUint8(3) // dwarf version (appendix F) fsu.AddUint8(0) // augmentation "" dwarf.Uleb128put(d, fsd, 1) // code_alignment_factor dwarf.Sleb128put(d, fsd, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register fsu.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... if haslr { dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset. fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) fsu.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... dwarf.Uleb128put(d, fsd, int64(0)) // ...is CFA+0. } else { dwarf.Uleb128put(d, fsd, int64(d.arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). fsu.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // ...of the return address... dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. } pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs))) if pad < 0 { Exitf("dwarf: cieReserve too small by %d bytes.", -pad) } internalExec := d.linkctxt.BuildMode == BuildModeExe && d.linkctxt.IsInternal() addAddrPlus := loader.GenAddAddrPlusFunc(internalExec) fsu.AddBytes(zeros[:pad]) var deltaBuf []byte pcsp := obj.NewPCIter(uint32(d.arch.MinLC)) for _, s := range d.linkctxt.Textp { fn := loader.Sym(s) fi := d.ldr.FuncInfo(fn) if !fi.Valid() { continue } fpcsp := d.ldr.Pcsp(s) // Emit a FDE, Section 6.4.1. // First build the section contents into a byte buffer. deltaBuf = deltaBuf[:0] if haslr && fi.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(d.linkctxt.loader.Data(fpcsp)); !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) == int64(len(d.ldr.Data(fn))) { nextpc-- if nextpc < pcsp.PC { continue } } spdelta := int64(pcsp.Value) if !haslr { // Return address has been pushed onto stack. spdelta += int64(d.arch.PtrSize) } if haslr && !fi.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(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) } pad := int(Rnd(int64(len(deltaBuf)), int64(d.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*d.arch.PtrSize + len(deltaBuf)) if isdw64 { fdeLength += 4 // 4 bytes added for CIE pointer } d.createUnitLength(fsu, fdeLength) if d.linkctxt.LinkMode == LinkExternal { d.addDwarfAddrRef(fsu, fs) } else { d.addDwarfAddrField(fsu, 0) // CIE offset } addAddrPlus(fsu, d.arch, s, 0) fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range fsu.AddBytes(deltaBuf) if d.linkctxt.HeadType == objabi.Haix { addDwsectCUSize(".debug_frame", d.ldr.SymPkg(fn), fdeLength+uint64(lengthFieldSize)) } } return dwarfSecInfo{syms: []loader.Sym{fs}} } /* * Walk DWarfDebugInfoEntries, and emit .debug_info */ const ( COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 ) func (d *dwctxt) writeUnitInfo(u *sym.CompilationUnit, abbrevsym loader.Sym, infoEpilog loader.Sym) []loader.Sym { syms := []loader.Sym{} if len(u.Textp) == 0 && u.DWInfo.Child == nil && len(u.VarDIEs) == 0 { return syms } compunit := u.DWInfo s := d.dtolsym(compunit.Sym) su := d.ldr.MakeSymbolUpdater(s) // 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. d.createUnitLength(su, 0) // unit_length (*), will be filled in later. su.AddUint16(d.arch, 4) // dwarf version (appendix F) // debug_abbrev_offset (*) d.addDwarfAddrRef(su, abbrevsym) su.AddUint8(uint8(d.arch.PtrSize)) // address_size ds := dwSym(s) dwarf.Uleb128put(d, ds, int64(compunit.Abbrev)) dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr) // This is an under-estimate; more will be needed for type DIEs. cu := make([]loader.Sym, 0, len(u.AbsFnDIEs)+len(u.FuncDIEs)) cu = append(cu, s) cu = append(cu, u.AbsFnDIEs...) cu = append(cu, u.FuncDIEs...) if u.Consts != 0 { cu = append(cu, loader.Sym(u.Consts)) } cu = append(cu, u.VarDIEs...) var cusize int64 for _, child := range cu { cusize += int64(len(d.ldr.Data(child))) } for die := compunit.Child; die != nil; die = die.Link { l := len(cu) lastSymSz := int64(len(d.ldr.Data(cu[l-1]))) cu = d.putdie(cu, die) if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) { // putdie will sometimes append directly to the last symbol of the list cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1]))) } for _, child := range cu[l:] { cusize += int64(len(d.ldr.Data(child))) } } culu := d.ldr.MakeSymbolUpdater(infoEpilog) culu.AddUint8(0) // closes compilation unit DIE cu = append(cu, infoEpilog) cusize++ // Save size for AIX symbol table. if d.linkctxt.HeadType == objabi.Haix { addDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize)) } if isDwarf64(d.linkctxt) { cusize -= 12 // exclude the length field. su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF } else { cusize -= 4 // exclude the length field. su.SetUint32(d.arch, 0, uint32(cusize)) } return append(syms, cu...) } func (d *dwctxt) writegdbscript() dwarfSecInfo { // TODO (aix): make it available if d.linkctxt.HeadType == objabi.Haix { return dwarfSecInfo{} } if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.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 dwarfSecInfo{} } if gdbscript == "" { return dwarfSecInfo{} } gs := d.ldr.CreateSymForUpdate(".debug_gdb_scripts", 0) gs.SetType(sym.SDWARFSECT) gs.AddUint8(GdbScriptPythonFileId) gs.Addstring(gdbscript) return dwarfSecInfo{syms: []loader.Sym{gs.Sym()}} } // FIXME: might be worth looking replacing this map with a function // that switches based on symbol instead. var prototypedies map[string]*dwarf.DWDie func dwarfEnabled(ctxt *Link) bool { if *FlagW { // disable dwarf return false } if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs || ctxt.HeadType == objabi.Hwasip1 { return false } if ctxt.LinkMode == LinkExternal { switch { case ctxt.IsELF: case ctxt.HeadType == objabi.Hdarwin: case ctxt.HeadType == objabi.Hwindows: case ctxt.HeadType == objabi.Haix: res, err := dwarf.IsDWARFEnabledOnAIXLd(ctxt.extld()) if err != nil { Exitf("%v", err) } return res default: return false } } return true } // mkBuiltinType populates the dwctxt2 sym lookup maps for the // newly created builtin type DIE 'typeDie'. func (d *dwctxt) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie { // create type DIE die := d.newdie(&dwtypes, abrv, tname) // Look up type symbol. gotype := d.lookupOrDiag("type:" + tname) // Map from die sym to type sym ds := loader.Sym(die.Sym.(dwSym)) d.rtmap[ds] = gotype // Map from type to def sym d.tdmap[gotype] = ds return die } // dwarfVisitFunction takes a function (text) symbol and processes the // subprogram DIE for the function and picks up any other DIEs // (absfns, types) that it references. func (d *dwctxt) dwarfVisitFunction(fnSym loader.Sym, unit *sym.CompilationUnit) { // The DWARF subprogram DIE symbol is listed as an aux sym // of the text (fcn) symbol, so ask the loader to retrieve it, // as well as the associated range symbol. infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym) if infosym == 0 { return } d.ldr.SetAttrNotInSymbolTable(infosym, true) d.ldr.SetAttrReachable(infosym, true) unit.FuncDIEs = append(unit.FuncDIEs, sym.LoaderSym(infosym)) if rangesym != 0 { d.ldr.SetAttrNotInSymbolTable(rangesym, true) d.ldr.SetAttrReachable(rangesym, true) unit.RangeSyms = append(unit.RangeSyms, sym.LoaderSym(rangesym)) } // Walk the relocations of the subprogram DIE symbol to discover // references to abstract function DIEs, Go type DIES, and // (via R_USETYPE relocs) types that were originally assigned to // locals/params but were optimized away. drelocs := d.ldr.Relocs(infosym) for ri := 0; ri < drelocs.Count(); ri++ { r := drelocs.At(ri) // Look for "use type" relocs. if r.Type() == objabi.R_USETYPE { d.defgotype(r.Sym()) continue } if r.Type() != objabi.R_DWARFSECREF { continue } rsym := r.Sym() rst := d.ldr.SymType(rsym) // Look for abstract function references. if rst == sym.SDWARFABSFCN { if !d.ldr.AttrOnList(rsym) { // abstract function d.ldr.SetAttrOnList(rsym, true) unit.AbsFnDIEs = append(unit.AbsFnDIEs, sym.LoaderSym(rsym)) d.importInfoSymbol(rsym) } continue } // Look for type references. if rst != sym.SDWARFTYPE && rst != sym.Sxxx { continue } if _, ok := d.rtmap[rsym]; ok { // type already generated continue } rsn := d.ldr.SymName(rsym) tn := rsn[len(dwarf.InfoPrefix):] ts := d.ldr.Lookup("type:"+tn, 0) d.defgotype(ts) } } // 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 } d := &dwctxt{ linkctxt: ctxt, ldr: ctxt.loader, arch: ctxt.Arch, tmap: make(map[string]loader.Sym), tdmap: make(map[loader.Sym]loader.Sym), rtmap: make(map[loader.Sym]loader.Sym), } d.typeRuntimeEface = d.lookupOrDiag("type:runtime.eface") d.typeRuntimeIface = d.lookupOrDiag("type:runtime.iface") if ctxt.HeadType == objabi.Haix { // Initial map used to store package size for each DWARF section. dwsectCUSize = make(map[string]uint64) } // For ctxt.Diagnostic messages. newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") // Unspecified type. There are no references to this in the symbol table. d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "") // 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(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, dwSym(d.lookupOrDiag("type:uintptr"))) d.uintptrInfoSym = d.mustFind("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:internal/abi.Type", "type:internal/abi.ArrayType", "type:internal/abi.ChanType", "type:internal/abi.FuncType", "type:internal/abi.MapType", "type:internal/abi.PtrType", "type:internal/abi.SliceType", "type:internal/abi.StructType", "type:internal/abi.InterfaceType", "type:runtime.itab", "type:internal/abi.Imethod"} { d.defgotype(d.lookupOrDiag(typ)) } // fake root DIE for compile unit DIEs var dwroot dwarf.DWDie flagVariants := make(map[string]bool) for _, lib := range ctxt.Library { consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) for _, unit := range lib.Units { // We drop the constants into the first CU. if consts != 0 { unit.Consts = sym.LoaderSym(consts) d.importInfoSymbol(consts) consts = 0 } ctxt.compUnits = append(ctxt.compUnits, unit) // We need at least one runtime unit. if unit.Lib.Pkg == "runtime" { ctxt.runtimeCU = unit } cuabrv := dwarf.DW_ABRV_COMPUNIT if len(unit.Textp) == 0 { cuabrv = dwarf.DW_ABRV_COMPUNIT_TEXTLESS } unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg) 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) 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 " + buildcfg.Version 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(peData) flagVariants[string(peData)] = true } else { flagVariants[""] = true } newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer) var pkgname string 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) // Scan all functions in this compilation unit, create // DIEs for all referenced types, find all referenced // abstract functions, visit range symbols. Note that // Textp has been dead-code-eliminated already. for _, s := range unit.Textp { d.dwarfVisitFunction(loader.Sym(s), unit) } } } // 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 } // Make a pass through all data symbols, looking for those // corresponding to reachable, Go-generated, user-visible // global variables. For each global of this sort, locate // the corresponding compiler-generated DIE symbol and tack // it onto the list associated with the unit. // Also looks for dictionary symbols and generates DIE symbols for each // type they reference. 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, unless it's a dictionary gt := d.ldr.SymGoType(idx) if gt == 0 { if t == sym.SRODATA { if d.ldr.IsDict(idx) { // This is a dictionary, make sure that all types referenced by this dictionary are reachable relocs := d.ldr.Relocs(idx) for i := 0; i < relocs.Count(); i++ { reloc := relocs.At(i) if reloc.Type() == objabi.R_USEIFACE { d.defgotype(reloc.Sym()) } } } } continue } // Skip file local symbols (this includes static tmps, stack // object symbols, and local symbols in assembler src files). if d.ldr.IsFileLocal(idx) { continue } // Find compiler-generated DWARF info sym for global in question, // and tack it onto the appropriate unit. Note that there are // circumstances under which we can't find the compiler-generated // symbol-- this typically happens as a result of compiler options // (e.g. compile package X with "-dwarf=0"). varDIE := d.ldr.GetVarDwarfAuxSym(idx) if varDIE != 0 { unit := d.ldr.SymUnit(idx) d.defgotype(gt) unit.VarDIEs = append(unit.VarDIEs, sym.LoaderSym(varDIE)) } } d.synthesizestringtypes(ctxt, dwtypes.Child) d.synthesizeslicetypes(ctxt, dwtypes.Child) d.synthesizemaptypes(ctxt, dwtypes.Child) d.synthesizechantypes(ctxt, dwtypes.Child) } // dwarfGenerateDebugSyms constructs debug_line, debug_frame, and // debug_loc. It also writes out the debug_info section using symbols // generated in dwarfGenerateDebugInfo2. func dwarfGenerateDebugSyms(ctxt *Link) { if !dwarfEnabled(ctxt) { return } d := &dwctxt{ linkctxt: ctxt, ldr: ctxt.loader, arch: ctxt.Arch, dwmu: new(sync.Mutex), } d.dwarfGenerateDebugSyms() } // dwUnitSyms stores input and output symbols for DWARF generation // for a given compilation unit. type dwUnitSyms struct { // Inputs for a given unit. lineProlog loader.Sym rangeProlog loader.Sym infoEpilog loader.Sym // Outputs for a given unit. linesyms []loader.Sym infosyms []loader.Sym locsyms []loader.Sym rangessyms []loader.Sym } // dwUnitPortion assembles the DWARF content for a given compilation // unit: debug_info, debug_lines, debug_ranges, debug_loc (debug_frame // is handled elsewhere). Order is important; the calls to writelines // and writepcranges below make updates to the compilation unit DIE, // hence they have to happen before the call to writeUnitInfo. func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us *dwUnitSyms) { if u.DWInfo.Abbrev != dwarf.DW_ABRV_COMPUNIT_TEXTLESS { us.linesyms = d.writelines(u, us.lineProlog) base := loader.Sym(u.Textp[0]) us.rangessyms = d.writepcranges(u, base, u.PCs, us.rangeProlog) us.locsyms = d.collectUnitLocs(u) } us.infosyms = d.writeUnitInfo(u, abbrevsym, us.infoEpilog) } func (d *dwctxt) dwarfGenerateDebugSyms() { abbrevSec := d.writeabbrev() dwarfp = append(dwarfp, abbrevSec) d.calcCompUnitRanges() sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits)) // 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. for _, u := range d.linkctxt.compUnits { reversetree(&u.DWInfo.Child) } reversetree(&dwtypes.Child) movetomodule(d.linkctxt, &dwtypes) mkSecSym := func(name string) loader.Sym { s := d.ldr.CreateSymForUpdate(name, 0) s.SetType(sym.SDWARFSECT) s.SetReachable(true) return s.Sym() } mkAnonSym := func(kind sym.SymKind) loader.Sym { s := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0)) s.SetType(kind) s.SetReachable(true) return s.Sym() } // Create the section symbols. frameSym := mkSecSym(".debug_frame") locSym := mkSecSym(".debug_loc") lineSym := mkSecSym(".debug_line") rangesSym := mkSecSym(".debug_ranges") infoSym := mkSecSym(".debug_info") // Create the section objects lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}} locSec := dwarfSecInfo{syms: []loader.Sym{locSym}} rangesSec := dwarfSecInfo{syms: []loader.Sym{rangesSym}} frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}} infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}} // Create any new symbols that will be needed during the // parallel portion below. ncu := len(d.linkctxt.compUnits) unitSyms := make([]dwUnitSyms, ncu) for i := 0; i < ncu; i++ { us := &unitSyms[i] us.lineProlog = mkAnonSym(sym.SDWARFLINES) us.rangeProlog = mkAnonSym(sym.SDWARFRANGE) us.infoEpilog = mkAnonSym(sym.SDWARFFCN) } var wg sync.WaitGroup sema := make(chan struct{}, runtime.GOMAXPROCS(0)) // Kick off generation of .debug_frame, since it doesn't have // any entanglements and can be started right away. wg.Add(1) go func() { sema <- struct{}{} defer func() { <-sema wg.Done() }() frameSec = d.writeframes(frameSym) }() // Create a goroutine per comp unit to handle the generation that // unit's portion of .debug_line, .debug_loc, .debug_ranges, and // .debug_info. wg.Add(len(d.linkctxt.compUnits)) for i := 0; i < ncu; i++ { go func(u *sym.CompilationUnit, us *dwUnitSyms) { sema <- struct{}{} defer func() { <-sema wg.Done() }() d.dwUnitPortion(u, abbrevSec.secSym(), us) }(d.linkctxt.compUnits[i], &unitSyms[i]) } wg.Wait() markReachable := func(syms []loader.Sym) []loader.Sym { for _, s := range syms { d.ldr.SetAttrNotInSymbolTable(s, true) d.ldr.SetAttrReachable(s, true) } return syms } // Stitch together the results. for i := 0; i < ncu; i++ { r := &unitSyms[i] lineSec.syms = append(lineSec.syms, markReachable(r.linesyms)...) infoSec.syms = append(infoSec.syms, markReachable(r.infosyms)...) locSec.syms = append(locSec.syms, markReachable(r.locsyms)...) rangesSec.syms = append(rangesSec.syms, markReachable(r.rangessyms)...) } dwarfp = append(dwarfp, lineSec) dwarfp = append(dwarfp, frameSec) gdbScriptSec := d.writegdbscript() if gdbScriptSec.secSym() != 0 { dwarfp = append(dwarfp, gdbScriptSec) } dwarfp = append(dwarfp, infoSec) if len(locSec.syms) > 1 { dwarfp = append(dwarfp, locSec) } dwarfp = append(dwarfp, rangesSec) // Check to make sure we haven't listed any symbols more than once // in the info section. This used to be done by setting and // checking the OnList attribute in "putdie", but that strategy // was not friendly for concurrency. seen := loader.MakeBitmap(d.ldr.NSym()) for _, s := range infoSec.syms { if seen.Has(s) { log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s)) } seen.Set(s) } } func (d *dwctxt) collectUnitLocs(u *sym.CompilationUnit) []loader.Sym { syms := []loader.Sym{} for _, fn := range u.FuncDIEs { relocs := d.ldr.Relocs(loader.Sym(fn)) for i := 0; i < relocs.Count(); i++ { reloc := relocs.At(i) if reloc.Type() != objabi.R_DWARFSECREF { continue } rsym := reloc.Sym() if d.ldr.SymType(rsym) == sym.SDWARFLOC { syms = append(syms, rsym) // One location list entry per function, but many relocations to it. Don't duplicate. break } } } return syms } // Add DWARF section names to the section header string table, by calling add // on each name. ELF only. func dwarfaddshstrings(ctxt *Link, add func(string)) { if *FlagW { // disable dwarf return } secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts", "ranges"} for _, sec := range secs { add(".debug_" + sec) if ctxt.IsExternal() { add(elfRelType + ".debug_" + sec) } } } func dwarfaddelfsectionsyms(ctxt *Link) { if *FlagW { // disable dwarf return } if ctxt.LinkMode != LinkExternal { return } ldr := ctxt.loader for _, si := range dwarfp { s := si.secSym() sect := ldr.SymSect(si.secSym()) putelfsectionsym(ctxt, ctxt.Out, 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) { // compressedSect is a helper type for parallelizing compression. type compressedSect struct { index int compressed []byte syms []loader.Sym } supported := ctxt.IsELF || ctxt.IsWindows() || ctxt.IsDarwin() if !ctxt.compressDWARF || !supported || ctxt.IsExternal() { return } var compressedCount int resChannel := make(chan compressedSect) for i := range dwarfp { go func(resIndex int, syms []loader.Sym) { resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms} }(compressedCount, dwarfp[i].syms) compressedCount++ } res := make([]compressedSect, compressedCount) for ; compressedCount > 0; compressedCount-- { r := <-resChannel res[r.index] = r } ldr := ctxt.loader var newDwarfp []dwarfSecInfo Segdwarf.Sections = Segdwarf.Sections[:0] for _, z := range res { s := z.syms[0] if z.compressed == nil { // Compression didn't help. ds := dwarfSecInfo{syms: z.syms} newDwarfp = append(newDwarfp, ds) Segdwarf.Sections = append(Segdwarf.Sections, ldr.SymSect(s)) } else { var compressedSegName string if ctxt.IsELF { compressedSegName = ldr.SymSect(s).Name } else { compressedSegName = ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):] } sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04) sect.Align = int32(ctxt.Arch.Alignment) sect.Length = uint64(len(z.compressed)) sect.Compressed = true newSym := ldr.MakeSymbolBuilder(compressedSegName) ldr.SetAttrReachable(s, true) newSym.SetData(z.compressed) newSym.SetSize(int64(len(z.compressed))) ldr.SetSymSect(newSym.Sym(), sect) ds := dwarfSecInfo{syms: []loader.Sym{newSym.Sym()}} newDwarfp = append(newDwarfp, ds) // compressed symbols are no longer needed. for _, s := range z.syms { ldr.SetAttrReachable(s, false) ldr.FreeSym(s) } } } dwarfp = newDwarfp // 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 _, si := range dwarfp { for _, s := range si.syms { ldr.SetSymValue(s, int64(pos)) sect := ldr.SymSect(s) if sect != prevSect { sect.Vaddr = uint64(pos) prevSect = sect } if ldr.SubSym(s) != 0 { log.Fatalf("%s: unexpected sub-symbols", ldr.SymName(s)) } pos += uint64(ldr.SymSize(s)) if ctxt.IsWindows() { 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].PCs[0].Start < v[j].PCs[0].Start } } // 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 (d *dwctxt) getPkgFromCUSym(s loader.Sym) string { return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.") } // 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 dwsectCUSizeMu sync.Mutex 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 addDwsectCUSize(sname string, pkgname string, size uint64) { dwsectCUSizeMu.Lock() defer dwsectCUSizeMu.Unlock() dwsectCUSize[sname+"."+pkgname] += size }