From: Cherry Zhang Date: Tue, 11 Aug 2020 11:24:52 +0000 (-0400) Subject: [dev.link] cmd/internal/obj: combine objfile.go and objfile2.go X-Git-Tag: go1.16beta1~1378^2~3 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=3fc2e6b0ce77a94504b5e83c0ef4adb6d6fabbbc;p=gostls13.git [dev.link] cmd/internal/obj: combine objfile.go and objfile2.go Combine objfile2.go into objfile.go. objfile.go has a lot of code for DWARF generation. Move them to dwarf.go. Change-Id: I2a27c672e9e9b8eea35d5e0a71433dcc80b7afa4 Reviewed-on: https://go-review.googlesource.com/c/go/+/247918 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go index 724aea2f8f..9abb31b558 100644 --- a/src/cmd/internal/obj/dwarf.go +++ b/src/cmd/internal/obj/dwarf.go @@ -8,8 +8,11 @@ package obj import ( "cmd/internal/dwarf" + "cmd/internal/objabi" "cmd/internal/src" "fmt" + "sort" + "sync" ) // Generate a sequence of opcodes that is as short as possible. @@ -196,3 +199,492 @@ func putpclcdelta(linkctxt *Link, dctxt dwCtxt, s *LSym, deltaPC uint64, deltaLC // Output the special opcode. dctxt.AddUint8(s, uint8(opcode)) } + +// implement dwarf.Context +type dwCtxt struct{ *Link } + +func (c dwCtxt) PtrSize() int { + return c.Arch.PtrSize +} +func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) { + ls := s.(*LSym) + ls.WriteInt(c.Link, ls.Size, size, i) +} +func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) { + c.AddInt(s, 2, int64(i)) +} +func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) { + b := []byte{byte(i)} + c.AddBytes(s, b) +} +func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) { + ls := s.(*LSym) + ls.WriteBytes(c.Link, ls.Size, b) +} +func (c dwCtxt) AddString(s dwarf.Sym, v string) { + ls := s.(*LSym) + ls.WriteString(c.Link, ls.Size, len(v), v) + ls.WriteInt(c.Link, ls.Size, 1, 0) +} +func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { + ls := s.(*LSym) + size := c.PtrSize() + if data != nil { + rsym := data.(*LSym) + ls.WriteAddr(c.Link, ls.Size, size, rsym, value) + } else { + ls.WriteInt(c.Link, ls.Size, size, value) + } +} +func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + ls := s.(*LSym) + rsym := data.(*LSym) + ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value) +} +func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { + panic("should be used only in the linker") +} +func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { + size := 4 + if isDwarf64(c.Link) { + size = 8 + } + + ls := s.(*LSym) + rsym := t.(*LSym) + ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs) + r := &ls.R[len(ls.R)-1] + r.Type = objabi.R_DWARFSECREF +} + +func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) { + ls := s.(*LSym) + rsym := f.(*LSym) + fidx := c.Link.PosTable.FileIndex(rsym.Name) + // Note the +1 here -- the value we're writing is going to be an + // index into the DWARF line table file section, whose entries + // are numbered starting at 1, not 0. + ls.WriteInt(c.Link, ls.Size, 4, int64(fidx+1)) +} + +func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 { + ls := s.(*LSym) + return ls.Size +} + +// Here "from" is a symbol corresponding to an inlined or concrete +// function, "to" is the symbol for the corresponding abstract +// function, and "dclIdx" is the index of the symbol of interest with +// respect to the Dcl slice of the original pre-optimization version +// of the inlined function. +func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) { + ls := from.(*LSym) + tls := to.(*LSym) + ridx := len(ls.R) - 1 + c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex) +} + +func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { + ls := s.(*LSym) + c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets) +} + +func (c dwCtxt) Logf(format string, args ...interface{}) { + c.Link.Logf(format, args...) +} + +func isDwarf64(ctxt *Link) bool { + return ctxt.Headtype == objabi.Haix +} + +func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) { + if s.Type != objabi.STEXT { + ctxt.Diag("dwarfSym of non-TEXT %v", s) + } + if s.Func.dwarfInfoSym == nil { + s.Func.dwarfInfoSym = &LSym{ + Type: objabi.SDWARFFCN, + } + if ctxt.Flag_locationlists { + s.Func.dwarfLocSym = &LSym{ + Type: objabi.SDWARFLOC, + } + } + s.Func.dwarfRangesSym = &LSym{ + Type: objabi.SDWARFRANGE, + } + s.Func.dwarfDebugLinesSym = &LSym{ + Type: objabi.SDWARFLINES, + } + if s.WasInlined() { + s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) + } + } + return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym +} + +func (s *LSym) Length(dwarfContext interface{}) int64 { + return s.Size +} + +// fileSymbol returns a symbol corresponding to the source file of the +// first instruction (prog) of the specified function. This will +// presumably be the file in which the function is defined. +func (ctxt *Link) fileSymbol(fn *LSym) *LSym { + p := fn.Func.Text + if p != nil { + f, _ := linkgetlineFromPos(ctxt, p.Pos) + fsym := ctxt.Lookup(f) + return fsym + } + return nil +} + +// populateDWARF fills in the DWARF Debugging Information Entries for +// TEXT symbol 's'. The various DWARF symbols must already have been +// initialized in InitTextSym. +func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) { + info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s) + if info.Size != 0 { + ctxt.Diag("makeFuncDebugEntry double process %v", s) + } + var scopes []dwarf.Scope + var inlcalls dwarf.InlCalls + if ctxt.DebugInfo != nil { + scopes, inlcalls = ctxt.DebugInfo(s, info, curfn) + } + var err error + dwctxt := dwCtxt{ctxt} + filesym := ctxt.fileSymbol(s) + fnstate := &dwarf.FnState{ + Name: s.Name, + Importpath: myimportpath, + Info: info, + Filesym: filesym, + Loc: loc, + Ranges: ranges, + Absfn: absfunc, + StartPC: s, + Size: s.Size, + External: !s.Static(), + Scopes: scopes, + InlCalls: inlcalls, + UseBASEntries: ctxt.UseBASEntries, + } + if absfunc != nil { + err = dwarf.PutAbstractFunc(dwctxt, fnstate) + if err != nil { + ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) + } + err = dwarf.PutConcreteFunc(dwctxt, fnstate) + } else { + err = dwarf.PutDefaultFunc(dwctxt, fnstate) + } + if err != nil { + ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) + } + // Fill in the debug lines symbol. + ctxt.generateDebugLinesSymbol(s, lines) +} + +// DwarfIntConst creates a link symbol for an integer constant with the +// given name, type and value. +func (ctxt *Link) DwarfIntConst(myimportpath, name, typename string, val int64) { + if myimportpath == "" { + return + } + s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) { + s.Type = objabi.SDWARFCONST + ctxt.Data = append(ctxt.Data, s) + }) + dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val) +} + +func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath string) { + absfn := ctxt.DwFixups.AbsFuncDwarfSym(s) + if absfn.Size != 0 { + ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s) + } + if s.Func == nil { + s.Func = new(FuncInfo) + } + scopes, _ := ctxt.DebugInfo(s, absfn, curfn) + dwctxt := dwCtxt{ctxt} + filesym := ctxt.fileSymbol(s) + fnstate := dwarf.FnState{ + Name: s.Name, + Importpath: myimportpath, + Info: absfn, + Filesym: filesym, + Absfn: absfn, + External: !s.Static(), + Scopes: scopes, + UseBASEntries: ctxt.UseBASEntries, + } + if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil { + ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) + } +} + +// This table is designed to aid in the creation of references between +// DWARF subprogram DIEs. +// +// In most cases when one DWARF DIE has to refer to another DWARF DIE, +// the target of the reference has an LSym, which makes it easy to use +// the existing relocation mechanism. For DWARF inlined routine DIEs, +// however, the subprogram DIE has to refer to a child +// parameter/variable DIE of the abstract subprogram. This child DIE +// doesn't have an LSym, and also of interest is the fact that when +// DWARF generation is happening for inlined function F within caller +// G, it's possible that DWARF generation hasn't happened yet for F, +// so there is no way to know the offset of a child DIE within F's +// abstract function. Making matters more complex, each inlined +// instance of F may refer to a subset of the original F's variables +// (depending on what happens with optimization, some vars may be +// eliminated). +// +// The fixup table below helps overcome this hurdle. At the point +// where a parameter/variable reference is made (via a call to +// "ReferenceChildDIE"), a fixup record is generate that records +// the relocation that is targeting that child variable. At a later +// point when the abstract function DIE is emitted, there will be +// a call to "RegisterChildDIEOffsets", at which point the offsets +// needed to apply fixups are captured. Finally, once the parallel +// portion of the compilation is done, fixups can actually be applied +// during the "Finalize" method (this can't be done during the +// parallel portion of the compile due to the possibility of data +// races). +// +// This table is also used to record the "precursor" function node for +// each function that is the target of an inline -- child DIE references +// have to be made with respect to the original pre-optimization +// version of the function (to allow for the fact that each inlined +// body may be optimized differently). +type DwarfFixupTable struct { + ctxt *Link + mu sync.Mutex + symtab map[*LSym]int // maps abstract fn LSYM to index in svec + svec []symFixups + precursor map[*LSym]fnState // maps fn Lsym to precursor Node, absfn sym +} + +type symFixups struct { + fixups []relFixup + doffsets []declOffset + inlIndex int32 + defseen bool +} + +type declOffset struct { + // Index of variable within DCL list of pre-optimization function + dclIdx int32 + // Offset of var's child DIE with respect to containing subprogram DIE + offset int32 +} + +type relFixup struct { + refsym *LSym + relidx int32 + dclidx int32 +} + +type fnState struct { + // precursor function (really *gc.Node) + precursor interface{} + // abstract function symbol + absfn *LSym +} + +func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable { + return &DwarfFixupTable{ + ctxt: ctxt, + symtab: make(map[*LSym]int), + precursor: make(map[*LSym]fnState), + } +} + +func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) interface{} { + if fnstate, found := ft.precursor[s]; found { + return fnstate.precursor + } + return nil +} + +func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) { + if _, found := ft.precursor[s]; found { + ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s) + } + + // initialize abstract function symbol now. This is done here so + // as to avoid data races later on during the parallel portion of + // the back end. + absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix) + absfn.Set(AttrDuplicateOK, true) + absfn.Type = objabi.SDWARFABSFCN + ft.ctxt.Data = append(ft.ctxt.Data, absfn) + + // In the case of "late" inlining (inlines that happen during + // wrapper generation as opposed to the main inlining phase) it's + // possible that we didn't cache the abstract function sym for the + // text symbol -- do so now if needed. See issue 38068. + if s.Func != nil && s.Func.dwarfAbsFnSym == nil { + s.Func.dwarfAbsFnSym = absfn + } + + ft.precursor[s] = fnState{precursor: fn, absfn: absfn} +} + +// Make a note of a child DIE reference: relocation 'ridx' within symbol 's' +// is targeting child 'c' of DIE with symbol 'tgt'. +func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) { + // Protect against concurrent access if multiple backend workers + ft.mu.Lock() + defer ft.mu.Unlock() + + // Create entry for symbol if not already present. + idx, found := ft.symtab[tgt] + if !found { + ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)}) + idx = len(ft.svec) - 1 + ft.symtab[tgt] = idx + } + + // Do we have child DIE offsets available? If so, then apply them, + // otherwise create a fixup record. + sf := &ft.svec[idx] + if len(sf.doffsets) > 0 { + found := false + for _, do := range sf.doffsets { + if do.dclIdx == int32(dclidx) { + off := do.offset + s.R[ridx].Add += int64(off) + found = true + break + } + } + if !found { + ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt) + } + } else { + sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)}) + } +} + +// Called once DWARF generation is complete for a given abstract function, +// whose children might have been referenced via a call above. Stores +// the offsets for any child DIEs (vars, params) so that they can be +// consumed later in on DwarfFixupTable.Finalize, which applies any +// outstanding fixups. +func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) { + // Length of these two slices should agree + if len(vars) != len(coffsets) { + ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch") + return + } + + // Generate the slice of declOffset's based in vars/coffsets + doffsets := make([]declOffset, len(coffsets)) + for i := range coffsets { + doffsets[i].dclIdx = vars[i].ChildIndex + doffsets[i].offset = coffsets[i] + } + + ft.mu.Lock() + defer ft.mu.Unlock() + + // Store offsets for this symbol. + idx, found := ft.symtab[s] + if !found { + sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets} + ft.svec = append(ft.svec, sf) + ft.symtab[s] = len(ft.svec) - 1 + } else { + sf := &ft.svec[idx] + sf.doffsets = doffsets + sf.defseen = true + } +} + +func (ft *DwarfFixupTable) processFixups(slot int, s *LSym) { + sf := &ft.svec[slot] + for _, f := range sf.fixups { + dfound := false + for _, doffset := range sf.doffsets { + if doffset.dclIdx == f.dclidx { + f.refsym.R[f.relidx].Add += int64(doffset.offset) + dfound = true + break + } + } + if !dfound { + ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx) + } + } +} + +// return the LSym corresponding to the 'abstract subprogram' DWARF +// info entry for a function. +func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym { + // Protect against concurrent access if multiple backend workers + ft.mu.Lock() + defer ft.mu.Unlock() + + if fnstate, found := ft.precursor[fnsym]; found { + return fnstate.absfn + } + ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym) + return nil +} + +// Called after all functions have been compiled; the main job of this +// function is to identify cases where there are outstanding fixups. +// This scenario crops up when we have references to variables of an +// inlined routine, but that routine is defined in some other package. +// This helper walks through and locate these fixups, then invokes a +// helper to create an abstract subprogram DIE for each one. +func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) { + if trace { + ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath) + } + + // Collect up the keys from the precursor map, then sort the + // resulting list (don't want to rely on map ordering here). + fns := make([]*LSym, len(ft.precursor)) + idx := 0 + for fn := range ft.precursor { + fns[idx] = fn + idx++ + } + sort.Sort(BySymName(fns)) + + // Should not be called during parallel portion of compilation. + if ft.ctxt.InParallel { + ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend") + } + + // Generate any missing abstract functions. + for _, s := range fns { + absfn := ft.AbsFuncDwarfSym(s) + slot, found := ft.symtab[absfn] + if !found || !ft.svec[slot].defseen { + ft.ctxt.GenAbstractFunc(s) + } + } + + // Apply fixups. + for _, s := range fns { + absfn := ft.AbsFuncDwarfSym(s) + slot, found := ft.symtab[absfn] + if !found { + ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s) + } else { + ft.processFixups(slot, s) + } + } +} + +type BySymName []*LSym + +func (s BySymName) Len() int { return len(s) } +func (s BySymName) Less(i, j int) bool { return s[i].Name < s[j].Name } +func (s BySymName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 2f28b6eeec..7bc4f4992e 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -2,591 +2,746 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// Writing Go object files. + package obj import ( - "cmd/internal/dwarf" + "bytes" + "cmd/internal/bio" + "cmd/internal/goobj" "cmd/internal/objabi" "cmd/internal/sys" + "crypto/sha1" + "encoding/binary" "fmt" "io" + "path/filepath" "sort" - "sync" + "strings" ) -func (ctxt *Link) writeSymDebug(s *LSym) { - ctxt.writeSymDebugNamed(s, s.Name) -} +// Entry point of writing new object file. +func WriteObjFile(ctxt *Link, b *bio.Writer) { -func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { - fmt.Fprintf(ctxt.Bso, "%s ", name) - if s.Type != 0 { - fmt.Fprintf(ctxt.Bso, "%v ", s.Type) - } - if s.Static() { - fmt.Fprint(ctxt.Bso, "static ") - } - if s.DuplicateOK() { - fmt.Fprintf(ctxt.Bso, "dupok ") + debugAsmEmit(ctxt) + + genFuncInfoSyms(ctxt) + + w := writer{ + Writer: goobj.NewWriter(b), + ctxt: ctxt, + pkgpath: objabi.PathToPrefix(ctxt.Pkgpath), } - if s.CFunc() { - fmt.Fprintf(ctxt.Bso, "cfunc ") + + start := b.Offset() + w.init() + + // Header + // We just reserve the space. We'll fill in the offsets later. + flags := uint32(0) + if ctxt.Flag_shared { + flags |= goobj.ObjFlagShared } - if s.NoSplit() { - fmt.Fprintf(ctxt.Bso, "nosplit ") + if w.pkgpath == "" { + flags |= goobj.ObjFlagNeedNameExpansion } - if s.TopFrame() { - fmt.Fprintf(ctxt.Bso, "topframe ") + if ctxt.IsAsm { + flags |= goobj.ObjFlagFromAssembly } - fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) - if s.Type == objabi.STEXT { - fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(s.Func.Args), uint64(s.Func.Locals), uint64(s.Func.FuncID)) - if s.Leaf() { - fmt.Fprintf(ctxt.Bso, " leaf") - } + h := goobj.Header{ + Magic: goobj.Magic, + Fingerprint: ctxt.Fingerprint, + Flags: flags, } - fmt.Fprintf(ctxt.Bso, "\n") - if s.Type == objabi.STEXT { - for p := s.Func.Text; p != nil; p = p.Link { - fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc))) - if ctxt.Debugasm > 1 { - io.WriteString(ctxt.Bso, p.String()) - } else { - p.InnermostString(ctxt.Bso) - } - fmt.Fprintln(ctxt.Bso) - } - } - for i := 0; i < len(s.P); i += 16 { - fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) - j := i - for ; j < i+16 && j < len(s.P); j++ { - fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) - } - for ; j < i+16; j++ { - fmt.Fprintf(ctxt.Bso, " ") - } - fmt.Fprintf(ctxt.Bso, " ") - for j = i; j < i+16 && j < len(s.P); j++ { - c := int(s.P[j]) - b := byte('.') - if ' ' <= c && c <= 0x7e { - b = byte(c) - } - ctxt.Bso.WriteByte(b) - } + h.Write(w.Writer) - fmt.Fprintf(ctxt.Bso, "\n") - } + // String table + w.StringTable() - sort.Sort(relocByOff(s.R)) // generate stable output - for _, r := range s.R { - name := "" - if r.Sym != nil { - name = r.Sym.Name - } else if r.Type == objabi.R_TLS_LE { - name = "TLS" - } - if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { - fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add)) - } else { - fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add) - } + // Autolib + h.Offsets[goobj.BlkAutolib] = w.Offset() + for i := range ctxt.Imports { + ctxt.Imports[i].Write(w.Writer) } -} -// relocByOff sorts relocations by their offsets. -type relocByOff []Reloc - -func (x relocByOff) Len() int { return len(x) } -func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off } -func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - -// implement dwarf.Context -type dwCtxt struct{ *Link } + // Package references + h.Offsets[goobj.BlkPkgIdx] = w.Offset() + for _, pkg := range w.pkglist { + w.StringRef(pkg) + } -func (c dwCtxt) PtrSize() int { - return c.Arch.PtrSize -} -func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) { - ls := s.(*LSym) - ls.WriteInt(c.Link, ls.Size, size, i) -} -func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) { - c.AddInt(s, 2, int64(i)) -} -func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) { - b := []byte{byte(i)} - c.AddBytes(s, b) -} -func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) { - ls := s.(*LSym) - ls.WriteBytes(c.Link, ls.Size, b) -} -func (c dwCtxt) AddString(s dwarf.Sym, v string) { - ls := s.(*LSym) - ls.WriteString(c.Link, ls.Size, len(v), v) - ls.WriteInt(c.Link, ls.Size, 1, 0) -} -func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { - ls := s.(*LSym) - size := c.PtrSize() - if data != nil { - rsym := data.(*LSym) - ls.WriteAddr(c.Link, ls.Size, size, rsym, value) - } else { - ls.WriteInt(c.Link, ls.Size, size, value) + // File table (for DWARF and pcln generation). + h.Offsets[goobj.BlkFile] = w.Offset() + for _, f := range ctxt.PosTable.FileTable() { + w.StringRef(filepath.ToSlash(f)) } -} -func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { - ls := s.(*LSym) - rsym := data.(*LSym) - ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value) -} -func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { - panic("should be used only in the linker") -} -func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { - size := 4 - if isDwarf64(c.Link) { - size = 8 + + // Symbol definitions + h.Offsets[goobj.BlkSymdef] = w.Offset() + for _, s := range ctxt.defs { + w.Sym(s) } - ls := s.(*LSym) - rsym := t.(*LSym) - ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs) - r := &ls.R[len(ls.R)-1] - r.Type = objabi.R_DWARFSECREF -} + // Short hashed symbol definitions + h.Offsets[goobj.BlkHashed64def] = w.Offset() + for _, s := range ctxt.hashed64defs { + w.Sym(s) + } -func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) { - ls := s.(*LSym) - rsym := f.(*LSym) - fidx := c.Link.PosTable.FileIndex(rsym.Name) - // Note the +1 here -- the value we're writing is going to be an - // index into the DWARF line table file section, whose entries - // are numbered starting at 1, not 0. - ls.WriteInt(c.Link, ls.Size, 4, int64(fidx+1)) -} + // Hashed symbol definitions + h.Offsets[goobj.BlkHasheddef] = w.Offset() + for _, s := range ctxt.hasheddefs { + w.Sym(s) + } -func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 { - ls := s.(*LSym) - return ls.Size -} + // Non-pkg symbol definitions + h.Offsets[goobj.BlkNonpkgdef] = w.Offset() + for _, s := range ctxt.nonpkgdefs { + w.Sym(s) + } -// Here "from" is a symbol corresponding to an inlined or concrete -// function, "to" is the symbol for the corresponding abstract -// function, and "dclIdx" is the index of the symbol of interest with -// respect to the Dcl slice of the original pre-optimization version -// of the inlined function. -func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) { - ls := from.(*LSym) - tls := to.(*LSym) - ridx := len(ls.R) - 1 - c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex) -} + // Non-pkg symbol references + h.Offsets[goobj.BlkNonpkgref] = w.Offset() + for _, s := range ctxt.nonpkgrefs { + w.Sym(s) + } -func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { - ls := s.(*LSym) - c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets) -} + // Referenced package symbol flags + h.Offsets[goobj.BlkRefFlags] = w.Offset() + w.refFlags() -func (c dwCtxt) Logf(format string, args ...interface{}) { - c.Link.Logf(format, args...) -} + // Hashes + h.Offsets[goobj.BlkHash64] = w.Offset() + for _, s := range ctxt.hashed64defs { + w.Hash64(s) + } + h.Offsets[goobj.BlkHash] = w.Offset() + for _, s := range ctxt.hasheddefs { + w.Hash(s) + } + // TODO: hashedrefs unused/unsupported for now -func isDwarf64(ctxt *Link) bool { - return ctxt.Headtype == objabi.Haix -} + // Reloc indexes + h.Offsets[goobj.BlkRelocIdx] = w.Offset() + nreloc := uint32(0) + lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs} + for _, list := range lists { + for _, s := range list { + w.Uint32(nreloc) + nreloc += uint32(len(s.R)) + } + } + w.Uint32(nreloc) -func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) { - if s.Type != objabi.STEXT { - ctxt.Diag("dwarfSym of non-TEXT %v", s) + // Symbol Info indexes + h.Offsets[goobj.BlkAuxIdx] = w.Offset() + naux := uint32(0) + for _, list := range lists { + for _, s := range list { + w.Uint32(naux) + naux += uint32(nAuxSym(s)) + } } - if s.Func.dwarfInfoSym == nil { - s.Func.dwarfInfoSym = &LSym{ - Type: objabi.SDWARFFCN, + w.Uint32(naux) + + // Data indexes + h.Offsets[goobj.BlkDataIdx] = w.Offset() + dataOff := uint32(0) + for _, list := range lists { + for _, s := range list { + w.Uint32(dataOff) + dataOff += uint32(len(s.P)) } - if ctxt.Flag_locationlists { - s.Func.dwarfLocSym = &LSym{ - Type: objabi.SDWARFLOC, + } + w.Uint32(dataOff) + + // Relocs + h.Offsets[goobj.BlkReloc] = w.Offset() + for _, list := range lists { + for _, s := range list { + for i := range s.R { + w.Reloc(&s.R[i]) } } - s.Func.dwarfRangesSym = &LSym{ - Type: objabi.SDWARFRANGE, + } + + // Aux symbol info + h.Offsets[goobj.BlkAux] = w.Offset() + for _, list := range lists { + for _, s := range list { + w.Aux(s) } - s.Func.dwarfDebugLinesSym = &LSym{ - Type: objabi.SDWARFLINES, + } + + // Data + h.Offsets[goobj.BlkData] = w.Offset() + for _, list := range lists { + for _, s := range list { + w.Bytes(s.P) } - if s.WasInlined() { - s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) + } + + // Pcdata + h.Offsets[goobj.BlkPcdata] = w.Offset() + for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms + if s.Func != nil { + pc := &s.Func.Pcln + w.Bytes(pc.Pcsp.P) + w.Bytes(pc.Pcfile.P) + w.Bytes(pc.Pcline.P) + w.Bytes(pc.Pcinline.P) + for i := range pc.Pcdata { + w.Bytes(pc.Pcdata[i].P) + } } } - return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym -} -func (s *LSym) Length(dwarfContext interface{}) int64 { - return s.Size + // Blocks used only by tools (objdump, nm). + + // Referenced symbol names from other packages + h.Offsets[goobj.BlkRefName] = w.Offset() + w.refNames() + + h.Offsets[goobj.BlkEnd] = w.Offset() + + // Fix up block offsets in the header + end := start + int64(w.Offset()) + b.MustSeek(start, 0) + h.Write(w.Writer) + b.MustSeek(end, 0) } -// fileSymbol returns a symbol corresponding to the source file of the -// first instruction (prog) of the specified function. This will -// presumably be the file in which the function is defined. -func (ctxt *Link) fileSymbol(fn *LSym) *LSym { - p := fn.Func.Text - if p != nil { - f, _ := linkgetlineFromPos(ctxt, p.Pos) - fsym := ctxt.Lookup(f) - return fsym - } - return nil +type writer struct { + *goobj.Writer + ctxt *Link + pkgpath string // the package import path (escaped), "" if unknown + pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx } -// populateDWARF fills in the DWARF Debugging Information Entries for -// TEXT symbol 's'. The various DWARF symbols must already have been -// initialized in InitTextSym. -func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) { - info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s) - if info.Size != 0 { - ctxt.Diag("makeFuncDebugEntry double process %v", s) - } - var scopes []dwarf.Scope - var inlcalls dwarf.InlCalls - if ctxt.DebugInfo != nil { - scopes, inlcalls = ctxt.DebugInfo(s, info, curfn) - } - var err error - dwctxt := dwCtxt{ctxt} - filesym := ctxt.fileSymbol(s) - fnstate := &dwarf.FnState{ - Name: s.Name, - Importpath: myimportpath, - Info: info, - Filesym: filesym, - Loc: loc, - Ranges: ranges, - Absfn: absfunc, - StartPC: s, - Size: s.Size, - External: !s.Static(), - Scopes: scopes, - InlCalls: inlcalls, - UseBASEntries: ctxt.UseBASEntries, - } - if absfunc != nil { - err = dwarf.PutAbstractFunc(dwctxt, fnstate) - if err != nil { - ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) - } - err = dwarf.PutConcreteFunc(dwctxt, fnstate) - } else { - err = dwarf.PutDefaultFunc(dwctxt, fnstate) - } - if err != nil { - ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) - } - // Fill in the debug lines symbol. - ctxt.generateDebugLinesSymbol(s, lines) +// prepare package index list +func (w *writer) init() { + w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1) + w.pkglist[0] = "" // dummy invalid package for index 0 + for pkg, i := range w.ctxt.pkgIdx { + w.pkglist[i] = pkg + } } -// DwarfIntConst creates a link symbol for an integer constant with the -// given name, type and value. -func (ctxt *Link) DwarfIntConst(myimportpath, name, typename string, val int64) { - if myimportpath == "" { - return +func (w *writer) StringTable() { + w.AddString("") + for _, p := range w.ctxt.Imports { + w.AddString(p.Pkg) } - s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) { - s.Type = objabi.SDWARFCONST - ctxt.Data = append(ctxt.Data, s) + for _, pkg := range w.pkglist { + w.AddString(pkg) + } + w.ctxt.traverseSyms(traverseAll, func(s *LSym) { + // TODO: this includes references of indexed symbols from other packages, + // for which the linker doesn't need the name. Consider moving them to + // a separate block (for tools only). + if w.pkgpath != "" { + s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1) + } + // Don't put names of builtins into the string table (to save + // space). + if s.PkgIdx == goobj.PkgIdxBuiltin { + return + } + w.AddString(s.Name) }) - dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val) -} -func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath string) { - absfn := ctxt.DwFixups.AbsFuncDwarfSym(s) - if absfn.Size != 0 { - ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s) - } - if s.Func == nil { - s.Func = new(FuncInfo) - } - scopes, _ := ctxt.DebugInfo(s, absfn, curfn) - dwctxt := dwCtxt{ctxt} - filesym := ctxt.fileSymbol(s) - fnstate := dwarf.FnState{ - Name: s.Name, - Importpath: myimportpath, - Info: absfn, - Filesym: filesym, - Absfn: absfn, - External: !s.Static(), - Scopes: scopes, - UseBASEntries: ctxt.UseBASEntries, - } - if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil { - ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) + // All filenames are in the postable. + for _, f := range w.ctxt.PosTable.FileTable() { + w.AddString(filepath.ToSlash(f)) } } -// This table is designed to aid in the creation of references between -// DWARF subprogram DIEs. -// -// In most cases when one DWARF DIE has to refer to another DWARF DIE, -// the target of the reference has an LSym, which makes it easy to use -// the existing relocation mechanism. For DWARF inlined routine DIEs, -// however, the subprogram DIE has to refer to a child -// parameter/variable DIE of the abstract subprogram. This child DIE -// doesn't have an LSym, and also of interest is the fact that when -// DWARF generation is happening for inlined function F within caller -// G, it's possible that DWARF generation hasn't happened yet for F, -// so there is no way to know the offset of a child DIE within F's -// abstract function. Making matters more complex, each inlined -// instance of F may refer to a subset of the original F's variables -// (depending on what happens with optimization, some vars may be -// eliminated). -// -// The fixup table below helps overcome this hurdle. At the point -// where a parameter/variable reference is made (via a call to -// "ReferenceChildDIE"), a fixup record is generate that records -// the relocation that is targeting that child variable. At a later -// point when the abstract function DIE is emitted, there will be -// a call to "RegisterChildDIEOffsets", at which point the offsets -// needed to apply fixups are captured. Finally, once the parallel -// portion of the compilation is done, fixups can actually be applied -// during the "Finalize" method (this can't be done during the -// parallel portion of the compile due to the possibility of data -// races). -// -// This table is also used to record the "precursor" function node for -// each function that is the target of an inline -- child DIE references -// have to be made with respect to the original pre-optimization -// version of the function (to allow for the fact that each inlined -// body may be optimized differently). -type DwarfFixupTable struct { - ctxt *Link - mu sync.Mutex - symtab map[*LSym]int // maps abstract fn LSYM to index in svec - svec []symFixups - precursor map[*LSym]fnState // maps fn Lsym to precursor Node, absfn sym +func (w *writer) Sym(s *LSym) { + abi := uint16(s.ABI()) + if s.Static() { + abi = goobj.SymABIstatic + } + flag := uint8(0) + if s.DuplicateOK() { + flag |= goobj.SymFlagDupok + } + if s.Local() { + flag |= goobj.SymFlagLocal + } + if s.MakeTypelink() { + flag |= goobj.SymFlagTypelink + } + if s.Leaf() { + flag |= goobj.SymFlagLeaf + } + if s.NoSplit() { + flag |= goobj.SymFlagNoSplit + } + if s.ReflectMethod() { + flag |= goobj.SymFlagReflectMethod + } + if s.TopFrame() { + flag |= goobj.SymFlagTopFrame + } + if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA { + flag |= goobj.SymFlagGoType + } + flag2 := uint8(0) + if s.UsedInIface() { + flag2 |= goobj.SymFlagUsedInIface + } + if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA { + flag2 |= goobj.SymFlagItab + } + name := s.Name + if strings.HasPrefix(name, "gofile..") { + name = filepath.ToSlash(name) + } + var align uint32 + if s.Func != nil { + align = uint32(s.Func.Align) + } + if s.ContentAddressable() { + // We generally assume data symbols are natually aligned, + // except for strings. If we dedup a string symbol and a + // non-string symbol with the same content, we should keep + // the largest alignment. + // TODO: maybe the compiler could set the alignment for all + // data symbols more carefully. + if s.Size != 0 && !strings.HasPrefix(s.Name, "go.string.") { + switch { + case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0: + align = 8 + case s.Size%4 == 0: + align = 4 + case s.Size%2 == 0: + align = 2 + } + // don't bother setting align to 1. + } + } + var o goobj.Sym + o.SetName(name, w.Writer) + o.SetABI(abi) + o.SetType(uint8(s.Type)) + o.SetFlag(flag) + o.SetFlag2(flag2) + o.SetSiz(uint32(s.Size)) + o.SetAlign(align) + o.Write(w.Writer) } -type symFixups struct { - fixups []relFixup - doffsets []declOffset - inlIndex int32 - defseen bool +func (w *writer) Hash64(s *LSym) { + if !s.ContentAddressable() || len(s.R) != 0 { + panic("Hash of non-content-addresable symbol") + } + b := contentHash64(s) + w.Bytes(b[:]) } -type declOffset struct { - // Index of variable within DCL list of pre-optimization function - dclIdx int32 - // Offset of var's child DIE with respect to containing subprogram DIE - offset int32 +func (w *writer) Hash(s *LSym) { + if !s.ContentAddressable() { + panic("Hash of non-content-addresable symbol") + } + b := w.contentHash(s) + w.Bytes(b[:]) } -type relFixup struct { - refsym *LSym - relidx int32 - dclidx int32 +func contentHash64(s *LSym) goobj.Hash64Type { + var b goobj.Hash64Type + copy(b[:], s.P) + return b } -type fnState struct { - // precursor function (really *gc.Node) - precursor interface{} - // abstract function symbol - absfn *LSym +// Compute the content hash for a content-addressable symbol. +// We build a content hash based on its content and relocations. +// Depending on the category of the referenced symbol, we choose +// different hash algorithms such that the hash is globally +// consistent. +// - For referenced content-addressable symbol, its content hash +// is globally consistent. +// - For package symbol and builtin symbol, its local index is +// globally consistent. +// - For non-package symbol, its fully-expanded name is globally +// consistent. For now, we require we know the current package +// path so we can always expand symbol names. (Otherwise, +// symbols with relocations are not considered hashable.) +// +// For now, we assume there is no circular dependencies among +// hashed symbols. +func (w *writer) contentHash(s *LSym) goobj.HashType { + h := sha1.New() + // The compiler trims trailing zeros _sometimes_. We just do + // it always. + h.Write(bytes.TrimRight(s.P, "\x00")) + var tmp [14]byte + for i := range s.R { + r := &s.R[i] + binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off)) + tmp[4] = r.Siz + tmp[5] = uint8(r.Type) + binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add)) + h.Write(tmp[:]) + rs := r.Sym + switch rs.PkgIdx { + case goobj.PkgIdxHashed64: + h.Write([]byte{0}) + t := contentHash64(rs) + h.Write(t[:]) + case goobj.PkgIdxHashed: + h.Write([]byte{1}) + t := w.contentHash(rs) + h.Write(t[:]) + case goobj.PkgIdxNone: + h.Write([]byte{2}) + io.WriteString(h, rs.Name) // name is already expanded at this point + case goobj.PkgIdxBuiltin: + h.Write([]byte{3}) + binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) + h.Write(tmp[:4]) + case goobj.PkgIdxSelf: + io.WriteString(h, w.pkgpath) + binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) + h.Write(tmp[:4]) + default: + io.WriteString(h, rs.Pkg) + binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) + h.Write(tmp[:4]) + } + } + var b goobj.HashType + copy(b[:], h.Sum(nil)) + return b } -func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable { - return &DwarfFixupTable{ - ctxt: ctxt, - symtab: make(map[*LSym]int), - precursor: make(map[*LSym]fnState), +func makeSymRef(s *LSym) goobj.SymRef { + if s == nil { + return goobj.SymRef{} + } + if s.PkgIdx == 0 || !s.Indexed() { + fmt.Printf("unindexed symbol reference: %v\n", s) + panic("unindexed symbol reference") } + return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)} } -func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) interface{} { - if fnstate, found := ft.precursor[s]; found { - return fnstate.precursor - } - return nil +func (w *writer) Reloc(r *Reloc) { + var o goobj.Reloc + o.SetOff(r.Off) + o.SetSiz(r.Siz) + o.SetType(uint8(r.Type)) + o.SetAdd(r.Add) + o.SetSym(makeSymRef(r.Sym)) + o.Write(w.Writer) +} + +func (w *writer) aux1(typ uint8, rs *LSym) { + var o goobj.Aux + o.SetType(typ) + o.SetSym(makeSymRef(rs)) + o.Write(w.Writer) } -func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) { - if _, found := ft.precursor[s]; found { - ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s) +func (w *writer) Aux(s *LSym) { + if s.Gotype != nil { + w.aux1(goobj.AuxGotype, s.Gotype) } + if s.Func != nil { + w.aux1(goobj.AuxFuncInfo, s.Func.FuncInfoSym) - // initialize abstract function symbol now. This is done here so - // as to avoid data races later on during the parallel portion of - // the back end. - absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix) - absfn.Set(AttrDuplicateOK, true) - absfn.Type = objabi.SDWARFABSFCN - ft.ctxt.Data = append(ft.ctxt.Data, absfn) + for _, d := range s.Func.Pcln.Funcdata { + w.aux1(goobj.AuxFuncdata, d) + } - // In the case of "late" inlining (inlines that happen during - // wrapper generation as opposed to the main inlining phase) it's - // possible that we didn't cache the abstract function sym for the - // text symbol -- do so now if needed. See issue 38068. - if s.Func != nil && s.Func.dwarfAbsFnSym == nil { - s.Func.dwarfAbsFnSym = absfn + if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { + w.aux1(goobj.AuxDwarfInfo, s.Func.dwarfInfoSym) + } + if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { + w.aux1(goobj.AuxDwarfLoc, s.Func.dwarfLocSym) + } + if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { + w.aux1(goobj.AuxDwarfRanges, s.Func.dwarfRangesSym) + } + if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { + w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym) + } } +} - ft.precursor[s] = fnState{precursor: fn, absfn: absfn} +// Emits flags of referenced indexed symbols. +func (w *writer) refFlags() { + seen := make(map[*LSym]bool) + w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs + switch rs.PkgIdx { + case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference + return + case goobj.PkgIdxInvalid: + panic("unindexed symbol reference") + } + if seen[rs] { + return + } + seen[rs] = true + symref := makeSymRef(rs) + flag2 := uint8(0) + if rs.UsedInIface() { + flag2 |= goobj.SymFlagUsedInIface + } + if flag2 == 0 { + return // no need to write zero flags + } + var o goobj.RefFlags + o.SetSym(symref) + o.SetFlag2(flag2) + o.Write(w.Writer) + }) } -// Make a note of a child DIE reference: relocation 'ridx' within symbol 's' -// is targeting child 'c' of DIE with symbol 'tgt'. -func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) { - // Protect against concurrent access if multiple backend workers - ft.mu.Lock() - defer ft.mu.Unlock() - - // Create entry for symbol if not already present. - idx, found := ft.symtab[tgt] - if !found { - ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)}) - idx = len(ft.svec) - 1 - ft.symtab[tgt] = idx - } - - // Do we have child DIE offsets available? If so, then apply them, - // otherwise create a fixup record. - sf := &ft.svec[idx] - if len(sf.doffsets) > 0 { - found := false - for _, do := range sf.doffsets { - if do.dclIdx == int32(dclidx) { - off := do.offset - s.R[ridx].Add += int64(off) - found = true - break - } +// Emits names of referenced indexed symbols, used by tools (objdump, nm) +// only. +func (w *writer) refNames() { + seen := make(map[*LSym]bool) + w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs + switch rs.PkgIdx { + case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference + return + case goobj.PkgIdxInvalid: + panic("unindexed symbol reference") } - if !found { - ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt) + if seen[rs] { + return + } + seen[rs] = true + symref := makeSymRef(rs) + var o goobj.RefName + o.SetSym(symref) + o.SetName(rs.Name, w.Writer) + o.Write(w.Writer) + }) + // TODO: output in sorted order? + // Currently tools (cmd/internal/goobj package) doesn't use mmap, + // and it just read it into a map in memory upfront. If it uses + // mmap, if the output is sorted, it probably could avoid reading + // into memory and just do lookups in the mmap'd object file. +} + +// return the number of aux symbols s have. +func nAuxSym(s *LSym) int { + n := 0 + if s.Gotype != nil { + n++ + } + if s.Func != nil { + // FuncInfo is an aux symbol, each Funcdata is an aux symbol + n += 1 + len(s.Func.Pcln.Funcdata) + if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { + n++ + } + if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { + n++ + } + if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { + n++ + } + if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { + n++ } - } else { - sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)}) } + return n } -// Called once DWARF generation is complete for a given abstract function, -// whose children might have been referenced via a call above. Stores -// the offsets for any child DIEs (vars, params) so that they can be -// consumed later in on DwarfFixupTable.Finalize, which applies any -// outstanding fixups. -func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) { - // Length of these two slices should agree - if len(vars) != len(coffsets) { - ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch") - return - } +// generate symbols for FuncInfo. +func genFuncInfoSyms(ctxt *Link) { + infosyms := make([]*LSym, 0, len(ctxt.Text)) + var pcdataoff uint32 + var b bytes.Buffer + symidx := int32(len(ctxt.defs)) + for _, s := range ctxt.Text { + if s.Func == nil { + continue + } + o := goobj.FuncInfo{ + Args: uint32(s.Func.Args), + Locals: uint32(s.Func.Locals), + FuncID: objabi.FuncID(s.Func.FuncID), + } + pc := &s.Func.Pcln + o.Pcsp = pcdataoff + pcdataoff += uint32(len(pc.Pcsp.P)) + o.Pcfile = pcdataoff + pcdataoff += uint32(len(pc.Pcfile.P)) + o.Pcline = pcdataoff + pcdataoff += uint32(len(pc.Pcline.P)) + o.Pcinline = pcdataoff + pcdataoff += uint32(len(pc.Pcinline.P)) + o.Pcdata = make([]uint32, len(pc.Pcdata)) + for i, pcd := range pc.Pcdata { + o.Pcdata[i] = pcdataoff + pcdataoff += uint32(len(pcd.P)) + } + o.PcdataEnd = pcdataoff + o.Funcdataoff = make([]uint32, len(pc.Funcdataoff)) + for i, x := range pc.Funcdataoff { + o.Funcdataoff[i] = uint32(x) + } + i := 0 + o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles)) + for f := range pc.UsedFiles { + o.File[i] = f + i++ + } + sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] }) + o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes)) + for i, inl := range pc.InlTree.nodes { + f, l := getFileIndexAndLine(ctxt, inl.Pos) + o.InlTree[i] = goobj.InlTreeNode{ + Parent: int32(inl.Parent), + File: goobj.CUFileIndex(f), + Line: l, + Func: makeSymRef(inl.Func), + ParentPC: inl.ParentPC, + } + } - // Generate the slice of declOffset's based in vars/coffsets - doffsets := make([]declOffset, len(coffsets)) - for i := range coffsets { - doffsets[i].dclIdx = vars[i].ChildIndex - doffsets[i].offset = coffsets[i] + o.Write(&b) + isym := &LSym{ + Type: objabi.SDATA, // for now, I don't think it matters + PkgIdx: goobj.PkgIdxSelf, + SymIdx: symidx, + P: append([]byte(nil), b.Bytes()...), + } + isym.Set(AttrIndexed, true) + symidx++ + infosyms = append(infosyms, isym) + s.Func.FuncInfoSym = isym + b.Reset() + + dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym} + for _, s := range dwsyms { + if s == nil || s.Size == 0 { + continue + } + s.PkgIdx = goobj.PkgIdxSelf + s.SymIdx = symidx + s.Set(AttrIndexed, true) + symidx++ + infosyms = append(infosyms, s) + } } + ctxt.defs = append(ctxt.defs, infosyms...) +} - ft.mu.Lock() - defer ft.mu.Unlock() - - // Store offsets for this symbol. - idx, found := ft.symtab[s] - if !found { - sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets} - ft.svec = append(ft.svec, sf) - ft.symtab[s] = len(ft.svec) - 1 - } else { - sf := &ft.svec[idx] - sf.doffsets = doffsets - sf.defseen = true +// debugDumpAux is a dumper for selected aux symbols. +func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) { + // Most aux symbols (ex: funcdata) are not interesting-- + // pick out just the DWARF ones for now. + if aux.Type != objabi.SDWARFLOC && + aux.Type != objabi.SDWARFFCN && + aux.Type != objabi.SDWARFABSFCN && + aux.Type != objabi.SDWARFLINES && + aux.Type != objabi.SDWARFRANGE { + return } + ctxt.writeSymDebugNamed(aux, "aux for "+par.Name) } -func (ft *DwarfFixupTable) processFixups(slot int, s *LSym) { - sf := &ft.svec[slot] - for _, f := range sf.fixups { - dfound := false - for _, doffset := range sf.doffsets { - if doffset.dclIdx == f.dclidx { - f.refsym.R[f.relidx].Add += int64(doffset.offset) - dfound = true - break +func debugAsmEmit(ctxt *Link) { + if ctxt.Debugasm > 0 { + ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug) + if ctxt.Debugasm > 1 { + fn := func(par *LSym, aux *LSym) { + writeAuxSymDebug(ctxt, par, aux) } - } - if !dfound { - ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx) + ctxt.traverseAuxSyms(traverseAux, fn) } } } -// return the LSym corresponding to the 'abstract subprogram' DWARF -// info entry for a function. -func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym { - // Protect against concurrent access if multiple backend workers - ft.mu.Lock() - defer ft.mu.Unlock() - - if fnstate, found := ft.precursor[fnsym]; found { - return fnstate.absfn - } - ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym) - return nil +func (ctxt *Link) writeSymDebug(s *LSym) { + ctxt.writeSymDebugNamed(s, s.Name) } -// Called after all functions have been compiled; the main job of this -// function is to identify cases where there are outstanding fixups. -// This scenario crops up when we have references to variables of an -// inlined routine, but that routine is defined in some other package. -// This helper walks through and locate these fixups, then invokes a -// helper to create an abstract subprogram DIE for each one. -func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) { - if trace { - ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath) +func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { + fmt.Fprintf(ctxt.Bso, "%s ", name) + if s.Type != 0 { + fmt.Fprintf(ctxt.Bso, "%v ", s.Type) } - - // Collect up the keys from the precursor map, then sort the - // resulting list (don't want to rely on map ordering here). - fns := make([]*LSym, len(ft.precursor)) - idx := 0 - for fn := range ft.precursor { - fns[idx] = fn - idx++ + if s.Static() { + fmt.Fprint(ctxt.Bso, "static ") } - sort.Sort(BySymName(fns)) - - // Should not be called during parallel portion of compilation. - if ft.ctxt.InParallel { - ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend") + if s.DuplicateOK() { + fmt.Fprintf(ctxt.Bso, "dupok ") } - - // Generate any missing abstract functions. - for _, s := range fns { - absfn := ft.AbsFuncDwarfSym(s) - slot, found := ft.symtab[absfn] - if !found || !ft.svec[slot].defseen { - ft.ctxt.GenAbstractFunc(s) + if s.CFunc() { + fmt.Fprintf(ctxt.Bso, "cfunc ") + } + if s.NoSplit() { + fmt.Fprintf(ctxt.Bso, "nosplit ") + } + if s.TopFrame() { + fmt.Fprintf(ctxt.Bso, "topframe ") + } + fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) + if s.Type == objabi.STEXT { + fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(s.Func.Args), uint64(s.Func.Locals), uint64(s.Func.FuncID)) + if s.Leaf() { + fmt.Fprintf(ctxt.Bso, " leaf") + } + } + fmt.Fprintf(ctxt.Bso, "\n") + if s.Type == objabi.STEXT { + for p := s.Func.Text; p != nil; p = p.Link { + fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc))) + if ctxt.Debugasm > 1 { + io.WriteString(ctxt.Bso, p.String()) + } else { + p.InnermostString(ctxt.Bso) + } + fmt.Fprintln(ctxt.Bso) + } + } + for i := 0; i < len(s.P); i += 16 { + fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) + j := i + for ; j < i+16 && j < len(s.P); j++ { + fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) } + for ; j < i+16; j++ { + fmt.Fprintf(ctxt.Bso, " ") + } + fmt.Fprintf(ctxt.Bso, " ") + for j = i; j < i+16 && j < len(s.P); j++ { + c := int(s.P[j]) + b := byte('.') + if ' ' <= c && c <= 0x7e { + b = byte(c) + } + ctxt.Bso.WriteByte(b) + } + + fmt.Fprintf(ctxt.Bso, "\n") } - // Apply fixups. - for _, s := range fns { - absfn := ft.AbsFuncDwarfSym(s) - slot, found := ft.symtab[absfn] - if !found { - ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s) + sort.Sort(relocByOff(s.R)) // generate stable output + for _, r := range s.R { + name := "" + if r.Sym != nil { + name = r.Sym.Name + } else if r.Type == objabi.R_TLS_LE { + name = "TLS" + } + if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add)) } else { - ft.processFixups(slot, s) + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add) } } } -type BySymName []*LSym +// relocByOff sorts relocations by their offsets. +type relocByOff []Reloc -func (s BySymName) Len() int { return len(s) } -func (s BySymName) Less(i, j int) bool { return s[i].Name < s[j].Name } -func (s BySymName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (x relocByOff) Len() int { return len(x) } +func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off } +func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go deleted file mode 100644 index f9720f0e57..0000000000 --- a/src/cmd/internal/obj/objfile2.go +++ /dev/null @@ -1,658 +0,0 @@ -// 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. - -// Writing Go object files. - -package obj - -import ( - "bytes" - "cmd/internal/bio" - "cmd/internal/goobj" - "cmd/internal/objabi" - "crypto/sha1" - "encoding/binary" - "fmt" - "io" - "path/filepath" - "sort" - "strings" -) - -// Entry point of writing new object file. -func WriteObjFile(ctxt *Link, b *bio.Writer) { - - debugAsmEmit(ctxt) - - genFuncInfoSyms(ctxt) - - w := writer{ - Writer: goobj.NewWriter(b), - ctxt: ctxt, - pkgpath: objabi.PathToPrefix(ctxt.Pkgpath), - } - - start := b.Offset() - w.init() - - // Header - // We just reserve the space. We'll fill in the offsets later. - flags := uint32(0) - if ctxt.Flag_shared { - flags |= goobj.ObjFlagShared - } - if w.pkgpath == "" { - flags |= goobj.ObjFlagNeedNameExpansion - } - if ctxt.IsAsm { - flags |= goobj.ObjFlagFromAssembly - } - h := goobj.Header{ - Magic: goobj.Magic, - Fingerprint: ctxt.Fingerprint, - Flags: flags, - } - h.Write(w.Writer) - - // String table - w.StringTable() - - // Autolib - h.Offsets[goobj.BlkAutolib] = w.Offset() - for i := range ctxt.Imports { - ctxt.Imports[i].Write(w.Writer) - } - - // Package references - h.Offsets[goobj.BlkPkgIdx] = w.Offset() - for _, pkg := range w.pkglist { - w.StringRef(pkg) - } - - // File table (for DWARF and pcln generation). - h.Offsets[goobj.BlkFile] = w.Offset() - for _, f := range ctxt.PosTable.FileTable() { - w.StringRef(filepath.ToSlash(f)) - } - - // Symbol definitions - h.Offsets[goobj.BlkSymdef] = w.Offset() - for _, s := range ctxt.defs { - w.Sym(s) - } - - // Short hashed symbol definitions - h.Offsets[goobj.BlkHashed64def] = w.Offset() - for _, s := range ctxt.hashed64defs { - w.Sym(s) - } - - // Hashed symbol definitions - h.Offsets[goobj.BlkHasheddef] = w.Offset() - for _, s := range ctxt.hasheddefs { - w.Sym(s) - } - - // Non-pkg symbol definitions - h.Offsets[goobj.BlkNonpkgdef] = w.Offset() - for _, s := range ctxt.nonpkgdefs { - w.Sym(s) - } - - // Non-pkg symbol references - h.Offsets[goobj.BlkNonpkgref] = w.Offset() - for _, s := range ctxt.nonpkgrefs { - w.Sym(s) - } - - // Referenced package symbol flags - h.Offsets[goobj.BlkRefFlags] = w.Offset() - w.refFlags() - - // Hashes - h.Offsets[goobj.BlkHash64] = w.Offset() - for _, s := range ctxt.hashed64defs { - w.Hash64(s) - } - h.Offsets[goobj.BlkHash] = w.Offset() - for _, s := range ctxt.hasheddefs { - w.Hash(s) - } - // TODO: hashedrefs unused/unsupported for now - - // Reloc indexes - h.Offsets[goobj.BlkRelocIdx] = w.Offset() - nreloc := uint32(0) - lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs} - for _, list := range lists { - for _, s := range list { - w.Uint32(nreloc) - nreloc += uint32(len(s.R)) - } - } - w.Uint32(nreloc) - - // Symbol Info indexes - h.Offsets[goobj.BlkAuxIdx] = w.Offset() - naux := uint32(0) - for _, list := range lists { - for _, s := range list { - w.Uint32(naux) - naux += uint32(nAuxSym(s)) - } - } - w.Uint32(naux) - - // Data indexes - h.Offsets[goobj.BlkDataIdx] = w.Offset() - dataOff := uint32(0) - for _, list := range lists { - for _, s := range list { - w.Uint32(dataOff) - dataOff += uint32(len(s.P)) - } - } - w.Uint32(dataOff) - - // Relocs - h.Offsets[goobj.BlkReloc] = w.Offset() - for _, list := range lists { - for _, s := range list { - for i := range s.R { - w.Reloc(&s.R[i]) - } - } - } - - // Aux symbol info - h.Offsets[goobj.BlkAux] = w.Offset() - for _, list := range lists { - for _, s := range list { - w.Aux(s) - } - } - - // Data - h.Offsets[goobj.BlkData] = w.Offset() - for _, list := range lists { - for _, s := range list { - w.Bytes(s.P) - } - } - - // Pcdata - h.Offsets[goobj.BlkPcdata] = w.Offset() - for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms - if s.Func != nil { - pc := &s.Func.Pcln - w.Bytes(pc.Pcsp.P) - w.Bytes(pc.Pcfile.P) - w.Bytes(pc.Pcline.P) - w.Bytes(pc.Pcinline.P) - for i := range pc.Pcdata { - w.Bytes(pc.Pcdata[i].P) - } - } - } - - // Blocks used only by tools (objdump, nm). - - // Referenced symbol names from other packages - h.Offsets[goobj.BlkRefName] = w.Offset() - w.refNames() - - h.Offsets[goobj.BlkEnd] = w.Offset() - - // Fix up block offsets in the header - end := start + int64(w.Offset()) - b.MustSeek(start, 0) - h.Write(w.Writer) - b.MustSeek(end, 0) -} - -type writer struct { - *goobj.Writer - ctxt *Link - pkgpath string // the package import path (escaped), "" if unknown - pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx -} - -// prepare package index list -func (w *writer) init() { - w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1) - w.pkglist[0] = "" // dummy invalid package for index 0 - for pkg, i := range w.ctxt.pkgIdx { - w.pkglist[i] = pkg - } -} - -func (w *writer) StringTable() { - w.AddString("") - for _, p := range w.ctxt.Imports { - w.AddString(p.Pkg) - } - for _, pkg := range w.pkglist { - w.AddString(pkg) - } - w.ctxt.traverseSyms(traverseAll, func(s *LSym) { - // TODO: this includes references of indexed symbols from other packages, - // for which the linker doesn't need the name. Consider moving them to - // a separate block (for tools only). - if w.pkgpath != "" { - s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1) - } - // Don't put names of builtins into the string table (to save - // space). - if s.PkgIdx == goobj.PkgIdxBuiltin { - return - } - w.AddString(s.Name) - }) - - // All filenames are in the postable. - for _, f := range w.ctxt.PosTable.FileTable() { - w.AddString(filepath.ToSlash(f)) - } -} - -func (w *writer) Sym(s *LSym) { - abi := uint16(s.ABI()) - if s.Static() { - abi = goobj.SymABIstatic - } - flag := uint8(0) - if s.DuplicateOK() { - flag |= goobj.SymFlagDupok - } - if s.Local() { - flag |= goobj.SymFlagLocal - } - if s.MakeTypelink() { - flag |= goobj.SymFlagTypelink - } - if s.Leaf() { - flag |= goobj.SymFlagLeaf - } - if s.NoSplit() { - flag |= goobj.SymFlagNoSplit - } - if s.ReflectMethod() { - flag |= goobj.SymFlagReflectMethod - } - if s.TopFrame() { - flag |= goobj.SymFlagTopFrame - } - if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA { - flag |= goobj.SymFlagGoType - } - flag2 := uint8(0) - if s.UsedInIface() { - flag2 |= goobj.SymFlagUsedInIface - } - if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA { - flag2 |= goobj.SymFlagItab - } - name := s.Name - if strings.HasPrefix(name, "gofile..") { - name = filepath.ToSlash(name) - } - var align uint32 - if s.Func != nil { - align = uint32(s.Func.Align) - } - if s.ContentAddressable() { - // We generally assume data symbols are natually aligned, - // except for strings. If we dedup a string symbol and a - // non-string symbol with the same content, we should keep - // the largest alignment. - // TODO: maybe the compiler could set the alignment for all - // data symbols more carefully. - if s.Size != 0 && !strings.HasPrefix(s.Name, "go.string.") { - switch { - case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0: - align = 8 - case s.Size%4 == 0: - align = 4 - case s.Size%2 == 0: - align = 2 - } - // don't bother setting align to 1. - } - } - var o goobj.Sym - o.SetName(name, w.Writer) - o.SetABI(abi) - o.SetType(uint8(s.Type)) - o.SetFlag(flag) - o.SetFlag2(flag2) - o.SetSiz(uint32(s.Size)) - o.SetAlign(align) - o.Write(w.Writer) -} - -func (w *writer) Hash64(s *LSym) { - if !s.ContentAddressable() || len(s.R) != 0 { - panic("Hash of non-content-addresable symbol") - } - b := contentHash64(s) - w.Bytes(b[:]) -} - -func (w *writer) Hash(s *LSym) { - if !s.ContentAddressable() { - panic("Hash of non-content-addresable symbol") - } - b := w.contentHash(s) - w.Bytes(b[:]) -} - -func contentHash64(s *LSym) goobj.Hash64Type { - var b goobj.Hash64Type - copy(b[:], s.P) - return b -} - -// Compute the content hash for a content-addressable symbol. -// We build a content hash based on its content and relocations. -// Depending on the category of the referenced symbol, we choose -// different hash algorithms such that the hash is globally -// consistent. -// - For referenced content-addressable symbol, its content hash -// is globally consistent. -// - For package symbol and builtin symbol, its local index is -// globally consistent. -// - For non-package symbol, its fully-expanded name is globally -// consistent. For now, we require we know the current package -// path so we can always expand symbol names. (Otherwise, -// symbols with relocations are not considered hashable.) -// -// For now, we assume there is no circular dependencies among -// hashed symbols. -func (w *writer) contentHash(s *LSym) goobj.HashType { - h := sha1.New() - // The compiler trims trailing zeros _sometimes_. We just do - // it always. - h.Write(bytes.TrimRight(s.P, "\x00")) - var tmp [14]byte - for i := range s.R { - r := &s.R[i] - binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off)) - tmp[4] = r.Siz - tmp[5] = uint8(r.Type) - binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add)) - h.Write(tmp[:]) - rs := r.Sym - switch rs.PkgIdx { - case goobj.PkgIdxHashed64: - h.Write([]byte{0}) - t := contentHash64(rs) - h.Write(t[:]) - case goobj.PkgIdxHashed: - h.Write([]byte{1}) - t := w.contentHash(rs) - h.Write(t[:]) - case goobj.PkgIdxNone: - h.Write([]byte{2}) - io.WriteString(h, rs.Name) // name is already expanded at this point - case goobj.PkgIdxBuiltin: - h.Write([]byte{3}) - binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) - h.Write(tmp[:4]) - case goobj.PkgIdxSelf: - io.WriteString(h, w.pkgpath) - binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) - h.Write(tmp[:4]) - default: - io.WriteString(h, rs.Pkg) - binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) - h.Write(tmp[:4]) - } - } - var b goobj.HashType - copy(b[:], h.Sum(nil)) - return b -} - -func makeSymRef(s *LSym) goobj.SymRef { - if s == nil { - return goobj.SymRef{} - } - if s.PkgIdx == 0 || !s.Indexed() { - fmt.Printf("unindexed symbol reference: %v\n", s) - panic("unindexed symbol reference") - } - return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)} -} - -func (w *writer) Reloc(r *Reloc) { - var o goobj.Reloc - o.SetOff(r.Off) - o.SetSiz(r.Siz) - o.SetType(uint8(r.Type)) - o.SetAdd(r.Add) - o.SetSym(makeSymRef(r.Sym)) - o.Write(w.Writer) -} - -func (w *writer) aux1(typ uint8, rs *LSym) { - var o goobj.Aux - o.SetType(typ) - o.SetSym(makeSymRef(rs)) - o.Write(w.Writer) -} - -func (w *writer) Aux(s *LSym) { - if s.Gotype != nil { - w.aux1(goobj.AuxGotype, s.Gotype) - } - if s.Func != nil { - w.aux1(goobj.AuxFuncInfo, s.Func.FuncInfoSym) - - for _, d := range s.Func.Pcln.Funcdata { - w.aux1(goobj.AuxFuncdata, d) - } - - if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { - w.aux1(goobj.AuxDwarfInfo, s.Func.dwarfInfoSym) - } - if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { - w.aux1(goobj.AuxDwarfLoc, s.Func.dwarfLocSym) - } - if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { - w.aux1(goobj.AuxDwarfRanges, s.Func.dwarfRangesSym) - } - if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { - w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym) - } - } -} - -// Emits flags of referenced indexed symbols. -func (w *writer) refFlags() { - seen := make(map[*LSym]bool) - w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs - switch rs.PkgIdx { - case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference - return - case goobj.PkgIdxInvalid: - panic("unindexed symbol reference") - } - if seen[rs] { - return - } - seen[rs] = true - symref := makeSymRef(rs) - flag2 := uint8(0) - if rs.UsedInIface() { - flag2 |= goobj.SymFlagUsedInIface - } - if flag2 == 0 { - return // no need to write zero flags - } - var o goobj.RefFlags - o.SetSym(symref) - o.SetFlag2(flag2) - o.Write(w.Writer) - }) -} - -// Emits names of referenced indexed symbols, used by tools (objdump, nm) -// only. -func (w *writer) refNames() { - seen := make(map[*LSym]bool) - w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs - switch rs.PkgIdx { - case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference - return - case goobj.PkgIdxInvalid: - panic("unindexed symbol reference") - } - if seen[rs] { - return - } - seen[rs] = true - symref := makeSymRef(rs) - var o goobj.RefName - o.SetSym(symref) - o.SetName(rs.Name, w.Writer) - o.Write(w.Writer) - }) - // TODO: output in sorted order? - // Currently tools (cmd/internal/goobj package) doesn't use mmap, - // and it just read it into a map in memory upfront. If it uses - // mmap, if the output is sorted, it probably could avoid reading - // into memory and just do lookups in the mmap'd object file. -} - -// return the number of aux symbols s have. -func nAuxSym(s *LSym) int { - n := 0 - if s.Gotype != nil { - n++ - } - if s.Func != nil { - // FuncInfo is an aux symbol, each Funcdata is an aux symbol - n += 1 + len(s.Func.Pcln.Funcdata) - if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { - n++ - } - if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { - n++ - } - if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { - n++ - } - if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { - n++ - } - } - return n -} - -// generate symbols for FuncInfo. -func genFuncInfoSyms(ctxt *Link) { - infosyms := make([]*LSym, 0, len(ctxt.Text)) - var pcdataoff uint32 - var b bytes.Buffer - symidx := int32(len(ctxt.defs)) - for _, s := range ctxt.Text { - if s.Func == nil { - continue - } - o := goobj.FuncInfo{ - Args: uint32(s.Func.Args), - Locals: uint32(s.Func.Locals), - FuncID: objabi.FuncID(s.Func.FuncID), - } - pc := &s.Func.Pcln - o.Pcsp = pcdataoff - pcdataoff += uint32(len(pc.Pcsp.P)) - o.Pcfile = pcdataoff - pcdataoff += uint32(len(pc.Pcfile.P)) - o.Pcline = pcdataoff - pcdataoff += uint32(len(pc.Pcline.P)) - o.Pcinline = pcdataoff - pcdataoff += uint32(len(pc.Pcinline.P)) - o.Pcdata = make([]uint32, len(pc.Pcdata)) - for i, pcd := range pc.Pcdata { - o.Pcdata[i] = pcdataoff - pcdataoff += uint32(len(pcd.P)) - } - o.PcdataEnd = pcdataoff - o.Funcdataoff = make([]uint32, len(pc.Funcdataoff)) - for i, x := range pc.Funcdataoff { - o.Funcdataoff[i] = uint32(x) - } - i := 0 - o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles)) - for f := range pc.UsedFiles { - o.File[i] = f - i++ - } - sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] }) - o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes)) - for i, inl := range pc.InlTree.nodes { - f, l := getFileIndexAndLine(ctxt, inl.Pos) - o.InlTree[i] = goobj.InlTreeNode{ - Parent: int32(inl.Parent), - File: goobj.CUFileIndex(f), - Line: l, - Func: makeSymRef(inl.Func), - ParentPC: inl.ParentPC, - } - } - - o.Write(&b) - isym := &LSym{ - Type: objabi.SDATA, // for now, I don't think it matters - PkgIdx: goobj.PkgIdxSelf, - SymIdx: symidx, - P: append([]byte(nil), b.Bytes()...), - } - isym.Set(AttrIndexed, true) - symidx++ - infosyms = append(infosyms, isym) - s.Func.FuncInfoSym = isym - b.Reset() - - dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym} - for _, s := range dwsyms { - if s == nil || s.Size == 0 { - continue - } - s.PkgIdx = goobj.PkgIdxSelf - s.SymIdx = symidx - s.Set(AttrIndexed, true) - symidx++ - infosyms = append(infosyms, s) - } - } - ctxt.defs = append(ctxt.defs, infosyms...) -} - -// debugDumpAux is a dumper for selected aux symbols. -func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) { - // Most aux symbols (ex: funcdata) are not interesting-- - // pick out just the DWARF ones for now. - if aux.Type != objabi.SDWARFLOC && - aux.Type != objabi.SDWARFFCN && - aux.Type != objabi.SDWARFABSFCN && - aux.Type != objabi.SDWARFLINES && - aux.Type != objabi.SDWARFRANGE { - return - } - ctxt.writeSymDebugNamed(aux, "aux for "+par.Name) -} - -func debugAsmEmit(ctxt *Link) { - if ctxt.Debugasm > 0 { - ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug) - if ctxt.Debugasm > 1 { - fn := func(par *LSym, aux *LSym) { - writeAuxSymDebug(ctxt, par, aux) - } - ctxt.traverseAuxSyms(traverseAux, fn) - } - } -}