]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/internal/obj: combine objfile.go and objfile2.go
authorCherry Zhang <cherryyz@google.com>
Tue, 11 Aug 2020 11:24:52 +0000 (07:24 -0400)
committerCherry Zhang <cherryyz@google.com>
Tue, 11 Aug 2020 21:10:24 +0000 (21:10 +0000)
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 <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
src/cmd/internal/obj/dwarf.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/objfile2.go [deleted file]

index 724aea2f8f53f16ffd7a79e22f90f7629cfa17fd..9abb31b558e7af0644b3353de9b6263341d7100f 100644 (file)
@@ -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] }
index 2f28b6eeecebbd5b594ad690067f56592e810844..7bc4f4992e93a06daa5194a69ba6534dcd674a83 100644 (file)
 // 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 (file)
index f9720f0..0000000
+++ /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)
-               }
-       }
-}