[dev.link] all: merge branch 'master' into dev.link

Clean merge.

Change-Id: I2ae070c60c011779a0f0a1344f5b6d45ef10d8a1
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index fad87b2..6aefc96 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -23,7 +23,7 @@
 	Dynlink    = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries")
 	AllErrors  = flag.Bool("e", false, "no limit on number of errors reported")
 	SymABIs    = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble")
-	Newobj     = flag.Bool("newobj", false, "use new object file format")
+	Importpath = flag.String("p", "", "set expected package import to path")
 )
 
 var (
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 6b0a609..d42093a 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -40,7 +40,6 @@
 	}
 	ctxt.Flag_dynlink = *flags.Dynlink
 	ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
-	ctxt.Flag_newobj = *flags.Newobj
 	ctxt.Bso = bufio.NewWriter(os.Stdout)
 	defer ctxt.Bso.Flush()
 
@@ -74,7 +73,7 @@
 			pList.Firstpc, ok = parser.Parse()
 			// reports errors to parser.Errorf
 			if ok {
-				obj.Flushplist(ctxt, pList, nil, "")
+				obj.Flushplist(ctxt, pList, nil, *flags.Importpath)
 			}
 		}
 		if !ok {
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index 7ee0876..a3baa24 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -991,18 +991,16 @@
 }
 
 func (w *exportWriter) symIdx(s *types.Sym) {
-	if Ctxt.Flag_newobj {
-		lsym := s.Linksym()
-		if lsym.PkgIdx > goobj2.PkgIdxSelf || (lsym.PkgIdx == goobj2.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
-			// Don't export index for non-package symbols, linkname'd symbols,
-			// and symbols without an index. They can only be referenced by
-			// name.
-			w.int64(-1)
-		} else {
-			// For a defined symbol, export its index.
-			// For re-exporting an imported symbol, pass its index through.
-			w.int64(int64(lsym.SymIdx))
-		}
+	lsym := s.Linksym()
+	if lsym.PkgIdx > goobj2.PkgIdxSelf || (lsym.PkgIdx == goobj2.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
+		// Don't export index for non-package symbols, linkname'd symbols,
+		// and symbols without an index. They can only be referenced by
+		// name.
+		w.int64(-1)
+	} else {
+		// For a defined symbol, export its index.
+		// For re-exporting an imported symbol, pass its index through.
+		w.int64(int64(lsym.SymIdx))
 	}
 }
 
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index f99b70a..dfcaa56 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -687,16 +687,14 @@
 }
 
 func (r *importReader) symIdx(s *types.Sym) {
-	if Ctxt.Flag_newobj {
-		lsym := s.Linksym()
-		idx := int32(r.int64())
-		if idx != -1 {
-			if s.Linkname != "" {
-				Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
-			}
-			lsym.SymIdx = idx
-			lsym.Set(obj.AttrIndexed, true)
+	lsym := s.Linksym()
+	idx := int32(r.int64())
+	if idx != -1 {
+		if s.Linkname != "" {
+			Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
 		}
+		lsym.SymIdx = idx
+		lsym.Set(obj.AttrIndexed, true)
 	}
 }
 
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 18a210b..745973e 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -277,7 +277,6 @@
 	flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
 	flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
 	flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
-	flag.BoolVar(&Ctxt.Flag_newobj, "newobj", false, "use new object file format")
 	flag.StringVar(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging")
 
 	objabi.Flagparse(usage)
@@ -285,7 +284,7 @@
 	// Record flags that affect the build result. (And don't
 	// record flags that don't, since that would cause spurious
 	// changes in the binary.)
-	recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "newobj")
+	recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes")
 
 	if smallFrames {
 		maxStackVarSize = 128 * 1024
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index a07e64b..5ec2381 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -73,6 +73,7 @@
 	"cmd/link/internal/amd64",
 	"cmd/link/internal/arm",
 	"cmd/link/internal/arm64",
+	"cmd/link/internal/benchmark",
 	"cmd/link/internal/ld",
 	"cmd/link/internal/loadelf",
 	"cmd/link/internal/loader",
@@ -81,7 +82,6 @@
 	"cmd/link/internal/loadxcoff",
 	"cmd/link/internal/mips",
 	"cmd/link/internal/mips64",
-	"cmd/link/internal/objfile",
 	"cmd/link/internal/ppc64",
 	"cmd/link/internal/riscv64",
 	"cmd/link/internal/s390x",
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index 7d17c0c..78db845 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -37,6 +37,17 @@
 	return base.Tool("link")
 }
 
+func pkgPath(a *Action) string {
+	p := a.Package
+	ppath := p.ImportPath
+	if cfg.BuildBuildmode == "plugin" {
+		ppath = pluginPath(a)
+	} else if p.Name == "main" && !p.Internal.ForceLibrary {
+		ppath = "main"
+	}
+	return ppath
+}
+
 func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
 	p := a.Package
 	objdir := a.Objdir
@@ -47,12 +58,7 @@
 		ofile = objdir + out
 	}
 
-	pkgpath := p.ImportPath
-	if cfg.BuildBuildmode == "plugin" {
-		pkgpath = pluginPath(a)
-	} else if p.Name == "main" && !p.Internal.ForceLibrary {
-		pkgpath = "main"
-	}
+	pkgpath := pkgPath(a)
 	gcargs := []string{"-p", pkgpath}
 	if p.Module != nil && p.Module.GoVersion != "" && allowedVersion(p.Module.GoVersion) {
 		gcargs = append(gcargs, "-lang=go"+p.Module.GoVersion)
@@ -240,7 +246,8 @@
 func asmArgs(a *Action, p *load.Package) []interface{} {
 	// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
 	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
-	args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
+	pkgpath := pkgPath(a)
+	args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
 	if p.ImportPath == "runtime" && cfg.Goarch == "386" {
 		for _, arg := range forcedAsmflags {
 			if arg == "-dynlink" {
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index 56b44a1..62036ae 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -21,15 +21,6 @@
 // InfoPrefix is the prefix for all the symbols containing DWARF info entries.
 const InfoPrefix = "go.info."
 
-// RangePrefix is the prefix for all the symbols containing DWARF location lists.
-const LocPrefix = "go.loc."
-
-// RangePrefix is the prefix for all the symbols containing DWARF range lists.
-const RangePrefix = "go.range."
-
-// DebugLinesPrefix is the prefix for all the symbols containing DWARF debug_line information from the compiler.
-const DebugLinesPrefix = "go.debuglines."
-
 // ConstInfoPrefix is the prefix for all symbols containing DWARF info
 // entries that contain constants.
 const ConstInfoPrefix = "go.constinfo."
@@ -48,7 +39,7 @@
 
 // Sym represents a symbol.
 type Sym interface {
-	Len() int64
+	Length(dwarfContext interface{}) int64
 }
 
 // A Var represents a local variable or a function parameter.
@@ -1279,7 +1270,7 @@
 	putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, callee)
 
 	if abbrev == DW_ABRV_INLINED_SUBROUTINE_RANGES {
-		putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Len(), s.Ranges)
+		putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
 		s.PutRanges(ctxt, ic.Ranges)
 	} else {
 		st := ic.Ranges[0].Start
@@ -1440,7 +1431,7 @@
 			putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, s.StartPC)
 		} else {
 			Uleb128put(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES)
-			putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Len(), s.Ranges)
+			putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
 
 			s.PutRanges(ctxt, scope.Ranges)
 		}
@@ -1585,7 +1576,7 @@
 	}
 
 	if abbrevUsesLoclist(abbrev) {
-		putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Len(), s.Loc)
+		putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Length(ctxt), s.Loc)
 		v.PutLocationList(s.Loc, s.StartPC)
 	} else {
 		loc := encbuf[:0]
diff --git a/src/cmd/internal/goobj/readnew.go b/src/cmd/internal/goobj/readnew.go
index 3f9d0d1..e09260f 100644
--- a/src/cmd/internal/goobj/readnew.go
+++ b/src/cmd/internal/goobj/readnew.go
@@ -149,7 +149,7 @@
 		f := &Func{
 			Args:     int64(info.Args),
 			Frame:    int64(info.Locals),
-			NoSplit:  info.NoSplit != 0,
+			NoSplit:  osym.NoSplit(),
 			Leaf:     osym.Leaf(),
 			TopFrame: osym.TopFrame(),
 			PCSP:     Data{int64(pcdataBase + info.Pcsp), int64(info.Pcfile - info.Pcsp)},
diff --git a/src/cmd/internal/goobj2/funcinfo.go b/src/cmd/internal/goobj2/funcinfo.go
index 8620931..053d7ad 100644
--- a/src/cmd/internal/goobj2/funcinfo.go
+++ b/src/cmd/internal/goobj2/funcinfo.go
@@ -14,8 +14,6 @@
 //
 // TODO: make each pcdata a separate symbol?
 type FuncInfo struct {
-	NoSplit uint8
-
 	Args   uint32
 	Locals uint32
 
@@ -32,8 +30,6 @@
 }
 
 func (a *FuncInfo) Write(w *bytes.Buffer) {
-	w.WriteByte(a.NoSplit)
-
 	var b [4]byte
 	writeUint32 := func(x uint32) {
 		binary.LittleEndian.PutUint32(b[:], x)
@@ -68,9 +64,6 @@
 }
 
 func (a *FuncInfo) Read(b []byte) {
-	a.NoSplit = b[0]
-	b = b[1:]
-
 	readUint32 := func() uint32 {
 		x := binary.LittleEndian.Uint32(b)
 		b = b[4:]
@@ -107,6 +100,16 @@
 	}
 }
 
+// Accessors reading only some fields.
+// TODO: more accessors.
+
+func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) }
+
+// return start and end offsets.
+func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) {
+	return binary.LittleEndian.Uint32(b[8:]), binary.LittleEndian.Uint32(b[12:])
+}
+
 // InlTreeNode is the serialized form of FileInfo.InlTree.
 type InlTreeNode struct {
 	Parent   int32
diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go
index 4c364b0..d5a9b4a 100644
--- a/src/cmd/internal/goobj2/objfile.go
+++ b/src/cmd/internal/goobj2/objfile.go
@@ -203,7 +203,7 @@
 	SymFlagLocal
 	SymFlagTypelink
 	SymFlagLeaf
-	SymFlagCFunc
+	SymFlagNoSplit
 	SymFlagReflectMethod
 	SymFlagGoType
 	SymFlagTopFrame
@@ -225,6 +225,20 @@
 	s.Siz = r.uint32At(off + 8)
 }
 
+// Read fields other than the symbol name. The name is not necessary
+// in some cases, and most of the time spent in Read is reading the
+// name.
+func (s *Sym) ReadWithoutName(r *Reader, off uint32) {
+	s.ABI = r.uint16At(off + 4)
+	s.Type = r.uint8At(off + 6)
+	s.Flag = r.uint8At(off + 7)
+	s.Siz = r.uint32At(off + 8)
+}
+
+func (s *Sym) ReadFlag(r *Reader, off uint32) {
+	s.Flag = r.uint8At(off + 7)
+}
+
 func (s *Sym) Size() int {
 	return 4 + 2 + 1 + 1 + 4
 }
@@ -233,7 +247,7 @@
 func (s *Sym) Local() bool         { return s.Flag&SymFlagLocal != 0 }
 func (s *Sym) Typelink() bool      { return s.Flag&SymFlagTypelink != 0 }
 func (s *Sym) Leaf() bool          { return s.Flag&SymFlagLeaf != 0 }
-func (s *Sym) CFunc() bool         { return s.Flag&SymFlagCFunc != 0 }
+func (s *Sym) NoSplit() bool       { return s.Flag&SymFlagNoSplit != 0 }
 func (s *Sym) ReflectMethod() bool { return s.Flag&SymFlagReflectMethod != 0 }
 func (s *Sym) IsGoType() bool      { return s.Flag&SymFlagGoType != 0 }
 func (s *Sym) TopFrame() bool      { return s.Flag&SymFlagTopFrame != 0 }
@@ -283,10 +297,39 @@
 	o.Sym.Read(r, off+14)
 }
 
+// Only reads the target symbol and reloc type, leaving other fields unset.
+func (o *Reloc) ReadSymType(r *Reader, off uint32) {
+	o.Type = r.uint8At(off + 5)
+	o.Sym.Read(r, off+14)
+}
+
 func (r *Reloc) Size() int {
 	return 4 + 1 + 1 + 8 + r.Sym.Size()
 }
 
+// XXX experiment with another way of accessing relocations.
+
+const RelocSize = 22 // TODO: is it possible to not hard-code this?
+
+type Reloc2 [RelocSize]byte
+
+func (r *Reloc2) Off() int32  { return int32(binary.LittleEndian.Uint32(r[:])) }
+func (r *Reloc2) Siz() uint8  { return r[4] }
+func (r *Reloc2) Type() uint8 { return r[5] }
+func (r *Reloc2) Add() int64  { return int64(binary.LittleEndian.Uint64(r[6:])) }
+func (r *Reloc2) Sym() SymRef {
+	return SymRef{binary.LittleEndian.Uint32(r[14:]), binary.LittleEndian.Uint32(r[18:])}
+}
+
+func (r *Reloc2) Set(off int32, size uint8, typ uint8, add int64, sym SymRef) {
+	binary.LittleEndian.PutUint32(r[:], uint32(off))
+	r[4] = size
+	r[5] = typ
+	binary.LittleEndian.PutUint64(r[6:], uint64(add))
+	binary.LittleEndian.PutUint32(r[14:], sym.PkgIdx)
+	binary.LittleEndian.PutUint32(r[18:], sym.SymIdx)
+}
+
 // Aux symbol info.
 type Aux struct {
 	Type uint8
@@ -316,6 +359,11 @@
 	a.Sym.Read(r, off+1)
 }
 
+// Only reads the target symbol, leaving other fields unset.
+func (a *Aux) ReadSym(r *Reader, off uint32) {
+	a.Sym.Read(r, off+1)
+}
+
 func (a *Aux) Size() int {
 	return 1 + a.Sym.Size()
 }
@@ -541,6 +589,12 @@
 	return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz)
 }
 
+// Reloc2 returns a pointer to the j-th relocation of the i-th symbol.
+func (r *Reader) Reloc2(i int, j int) *Reloc2 {
+	off := r.RelocOff(i, j)
+	return (*Reloc2)(unsafe.Pointer(&r.b[off]))
+}
+
 // NAux returns the number of aux symbols of the i-th symbol.
 func (r *Reader) NAux(i int) int {
 	auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4)
diff --git a/src/cmd/internal/goobj2/objfile_test.go b/src/cmd/internal/goobj2/objfile_test.go
new file mode 100644
index 0000000..eea97d4
--- /dev/null
+++ b/src/cmd/internal/goobj2/objfile_test.go
@@ -0,0 +1,29 @@
+// Copyright 2020 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.
+
+package goobj2
+
+import (
+	"bufio"
+	"bytes"
+	"cmd/internal/bio"
+	"testing"
+)
+
+func dummyWriter() *Writer {
+	var buf bytes.Buffer
+	wr := &bio.Writer{Writer: bufio.NewWriter(&buf)} // hacky: no file, so cannot seek
+	return NewWriter(wr)
+}
+
+func TestSize(t *testing.T) {
+	// This test checks that hard-coded sizes match the actual sizes
+	// in the object file format.
+	w := dummyWriter()
+	(&Reloc{}).Write(w)
+	off := w.off
+	if sz := uint32(RelocSize); off != sz {
+		t.Errorf("size mismatch: %d bytes written, but size=%d", off, sz)
+	}
+}
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 76c6f26..9701580 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -381,7 +381,6 @@
 	Type objabi.SymKind
 	Attribute
 
-	RefIdx int // Index of this symbol in the symbol reference list.
 	Size   int64
 	Gotype *LSym
 	P      []byte
@@ -391,7 +390,7 @@
 
 	Pkg    string
 	PkgIdx int32
-	SymIdx int32 // TODO: replace RefIdx
+	SymIdx int32
 }
 
 // A FuncInfo contains extra fields for STEXT symbols.
@@ -652,7 +651,6 @@
 	Flag_linkshared    bool
 	Flag_optimize      bool
 	Flag_locationlists bool
-	Flag_newobj        bool // use new object file format
 	Bso                *bufio.Writer
 	Pathname           string
 	hashmu             sync.Mutex       // protects hash, funchash
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 7fd97f7..717d471 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -7,229 +7,25 @@
 package obj
 
 import (
-	"bufio"
 	"cmd/internal/bio"
 	"cmd/internal/dwarf"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"fmt"
-	"log"
-	"path/filepath"
 	"sort"
-	"strings"
 	"sync"
 )
 
-// objWriter writes Go object files.
-type objWriter struct {
-	wr   *bufio.Writer
-	ctxt *Link
-	// Temporary buffer for zigzag int writing.
-	varintbuf [10]uint8
-
-	// Number of objects written of each type.
-	nRefs     int
-	nData     int
-	nReloc    int
-	nPcdata   int
-	nFuncdata int
-	nFile     int
-
-	pkgpath string // the package import path (escaped), "" if unknown
-}
-
-func (w *objWriter) addLengths(s *LSym) {
-	w.nData += len(s.P)
-	w.nReloc += len(s.R)
-
-	if s.Type != objabi.STEXT {
-		return
-	}
-
-	pc := &s.Func.Pcln
-
-	data := 0
-	data += len(pc.Pcsp.P)
-	data += len(pc.Pcfile.P)
-	data += len(pc.Pcline.P)
-	data += len(pc.Pcinline.P)
-	for _, pcd := range pc.Pcdata {
-		data += len(pcd.P)
-	}
-
-	w.nData += data
-	w.nPcdata += len(pc.Pcdata)
-
-	w.nFuncdata += len(pc.Funcdataoff)
-	w.nFile += len(pc.File)
-}
-
-func (w *objWriter) writeLengths() {
-	w.writeInt(int64(w.nData))
-	w.writeInt(int64(w.nReloc))
-	w.writeInt(int64(w.nPcdata))
-	w.writeInt(int64(0)) // TODO: remove at next object file rev
-	w.writeInt(int64(w.nFuncdata))
-	w.writeInt(int64(w.nFile))
-}
-
-func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter {
-	return &objWriter{
-		ctxt:    ctxt,
-		wr:      b,
-		pkgpath: objabi.PathToPrefix(pkgpath),
-	}
-}
-
 func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) {
-	if ctxt.Flag_newobj {
-		WriteObjFile2(ctxt, bout, pkgpath)
-		return
-	}
-
-	b := bout.Writer
-	w := newObjWriter(ctxt, b, pkgpath)
-
-	// Magic header
-	w.wr.WriteString("\x00go114ld")
-
-	// Version
-	w.wr.WriteByte(1)
-
-	// Autolib
-	for _, pkg := range ctxt.Imports {
-		w.writeString(pkg)
-	}
-	w.writeString("")
-
-	// DWARF File Table
-	fileTable := ctxt.PosTable.DebugLinesFileTable()
-	w.writeInt(int64(len(fileTable)))
-	for _, str := range fileTable {
-		w.writeString(filepath.ToSlash(str))
-	}
-
-	// Symbol references
-	for _, s := range ctxt.Text {
-		w.writeRefs(s)
-		w.addLengths(s)
-	}
-
-	if ctxt.Headtype == objabi.Haix {
-		// Data must be sorted to keep a constant order in TOC symbols.
-		// As they are created during Progedit, two symbols can be switched between
-		// two different compilations. Therefore, BuildID will be different.
-		// TODO: find a better place and optimize to only sort TOC symbols
-		sort.Slice(ctxt.Data, func(i, j int) bool {
-			return ctxt.Data[i].Name < ctxt.Data[j].Name
-		})
-	}
-
-	for _, s := range ctxt.Data {
-		w.writeRefs(s)
-		w.addLengths(s)
-	}
-	for _, s := range ctxt.ABIAliases {
-		w.writeRefs(s)
-		w.addLengths(s)
-	}
-	// End symbol references
-	w.wr.WriteByte(0xff)
-
-	// Lengths
-	w.writeLengths()
-
-	// Data block
-	for _, s := range ctxt.Text {
-		w.wr.Write(s.P)
-		pc := &s.Func.Pcln
-		w.wr.Write(pc.Pcsp.P)
-		w.wr.Write(pc.Pcfile.P)
-		w.wr.Write(pc.Pcline.P)
-		w.wr.Write(pc.Pcinline.P)
-		for _, pcd := range pc.Pcdata {
-			w.wr.Write(pcd.P)
-		}
-	}
-	for _, s := range ctxt.Data {
-		if len(s.P) > 0 {
-			switch s.Type {
-			case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
-				ctxt.Diag("cannot provide data for %v sym %v", s.Type, s.Name)
-			}
-		}
-		w.wr.Write(s.P)
-	}
-
-	// Symbols
-	for _, s := range ctxt.Text {
-		w.writeSym(s)
-	}
-	for _, s := range ctxt.Data {
-		w.writeSym(s)
-	}
-	for _, s := range ctxt.ABIAliases {
-		w.writeSym(s)
-	}
-
-	// Magic footer
-	w.wr.WriteString("\xffgo114ld")
-}
-
-// Symbols are prefixed so their content doesn't get confused with the magic footer.
-const symPrefix = 0xfe
-
-func (w *objWriter) writeRef(s *LSym, isPath bool) {
-	if s == nil || s.RefIdx != 0 {
-		return
-	}
-	w.wr.WriteByte(symPrefix)
-	if isPath {
-		w.writeString(filepath.ToSlash(s.Name))
-	} else if w.pkgpath != "" {
-		// w.pkgpath is already escaped.
-		n := strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
-		w.writeString(n)
-	} else {
-		w.writeString(s.Name)
-	}
-	// Write ABI/static information.
-	abi := int64(s.ABI())
-	if s.Static() {
-		abi = -1
-	}
-	w.writeInt(abi)
-	w.nRefs++
-	s.RefIdx = w.nRefs
-}
-
-func (w *objWriter) writeRefs(s *LSym) {
-	w.writeRef(s, false)
-	w.writeRef(s.Gotype, false)
-	for _, r := range s.R {
-		w.writeRef(r.Sym, false)
-	}
-
-	if s.Type == objabi.STEXT {
-		pc := &s.Func.Pcln
-		for _, d := range pc.Funcdata {
-			w.writeRef(d, false)
-		}
-		for _, f := range pc.File {
-			fsym := w.ctxt.Lookup(f)
-			w.writeRef(fsym, true)
-		}
-		for _, call := range pc.InlTree.nodes {
-			w.writeRef(call.Func, false)
-			f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
-			fsym := w.ctxt.Lookup(f)
-			w.writeRef(fsym, true)
-		}
-	}
+	WriteObjFile2(ctxt, bout, pkgpath)
 }
 
 func (ctxt *Link) writeSymDebug(s *LSym) {
-	fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
+	ctxt.writeSymDebugNamed(s, s.Name)
+}
+
+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)
 	}
@@ -305,137 +101,6 @@
 	}
 }
 
-func (w *objWriter) writeSym(s *LSym) {
-	ctxt := w.ctxt
-	if ctxt.Debugasm > 0 {
-		w.ctxt.writeSymDebug(s)
-	}
-
-	w.wr.WriteByte(symPrefix)
-	w.wr.WriteByte(byte(s.Type))
-	w.writeRefIndex(s)
-	flags := int64(0)
-	if s.DuplicateOK() {
-		flags |= 1
-	}
-	if s.Local() {
-		flags |= 1 << 1
-	}
-	if s.MakeTypelink() {
-		flags |= 1 << 2
-	}
-	w.writeInt(flags)
-	w.writeInt(s.Size)
-	w.writeRefIndex(s.Gotype)
-	w.writeInt(int64(len(s.P)))
-
-	w.writeInt(int64(len(s.R)))
-	var r *Reloc
-	for i := range s.R {
-		r = &s.R[i]
-		w.writeInt(int64(r.Off))
-		w.writeInt(int64(r.Siz))
-		w.writeInt(int64(r.Type))
-		w.writeInt(r.Add)
-		w.writeRefIndex(r.Sym)
-	}
-
-	if s.Type != objabi.STEXT {
-		return
-	}
-
-	w.writeInt(int64(s.Func.Args))
-	w.writeInt(int64(s.Func.Locals))
-	w.writeBool(s.NoSplit())
-	flags = int64(0)
-	if s.Leaf() {
-		flags |= 1
-	}
-	if s.CFunc() {
-		flags |= 1 << 1
-	}
-	if s.ReflectMethod() {
-		flags |= 1 << 2
-	}
-	if ctxt.Flag_shared {
-		flags |= 1 << 3
-	}
-	if s.TopFrame() {
-		flags |= 1 << 4
-	}
-	w.writeInt(flags)
-	w.writeInt(int64(0)) // TODO: remove at next object file rev
-
-	pc := &s.Func.Pcln
-	w.writeInt(int64(len(pc.Pcsp.P)))
-	w.writeInt(int64(len(pc.Pcfile.P)))
-	w.writeInt(int64(len(pc.Pcline.P)))
-	w.writeInt(int64(len(pc.Pcinline.P)))
-	w.writeInt(int64(len(pc.Pcdata)))
-	for _, pcd := range pc.Pcdata {
-		w.writeInt(int64(len(pcd.P)))
-	}
-	w.writeInt(int64(len(pc.Funcdataoff)))
-	for i := range pc.Funcdataoff {
-		w.writeRefIndex(pc.Funcdata[i])
-	}
-	for i := range pc.Funcdataoff {
-		w.writeInt(pc.Funcdataoff[i])
-	}
-	w.writeInt(int64(len(pc.File)))
-	for _, f := range pc.File {
-		fsym := ctxt.Lookup(f)
-		w.writeRefIndex(fsym)
-	}
-	w.writeInt(int64(len(pc.InlTree.nodes)))
-	for _, call := range pc.InlTree.nodes {
-		w.writeInt(int64(call.Parent))
-		f, l := linkgetlineFromPos(w.ctxt, call.Pos)
-		fsym := ctxt.Lookup(f)
-		w.writeRefIndex(fsym)
-		w.writeInt(int64(l))
-		w.writeRefIndex(call.Func)
-		w.writeInt(int64(call.ParentPC))
-	}
-}
-
-func (w *objWriter) writeBool(b bool) {
-	if b {
-		w.writeInt(1)
-	} else {
-		w.writeInt(0)
-	}
-}
-
-func (w *objWriter) writeInt(sval int64) {
-	var v uint64
-	uv := (uint64(sval) << 1) ^ uint64(sval>>63)
-	p := w.varintbuf[:]
-	for v = uv; v >= 0x80; v >>= 7 {
-		p[0] = uint8(v | 0x80)
-		p = p[1:]
-	}
-	p[0] = uint8(v)
-	p = p[1:]
-	w.wr.Write(w.varintbuf[:len(w.varintbuf)-len(p)])
-}
-
-func (w *objWriter) writeString(s string) {
-	w.writeInt(int64(len(s)))
-	w.wr.WriteString(s)
-}
-
-func (w *objWriter) writeRefIndex(s *LSym) {
-	if s == nil {
-		w.writeInt(0)
-		return
-	}
-	if s.RefIdx == 0 {
-		log.Fatalln("writing an unreferenced symbol", s.Name)
-	}
-	w.writeInt(int64(s.RefIdx))
-}
-
 // relocByOff sorts relocations by their offsets.
 type relocByOff []Reloc
 
@@ -544,18 +209,24 @@
 	if s.Func.dwarfInfoSym == nil {
 		s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
 		if ctxt.Flag_locationlists {
-			s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name)
+			s.Func.dwarfLocSym = &LSym{
+				Type: objabi.SDWARFLOC,
+			}
 		}
-		s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
+		s.Func.dwarfRangesSym = &LSym{
+			Type: objabi.SDWARFRANGE,
+		}
 		if s.WasInlined() {
 			s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
 		}
-		s.Func.dwarfDebugLinesSym = ctxt.LookupDerived(s, dwarf.DebugLinesPrefix+s.Name)
+		s.Func.dwarfDebugLinesSym = &LSym{
+			Type: objabi.SDWARFLINES,
+		}
 	}
 	return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym
 }
 
-func (s *LSym) Len() int64 {
+func (s *LSym) Length(dwarfContext interface{}) int64 {
 	return s.Size
 }
 
diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go
index 69019e0..359e82e 100644
--- a/src/cmd/internal/obj/objfile2.go
+++ b/src/cmd/internal/obj/objfile2.go
@@ -18,9 +18,8 @@
 
 // Entry point of writing new object file.
 func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) {
-	if ctxt.Debugasm > 0 {
-		ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
-	}
+
+	debugAsmEmit(ctxt)
 
 	genFuncInfoSyms(ctxt)
 
@@ -60,7 +59,7 @@
 	// DWARF file table
 	h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
 	for _, f := range ctxt.PosTable.DebugLinesFileTable() {
-		w.StringRef(f)
+		w.StringRef(filepath.ToSlash(f))
 	}
 
 	// Symbol definitions
@@ -207,7 +206,7 @@
 		}
 	})
 	for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
-		w.AddString(f)
+		w.AddString(filepath.ToSlash(f))
 	}
 }
 
@@ -229,8 +228,8 @@
 	if s.Leaf() {
 		flag |= goobj2.SymFlagLeaf
 	}
-	if s.CFunc() {
-		flag |= goobj2.SymFlagCFunc
+	if s.NoSplit() {
+		flag |= goobj2.SymFlagNoSplit
 	}
 	if s.ReflectMethod() {
 		flag |= goobj2.SymFlagReflectMethod
@@ -307,21 +306,21 @@
 			}
 			o.Write(w.Writer)
 		}
-		if s.Func.dwarfLocSym != nil {
+		if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
 			o := goobj2.Aux{
 				Type: goobj2.AuxDwarfLoc,
 				Sym:  makeSymRef(s.Func.dwarfLocSym),
 			}
 			o.Write(w.Writer)
 		}
-		if s.Func.dwarfRangesSym != nil {
+		if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
 			o := goobj2.Aux{
 				Type: goobj2.AuxDwarfRanges,
 				Sym:  makeSymRef(s.Func.dwarfRangesSym),
 			}
 			o.Write(w.Writer)
 		}
-		if s.Func.dwarfDebugLinesSym != nil {
+		if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
 			o := goobj2.Aux{
 				Type: goobj2.AuxDwarfLines,
 				Sym:  makeSymRef(s.Func.dwarfDebugLinesSym),
@@ -343,13 +342,13 @@
 		if s.Func.dwarfInfoSym != nil {
 			n++
 		}
-		if s.Func.dwarfLocSym != nil {
+		if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
 			n++
 		}
-		if s.Func.dwarfRangesSym != nil {
+		if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
 			n++
 		}
-		if s.Func.dwarfDebugLinesSym != nil {
+		if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
 			n++
 		}
 	}
@@ -366,14 +365,9 @@
 		if s.Func == nil {
 			continue
 		}
-		nosplit := uint8(0)
-		if s.NoSplit() {
-			nosplit = 1
-		}
 		o := goobj2.FuncInfo{
-			NoSplit: nosplit,
-			Args:    uint32(s.Func.Args),
-			Locals:  uint32(s.Func.Locals),
+			Args:   uint32(s.Func.Args),
+			Locals: uint32(s.Func.Locals),
 		}
 		pc := &s.Func.Pcln
 		o.Pcsp = pcdataoff
@@ -424,6 +418,43 @@
 		infosyms = append(infosyms, isym)
 		s.Func.FuncInfoSym = isym
 		b.Reset()
+
+		dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym}
+		for _, s := range dwsyms {
+			if s == nil || s.Size == 0 {
+				continue
+			}
+			s.PkgIdx = goobj2.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.SDWARFINFO &&
+		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(fn)
+		}
+	}
+}
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
index 7579dd0..917343e 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -109,7 +109,9 @@
 			continue
 		}
 		linkpcln(ctxt, s)
-		ctxt.populateDWARF(plist.Curfn, s, myimportpath)
+		if myimportpath != "" {
+			ctxt.populateDWARF(plist.Curfn, s, myimportpath)
+		}
 	}
 }
 
@@ -137,20 +139,10 @@
 	ctxt.Text = append(ctxt.Text, s)
 
 	// Set up DWARF entries for s.
-	info, loc, ranges, _, lines := ctxt.dwarfSym(s)
+	info, _, _, _, _ := ctxt.dwarfSym(s)
 	info.Type = objabi.SDWARFINFO
 	info.Set(AttrDuplicateOK, s.DuplicateOK())
-	if loc != nil {
-		loc.Type = objabi.SDWARFLOC
-		loc.Set(AttrDuplicateOK, s.DuplicateOK())
-		ctxt.Data = append(ctxt.Data, loc)
-	}
-	ranges.Type = objabi.SDWARFRANGE
-	ranges.Set(AttrDuplicateOK, s.DuplicateOK())
-	ctxt.Data = append(ctxt.Data, info, ranges)
-	lines.Type = objabi.SDWARFLINES
-	lines.Set(AttrDuplicateOK, s.DuplicateOK())
-	ctxt.Data = append(ctxt.Data, lines)
+	ctxt.Data = append(ctxt.Data, info)
 }
 
 func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
diff --git a/src/cmd/internal/obj/sizeof_test.go b/src/cmd/internal/obj/sizeof_test.go
index b5e170c..69e6047 100644
--- a/src/cmd/internal/obj/sizeof_test.go
+++ b/src/cmd/internal/obj/sizeof_test.go
@@ -21,7 +21,7 @@
 		_64bit uintptr     // size on 64bit platforms
 	}{
 		{Addr{}, 32, 48},
-		{LSym{}, 76, 128},
+		{LSym{}, 72, 120},
 		{Prog{}, 132, 200},
 	}
 
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go
index 3ef8866..9cd1786 100644
--- a/src/cmd/internal/obj/sym.go
+++ b/src/cmd/internal/obj/sym.go
@@ -164,10 +164,6 @@
 // asm is set to true if this is called by the assembler (i.e. not the compiler),
 // in which case all the symbols are non-package (for now).
 func (ctxt *Link) NumberSyms(asm bool) {
-	if !ctxt.Flag_newobj {
-		return
-	}
-
 	if ctxt.Headtype == objabi.Haix {
 		// Data must be sorted to keep a constant order in TOC symbols.
 		// As they are created during Progedit, two symbols can be switched between
@@ -298,28 +294,58 @@
 					fn(s.Gotype)
 				}
 				if s.Type == objabi.STEXT {
-					pc := &s.Func.Pcln
-					for _, d := range pc.Funcdata {
-						if d != nil {
-							fn(d)
-						}
+					f := func(parent *LSym, aux *LSym) {
+						fn(aux)
 					}
-					for _, f := range pc.File {
-						if fsym := ctxt.Lookup(f); fsym != nil {
-							fn(fsym)
-						}
-					}
-					for _, call := range pc.InlTree.nodes {
-						if call.Func != nil {
-							fn(call.Func)
-						}
-						f, _ := linkgetlineFromPos(ctxt, call.Pos)
-						if fsym := ctxt.Lookup(f); fsym != nil {
-							fn(fsym)
-						}
-					}
+					ctxt.traverseFuncAux(s, f)
 				}
 			}
 		}
 	}
 }
+
+func (ctxt *Link) traverseFuncAux(fsym *LSym, fn func(parent *LSym, aux *LSym)) {
+	pc := &fsym.Func.Pcln
+	for _, d := range pc.Funcdata {
+		if d != nil {
+			fn(fsym, d)
+		}
+	}
+	for _, f := range pc.File {
+		if filesym := ctxt.Lookup(f); filesym != nil {
+			fn(fsym, filesym)
+		}
+	}
+	for _, call := range pc.InlTree.nodes {
+		if call.Func != nil {
+			fn(fsym, call.Func)
+		}
+		f, _ := linkgetlineFromPos(ctxt, call.Pos)
+		if filesym := ctxt.Lookup(f); filesym != nil {
+			fn(fsym, filesym)
+		}
+	}
+	dwsyms := []*LSym{fsym.Func.dwarfRangesSym, fsym.Func.dwarfLocSym, fsym.Func.dwarfDebugLinesSym}
+	for _, dws := range dwsyms {
+		if dws == nil || dws.Size == 0 {
+			continue
+		}
+		fn(fsym, dws)
+	}
+}
+
+// Traverse aux symbols, calling fn for each sym/aux pair.
+func (ctxt *Link) traverseAuxSyms(fn func(parent *LSym, aux *LSym)) {
+	lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases}
+	for _, list := range lists {
+		for _, s := range list {
+			if s.Gotype != nil {
+				fn(s, s.Gotype)
+			}
+			if s.Type != objabi.STEXT {
+				continue
+			}
+			ctxt.traverseFuncAux(s, fn)
+		}
+	}
+}
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
index 26208cc..06c4a36 100644
--- a/src/cmd/link/internal/amd64/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -34,6 +34,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"log"
@@ -98,7 +99,15 @@
 	initarray_entry.AddAddr(ctxt.Arch, initfunc)
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+// makeWritable makes a readonly symbol writable if we do opcode rewriting.
+func makeWritable(s *sym.Symbol) {
+	if s.Attr.ReadOnly() {
+		s.Attr.Set(sym.AttrReadOnly, false)
+		s.P = append([]byte(nil), s.P...)
+	}
+}
+
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	targ := r.Sym
 
 	switch r.Type {
@@ -150,6 +159,7 @@
 		if targ.Type != sym.SDYNIMPORT {
 			// have symbol
 			if r.Off >= 2 && s.P[r.Off-2] == 0x8b {
+				makeWritable(s)
 				// turn MOVQ of GOT entry into LEAQ of symbol itself
 				s.P[r.Off-2] = 0x8d
 
@@ -225,6 +235,7 @@
 				return false
 			}
 
+			makeWritable(s)
 			s.P[r.Off-2] = 0x8d
 			r.Type = objabi.R_PCREL
 			return true
@@ -371,7 +382,7 @@
 			// just in case the C code assigns to the variable,
 			// and of course it only works for single pointers,
 			// but we only need to support cgo and that's all it needs.
-			ld.Adddynsym(ctxt, targ)
+			ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, targ)
 
 			got := ctxt.Syms.Lookup(".got", 0)
 			s.Type = got.Type
@@ -553,36 +564,34 @@
 	return true
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return t
 }
 
-func elfsetupplt(ctxt *ld.Link) {
-	plt := ctxt.Syms.Lookup(".plt", 0)
-	got := ctxt.Syms.Lookup(".got.plt", 0)
-	if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+	if plt.Size() == 0 {
 		// pushq got+8(IP)
 		plt.AddUint8(0xff)
 
 		plt.AddUint8(0x35)
-		plt.AddPCRelPlus(ctxt.Arch, got, 8)
+		plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 8)
 
 		// jmpq got+16(IP)
 		plt.AddUint8(0xff)
 
 		plt.AddUint8(0x25)
-		plt.AddPCRelPlus(ctxt.Arch, got, 16)
+		plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 16)
 
 		// nopl 0(AX)
 		plt.AddUint32(ctxt.Arch, 0x00401f0f)
 
 		// assume got->size == 0 too
-		got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+		got.AddAddrPlus(ctxt.Arch, dynamic, 0)
 
 		got.AddUint64(ctxt.Arch, 0)
 		got.AddUint64(ctxt.Arch, 0)
@@ -594,14 +603,14 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 
 	if ctxt.IsELF {
 		plt := ctxt.Syms.Lookup(".plt", 0)
 		got := ctxt.Syms.Lookup(".got.plt", 0)
 		rela := ctxt.Syms.Lookup(".rela.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt(ctxt)
+			panic("plt is not set up")
 		}
 
 		// jmpq *got+size(IP)
@@ -662,7 +671,7 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 	got := ctxt.Syms.Lookup(".got", 0)
 	s.SetGot(int32(got.Size))
 	got.AddUint64(ctxt.Arch, 0)
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
index f2fb654..e4a52e5 100644
--- a/src/cmd/link/internal/arm/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -34,6 +34,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"fmt"
@@ -115,7 +116,7 @@
 	return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	targ := r.Sym
 
 	switch r.Type {
@@ -243,7 +244,7 @@
 			break
 		}
 		if ctxt.IsELF {
-			ld.Adddynsym(ctxt, targ)
+			ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, targ)
 			rel := ctxt.Syms.Lookup(".rel", 0)
 			rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
 			rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc
@@ -300,10 +301,8 @@
 	return true
 }
 
-func elfsetupplt(ctxt *ld.Link) {
-	plt := ctxt.Syms.Lookup(".plt", 0)
-	got := ctxt.Syms.Lookup(".got.plt", 0)
-	if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+	if plt.Size() == 0 {
 		// str lr, [sp, #-4]!
 		plt.AddUint32(ctxt.Arch, 0xe52de004)
 
@@ -317,7 +316,7 @@
 		plt.AddUint32(ctxt.Arch, 0xe5bef008)
 
 		// .word &GLOBAL_OFFSET_TABLE[0] - .
-		plt.AddPCRelPlus(ctxt.Arch, got, 4)
+		plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 4)
 
 		// the first .plt entry requires 3 .plt.got entries
 		got.AddUint32(ctxt.Arch, 0)
@@ -597,8 +596,8 @@
 	}
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+	if target.IsExternal() {
 		switch r.Type {
 		case objabi.R_CALLARM:
 			r.Done = false
@@ -623,7 +622,7 @@
 			// the section load address.
 			// we need to compensate that by removing the instruction's address
 			// from addend.
-			if ctxt.HeadType == objabi.Hdarwin {
+			if target.IsDarwin() {
 				r.Xadd -= ld.Symaddr(s) + int64(r.Off)
 			}
 
@@ -641,19 +640,19 @@
 	case objabi.R_CONST:
 		return r.Add, true
 	case objabi.R_GOTOFF:
-		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
 
 	// The following three arch specific relocations are only for generation of
 	// Linux/ARM ELF's PLT entry (3 assembler instruction)
 	case objabi.R_PLT0: // add ip, pc, #0xXX00000
-		if ld.Symaddr(ctxt.Syms.Lookup(".got.plt", 0)) < ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) {
+		if ld.Symaddr(syms.GOTPLT) < ld.Symaddr(syms.PLT) {
 			ld.Errorf(s, ".got.plt should be placed after .plt section.")
 		}
-		return 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add)) >> 20)), true
+		return 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+int64(r.Off))+r.Add)) >> 20)), true
 	case objabi.R_PLT1: // add ip, ip, #0xYY000
-		return 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+4)) >> 12)), true
+		return 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+int64(r.Off))+r.Add+4)) >> 12)), true
 	case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]!
-		return 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+8))), true
+		return 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+int64(r.Off))+r.Add+8))), true
 	case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY
 		// r.Add is the instruction
 		// low 24-bit encodes the target address
@@ -667,7 +666,7 @@
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return t
 }
@@ -690,14 +689,14 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 
 	if ctxt.IsELF {
 		plt := ctxt.Syms.Lookup(".plt", 0)
 		got := ctxt.Syms.Lookup(".got.plt", 0)
 		rel := ctxt.Syms.Lookup(".rel.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt(ctxt)
+			panic("plt is not set up")
 		}
 
 		// .got entry
@@ -745,7 +744,7 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 	got := ctxt.Syms.Lookup(".got", 0)
 	s.SetGot(int32(got.Size))
 	got.AddUint32(ctxt.Arch, 0)
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 9c3f442..0b4ecd6 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -34,9 +34,9 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
-	"encoding/binary"
 	"fmt"
 	"log"
 )
@@ -92,7 +92,7 @@
 	initarray_entry.AddAddr(ctxt.Arch, initfunc)
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	targ := r.Sym
 
 	switch r.Type {
@@ -434,14 +434,14 @@
 	return true
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+	if target.IsExternal() {
 		switch r.Type {
 		default:
 			return val, false
 		case objabi.R_ARM64_GOTPCREL:
 			var o1, o2 uint32
-			if ctxt.Arch.ByteOrder == binary.BigEndian {
+			if target.IsBigEndian() {
 				o1 = uint32(val >> 32)
 				o2 = uint32(val)
 			} else {
@@ -456,14 +456,14 @@
 			// (https://sourceware.org/bugzilla/show_bug.cgi?id=18270). So
 			// we convert the adrp; ld64 + R_ARM64_GOTPCREL into adrp;
 			// add + R_ADDRARM64.
-			if !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && ctxt.DynlinkingGo() {
+			if !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && target.IsDynlinkingGo() {
 				if o2&0xffc00000 != 0xf9400000 {
 					ld.Errorf(s, "R_ARM64_GOTPCREL against unexpected instruction %x", o2)
 				}
 				o2 = 0x91000000 | (o2 & 0x000003ff)
 				r.Type = objabi.R_ADDRARM64
 			}
-			if ctxt.Arch.ByteOrder == binary.BigEndian {
+			if target.IsBigEndian() {
 				val = int64(o1)<<32 | int64(o2)
 			} else {
 				val = int64(o2)<<32 | int64(o1)
@@ -490,10 +490,10 @@
 			// the BR26 relocation should be fully resolved at link time.
 			// That is the reason why the next if block is disabled. When the bug in ld64
 			// is fixed, we can enable this block and also enable duff's device in cmd/7g.
-			if false && ctxt.HeadType == objabi.Hdarwin {
+			if false && target.IsDarwin() {
 				var o0, o1 uint32
 
-				if ctxt.Arch.ByteOrder == binary.BigEndian {
+				if target.IsBigEndian() {
 					o0 = uint32(val >> 32)
 					o1 = uint32(val)
 				} else {
@@ -510,7 +510,7 @@
 				r.Xadd = 0
 
 				// when laid out, the instruction order must always be o1, o2.
-				if ctxt.Arch.ByteOrder == binary.BigEndian {
+				if target.IsBigEndian() {
 					val = int64(o0)<<32 | int64(o1)
 				} else {
 					val = int64(o1)<<32 | int64(o0)
@@ -533,7 +533,7 @@
 		return r.Add, true
 
 	case objabi.R_GOTOFF:
-		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
 
 	case objabi.R_ADDRARM64:
 		t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
@@ -543,7 +543,7 @@
 
 		var o0, o1 uint32
 
-		if ctxt.Arch.ByteOrder == binary.BigEndian {
+		if target.IsBigEndian() {
 			o0 = uint32(val >> 32)
 			o1 = uint32(val)
 		} else {
@@ -555,42 +555,42 @@
 		o1 |= uint32(t&0xfff) << 10
 
 		// when laid out, the instruction order must always be o1, o2.
-		if ctxt.Arch.ByteOrder == binary.BigEndian {
+		if target.IsBigEndian() {
 			return int64(o0)<<32 | int64(o1), true
 		}
 		return int64(o1)<<32 | int64(o0), true
 
 	case objabi.R_ARM64_TLS_LE:
 		r.Done = false
-		if ctxt.HeadType == objabi.Hdarwin {
-			ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType)
+		if target.IsDarwin() {
+			ld.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
 		}
 		// The TCB is two pointers. This is not documented anywhere, but is
 		// de facto part of the ABI.
-		v := r.Sym.Value + int64(2*ctxt.Arch.PtrSize)
+		v := r.Sym.Value + int64(2*target.Arch.PtrSize)
 		if v < 0 || v >= 32678 {
 			ld.Errorf(s, "TLS offset out of range %d", v)
 		}
 		return val | (v << 5), true
 
 	case objabi.R_ARM64_TLS_IE:
-		if ctxt.BuildMode == ld.BuildModePIE && ctxt.IsELF {
+		if target.IsPIE() && target.IsElf() {
 			// We are linking the final executable, so we
 			// can optimize any TLS IE relocation to LE.
 			r.Done = false
-			if ctxt.HeadType != objabi.Hlinux {
-				ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType)
+			if !target.IsLinux() {
+				ld.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
 			}
 
 			// The TCB is two pointers. This is not documented anywhere, but is
 			// de facto part of the ABI.
-			v := ld.Symaddr(r.Sym) + int64(2*ctxt.Arch.PtrSize) + r.Add
+			v := ld.Symaddr(r.Sym) + int64(2*target.Arch.PtrSize) + r.Add
 			if v < 0 || v >= 32678 {
 				ld.Errorf(s, "TLS offset out of range %d", v)
 			}
 
 			var o0, o1 uint32
-			if ctxt.Arch.ByteOrder == binary.BigEndian {
+			if target.IsBigEndian() {
 				o0 = uint32(val >> 32)
 				o1 = uint32(val)
 			} else {
@@ -609,7 +609,7 @@
 			o1 = 0xf2800000 | uint32(o1&0x1f) | (uint32(v&0xffff) << 5)
 
 			// when laid out, the instruction order must always be o0, o1.
-			if ctxt.Arch.ByteOrder == binary.BigEndian {
+			if target.IsBigEndian() {
 				return int64(o0)<<32 | int64(o1), true
 			}
 			return int64(o1)<<32 | int64(o0), true
@@ -620,7 +620,7 @@
 	case objabi.R_CALLARM64:
 		var t int64
 		if r.Sym.Type == sym.SDYNIMPORT {
-			t = (ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) + r.Add) - (s.Value + int64(r.Off))
+			t = (ld.Symaddr(syms.PLT) + r.Add) - (s.Value + int64(r.Off))
 		} else {
 			t = (ld.Symaddr(r.Sym) + r.Add) - (s.Value + int64(r.Off))
 		}
@@ -707,35 +707,30 @@
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return -1
 }
 
-func elfsetupplt(ctxt *ld.Link) {
-	plt := ctxt.Syms.Lookup(".plt", 0)
-	gotplt := ctxt.Syms.Lookup(".got.plt", 0)
-	if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+	if plt.Size() == 0 {
 		// stp     x16, x30, [sp, #-16]!
 		// identifying information
 		plt.AddUint32(ctxt.Arch, 0xa9bf7bf0)
 
 		// the following two instructions (adrp + ldr) load *got[2] into x17
 		// adrp    x16, &got[0]
-		plt.AddAddrPlus4(gotplt, 16)
-		plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010)
-		plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+		plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+		plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x90000010)
 
 		// <imm> is the offset value of &got[2] to &got[0], the same below
 		// ldr     x17, [x16, <imm>]
-		plt.AddAddrPlus4(gotplt, 16)
-		plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211)
-		plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+		plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+		plt.SetUint32(ctxt.Arch, plt.Size()-4, 0xf9400211)
 
 		// add     x16, x16, <imm>
-		plt.AddAddrPlus4(gotplt, 16)
-		plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210)
-		plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL
+		plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_PCREL, 4)
+		plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x91000210)
 
 		// br      x17
 		plt.AddUint32(ctxt.Arch, 0xd61f0220)
@@ -746,10 +741,10 @@
 		plt.AddUint32(ctxt.Arch, 0xd503201f)
 
 		// check gotplt.size == 0
-		if gotplt.Size != 0 {
-			ld.Errorf(gotplt, "got.plt is not empty at the very beginning")
+		if gotplt.Size() != 0 {
+			ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning")
 		}
-		gotplt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+		gotplt.AddAddrPlus(ctxt.Arch, dynamic, 0)
 
 		gotplt.AddUint64(ctxt.Arch, 0)
 		gotplt.AddUint64(ctxt.Arch, 0)
@@ -761,14 +756,14 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 
 	if ctxt.IsELF {
 		plt := ctxt.Syms.Lookup(".plt", 0)
 		gotplt := ctxt.Syms.Lookup(".got.plt", 0)
 		rela := ctxt.Syms.Lookup(".rela.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt(ctxt)
+			panic("plt is not set up")
 		}
 
 		// adrp    x16, &got.plt[0]
@@ -809,7 +804,7 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 	got := ctxt.Syms.Lookup(".got", 0)
 	s.SetGot(int32(got.Size))
 	got.AddUint64(ctxt.Arch, 0)
diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go
new file mode 100644
index 0000000..52f9826
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench.go
@@ -0,0 +1,176 @@
+// Copyright 2020 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.
+
+// Package benchmark provides a Metrics object that enables memory and CPU
+// profiling for the linker. The Metrics objects can be used to mark stages
+// of the code, and name the measurements during that stage. There is also
+// optional GCs that can be performed at the end of each stage, so you
+// can get an accurate measurement of how each stage changes live memory.
+package benchmark
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"runtime"
+	"runtime/pprof"
+	"time"
+	"unicode"
+)
+
+type Flags int
+
+const (
+	GC         = 1 << iota
+	NoGC Flags = 0
+)
+
+type Metrics struct {
+	gc        Flags
+	marks     []*mark
+	curMark   *mark
+	filebase  string
+	pprofFile *os.File
+}
+
+type mark struct {
+	name              string
+	startM, endM, gcM runtime.MemStats
+	startT, endT      time.Time
+}
+
+// New creates a new Metrics object.
+//
+// Typical usage should look like:
+//
+// func main() {
+//   filename := "" // Set to enable per-phase pprof file output.
+//   bench := benchmark.New(benchmark.GC, filename)
+//   defer bench.Report(os.Stdout)
+//   // etc
+//   bench.Start("foo")
+//   foo()
+//   bench.Start("bar")
+//   bar()
+// }
+//
+// Note that a nil Metrics object won't cause any errors, so one could write
+// code like:
+//
+//  func main() {
+//    enableBenchmarking := flag.Bool("enable", true, "enables benchmarking")
+//    flag.Parse()
+//    var bench *benchmark.Metrics
+//    if *enableBenchmarking {
+//      bench = benchmark.New(benchmark.GC)
+//    }
+//    bench.Start("foo")
+//    // etc.
+//  }
+func New(gc Flags, filebase string) *Metrics {
+	if gc == GC {
+		runtime.GC()
+	}
+	return &Metrics{gc: gc, filebase: filebase}
+}
+
+// Report reports the metrics.
+// Closes the currently Start(ed) range, and writes the report to the given io.Writer.
+func (m *Metrics) Report(w io.Writer) {
+	if m == nil {
+		return
+	}
+
+	m.closeMark()
+
+	gcString := ""
+	if m.gc == GC {
+		gcString = "_GC"
+	}
+
+	var totTime time.Duration
+	for _, curMark := range m.marks {
+		dur := curMark.endT.Sub(curMark.startT)
+		totTime += dur
+		fmt.Fprintf(w, "%s 1 %d ns/op", makeBenchString(curMark.name+gcString), dur.Nanoseconds())
+		fmt.Fprintf(w, "\t%d B/op", curMark.endM.TotalAlloc-curMark.startM.TotalAlloc)
+		fmt.Fprintf(w, "\t%d allocs/op", curMark.endM.Mallocs-curMark.startM.Mallocs)
+		if m.gc == GC {
+			fmt.Fprintf(w, "\t%d live-B", curMark.gcM.HeapAlloc)
+		} else {
+			fmt.Fprintf(w, "\t%d heap-B", curMark.endM.HeapAlloc)
+		}
+		fmt.Fprintf(w, "\n")
+	}
+	fmt.Fprintf(w, "%s 1 %d ns/op\n", makeBenchString("total time"+gcString), totTime.Nanoseconds())
+}
+
+// Starts marks the beginning of a new measurement phase.
+// Once a metric is started, it continues until either a Report is issued, or another Start is called.
+func (m *Metrics) Start(name string) {
+	if m == nil {
+		return
+	}
+	m.closeMark()
+	m.curMark = &mark{name: name}
+	// Unlikely we need to a GC here, as one was likely just done in closeMark.
+	if m.shouldPProf() {
+		f, err := os.Create(makePProfFilename(m.filebase, name))
+		if err != nil {
+			panic(err)
+		}
+		m.pprofFile = f
+		if err = pprof.StartCPUProfile(m.pprofFile); err != nil {
+			panic(err)
+		}
+	}
+	runtime.ReadMemStats(&m.curMark.startM)
+	m.curMark.startT = time.Now()
+}
+
+func (m *Metrics) closeMark() {
+	if m == nil || m.curMark == nil {
+		return
+	}
+	m.curMark.endT = time.Now()
+	runtime.ReadMemStats(&m.curMark.endM)
+	if m.gc == GC {
+		runtime.GC()
+		runtime.ReadMemStats(&m.curMark.gcM)
+	}
+	if m.shouldPProf() {
+		pprof.StopCPUProfile()
+		m.pprofFile.Close()
+		m.pprofFile = nil
+	}
+	m.marks = append(m.marks, m.curMark)
+	m.curMark = nil
+}
+
+// shouldPProf returns true if we should be doing pprof runs.
+func (m *Metrics) shouldPProf() bool {
+	return m != nil && len(m.filebase) > 0
+}
+
+// makeBenchString makes a benchmark string consumable by Go's benchmarking tools.
+func makeBenchString(name string) string {
+	needCap := true
+	ret := []rune("Benchmark")
+	for _, r := range name {
+		if unicode.IsSpace(r) {
+			needCap = true
+			continue
+		}
+		if needCap {
+			r = unicode.ToUpper(r)
+			needCap = false
+		}
+		ret = append(ret, r)
+	}
+	return string(ret)
+}
+
+func makePProfFilename(filebase, name string) string {
+	return fmt.Sprintf("%s_%s.profile", filebase, makeBenchString(name))
+}
diff --git a/src/cmd/link/internal/benchmark/bench_test.go b/src/cmd/link/internal/benchmark/bench_test.go
new file mode 100644
index 0000000..48d4d74
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench_test.go
@@ -0,0 +1,53 @@
+// Copyright 2020 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.
+package benchmark
+
+import (
+	"testing"
+)
+
+func TestMakeBenchString(t *testing.T) {
+	tests := []struct {
+		have, want string
+	}{
+		{"foo", "BenchmarkFoo"},
+		{"  foo  ", "BenchmarkFoo"},
+		{"foo bar", "BenchmarkFooBar"},
+	}
+	for i, test := range tests {
+		if v := makeBenchString(test.have); test.want != v {
+			t.Errorf("test[%d] makeBenchString(%q) == %q, want %q", i, test.have, v, test.want)
+		}
+	}
+}
+
+func TestPProfFlag(t *testing.T) {
+	tests := []struct {
+		name string
+		want bool
+	}{
+		{"", false},
+		{"foo", true},
+	}
+	for i, test := range tests {
+		b := New(GC, test.name)
+		if v := b.shouldPProf(); test.want != v {
+			t.Errorf("test[%d] shouldPProf() == %v, want %v", i, v, test.want)
+		}
+	}
+}
+
+func TestPProfNames(t *testing.T) {
+	want := "foo_BenchmarkTest.profile"
+	if v := makePProfFilename("foo", "test"); v != want {
+		t.Errorf("makePProfFilename() == %q, want %q", v, want)
+	}
+}
+
+// Ensure that public APIs work with a nil Metrics object.
+func TestNilBenchmarkObject(t *testing.T) {
+	var b *Metrics
+	b.Start("TEST")
+	b.Report(nil)
+}
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
index 4a20d96..268f40e 100644
--- a/src/cmd/link/internal/ld/ar.go
+++ b/src/cmd/link/internal/ld/ar.go
@@ -104,15 +104,13 @@
 	any := true
 	for any {
 		var load []uint64
-		for _, s := range ctxt.Syms.Allsym {
-			for i := range s.R {
-				r := &s.R[i] // Copying sym.Reloc has measurable impact on performance
-				if r.Sym != nil && r.Sym.Type == sym.SXREF {
-					if off := armap[r.Sym.Name]; off != 0 && !loaded[off] {
-						load = append(load, off)
-						loaded[off] = true
-					}
-				}
+		returnAllUndefs := -1
+		undefs := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
+		for _, symIdx := range undefs {
+			name := ctxt.loader.SymName(symIdx)
+			if off := armap[name]; off != 0 && !loaded[off] {
+				load = append(load, off)
+				loaded[off] = true
 			}
 		}
 
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 7ca01c8..39ec054 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -37,6 +37,7 @@
 	"cmd/internal/gcprog"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"compress/zlib"
 	"encoding/binary"
@@ -99,7 +100,7 @@
 		if Symaddr(r.Sym) == 0 && (r.Sym.Type != sym.SDYNIMPORT && r.Sym.Type != sym.SUNDEFEXT) {
 			if r.Sym.File != s.File {
 				if !isRuntimeDepPkg(s.File) || !isRuntimeDepPkg(r.Sym.File) {
-					ctxt.ErrorUnresolved(s, r)
+					ctxt.errorUnresolved(s, r)
 				}
 				// runtime and its dependent packages may call to each other.
 				// they are fine, as they will be laid down together.
@@ -126,7 +127,9 @@
 //
 // This is a performance-critical function for the linker; be careful
 // to avoid introducing unnecessary allocations in the main loop.
-func relocsym(ctxt *Link, s *sym.Symbol) {
+// TODO: This function is called in parallel. When the Loader wavefront
+// reaches here, calls into the loader need to be parallel as well.
+func relocsym(target *Target, err *ErrorReporter, lookup LookupFn, syms *ArchSyms, s *sym.Symbol) {
 	if len(s.R) == 0 {
 		return
 	}
@@ -157,8 +160,8 @@
 		if r.Sym != nil && ((r.Sym.Type == sym.Sxxx && !r.Sym.Attr.VisibilityHidden()) || r.Sym.Type == sym.SXREF) {
 			// When putting the runtime but not main into a shared library
 			// these symbols are undefined and that's OK.
-			if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin {
-				if r.Sym.Name == "main.main" || (ctxt.BuildMode != BuildModePlugin && r.Sym.Name == "main..inittask") {
+			if target.IsShared() || target.IsPlugin() {
+				if r.Sym.Name == "main.main" || (!target.IsPlugin() && r.Sym.Name == "main..inittask") {
 					r.Sym.Type = sym.SDYNIMPORT
 				} else if strings.HasPrefix(r.Sym.Name, "go.info.") {
 					// Skip go.info symbols. They are only needed to communicate
@@ -166,7 +169,7 @@
 					continue
 				}
 			} else {
-				ctxt.ErrorUnresolved(s, r)
+				err.errorUnresolved(s, r)
 				continue
 			}
 		}
@@ -180,21 +183,21 @@
 
 		// We need to be able to reference dynimport symbols when linking against
 		// shared libraries, and Solaris, Darwin and AIX need it always
-		if ctxt.HeadType != objabi.Hsolaris && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Haix && r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT && !ctxt.DynlinkingGo() && !r.Sym.Attr.SubSymbol() {
-			if !(ctxt.Arch.Family == sys.PPC64 && ctxt.LinkMode == LinkExternal && r.Sym.Name == ".TOC.") {
-				Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", r.Sym.Name, r.Sym.Type, r.Sym.Type, r.Type, sym.RelocName(ctxt.Arch, r.Type))
+		if !target.IsSolaris() && !target.IsDarwin() && !target.IsAIX() && r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !r.Sym.Attr.SubSymbol() {
+			if !(target.IsPPC64() && target.IsExternal() && r.Sym.Name == ".TOC.") {
+				Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", r.Sym.Name, r.Sym.Type, r.Sym.Type, r.Type, sym.RelocName(target.Arch, r.Type))
 			}
 		}
 		if r.Sym != nil && r.Sym.Type != sym.STLSBSS && r.Type != objabi.R_WEAKADDROFF && !r.Sym.Attr.Reachable() {
 			Errorf(s, "unreachable sym in relocation: %s", r.Sym.Name)
 		}
 
-		if ctxt.LinkMode == LinkExternal {
+		if target.IsExternal() {
 			r.InitExt()
 		}
 
 		// TODO(mundaym): remove this special case - see issue 14218.
-		if ctxt.Arch.Family == sys.S390X {
+		if target.IsS390X() {
 			switch r.Type {
 			case objabi.R_PCRELDBL:
 				r.InitExt()
@@ -215,33 +218,33 @@
 			case 1:
 				o = int64(s.P[off])
 			case 2:
-				o = int64(ctxt.Arch.ByteOrder.Uint16(s.P[off:]))
+				o = int64(target.Arch.ByteOrder.Uint16(s.P[off:]))
 			case 4:
-				o = int64(ctxt.Arch.ByteOrder.Uint32(s.P[off:]))
+				o = int64(target.Arch.ByteOrder.Uint32(s.P[off:]))
 			case 8:
-				o = int64(ctxt.Arch.ByteOrder.Uint64(s.P[off:]))
+				o = int64(target.Arch.ByteOrder.Uint64(s.P[off:]))
 			}
-			if offset, ok := thearch.Archreloc(ctxt, r, s, o); ok {
+			if offset, ok := thearch.Archreloc(target, syms, r, s, o); ok {
 				o = offset
 			} else {
-				Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type))
+				Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(target.Arch, r.Type))
 			}
 		case objabi.R_TLS_LE:
-			if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
+			if target.IsExternal() && target.IsElf() {
 				r.Done = false
 				if r.Sym == nil {
-					r.Sym = ctxt.Tlsg
+					r.Sym = syms.Tlsg
 				}
 				r.Xsym = r.Sym
 				r.Xadd = r.Add
 				o = 0
-				if ctxt.Arch.Family != sys.AMD64 {
+				if !target.IsAMD64() {
 					o = r.Add
 				}
 				break
 			}
 
-			if ctxt.IsELF && ctxt.Arch.Family == sys.ARM {
+			if target.IsElf() && target.IsARM() {
 				// On ELF ARM, the thread pointer is 8 bytes before
 				// the start of the thread-local data block, so add 8
 				// to the actual TLS offset (r->sym->value).
@@ -250,43 +253,43 @@
 				// related to the fact that our own TLS storage happens
 				// to take up 8 bytes.
 				o = 8 + r.Sym.Value
-			} else if ctxt.IsELF || ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hdarwin {
-				o = int64(ctxt.Tlsoffset) + r.Add
-			} else if ctxt.HeadType == objabi.Hwindows {
+			} else if target.IsElf() || target.IsPlan9() || target.IsDarwin() {
+				o = int64(syms.Tlsoffset) + r.Add
+			} else if target.IsWindows() {
 				o = r.Add
 			} else {
-				log.Fatalf("unexpected R_TLS_LE relocation for %v", ctxt.HeadType)
+				log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType)
 			}
 		case objabi.R_TLS_IE:
-			if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
+			if target.IsExternal() && target.IsElf() {
 				r.Done = false
 				if r.Sym == nil {
-					r.Sym = ctxt.Tlsg
+					r.Sym = syms.Tlsg
 				}
 				r.Xsym = r.Sym
 				r.Xadd = r.Add
 				o = 0
-				if ctxt.Arch.Family != sys.AMD64 {
+				if !target.IsAMD64() {
 					o = r.Add
 				}
 				break
 			}
-			if ctxt.BuildMode == BuildModePIE && ctxt.IsELF {
+			if target.IsPIE() && target.IsElf() {
 				// We are linking the final executable, so we
 				// can optimize any TLS IE relocation to LE.
 				if thearch.TLSIEtoLE == nil {
-					log.Fatalf("internal linking of TLS IE not supported on %v", ctxt.Arch.Family)
+					log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family)
 				}
 				thearch.TLSIEtoLE(s, int(off), int(r.Siz))
-				o = int64(ctxt.Tlsoffset)
-				// TODO: o += r.Add when ctxt.Arch.Family != sys.AMD64?
+				o = int64(syms.Tlsoffset)
+				// TODO: o += r.Add when !target.IsAmd64()?
 				// Why do we treat r.Add differently on AMD64?
 				// Is the external linker using Xadd at all?
 			} else {
 				log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", s.Name)
 			}
 		case objabi.R_ADDR:
-			if ctxt.LinkMode == LinkExternal && r.Sym.Type != sym.SCONST {
+			if target.IsExternal() && r.Sym.Type != sym.SCONST {
 				r.Done = false
 
 				// set up addend for eventual relocation via outer symbol.
@@ -304,20 +307,20 @@
 				r.Xsym = rs
 
 				o = r.Xadd
-				if ctxt.IsELF {
-					if ctxt.Arch.Family == sys.AMD64 {
+				if target.IsElf() {
+					if target.IsAMD64() {
 						o = 0
 					}
-				} else if ctxt.HeadType == objabi.Hdarwin {
+				} else if target.IsDarwin() {
 					if rs.Type != sym.SHOSTOBJ {
 						o += Symaddr(rs)
 					}
-				} else if ctxt.HeadType == objabi.Hwindows {
+				} else if target.IsWindows() {
 					// nothing to do
-				} else if ctxt.HeadType == objabi.Haix {
+				} else if target.IsAIX() {
 					o = Symaddr(r.Sym) + r.Add
 				} else {
-					Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType)
+					Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType)
 				}
 
 				break
@@ -327,14 +330,14 @@
 			// as section addresses can change once loaded.
 			// The "default" symbol address is still needed by the loader so
 			// the current relocation can't be skipped.
-			if ctxt.HeadType == objabi.Haix && r.Sym.Type != sym.SDYNIMPORT {
+			if target.IsAIX() && r.Sym.Type != sym.SDYNIMPORT {
 				// It's not possible to make a loader relocation in a
 				// symbol which is not inside .data section.
 				// FIXME: It should be forbidden to have R_ADDR from a
 				// symbol which isn't in .data. However, as .text has the
 				// same address once loaded, this is possible.
 				if s.Sect.Seg == &Segdata {
-					Xcoffadddynrel(ctxt, s, r)
+					Xcoffadddynrel(target, s, r)
 				}
 			}
 
@@ -345,7 +348,7 @@
 			// fail at runtime. See https://golang.org/issue/7980.
 			// Instead of special casing only amd64, we treat this as an error on all
 			// 64-bit architectures so as to be future-proof.
-			if int32(o) < 0 && ctxt.Arch.PtrSize > 4 && siz == 4 {
+			if int32(o) < 0 && target.Arch.PtrSize > 4 && siz == 4 {
 				Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", r.Sym.Name, uint64(o), Symaddr(r.Sym), r.Add)
 				errorexit()
 			}
@@ -354,7 +357,7 @@
 				Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
 			}
 
-			if ctxt.LinkMode == LinkExternal {
+			if target.IsExternal() {
 				r.Done = false
 
 				// On most platforms, the external linker needs to adjust DWARF references
@@ -362,7 +365,7 @@
 				// DWARF linking, and it understands how to follow section offsets.
 				// Leaving in the relocation records confuses it (see
 				// https://golang.org/issue/22068) so drop them for Darwin.
-				if ctxt.HeadType == objabi.Hdarwin {
+				if target.IsDarwin() {
 					r.Done = true
 				}
 
@@ -371,15 +374,15 @@
 				// IMAGE_REL_I386_DIR32, IMAGE_REL_AMD64_ADDR64 and IMAGE_REL_AMD64_ADDR32.
 				// Do not replace R_DWARFSECREF with R_ADDR for windows -
 				// let PE code emit correct relocations.
-				if ctxt.HeadType != objabi.Hwindows {
+				if !target.IsWindows() {
 					r.Type = objabi.R_ADDR
 				}
 
-				r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0)
+				r.Xsym = lookup(r.Sym.Sect.Name, 0)
 				r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
 
 				o = r.Xadd
-				if ctxt.IsELF && ctxt.Arch.Family == sys.AMD64 {
+				if target.IsElf() && target.IsAMD64() {
 					o = 0
 				}
 				break
@@ -406,7 +409,7 @@
 
 			// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
 		case objabi.R_GOTPCREL:
-			if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin && r.Sym != nil && r.Sym.Type != sym.SCONST {
+			if target.IsDynlinkingGo() && target.IsDarwin() && r.Sym != nil && r.Sym.Type != sym.SCONST {
 				r.Done = false
 				r.Xadd = r.Add
 				r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk
@@ -418,18 +421,18 @@
 			}
 			fallthrough
 		case objabi.R_CALL, objabi.R_PCREL:
-			if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT {
+			if target.IsExternal() && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT {
 				// pass through to the external linker.
 				r.Done = false
 				r.Xadd = 0
-				if ctxt.IsELF {
+				if target.IsElf() {
 					r.Xadd -= int64(r.Siz)
 				}
 				r.Xsym = r.Sym
 				o = 0
 				break
 			}
-			if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type != sym.SCONST && (r.Sym.Sect != s.Sect || r.Type == objabi.R_GOTPCREL) {
+			if target.IsExternal() && r.Sym != nil && r.Sym.Type != sym.SCONST && (r.Sym.Sect != s.Sect || r.Type == objabi.R_GOTPCREL) {
 				r.Done = false
 
 				// set up addend for eventual relocation via outer symbol.
@@ -448,14 +451,14 @@
 				r.Xsym = rs
 
 				o = r.Xadd
-				if ctxt.IsELF {
-					if ctxt.Arch.Family == sys.AMD64 {
+				if target.IsElf() {
+					if target.IsAMD64() {
 						o = 0
 					}
-				} else if ctxt.HeadType == objabi.Hdarwin {
+				} else if target.IsDarwin() {
 					if r.Type == objabi.R_CALL {
-						if ctxt.LinkMode == LinkExternal && rs.Type == sym.SDYNIMPORT {
-							switch ctxt.Arch.Family {
+						if target.IsExternal() && rs.Type == sym.SDYNIMPORT {
+							switch target.Arch.Family {
 							case sys.AMD64:
 								// AMD64 dynamic relocations are relative to the end of the relocation.
 								o += int64(r.Siz)
@@ -470,18 +473,18 @@
 							}
 							o -= int64(r.Off) // relative to section offset, not symbol
 						}
-					} else if ctxt.Arch.Family == sys.ARM {
+					} else if target.IsARM() {
 						// see ../arm/asm.go:/machoreloc1
 						o += Symaddr(rs) - s.Value - int64(r.Off)
 					} else {
 						o += int64(r.Siz)
 					}
-				} else if ctxt.HeadType == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 { // only amd64 needs PCREL
+				} else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL
 					// PE/COFF's PC32 relocation uses the address after the relocated
 					// bytes as the base. Compensate by skewing the addend.
 					o += int64(r.Siz)
 				} else {
-					Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType)
+					Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType)
 				}
 
 				break
@@ -497,10 +500,10 @@
 			o = r.Sym.Size + r.Add
 
 		case objabi.R_XCOFFREF:
-			if ctxt.HeadType != objabi.Haix {
+			if !target.IsAIX() {
 				Errorf(s, "find XCOFF R_REF on non-XCOFF files")
 			}
-			if ctxt.LinkMode != LinkExternal {
+			if !target.IsExternal() {
 				Errorf(s, "find XCOFF R_REF with internal linking")
 			}
 			r.Xsym = r.Sym
@@ -516,10 +519,10 @@
 			o = r.Add
 		}
 
-		if ctxt.Arch.Family == sys.PPC64 || ctxt.Arch.Family == sys.S390X {
+		if target.IsPPC64() || target.IsS390X() {
 			r.InitExt()
 			if r.Variant != sym.RV_NONE {
-				o = thearch.Archrelocvariant(ctxt, r, s, o)
+				o = thearch.Archrelocvariant(target, syms, r, s, o)
 			}
 		}
 
@@ -534,7 +537,7 @@
 			if r.Xsym != nil {
 				xnam = r.Xsym.Name
 			}
-			fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x (xsym: %s +%#x) [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, addr, r.Add, xnam, r.Xadd, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Variant, o)
+			fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x (xsym: %s +%#x) [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, addr, r.Add, xnam, r.Xadd, r.Type, sym.RelocName(target.Arch, r.Type), r.Variant, o)
 		}
 		switch siz {
 		default:
@@ -549,7 +552,7 @@
 				Errorf(s, "relocation address for %s is too big: %#x", r.Sym.Name, o)
 			}
 			i16 := int16(o)
-			ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16))
+			target.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16))
 		case 4:
 			if r.Type == objabi.R_PCREL || r.Type == objabi.R_CALL {
 				if o != int64(int32(o)) {
@@ -562,23 +565,39 @@
 			}
 
 			fl := int32(o)
-			ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl))
+			target.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl))
 		case 8:
-			ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o))
+			target.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o))
 		}
 	}
 }
 
 func (ctxt *Link) reloc() {
-	for _, s := range ctxt.Textp {
-		relocsym(ctxt, s)
-	}
-	for _, s := range datap {
-		relocsym(ctxt, s)
-	}
-	for _, s := range dwarfp {
-		relocsym(ctxt, s)
-	}
+	var wg sync.WaitGroup
+	target := &ctxt.Target
+	reporter := &ctxt.ErrorReporter
+	lookup := ctxt.Syms.ROLookup
+	syms := &ctxt.ArchSyms
+	wg.Add(3)
+	go func() {
+		for _, s := range ctxt.Textp {
+			relocsym(target, reporter, lookup, syms, s)
+		}
+		wg.Done()
+	}()
+	go func() {
+		for _, s := range datap {
+			relocsym(target, reporter, lookup, syms, s)
+		}
+		wg.Done()
+	}()
+	go func() {
+		for _, s := range dwarfp {
+			relocsym(target, reporter, lookup, syms, s)
+		}
+		wg.Done()
+	}()
+	wg.Wait()
 }
 
 func windynrelocsym(ctxt *Link, rel, s *sym.Symbol) {
@@ -646,13 +665,15 @@
 }
 
 func dynrelocsym(ctxt *Link, s *sym.Symbol) {
+	target := &ctxt.Target
+	syms := &ctxt.ArchSyms
 	for ri := range s.R {
 		r := &s.R[ri]
 		if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal {
 			// It's expected that some relocations will be done
 			// later by relocsym (R_TLS_LE, R_ADDROFF), so
 			// don't worry if Adddynrel returns false.
-			thearch.Adddynrel(ctxt, s, r)
+			thearch.Adddynrel(ctxt, target, syms, s, r)
 			continue
 		}
 
@@ -660,7 +681,7 @@
 			if r.Sym != nil && !r.Sym.Attr.Reachable() {
 				Errorf(s, "dynamic relocation to unreachable symbol %s", r.Sym.Name)
 			}
-			if !thearch.Adddynrel(ctxt, s, r) {
+			if !thearch.Adddynrel(ctxt, target, syms, s, r) {
 				Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Sym.Type, r.Sym.Type)
 			}
 		}
@@ -927,48 +948,40 @@
 }
 
 // addstrdata sets the initial value of the string variable name to value.
-func addstrdata(ctxt *Link, name, value string) {
-	s := ctxt.Syms.ROLookup(name, 0)
-	if s == nil || s.Gotype == nil {
-		// Not defined in the loaded packages.
+func addstrdata(arch *sys.Arch, l *loader.Loader, name, value string) {
+	s := l.Lookup(name, 0)
+	if s == 0 {
 		return
 	}
-	if s.Gotype.Name != "type.string" {
-		Errorf(s, "cannot set with -X: not a var of type string (%s)", s.Gotype.Name)
+	if goType := l.SymGoType(s); goType == 0 {
+		return
+	} else if typeName := l.SymName(goType); typeName != "type.string" {
+		Errorf(nil, "%s: cannot set with -X: not a var of type string (%s)", name, typeName)
 		return
 	}
-	if s.Type == sym.SBSS {
-		s.Type = sym.SDATA
+	bld := l.MakeSymbolUpdater(s)
+	if bld.Type() == sym.SBSS {
+		bld.SetType(sym.SDATA)
 	}
 
-	p := fmt.Sprintf("%s.str", s.Name)
-	sp := ctxt.Syms.Lookup(p, 0)
+	p := fmt.Sprintf("%s.str", name)
+	sp := l.LookupOrCreateSym(p, 0)
+	sbld := l.MakeSymbolUpdater(sp)
 
-	Addstring(sp, value)
-	sp.Type = sym.SRODATA
+	sbld.Addstring(value)
+	sbld.SetType(sym.SRODATA)
 
-	s.Size = 0
-	s.P = s.P[:0]
-	if s.Attr.ReadOnly() {
-		s.P = make([]byte, 0, ctxt.Arch.PtrSize*2)
-		s.Attr.Set(sym.AttrReadOnly, false)
-	}
-	s.R = s.R[:0]
-	reachable := s.Attr.Reachable()
-	s.AddAddr(ctxt.Arch, sp)
-	s.AddUint(ctxt.Arch, uint64(len(value)))
-
-	// addstring, addaddr, etc., mark the symbols as reachable.
-	// In this case that is not necessarily true, so stick to what
-	// we know before entering this function.
-	s.Attr.Set(sym.AttrReachable, reachable)
-
-	sp.Attr.Set(sym.AttrReachable, reachable)
+	bld.SetSize(0)
+	bld.SetData(make([]byte, 0, arch.PtrSize*2))
+	bld.SetReadOnly(false)
+	bld.SetRelocs(nil)
+	bld.AddAddrPlus(arch, sp, 0)
+	bld.AddUint(arch, uint64(len(value)))
 }
 
 func (ctxt *Link) dostrdata() {
 	for _, name := range strnames {
-		addstrdata(ctxt, name, strdata[name])
+		addstrdata(ctxt.Arch, ctxt.loader, name, strdata[name])
 	}
 }
 
@@ -2450,6 +2463,8 @@
 	binary.BigEndian.PutUint64(sizeBytes[:], uint64(total))
 	buf.Write(sizeBytes[:])
 
+	var relocbuf []byte // temporary buffer for applying relocations
+
 	// Using zlib.BestSpeed achieves very nearly the same
 	// compression levels of zlib.DefaultCompression, but takes
 	// substantially less time. This is important because DWARF
@@ -2458,17 +2473,22 @@
 	if err != nil {
 		log.Fatalf("NewWriterLevel failed: %s", err)
 	}
+	target := &ctxt.Target
+	reporter := &ctxt.ErrorReporter
+	lookup := ctxt.Syms.ROLookup
+	archSyms := &ctxt.ArchSyms
 	for _, s := range syms {
 		// s.P may be read-only. Apply relocations in a
 		// temporary buffer, and immediately write it out.
 		oldP := s.P
 		wasReadOnly := s.Attr.ReadOnly()
 		if len(s.R) != 0 && wasReadOnly {
-			ctxt.relocbuf = append(ctxt.relocbuf[:0], s.P...)
-			s.P = ctxt.relocbuf
+			relocbuf = append(relocbuf[:0], s.P...)
+			s.P = relocbuf
+			// TODO: This function call needs to be parallelized when the loader wavefront gets here.
 			s.Attr.Set(sym.AttrReadOnly, false)
 		}
-		relocsym(ctxt, s)
+		relocsym(target, reporter, lookup, archSyms, s)
 		if _, err := z.Write(s.P); err != nil {
 			log.Fatalf("compression failed: %s", err)
 		}
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 6d96ae5..13ddcda 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -6,11 +6,7 @@
 
 import (
 	"cmd/internal/objabi"
-	"cmd/internal/sys"
 	"cmd/link/internal/sym"
-	"fmt"
-	"strings"
-	"unicode"
 )
 
 // deadcode marks all reachable symbols.
@@ -46,363 +42,59 @@
 //
 // Any unreached text symbols are removed from ctxt.Textp.
 func deadcode(ctxt *Link) {
-	if ctxt.Debugvlog != 0 {
-		ctxt.Logf("deadcode\n")
-	}
-
-	if *flagNewobj {
-		deadcode2(ctxt)
-		return
-	}
-
-	d := &deadcodepass{
-		ctxt:        ctxt,
-		ifaceMethod: make(map[methodsig]bool),
-	}
-
-	// First, flood fill any symbols directly reachable in the call
-	// graph from *flagEntrySymbol. Ignore all methods not directly called.
-	d.init()
-	d.flood()
-
-	callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal)
-	methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal)
-	reflectSeen := false
-
-	if ctxt.DynlinkingGo() {
-		// Exported methods may satisfy interfaces we don't know
-		// about yet when dynamically linking.
-		reflectSeen = true
-	}
-
-	for {
-		if !reflectSeen {
-			if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) {
-				// Methods might be called via reflection. Give up on
-				// static analysis, mark all exported methods of
-				// all reachable types as reachable.
-				reflectSeen = true
-			}
-		}
-
-		// Mark all methods that could satisfy a discovered
-		// interface as reachable. We recheck old marked interfaces
-		// as new types (with new methods) may have been discovered
-		// in the last pass.
-		var rem []methodref
-		for _, m := range d.markableMethods {
-			if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
-				d.markMethod(m)
-			} else {
-				rem = append(rem, m)
-			}
-		}
-		d.markableMethods = rem
-
-		if len(d.markQueue) == 0 {
-			// No new work was discovered. Done.
-			break
-		}
-		d.flood()
-	}
-
-	// Remove all remaining unreached R_METHODOFF relocations.
-	for _, m := range d.markableMethods {
-		for _, r := range m.r {
-			d.cleanupReloc(r)
-		}
-	}
-
-	if ctxt.BuildMode != BuildModeShared {
-		// Keep a itablink if the symbol it points at is being kept.
-		// (When BuildModeShared, always keep itablinks.)
-		for _, s := range ctxt.Syms.Allsym {
-			if strings.HasPrefix(s.Name, "go.itablink.") {
-				s.Attr.Set(sym.AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable())
-			}
-		}
-	}
-
-	addToTextp(ctxt)
+	deadcode2(ctxt)
 }
 
+// addToTextp populates the context Textp slice (needed in various places
+// in the linker) and also the unit Textp slices (needed by the "old"
+// phase 2 DWARF generation).
 func addToTextp(ctxt *Link) {
-	// Remove dead text but keep file information (z symbols).
-	textp := []*sym.Symbol{}
-	for _, s := range ctxt.Textp {
-		if s.Attr.Reachable() {
-			textp = append(textp, s)
-		}
-	}
 
-	// Put reachable text symbols into Textp.
-	// do it in postorder so that packages are laid down in dependency order
-	// internal first, then everything else
-	ctxt.Library = postorder(ctxt.Library)
+	// First set up ctxt.Textp, based on ctxt.Textp2.
+	textp := make([]*sym.Symbol, 0, len(ctxt.Textp2))
+	haveshlibs := len(ctxt.Shlibs) > 0
+	for _, tsym := range ctxt.Textp2 {
+		sp := ctxt.loader.Syms[tsym]
+		if sp == nil || !ctxt.loader.AttrReachable(tsym) {
+			panic("should never happen")
+		}
+		if haveshlibs && sp.Type == sym.SDYNIMPORT {
+			continue
+		}
+		textp = append(textp, sp)
+	}
+	ctxt.Textp = textp
+
+	// Dupok symbols may be defined in multiple packages; the
+	// associated package for a dupok sym is chosen sort of
+	// arbitrarily (the first containing package that the linker
+	// loads). The loop below canonicalizes the File to the package
+	// with which it will be laid down in text. Assumes that
+	// ctxt.Library is already in postorder.
 	for _, doInternal := range [2]bool{true, false} {
 		for _, lib := range ctxt.Library {
 			if isRuntimeDepPkg(lib.Pkg) != doInternal {
 				continue
 			}
-			libtextp := lib.Textp[:0]
-			for _, s := range lib.Textp {
-				if s.Attr.Reachable() {
-					textp = append(textp, s)
-					libtextp = append(libtextp, s)
-					if s.Unit != nil {
-						s.Unit.Textp = append(s.Unit.Textp, s)
-					}
-				}
-			}
-			for _, s := range lib.DupTextSyms {
-				if s.Attr.Reachable() && !s.Attr.OnList() {
-					textp = append(textp, s)
-					libtextp = append(libtextp, s)
-					if s.Unit != nil {
-						s.Unit.Textp = append(s.Unit.Textp, s)
-					}
-					s.Attr |= sym.AttrOnList
-					// dupok symbols may be defined in multiple packages. its
-					// associated package is chosen sort of arbitrarily (the
-					// first containing package that the linker loads). canonicalize
-					// it here to the package with which it will be laid down
-					// in text.
-					s.File = objabi.PathToPrefix(lib.Pkg)
-				}
-			}
-			lib.Textp = libtextp
-		}
-	}
-	ctxt.Textp = textp
-
-	if len(ctxt.Shlibs) > 0 {
-		// We might have overwritten some functions above (this tends to happen for the
-		// autogenerated type equality/hashing functions) and we don't want to generated
-		// pcln table entries for these any more so remove them from Textp.
-		textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
-		for _, s := range ctxt.Textp {
-			if s.Type != sym.SDYNIMPORT {
-				textp = append(textp, s)
-			}
-		}
-		ctxt.Textp = textp
-	}
-}
-
-// methodref holds the relocations from a receiver type symbol to its
-// method. There are three relocations, one for each of the fields in
-// the reflect.method struct: mtyp, ifn, and tfn.
-type methodref struct {
-	m   methodsig
-	src *sym.Symbol   // receiver type symbol
-	r   [3]*sym.Reloc // R_METHODOFF relocations to fields of runtime.method
-}
-
-func (m methodref) ifn() *sym.Symbol { return m.r[1].Sym }
-
-func (m methodref) isExported() bool {
-	for _, r := range m.m {
-		return unicode.IsUpper(r)
-	}
-	panic("methodref has no signature")
-}
-
-// deadcodepass holds state for the deadcode flood fill.
-type deadcodepass struct {
-	ctxt            *Link
-	markQueue       []*sym.Symbol      // symbols to flood fill in next pass
-	ifaceMethod     map[methodsig]bool // methods declared in reached interfaces
-	markableMethods []methodref        // methods of reached types
-	reflectMethod   bool
-}
-
-func (d *deadcodepass) cleanupReloc(r *sym.Reloc) {
-	if r.Sym.Attr.Reachable() {
-		r.Type = objabi.R_ADDROFF
-	} else {
-		if d.ctxt.Debugvlog > 1 {
-			d.ctxt.Logf("removing method %s\n", r.Sym.Name)
-		}
-		r.Sym = nil
-		r.Siz = 0
-	}
-}
-
-// mark appends a symbol to the mark queue for flood filling.
-func (d *deadcodepass) mark(s, parent *sym.Symbol) {
-	if s == nil || s.Attr.Reachable() {
-		return
-	}
-	if s.Attr.ReflectMethod() {
-		d.reflectMethod = true
-	}
-	if *flagDumpDep {
-		p := "_"
-		if parent != nil {
-			p = parent.Name
-		}
-		fmt.Printf("%s -> %s\n", p, s.Name)
-	}
-	s.Attr |= sym.AttrReachable
-	if d.ctxt.Reachparent != nil {
-		d.ctxt.Reachparent[s] = parent
-	}
-	d.markQueue = append(d.markQueue, s)
-}
-
-// markMethod marks a method as reachable.
-func (d *deadcodepass) markMethod(m methodref) {
-	for _, r := range m.r {
-		d.mark(r.Sym, m.src)
-		r.Type = objabi.R_ADDROFF
-	}
-}
-
-// init marks all initial symbols as reachable.
-// In a typical binary, this is *flagEntrySymbol.
-func (d *deadcodepass) init() {
-	var names []string
-
-	if d.ctxt.BuildMode == BuildModeShared {
-		// Mark all symbols defined in this library as reachable when
-		// building a shared library.
-		for _, s := range d.ctxt.Syms.Allsym {
-			if s.Type != 0 && s.Type != sym.SDYNIMPORT {
-				d.mark(s, nil)
-			}
-		}
-	} else {
-		// In a normal binary, start at main.main and the init
-		// functions and mark what is reachable from there.
-
-		if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
-			names = append(names, "main.main", "main..inittask")
-		} else {
-			// The external linker refers main symbol directly.
-			if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
-				if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
-					*flagEntrySymbol = "_main"
-				} else {
-					*flagEntrySymbol = "main"
-				}
-			}
-			names = append(names, *flagEntrySymbol)
-			if d.ctxt.BuildMode == BuildModePlugin {
-				names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs")
-
-				// We don't keep the go.plugin.exports symbol,
-				// but we do keep the symbols it refers to.
-				exports := d.ctxt.Syms.ROLookup("go.plugin.exports", 0)
-				if exports != nil {
-					for i := range exports.R {
-						d.mark(exports.R[i].Sym, nil)
-					}
+			for _, dsym := range lib.DupTextSyms2 {
+				tsp := ctxt.loader.Syms[dsym]
+				if !tsp.Attr.OnList() {
+					tsp.Attr |= sym.AttrOnList
+					tsp.File = objabi.PathToPrefix(lib.Pkg)
 				}
 			}
 		}
-		for _, s := range dynexp {
-			d.mark(s, nil)
-		}
 	}
 
-	for _, name := range names {
-		// Mark symbol as a data/ABI0 symbol.
-		d.mark(d.ctxt.Syms.ROLookup(name, 0), nil)
-		// Also mark any Go functions (internal ABI).
-		d.mark(d.ctxt.Syms.ROLookup(name, sym.SymVerABIInternal), nil)
-	}
-}
-
-// flood fills symbols reachable from the markQueue symbols.
-// As it goes, it collects methodref and interface method declarations.
-func (d *deadcodepass) flood() {
-	for len(d.markQueue) > 0 {
-		s := d.markQueue[0]
-		d.markQueue = d.markQueue[1:]
-		if s.Type == sym.STEXT {
-			if d.ctxt.Debugvlog > 1 {
-				d.ctxt.Logf("marktext %s\n", s.Name)
+	// Finally, set up compilation unit Textp slices. Can be removed
+	// once loader-Sym DWARF-gen phase 2 is always enabled.
+	for _, lib := range ctxt.Library {
+		for _, unit := range lib.Units {
+			for _, usym := range unit.Textp2 {
+				usp := ctxt.loader.Syms[usym]
+				usp.Attr |= sym.AttrOnList
+				unit.Textp = append(unit.Textp, usp)
 			}
 		}
-
-		if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' {
-			if len(s.P) == 0 {
-				// Probably a bug. The undefined symbol check
-				// later will give a better error than deadcode.
-				continue
-			}
-			if decodetypeKind(d.ctxt.Arch, s.P)&kindMask == kindInterface {
-				for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) {
-					if d.ctxt.Debugvlog > 1 {
-						d.ctxt.Logf("reached iface method: %s\n", sig)
-					}
-					d.ifaceMethod[sig] = true
-				}
-			}
-		}
-
-		mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype
-		var methods []methodref
-		for i := range s.R {
-			r := &s.R[i]
-			if r.Sym == nil {
-				continue
-			}
-			if r.Type == objabi.R_WEAKADDROFF {
-				// An R_WEAKADDROFF relocation is not reason
-				// enough to mark the pointed-to symbol as
-				// reachable.
-				continue
-			}
-			if r.Sym.Type == sym.SABIALIAS {
-				// Patch this relocation through the
-				// ABI alias before marking.
-				r.Sym = resolveABIAlias(r.Sym)
-			}
-			if r.Type != objabi.R_METHODOFF {
-				d.mark(r.Sym, s)
-				continue
-			}
-			// Collect rtype pointers to methods for
-			// later processing in deadcode.
-			if mpos == 0 {
-				m := methodref{src: s}
-				m.r[0] = r
-				methods = append(methods, m)
-			} else {
-				methods[len(methods)-1].r[mpos] = r
-			}
-			mpos++
-			if mpos == len(methodref{}.r) {
-				mpos = 0
-			}
-		}
-		if len(methods) > 0 {
-			// Decode runtime type information for type methods
-			// to help work out which methods can be called
-			// dynamically via interfaces.
-			methodsigs := decodetypeMethods(d.ctxt.Arch, s)
-			if len(methods) != len(methodsigs) {
-				panic(fmt.Sprintf("%q has %d method relocations for %d methods", s.Name, len(methods), len(methodsigs)))
-			}
-			for i, m := range methodsigs {
-				name := string(m)
-				name = name[:strings.Index(name, "(")]
-				if !strings.HasSuffix(methods[i].ifn().Name, name) {
-					panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].ifn().Name, name))
-				}
-				methods[i].m = m
-			}
-			d.markableMethods = append(d.markableMethods, methods...)
-		}
-
-		if s.FuncInfo != nil {
-			for i := range s.FuncInfo.Funcdata {
-				d.mark(s.FuncInfo.Funcdata[i], s)
-			}
-		}
-		d.mark(s.Gotype, s)
-		d.mark(s.Sub, s)
-		d.mark(s.Outer, s)
 	}
 }
diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go
index 9197c70..a7a41e3 100644
--- a/src/cmd/link/internal/ld/deadcode2.go
+++ b/src/cmd/link/internal/ld/deadcode2.go
@@ -37,7 +37,6 @@
 	ctxt *Link
 	ldr  *loader.Loader
 	wq   workQueue
-	rtmp []loader.Reloc
 
 	ifaceMethod     map[methodsig]bool // methods declared in reached interfaces
 	markableMethods []methodref2       // methods of reached types
@@ -58,9 +57,7 @@
 		n := d.ldr.NDef()
 		for i := 1; i < n; i++ {
 			s := loader.Sym(i)
-			if !d.ldr.IsDup(s) {
-				d.mark(s, 0)
-			}
+			d.mark(s, 0)
 		}
 		return
 	}
@@ -88,9 +85,9 @@
 			// but we do keep the symbols it refers to.
 			exportsIdx := d.ldr.Lookup("go.plugin.exports", 0)
 			if exportsIdx != 0 {
-				d.ReadRelocs(exportsIdx)
-				for i := 0; i < len(d.rtmp); i++ {
-					d.mark(d.rtmp[i].Sym, 0)
+				relocs := d.ldr.Relocs(exportsIdx)
+				for i := 0; i < relocs.Count; i++ {
+					d.mark(relocs.At2(i).Sym(), 0)
 				}
 			}
 		}
@@ -121,20 +118,19 @@
 }
 
 func (d *deadcodePass2) flood() {
-	symRelocs := []loader.Reloc{}
 	auxSyms := []loader.Sym{}
 	for !d.wq.empty() {
 		symIdx := d.wq.pop()
 
 		d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
 
+		isgotype := d.ldr.IsGoType(symIdx)
 		relocs := d.ldr.Relocs(symIdx)
-		symRelocs = relocs.ReadAll(symRelocs)
 
-		if d.ldr.IsGoType(symIdx) {
+		if isgotype {
 			p := d.ldr.Data(symIdx)
 			if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
-				for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) {
+				for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) {
 					if d.ctxt.Debugvlog > 1 {
 						d.ctxt.Logf("reached iface method: %s\n", sig)
 					}
@@ -145,11 +141,12 @@
 
 		var methods []methodref2
 		for i := 0; i < relocs.Count; i++ {
-			r := symRelocs[i]
-			if r.Type == objabi.R_WEAKADDROFF {
+			r := relocs.At2(i)
+			t := r.Type()
+			if t == objabi.R_WEAKADDROFF {
 				continue
 			}
-			if r.Type == objabi.R_METHODOFF {
+			if t == objabi.R_METHODOFF {
 				if i+2 >= relocs.Count {
 					panic("expect three consecutive R_METHODOFF relocs")
 				}
@@ -157,13 +154,13 @@
 				i += 2
 				continue
 			}
-			if r.Type == objabi.R_USETYPE {
+			if t == objabi.R_USETYPE {
 				// type symbol used for DWARF. we need to load the symbol but it may not
 				// be otherwise reachable in the program.
 				// do nothing for now as we still load all type symbols.
 				continue
 			}
-			d.mark(r.Sym, symIdx)
+			d.mark(r.Sym(), symIdx)
 		}
 		auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms)
 		for i := 0; i < len(auxSyms); i++ {
@@ -179,10 +176,13 @@
 		d.mark(d.ldr.SubSym(symIdx), symIdx)
 
 		if len(methods) != 0 {
+			if !isgotype {
+				panic("method found on non-type symbol")
+			}
 			// Decode runtime type information for type methods
 			// to help work out which methods can be called
 			// dynamically via interfaces.
-			methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs)
+			methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs)
 			if len(methods) != len(methodsigs) {
 				panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
 			}
@@ -195,9 +195,9 @@
 }
 
 func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
-	if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) {
+	if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
 		d.wq.push(symIdx)
-		d.ldr.Reachable.Set(symIdx)
+		d.ldr.SetAttrReachable(symIdx, true)
 		if d.ctxt.Reachparent != nil {
 			d.ldr.Reachparent[symIdx] = parent
 		}
@@ -215,10 +215,10 @@
 }
 
 func (d *deadcodePass2) markMethod(m methodref2) {
-	d.ReadRelocs(m.src)
-	d.mark(d.rtmp[m.r].Sym, m.src)
-	d.mark(d.rtmp[m.r+1].Sym, m.src)
-	d.mark(d.rtmp[m.r+2].Sym, m.src)
+	relocs := d.ldr.Relocs(m.src)
+	d.mark(relocs.At2(m.r).Sym(), m.src)
+	d.mark(relocs.At2(m.r+1).Sym(), m.src)
+	d.mark(relocs.At2(m.r+2).Sym(), m.src)
 }
 
 func deadcode2(ctxt *Link) {
@@ -239,7 +239,7 @@
 		// Methods might be called via reflection. Give up on
 		// static analysis, mark all exported methods of
 		// all reachable types as reachable.
-		d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.Reachable.Has(callSym)) || (methSym != 0 && ldr.Reachable.Has(methSym))
+		d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.AttrReachable(callSym)) || (methSym != 0 && ldr.AttrReachable(methSym))
 
 		// Mark all methods that could satisfy a discovered
 		// interface as reachable. We recheck old marked interfaces
@@ -271,8 +271,8 @@
 			s := loader.Sym(i)
 			if ldr.IsItabLink(s) {
 				relocs := ldr.Relocs(s)
-				if relocs.Count > 0 && ldr.Reachable.Has(relocs.At(0).Sym) {
-					ldr.Reachable.Set(s)
+				if relocs.Count > 0 && ldr.AttrReachable(relocs.At(0).Sym) {
+					ldr.SetAttrReachable(s, true)
 				}
 			}
 		}
@@ -301,15 +301,15 @@
 // the function type.
 //
 // Conveniently this is the layout of both runtime.method and runtime.imethod.
-func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig {
+func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
 	var buf bytes.Buffer
 	var methods []methodsig
 	for i := 0; i < count; i++ {
-		buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off))
-		mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4))
+		buf.WriteString(decodetypeName3(ldr, symIdx, relocs, off))
+		mtypSym := decodeRelocSym3(ldr, symIdx, relocs, int32(off+4))
 		// FIXME: add some sort of caching here, since we may see some of the
 		// same symbols over time for param types.
-		d.ReadRelocs(mtypSym)
+		mrelocs := ldr.Relocs(mtypSym)
 		mp := ldr.Data(mtypSym)
 
 		buf.WriteRune('(')
@@ -318,7 +318,7 @@
 			if i > 0 {
 				buf.WriteString(", ")
 			}
-			a := d.decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i)
+			a := decodetypeFuncInType3(ldr, arch, mtypSym, &mrelocs, i)
 			buf.WriteString(ldr.SymName(a))
 		}
 		buf.WriteString(") (")
@@ -327,7 +327,7 @@
 			if i > 0 {
 				buf.WriteString(", ")
 			}
-			a := d.decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i)
+			a := decodetypeFuncOutType3(ldr, arch, mtypSym, &mrelocs, i)
 			buf.WriteString(ldr.SymName(a))
 		}
 		buf.WriteRune(')')
@@ -339,25 +339,26 @@
 	return methods
 }
 
-func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
+func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
 	p := ldr.Data(symIdx)
 	if decodetypeKind(arch, p)&kindMask != kindInterface {
 		panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
 	}
-	rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize))
-	if rel.Sym == 0 {
+	rel := decodeReloc3(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize))
+	s := rel.Sym()
+	if s == 0 {
 		return nil
 	}
-	if rel.Sym != symIdx {
+	if s != symIdx {
 		panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
 	}
-	off := int(rel.Add) // array of reflect.imethod values
+	off := int(rel.Add()) // array of reflect.imethod values
 	numMethods := int(decodetypeIfaceMethodCount(arch, p))
 	sizeofIMethod := 4 + 4
-	return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods)
+	return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods)
 }
 
-func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
+func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
 	p := ldr.Data(symIdx)
 	if !decodetypeHasUncommon(arch, p) {
 		panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
@@ -388,54 +389,5 @@
 	moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
 	off += moff                // offset to array of reflect.method values
 	const sizeofMethod = 4 * 4 // sizeof reflect.method in program
-	return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount)
-}
-
-func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Reloc {
-	for j := 0; j < len(symRelocs); j++ {
-		rel := symRelocs[j]
-		if rel.Off == off {
-			return rel
-		}
-	}
-	return loader.Reloc{}
-}
-
-func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym {
-	return decodeReloc2(ldr, symIdx, symRelocs, off).Sym
-}
-
-// decodetypeName2 decodes the name from a reflect.name.
-func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string {
-	r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off))
-	if r == 0 {
-		return ""
-	}
-
-	data := ldr.Data(r)
-	namelen := int(uint16(data[1])<<8 | uint16(data[2]))
-	return string(data[3 : 3+namelen])
-}
-
-func (d *deadcodePass2) decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
-	uadd := commonsize(arch) + 4
-	if arch.PtrSize == 8 {
-		uadd += 4
-	}
-	if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
-		uadd += uncommonSize()
-	}
-	return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize))
-}
-
-func (d *deadcodePass2) decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
-	return d.decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
-}
-
-// readRelocs reads the relocations for the specified symbol into the
-// deadcode relocs work array. Use with care, since the work array
-// is a singleton.
-func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) {
-	relocs := d.ldr.Relocs(symIdx)
-	d.rtmp = relocs.ReadAll(d.rtmp)
+	return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
 }
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 3271c85..5058608 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -125,11 +125,6 @@
 
 func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 {
 	if ctxt.Arch.Family == sys.ARM64 {
-		for _, shlib := range ctxt.Shlibs {
-			if shlib.Path == s.File {
-				return shlib.gcdataAddresses[s]
-			}
-		}
 		return 0
 	}
 	return decodeInuxi(ctxt.Arch, s.P[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
diff --git a/src/cmd/link/internal/ld/decodesym2.go b/src/cmd/link/internal/ld/decodesym2.go
new file mode 100644
index 0000000..e93cc91
--- /dev/null
+++ b/src/cmd/link/internal/ld/decodesym2.go
@@ -0,0 +1,187 @@
+// 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.
+
+package ld
+
+import (
+	"cmd/internal/sys"
+	"cmd/link/internal/loader"
+)
+
+// This file contains utilities to decode type.* symbols, for
+// loader.Sym symbols (uses new loader interfaces).
+
+// At some point we'll want to migrate the contents of this file
+// to decodesym.go once the rouetines there have been decprecated + removed.
+
+func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Reloc {
+	for j := 0; j < len(symRelocs); j++ {
+		rel := symRelocs[j]
+		if rel.Off == off {
+			return rel
+		}
+	}
+	return loader.Reloc{}
+}
+
+func decodeReloc3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc2 {
+	for j := 0; j < relocs.Count; j++ {
+		rel := relocs.At2(j)
+		if rel.Off() == off {
+			return rel
+		}
+	}
+	return loader.Reloc2{}
+}
+
+func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym {
+	return decodeReloc2(ldr, symIdx, symRelocs, off).Sym
+}
+
+func decodeRelocSym3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym {
+	return decodeReloc3(ldr, symIdx, relocs, off).Sym()
+}
+
+// decodetypeName2 decodes the name from a reflect.name.
+func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string {
+	r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off))
+	if r == 0 {
+		return ""
+	}
+
+	data := ldr.Data(r)
+	namelen := int(uint16(data[1])<<8 | uint16(data[2]))
+	return string(data[3 : 3+namelen])
+}
+
+func decodetypeName3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string {
+	r := decodeRelocSym3(ldr, symIdx, relocs, int32(off))
+	if r == 0 {
+		return ""
+	}
+
+	data := ldr.Data(r)
+	namelen := int(uint16(data[1])<<8 | uint16(data[2]))
+	return string(data[3 : 3+namelen])
+}
+
+func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
+	uadd := commonsize(arch) + 4
+	if arch.PtrSize == 8 {
+		uadd += 4
+	}
+	if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
+		uadd += uncommonSize()
+	}
+	return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize))
+}
+
+func decodetypeFuncInType3(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+	uadd := commonsize(arch) + 4
+	if arch.PtrSize == 8 {
+		uadd += 4
+	}
+	if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
+		uadd += uncommonSize()
+	}
+	return decodeRelocSym3(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize))
+}
+
+func decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
+	return decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
+}
+
+func decodetypeFuncOutType3(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+	return decodetypeFuncInType3(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
+}
+
+func decodetypeArrayElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+	// FIXME: it's inefficient to read the relocations each time. Add some
+	// sort of cache here, or pass in the relocs. Alternatively we could
+	// switch to relocs.At() to see if that performs better.
+	relocs := ldr.Relocs(symIdx)
+	rslice := relocs.ReadAll(nil)
+	return decodeRelocSym2(ldr, symIdx, rslice, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeArrayLen2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 {
+	data := ldr.Data(symIdx)
+	return int64(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeChanElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+	// FIXME: it's inefficient to read the relocations each time. Add some
+	// sort of cache here, or pass in the relocs.
+	relocs := ldr.Relocs(symIdx)
+	rslice := relocs.ReadAll(nil)
+	return decodeRelocSym2(ldr, symIdx, rslice, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapKey2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+	// FIXME: it's inefficient to read the relocations each time. Add some
+	// sort of cache here, or pass in the relocs. Alternatively we could
+	// switch to relocs.At() to see if that performs better.
+	relocs := ldr.Relocs(symIdx)
+	rslice := relocs.ReadAll(nil)
+	return decodeRelocSym2(ldr, symIdx, rslice, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapValue2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+	// FIXME: it's inefficient to read the relocations each time. Add some
+	// sort of cache here, or pass in the relocs. Alternatively we could
+	// switch to relocs.At() to see if that performs better.
+	relocs := ldr.Relocs(symIdx)
+	rslice := relocs.ReadAll(nil)
+	return decodeRelocSym2(ldr, symIdx, rslice, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38
+}
+
+func decodetypePtrElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+	// FIXME: it's inefficient to read the relocations each time. Add some
+	// sort of cache here, or pass in the relocs. Alternatively we could
+	// switch to relocs.At() to see if that performs better.
+	relocs := ldr.Relocs(symIdx)
+	rslice := relocs.ReadAll(nil)
+	return decodeRelocSym2(ldr, symIdx, rslice, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeStructFieldCount2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int {
+	data := ldr.Data(symIdx)
+	return int(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeStructFieldArrayOff2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int {
+	data := ldr.Data(symIdx)
+	off := commonsize(arch) + 4*arch.PtrSize
+	if decodetypeHasUncommon(arch, data) {
+		off += uncommonSize()
+	}
+	off += i * structfieldSize(arch)
+	return off
+}
+
+func decodetypeStructFieldName2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) string {
+	off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i)
+	// FIXME: it's inefficient to read the relocations each time. Add some
+	// sort of cache here, or pass in the relocs. Alternatively we could
+	// switch to relocs.At() to see if that performs better.
+	relocs := ldr.Relocs(symIdx)
+	rslice := relocs.ReadAll(nil)
+	return decodetypeName2(ldr, symIdx, rslice, off)
+}
+
+func decodetypeStructFieldType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
+	off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i)
+	// FIXME: it's inefficient to read the relocations each time. Add some
+	// sort of cache here, or pass in the relocs. Alternatively we could
+	// switch to relocs.At() to see if that performs better.
+	relocs := ldr.Relocs(symIdx)
+	rslice := relocs.ReadAll(nil)
+	return decodeRelocSym2(ldr, symIdx, rslice, int32(off+arch.PtrSize))
+}
+
+func decodetypeStructFieldOffsAnon2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 {
+	off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i)
+	data := ldr.Data(symIdx)
+	return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize))
+}
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 6eba39b..dcc9576 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2019 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -19,6 +19,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/src"
 	"cmd/internal/sys"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"fmt"
 	"log"
@@ -26,105 +27,181 @@
 	"strings"
 )
 
-type dwctxt struct {
+// dwctxt2 is a wrapper intended to satisfy the method set of
+// dwarf.Context, so that functions like dwarf.PutAttrs will work with
+// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also
+// being used as a place to store tables/maps that are useful as part
+// of type conversion (this is just a convenience; it would be easy to
+// split these things out into another type if need be).
+type dwctxt2 struct {
 	linkctxt *Link
+	ldr      *loader.Loader
+	arch     *sys.Arch
+
+	// This maps type name string (e.g. "uintptr") to loader symbol for
+	// the DWARF DIE for that type (e.g. "go.info.type.uintptr")
+	tmap map[string]loader.Sym
+
+	// This maps loader symbol for the DWARF DIE symbol generated for
+	// a type (e.g. "go.info.uintptr") to the type symbol itself
+	// ("type.uintptr").
+	// FIXME: try converting this map (and the next one) to a single
+	// array indexed by loader.Sym -- this may perform better.
+	rtmap map[loader.Sym]loader.Sym
+
+	// This maps Go type symbol (e.g. "type.XXX") to loader symbol for
+	// the typedef DIE for that type (e.g. "go.info.XXX..def")
+	tdmap map[loader.Sym]loader.Sym
+
+	// Cache these type symbols, so as to avoid repeatedly looking them up
+	typeRuntimeEface loader.Sym
+	typeRuntimeIface loader.Sym
+	uintptrInfoSym   loader.Sym
 }
 
-func (c dwctxt) PtrSize() int {
-	return c.linkctxt.Arch.PtrSize
-}
-func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) {
-	ls := s.(*sym.Symbol)
-	ls.AddUintXX(c.linkctxt.Arch, uint64(i), size)
-}
-func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) {
-	ls := s.(*sym.Symbol)
-	ls.AddBytes(b)
-}
-func (c dwctxt) AddString(s dwarf.Sym, v string) {
-	Addstring(s.(*sym.Symbol), v)
-}
-
-func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
-	if value != 0 {
-		value -= (data.(*sym.Symbol)).Value
+func newdwctxt2(linkctxt *Link, forTypeGen bool) dwctxt2 {
+	d := dwctxt2{
+		linkctxt: linkctxt,
+		ldr:      linkctxt.loader,
+		arch:     linkctxt.Arch,
+		tmap:     make(map[string]loader.Sym),
+		tdmap:    make(map[loader.Sym]loader.Sym),
+		rtmap:    make(map[loader.Sym]loader.Sym),
 	}
-	s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value)
+	d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface")
+	d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface")
+	return d
 }
 
-func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+// dwSym wraps a loader.Sym; this type is meant to obey the interface
+// rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and
+// DwAttr objects contain references to symbols via this type.
+type dwSym loader.Sym
+
+func (s dwSym) Length(dwarfContext interface{}) int64 {
+	l := dwarfContext.(dwctxt2).ldr
+	return int64(len(l.Data(loader.Sym(s))))
+}
+
+func (c dwctxt2) PtrSize() int {
+	return c.arch.PtrSize
+}
+
+func (c dwctxt2) AddInt(s dwarf.Sym, size int, i int64) {
+	ds := loader.Sym(s.(dwSym))
+	dsu := c.ldr.MakeSymbolUpdater(ds)
+	dsu.AddUintXX(c.arch, uint64(i), size)
+}
+
+func (c dwctxt2) AddBytes(s dwarf.Sym, b []byte) {
+	ds := loader.Sym(s.(dwSym))
+	dsu := c.ldr.MakeSymbolUpdater(ds)
+	dsu.AddBytes(b)
+}
+
+func (c dwctxt2) AddString(s dwarf.Sym, v string) {
+	ds := loader.Sym(s.(dwSym))
+	dsu := c.ldr.MakeSymbolUpdater(ds)
+	dsu.Addstring(v)
+}
+
+func (c dwctxt2) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+	ds := loader.Sym(s.(dwSym))
+	dsu := c.ldr.MakeSymbolUpdater(ds)
 	if value != 0 {
-		value -= (data.(*sym.Symbol)).Value
+		value -= dsu.Value()
 	}
-	s.(*sym.Symbol).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value)
+	tgtds := loader.Sym(data.(dwSym))
+	dsu.AddAddrPlus(c.arch, tgtds, value)
 }
 
-func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
-	ls := s.(*sym.Symbol)
+func (c dwctxt2) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+	ds := loader.Sym(s.(dwSym))
+	dsu := c.ldr.MakeSymbolUpdater(ds)
+	if value != 0 {
+		value -= dsu.Value()
+	}
+	tgtds := loader.Sym(data.(dwSym))
+	dsu.AddCURelativeAddrPlus(c.arch, tgtds, value)
+}
+
+func (c dwctxt2) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+	ds := loader.Sym(s.(dwSym))
+	dsu := c.ldr.MakeSymbolUpdater(ds)
+	tds := loader.Sym(t.(dwSym))
 	switch size {
 	default:
-		Errorf(ls, "invalid size %d in adddwarfref\n", size)
+		c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size)
 		fallthrough
-	case c.linkctxt.Arch.PtrSize:
-		ls.AddAddr(c.linkctxt.Arch, t.(*sym.Symbol))
+	case c.arch.PtrSize:
+		dsu.AddAddrPlus(c.arch, tds, 0)
 	case 4:
-		ls.AddAddrPlus4(t.(*sym.Symbol), 0)
+		dsu.AddAddrPlus4(c.arch, tds, 0)
 	}
-	r := &ls.R[len(ls.R)-1]
+	rsl := dsu.Relocs()
+	r := &rsl[len(rsl)-1]
 	r.Type = objabi.R_ADDROFF
 	r.Add = ofs
 }
 
-func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
+func (c dwctxt2) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
 	size := 4
 	if isDwarf64(c.linkctxt) {
 		size = 8
 	}
 
 	c.AddSectionOffset(s, size, t, ofs)
-	ls := s.(*sym.Symbol)
-	ls.R[len(ls.R)-1].Type = objabi.R_DWARFSECREF
+
+	ds := loader.Sym(s.(dwSym))
+	dsu := c.ldr.MakeSymbolUpdater(ds)
+	rsl := dsu.Relocs()
+	r := &rsl[len(rsl)-1]
+	r.Type = objabi.R_DWARFSECREF
 }
 
-func (c dwctxt) Logf(format string, args ...interface{}) {
+func (c dwctxt2) Logf(format string, args ...interface{}) {
 	c.linkctxt.Logf(format, args...)
 }
 
 // At the moment these interfaces are only used in the compiler.
 
-func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) {
+func (c dwctxt2) AddFileRef(s dwarf.Sym, f interface{}) {
 	panic("should be used only in the compiler")
 }
 
-func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 {
+func (c dwctxt2) CurrentOffset(s dwarf.Sym) int64 {
 	panic("should be used only in the compiler")
 }
 
-func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) {
+func (c dwctxt2) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) {
 	panic("should be used only in the compiler")
 }
 
-func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
+func (c dwctxt2) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
 	panic("should be used only in the compiler")
 }
 
-func isDwarf64(ctxt *Link) bool {
-	return ctxt.HeadType == objabi.Haix
-}
-
 var gdbscript string
 
-var dwarfp []*sym.Symbol
+var dwarfp2 []loader.Sym
 
-func writeabbrev(ctxt *Link) *sym.Symbol {
-	s := ctxt.Syms.Lookup(".debug_abbrev", 0)
-	s.Type = sym.SDWARFSECT
-	s.AddBytes(dwarf.GetAbbrev())
-	return s
+func (d *dwctxt2) writeabbrev() loader.Sym {
+	abrvs := d.ldr.AddExtSym(".debug_abbrev", 0)
+	u := d.ldr.MakeSymbolUpdater(abrvs)
+	u.SetType(sym.SDWARFSECT)
+	u.AddBytes(dwarf.GetAbbrev())
+	return abrvs
 }
 
 var dwtypes dwarf.DWDie
 
+// newattr attaches a new attribute to the specified DIE.
+//
+// FIXME: at the moment attributes are stored in a linked list in a
+// fairly space-inefficient way -- it might be better to instead look
+// up all attrs in a single large table, then store indices into the
+// table in the DIE. This would allow us to common up storage for
+// attributes that are shared by many DIEs (ex: byte size of N).
 func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr {
 	a := new(dwarf.DWAttr)
 	a.Link = die.Attr
@@ -165,7 +242,8 @@
 // attribute (but it will only be written out if it is listed in the abbrev).
 // The compiler does create nameless DWARF DIEs (ex: concrete subprogram
 // instance).
-func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie {
+// FIXME: it would be more efficient to bulk-allocate DIEs.
+func (d *dwctxt2) newdie(parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie {
 	die := new(dwarf.DWDie)
 	die.Abbrev = abbrev
 	die.Link = parent.Child
@@ -174,15 +252,22 @@
 	newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)
 
 	if name != "" && (abbrev <= dwarf.DW_ABRV_VARIABLE || abbrev >= dwarf.DW_ABRV_NULLTYPE) {
+		// Q: do we need version here? My understanding is that all these
+		// symbols should be version 0.
 		if abbrev != dwarf.DW_ABRV_VARIABLE || version == 0 {
 			if abbrev == dwarf.DW_ABRV_COMPUNIT {
 				// Avoid collisions with "real" symbol names.
-				name = fmt.Sprintf(".pkg.%s.%d", name, len(ctxt.compUnits))
+				name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits))
 			}
-			s := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version)
-			s.Attr |= sym.AttrNotInSymbolTable
-			s.Type = sym.SDWARFINFO
-			die.Sym = s
+			ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, version)
+			dsu := d.ldr.MakeSymbolUpdater(ds)
+			dsu.SetType(sym.SDWARFINFO)
+			d.ldr.SetAttrNotInSymbolTable(ds, true)
+			d.ldr.SetAttrReachable(ds, true)
+			die.Sym = dwSym(ds)
+			if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL {
+				d.tmap[name] = ds
+			}
 		}
 	}
 
@@ -205,11 +290,22 @@
 	return die
 }
 
-func walksymtypedef(ctxt *Link, s *sym.Symbol) *sym.Symbol {
-	if t := ctxt.Syms.ROLookup(s.Name+"..def", int(s.Version)); t != nil {
-		return t
+func (d *dwctxt2) walksymtypedef(symIdx loader.Sym) loader.Sym {
+
+	// We're being given the loader symbol for the type DIE, e.g.
+	// "go.info.type.uintptr". Map that first to the type symbol (e.g.
+	// "type.uintptr") and then to the typedef DIE for the type.
+	// FIXME: this seems clunky, maybe there is a better way to do this.
+
+	if ts, ok := d.rtmap[symIdx]; ok {
+		if def, ok := d.tdmap[ts]; ok {
+			return def
+		}
+		d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts)
+		return 0
 	}
-	return s
+	d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx)
+	return 0
 }
 
 // Find child by AT_name using hashtable if available or linear scan
@@ -230,73 +326,72 @@
 // Used to avoid string allocation when looking up dwarf symbols
 var prefixBuf = []byte(dwarf.InfoPrefix)
 
-func find(ctxt *Link, name string) *sym.Symbol {
-	n := append(prefixBuf, name...)
-	// The string allocation below is optimized away because it is only used in a map lookup.
-	s := ctxt.Syms.ROLookup(string(n), 0)
-	prefixBuf = n[:len(dwarf.InfoPrefix)]
-	if s != nil && s.Type == sym.SDWARFINFO {
-		return s
-	}
-	return nil
+// find looks up the loader symbol for the DWARF DIE generated for the
+// type with the specified name.
+func (d *dwctxt2) find(name string) loader.Sym {
+	return d.tmap[name]
 }
 
-func mustFind(ctxt *Link, name string) *sym.Symbol {
-	r := find(ctxt, name)
-	if r == nil {
+func (d *dwctxt2) mustFind(name string) loader.Sym {
+	r := d.find(name)
+	if r == 0 {
 		Exitf("dwarf find: cannot find %s", name)
 	}
 	return r
 }
 
-func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 {
+func (d *dwctxt2) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) int64 {
 	var result int64
 	switch size {
 	default:
-		Errorf(s, "invalid size %d in adddwarfref\n", size)
+		d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size)
 		fallthrough
-	case ctxt.Arch.PtrSize:
-		result = s.AddAddr(ctxt.Arch, t)
+	case d.arch.PtrSize:
+		result = sb.AddAddrPlus(d.arch, t, 0)
 	case 4:
-		result = s.AddAddrPlus4(t, 0)
+		result = sb.AddAddrPlus4(d.arch, t, 0)
 	}
-	r := &s.R[len(s.R)-1]
+	rsl := sb.Relocs()
+	r := &rsl[len(rsl)-1]
 	r.Type = objabi.R_DWARFSECREF
 	return result
 }
 
-func newrefattr(die *dwarf.DWDie, attr uint16, ref *sym.Symbol) *dwarf.DWAttr {
-	if ref == nil {
+func (d *dwctxt2) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr {
+	if ref == 0 {
 		return nil
 	}
-	return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref)
+	return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref))
 }
 
-func dtolsym(s dwarf.Sym) *sym.Symbol {
+func (d *dwctxt2) dtolsym(s dwarf.Sym) loader.Sym {
 	if s == nil {
-		return nil
+		return 0
 	}
-	return s.(*sym.Symbol)
+	dws := loader.Sym(s.(dwSym))
+	return dws
 }
 
-func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*sym.Symbol, die *dwarf.DWDie) []*sym.Symbol {
-	s := dtolsym(die.Sym)
-	if s == nil {
+func (d *dwctxt2) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym {
+	s := d.dtolsym(die.Sym)
+	if s == 0 {
 		s = syms[len(syms)-1]
 	} else {
-		if s.Attr.OnList() {
-			log.Fatalf("symbol %s listed multiple times", s.Name)
+		if d.ldr.AttrOnList(s) {
+			log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s))
 		}
-		s.Attr |= sym.AttrOnList
+		d.ldr.SetAttrOnList(s, true)
 		syms = append(syms, s)
 	}
-	dwarf.Uleb128put(ctxt, s, int64(die.Abbrev))
-	dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr)
+	sDwsym := dwSym(s)
+	dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev))
+	dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr)
 	if dwarf.HasChildren(die) {
 		for die := die.Child; die != nil; die = die.Link {
-			syms = putdie(linkctxt, ctxt, syms, die)
+			syms = d.putdie(syms, die)
 		}
-		syms[len(syms)-1].AddUint8(0)
+		dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1])
+		dsu.AddUint8(0)
 	}
 	return syms
 }
@@ -329,42 +424,23 @@
 
 // GDB doesn't like FORM_addr for AT_location, so emit a
 // location expression that evals to a const.
-func newabslocexprattr(die *dwarf.DWDie, addr int64, sym *sym.Symbol) {
-	newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, sym)
-	// below
+func (d *dwctxt2) newabslocexprattr(die *dwarf.DWDie, addr int64, symIdx loader.Sym) {
+	newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, dwSym(symIdx))
 }
 
-// Lookup predefined types
-func lookupOrDiag(ctxt *Link, n string) *sym.Symbol {
-	s := ctxt.Syms.ROLookup(n, 0)
-	if s == nil || s.Size == 0 {
+func (d *dwctxt2) lookupOrDiag(n string) loader.Sym {
+	symIdx := d.ldr.Lookup(n, 0)
+	if symIdx == 0 {
 		Exitf("dwarf: missing type: %s", n)
 	}
+	if len(d.ldr.Data(symIdx)) == 0 {
+		Exitf("dwarf: missing type (no data): %s", n)
+	}
 
-	return s
+	return symIdx
 }
 
-// dwarfFuncSym looks up a DWARF metadata symbol for function symbol s.
-// If the symbol does not exist, it creates it if create is true,
-// or returns nil otherwise.
-func dwarfFuncSym(ctxt *Link, s *sym.Symbol, meta string, create bool) *sym.Symbol {
-	// All function ABIs use symbol version 0 for the DWARF data.
-	//
-	// TODO(austin): It may be useful to have DWARF info for ABI
-	// wrappers, in which case we may want these versions to
-	// align. Better yet, replace these name lookups with a
-	// general way to attach metadata to a symbol.
-	ver := 0
-	if s.IsFileLocal() {
-		ver = int(s.Version)
-	}
-	if create {
-		return ctxt.Syms.Lookup(meta+s.Name, ver)
-	}
-	return ctxt.Syms.ROLookup(meta+s.Name, ver)
-}
-
-func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie {
+func (d *dwctxt2) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, def *dwarf.DWDie) *dwarf.DWDie {
 	// Only emit typedefs for real names.
 	if strings.HasPrefix(name, "map[") {
 		return nil
@@ -382,53 +458,66 @@
 		Errorf(nil, "dwarf: bad def in dotypedef")
 	}
 
-	s := ctxt.Syms.Lookup(dtolsym(def.Sym).Name+"..def", 0)
-	s.Attr |= sym.AttrNotInSymbolTable
-	s.Type = sym.SDWARFINFO
-	def.Sym = s
+	// Create a new loader symbol for the typedef. We no longer
+	// do lookups of typedef symbols by name, so this is going
+	// to be an anonymous symbol (we want this for perf reasons).
+	tds := d.ldr.CreateExtSym("")
+	tdsu := d.ldr.MakeSymbolUpdater(tds)
+	tdsu.SetType(sym.SDWARFINFO)
+	def.Sym = dwSym(tds)
+	d.ldr.SetAttrNotInSymbolTable(tds, true)
+	d.ldr.SetAttrReachable(tds, true)
 
 	// The typedef entry must be created after the def,
 	// so that future lookups will find the typedef instead
 	// of the real definition. This hooks the typedef into any
 	// circular definition loops, so that gdb can understand them.
-	die := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
+	die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
 
-	newrefattr(die, dwarf.DW_AT_type, s)
+	d.newrefattr(die, dwarf.DW_AT_type, tds)
 
 	return die
 }
 
 // Define gotype, for composite ones recurse into constituents.
-func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol {
-	if gotype == nil {
-		return mustFind(ctxt, "<unspecified>")
+func (d *dwctxt2) defgotype(gotype loader.Sym) loader.Sym {
+	if gotype == 0 {
+		return d.mustFind("<unspecified>")
 	}
 
-	if !strings.HasPrefix(gotype.Name, "type.") {
-		Errorf(gotype, "dwarf: type name doesn't start with \"type.\"")
-		return mustFind(ctxt, "<unspecified>")
+	// If we already have a tdmap entry for the gotype, return it.
+	if ds, ok := d.tdmap[gotype]; ok {
+		return ds
 	}
 
-	name := gotype.Name[5:] // could also decode from Type.string
+	sn := d.ldr.SymName(gotype)
+	if !strings.HasPrefix(sn, "type.") {
+		d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type.\"")
+		return d.mustFind("<unspecified>")
+	}
+	name := sn[5:] // could also decode from Type.string
 
-	sdie := find(ctxt, name)
-
-	if sdie != nil {
+	sdie := d.find(name)
+	if sdie != 0 {
 		return sdie
 	}
 
-	return newtype(ctxt, gotype).Sym.(*sym.Symbol)
+	gtdwSym := d.newtype(gotype)
+	d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym))
+	return loader.Sym(gtdwSym.Sym.(dwSym))
 }
 
-func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
-	name := gotype.Name[5:] // could also decode from Type.string
-	kind := decodetypeKind(ctxt.Arch, gotype.P)
-	bytesize := decodetypeSize(ctxt.Arch, gotype.P)
+func (d *dwctxt2) newtype(gotype loader.Sym) *dwarf.DWDie {
+	sn := d.ldr.SymName(gotype)
+	name := sn[5:] // could also decode from Type.string
+	tdata := d.ldr.Data(gotype)
+	kind := decodetypeKind(d.arch, tdata)
+	bytesize := decodetypeSize(d.arch, tdata)
 
 	var die, typedefdie *dwarf.DWDie
 	switch kind {
 	case objabi.KindBool:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
@@ -437,7 +526,7 @@
 		objabi.KindInt16,
 		objabi.KindInt32,
 		objabi.KindInt64:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
@@ -447,118 +536,126 @@
 		objabi.KindUint32,
 		objabi.KindUint64,
 		objabi.KindUintptr:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindFloat32,
 		objabi.KindFloat64:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindComplex64,
 		objabi.KindComplex128:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindArray:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
-		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
+		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-		s := decodetypeArrayElem(ctxt.Arch, gotype)
-		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
-		fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0)
+		s := decodetypeArrayElem2(d.ldr, d.arch, gotype)
+		d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+		fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0)
 
 		// use actual length not upper bound; correct for 0-length arrays.
-		newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0)
+		newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen2(d.ldr, d.arch, gotype), 0)
 
-		newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+		d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
 
 	case objabi.KindChan:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0)
-		s := decodetypeChanElem(ctxt.Arch, gotype)
-		newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s))
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0)
+		s := decodetypeChanElem2(d.ldr, d.arch, gotype)
+		d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
 		// Save elem type for synthesizechantypes. We could synthesize here
 		// but that would change the order of DIEs we output.
-		newrefattr(die, dwarf.DW_AT_type, s)
+		d.newrefattr(die, dwarf.DW_AT_type, s)
 
 	case objabi.KindFunc:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
-		nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P)
+		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		data := d.ldr.Data(gotype)
+		// FIXME: add caching or reuse reloc slice.
+		relocs := d.ldr.Relocs(gotype)
+		rslice := relocs.ReadAll(nil)
+		nfields := decodetypeFuncInCount(d.arch, data)
 		for i := 0; i < nfields; i++ {
-			s := decodetypeFuncInType(ctxt.Arch, gotype, i)
-			fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
-			newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
+			s := decodetypeFuncInType2(d.ldr, d.arch, gotype, rslice, i)
+			sn := d.ldr.SymName(s)
+			fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0)
+			d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
 		}
 
-		if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) {
-			newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
+		if decodetypeFuncDotdotdot(d.arch, data) {
+			d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
 		}
-		nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P)
+		nfields = decodetypeFuncOutCount(d.arch, data)
 		for i := 0; i < nfields; i++ {
-			s := decodetypeFuncOutType(ctxt.Arch, gotype, i)
-			fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
-			newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, defgotype(ctxt, s)))
+			s := decodetypeFuncOutType2(d.ldr, d.arch, gotype, rslice, i)
+			sn := d.ldr.SymName(s)
+			fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0)
+			d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s)))
 		}
 
 	case objabi.KindInterface:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
-		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
-		nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P))
-		var s *sym.Symbol
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
+		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		data := d.ldr.Data(gotype)
+		nfields := int(decodetypeIfaceMethodCount(d.arch, data))
+		var s loader.Sym
 		if nfields == 0 {
-			s = lookupOrDiag(ctxt, "type.runtime.eface")
+			s = d.typeRuntimeEface
 		} else {
-			s = lookupOrDiag(ctxt, "type.runtime.iface")
+			s = d.typeRuntimeIface
 		}
-		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
+		d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
 
 	case objabi.KindMap:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0)
-		s := decodetypeMapKey(ctxt.Arch, gotype)
-		newrefattr(die, dwarf.DW_AT_go_key, defgotype(ctxt, s))
-		s = decodetypeMapValue(ctxt.Arch, gotype)
-		newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s))
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0)
+		s := decodetypeMapKey2(d.ldr, d.arch, gotype)
+		d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s))
+		s = decodetypeMapValue2(d.ldr, d.arch, gotype)
+		d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
 		// Save gotype for use in synthesizemaptypes. We could synthesize here,
 		// but that would change the order of the DIEs.
-		newrefattr(die, dwarf.DW_AT_type, gotype)
+		d.newrefattr(die, dwarf.DW_AT_type, gotype)
 
 	case objabi.KindPtr:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0)
-		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
-		s := decodetypePtrElem(ctxt.Arch, gotype)
-		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0)
+		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		s := decodetypePtrElem2(d.ldr, d.arch, gotype)
+		d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
 
 	case objabi.KindSlice:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
-		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
+		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-		s := decodetypeArrayElem(ctxt.Arch, gotype)
-		elem := defgotype(ctxt, s)
-		newrefattr(die, dwarf.DW_AT_go_elem, elem)
+		s := decodetypeArrayElem2(d.ldr, d.arch, gotype)
+		elem := d.defgotype(s)
+		d.newrefattr(die, dwarf.DW_AT_go_elem, elem)
 
 	case objabi.KindString:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindStruct:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
-		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
+		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-		nfields := decodetypeStructFieldCount(ctxt.Arch, gotype)
+		nfields := decodetypeStructFieldCount2(d.ldr, d.arch, gotype)
 		for i := 0; i < nfields; i++ {
-			f := decodetypeStructFieldName(ctxt.Arch, gotype, i)
-			s := decodetypeStructFieldType(ctxt.Arch, gotype, i)
+			f := decodetypeStructFieldName2(d.ldr, d.arch, gotype, i)
+			s := decodetypeStructFieldType2(d.ldr, d.arch, gotype, i)
 			if f == "" {
-				f = s.Name[5:] // skip "type."
+				sn := d.ldr.SymName(s)
+				f = sn[5:] // skip "type."
 			}
-			fld := newdie(ctxt, die, dwarf.DW_ABRV_STRUCTFIELD, f, 0)
-			newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
-			offsetAnon := decodetypeStructFieldOffsAnon(ctxt.Arch, gotype, i)
+			fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f, 0)
+			d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+			offsetAnon := decodetypeStructFieldOffsAnon2(d.ldr, d.arch, gotype, i)
 			newmemberoffsetattr(fld, int32(offsetAnon>>1))
 			if offsetAnon&1 != 0 { // is embedded field
 				newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0)
@@ -566,21 +663,33 @@
 		}
 
 	case objabi.KindUnsafePointer:
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0)
 
 	default:
-		Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
-		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0)
-		newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "<unspecified>"))
+		d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0)
+		d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>"))
 	}
 
 	newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0)
-	if gotype.Attr.Reachable() {
-		newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype)
+
+	if d.ldr.AttrReachable(gotype) {
+		newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype))
 	}
 
-	if _, ok := prototypedies[gotype.Name]; ok {
-		prototypedies[gotype.Name] = die
+	// Sanity check.
+	if _, ok := d.rtmap[gotype]; ok {
+		log.Fatalf("internal error: rtmap entry already installed\n")
+	}
+
+	ds := loader.Sym(die.Sym.(dwSym))
+	if typedefdie != nil {
+		ds = loader.Sym(typedefdie.Sym.(dwSym))
+	}
+	d.rtmap[ds] = gotype
+
+	if _, ok := prototypedies[sn]; ok {
+		prototypedies[sn] = die
 	}
 
 	if typedefdie != nil {
@@ -589,55 +698,67 @@
 	return die
 }
 
-func nameFromDIESym(dwtype *sym.Symbol) string {
-	return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def")
+func (d *dwctxt2) nameFromDIESym(dwtypeDIESym loader.Sym) string {
+	sn := d.ldr.SymName(dwtypeDIESym)
+	return sn[len(dwarf.InfoPrefix):]
 }
 
-// Find or construct *T given T.
-func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol {
-	ptrname := "*" + nameFromDIESym(dwtype)
-	if die := find(ctxt, ptrname); die != nil {
+func (d *dwctxt2) defptrto(dwtype loader.Sym) loader.Sym {
+
+	// FIXME: it would be nice if the compiler attached an aux symbol
+	// ref from the element type to the pointer type -- it would be
+	// more efficient to do it this way as opposed to via name lookups.
+
+	ptrname := "*" + d.nameFromDIESym(dwtype)
+	if die := d.find(ptrname); die != 0 {
 		return die
 	}
 
-	pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
-	newrefattr(pdie, dwarf.DW_AT_type, dwtype)
+	pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
+	d.newrefattr(pdie, dwarf.DW_AT_type, dwtype)
 
 	// The DWARF info synthesizes pointer types that don't exist at the
 	// language level, like *hash<...> and *bucket<...>, and the data
 	// pointers of slices. Link to the ones we can find.
-	gotype := ctxt.Syms.ROLookup("type."+ptrname, 0)
-	if gotype != nil && gotype.Attr.Reachable() {
-		newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype)
+	gts := d.ldr.Lookup("type."+ptrname, 0)
+	if gts != 0 && d.ldr.AttrReachable(gts) {
+		newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts))
 	}
-	return dtolsym(pdie.Sym)
+
+	if gts != 0 {
+		ds := loader.Sym(pdie.Sym.(dwSym))
+		d.rtmap[ds] = gts
+		d.tdmap[gts] = ds
+	}
+
+	return d.dtolsym(pdie.Sym)
 }
 
 // Copies src's children into dst. Copies attributes by value.
 // DWAttr.data is copied as pointer only. If except is one of
 // the top-level children, it will not be copied.
-func copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) {
+func (d *dwctxt2) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) {
 	for src = src.Child; src != nil; src = src.Link {
 		if src == except {
 			continue
 		}
-		c := newdie(ctxt, dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0)
+		c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0)
 		for a := src.Attr; a != nil; a = a.Link {
 			newattr(c, a.Atr, int(a.Cls), a.Value, a.Data)
 		}
-		copychildrenexcept(ctxt, c, src, nil)
+		d.copychildrenexcept(ctxt, c, src, nil)
 	}
 
 	reverselist(&dst.Child)
 }
 
-func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
-	copychildrenexcept(ctxt, dst, src, nil)
+func (d *dwctxt2) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
+	d.copychildrenexcept(ctxt, dst, src, nil)
 }
 
 // Search children (assumed to have TAG_member) for the one named
 // field and set its AT_type to dwtype
-func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) {
+func (d *dwctxt2) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) {
 	child := findchild(structdie, field)
 	if child == nil {
 		Exitf("dwarf substitutetype: %s does not have member %s",
@@ -647,23 +768,26 @@
 
 	a := getattr(child, dwarf.DW_AT_type)
 	if a != nil {
-		a.Data = dwtype
+		a.Data = dwSym(dwtype)
 	} else {
-		newrefattr(child, dwarf.DW_AT_type, dwtype)
+		d.newrefattr(child, dwarf.DW_AT_type, dwtype)
 	}
 }
 
-func findprotodie(ctxt *Link, name string) *dwarf.DWDie {
+func (d *dwctxt2) findprotodie(ctxt *Link, name string) *dwarf.DWDie {
 	die, ok := prototypedies[name]
 	if ok && die == nil {
-		defgotype(ctxt, lookupOrDiag(ctxt, name))
+		d.defgotype(d.lookupOrDiag(name))
 		die = prototypedies[name]
 	}
+	if die == nil {
+		log.Fatalf("internal error: DIE generation failed for %s\n", name)
+	}
 	return die
 }
 
-func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
-	prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF"))
+func (d *dwctxt2) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
+	prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.stringStructDWARF"))
 	if prototype == nil {
 		return
 	}
@@ -672,12 +796,12 @@
 		if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE {
 			continue
 		}
-		copychildren(ctxt, die, prototype)
+		d.copychildren(ctxt, die, prototype)
 	}
 }
 
-func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
-	prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice"))
+func (d *dwctxt2) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
+	prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.slice"))
 	if prototype == nil {
 		return
 	}
@@ -686,9 +810,9 @@
 		if die.Abbrev != dwarf.DW_ABRV_SLICETYPE {
 			continue
 		}
-		copychildren(ctxt, die, prototype)
-		elem := getattr(die, dwarf.DW_AT_go_elem).Data.(*sym.Symbol)
-		substitutetype(die, "array", defptrto(ctxt, elem))
+		d.copychildren(ctxt, die, prototype)
+		elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym))
+		d.substitutetype(die, "array", d.defptrto(elem))
 	}
 }
 
@@ -706,21 +830,21 @@
 	BucketSize = 8
 )
 
-func mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *sym.Symbol {
+func (d *dwctxt2) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym {
 	name := mkinternaltypename(typename, keyname, valname)
 	symname := dwarf.InfoPrefix + name
-	s := ctxt.Syms.ROLookup(symname, 0)
-	if s != nil && s.Type == sym.SDWARFINFO {
+	s := d.ldr.Lookup(symname, 0)
+	if s != 0 && d.ldr.SymType(s) == sym.SDWARFINFO {
 		return s
 	}
-	die := newdie(ctxt, &dwtypes, abbrev, name, 0)
+	die := d.newdie(&dwtypes, abbrev, name, 0)
 	f(die)
-	return dtolsym(die.Sym)
+	return d.dtolsym(die.Sym)
 }
 
-func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
-	hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap"))
-	bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap"))
+func (d *dwctxt2) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
+	hash := walktypedef(d.findprotodie(ctxt, "type.runtime.hmap"))
+	bucket := walktypedef(d.findprotodie(ctxt, "type.runtime.bmap"))
 
 	if hash == nil {
 		return
@@ -730,92 +854,94 @@
 		if die.Abbrev != dwarf.DW_ABRV_MAPTYPE {
 			continue
 		}
-		gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol)
-		keytype := decodetypeMapKey(ctxt.Arch, gotype)
-		valtype := decodetypeMapValue(ctxt.Arch, gotype)
-		keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P)
-		keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype))
+		gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+		keytype := decodetypeMapKey2(d.ldr, d.arch, gotype)
+		valtype := decodetypeMapValue2(d.ldr, d.arch, gotype)
+		keydata := d.ldr.Data(keytype)
+		valdata := d.ldr.Data(valtype)
+		keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata)
+		keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype))
 
 		// compute size info like hashmap.c does.
 		indirectKey, indirectVal := false, false
 		if keysize > MaxKeySize {
-			keysize = int64(ctxt.Arch.PtrSize)
+			keysize = int64(d.arch.PtrSize)
 			indirectKey = true
 		}
 		if valsize > MaxValSize {
-			valsize = int64(ctxt.Arch.PtrSize)
+			valsize = int64(d.arch.PtrSize)
 			indirectVal = true
 		}
 
 		// Construct type to represent an array of BucketSize keys
-		keyname := nameFromDIESym(keytype)
-		dwhks := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
+		keyname := d.nameFromDIESym(keytype)
+		dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
 			newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0)
 			t := keytype
 			if indirectKey {
-				t = defptrto(ctxt, keytype)
+				t = d.defptrto(keytype)
 			}
-			newrefattr(dwhk, dwarf.DW_AT_type, t)
-			fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+			d.newrefattr(dwhk, dwarf.DW_AT_type, t)
+			fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
 			newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
-			newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+			d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
 		})
 
 		// Construct type to represent an array of BucketSize values
-		valname := nameFromDIESym(valtype)
-		dwhvs := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
+		valname := d.nameFromDIESym(valtype)
+		dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
 			newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0)
 			t := valtype
 			if indirectVal {
-				t = defptrto(ctxt, valtype)
+				t = d.defptrto(valtype)
 			}
-			newrefattr(dwhv, dwarf.DW_AT_type, t)
-			fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+			d.newrefattr(dwhv, dwarf.DW_AT_type, t)
+			fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
 			newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
-			newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+			d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
 		})
 
 		// Construct bucket<K,V>
-		dwhbs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
+		dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
 			// Copy over all fields except the field "data" from the generic
 			// bucket. "data" will be replaced with keys/values below.
-			copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
+			d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
 
-			fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0)
-			newrefattr(fld, dwarf.DW_AT_type, dwhks)
+			fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0)
+			d.newrefattr(fld, dwarf.DW_AT_type, dwhks)
 			newmemberoffsetattr(fld, BucketSize)
-			fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0)
-			newrefattr(fld, dwarf.DW_AT_type, dwhvs)
+			fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0)
+			d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
 			newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
-			fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
-			newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym)))
+			fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
+			d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym)))
 			newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
-			if ctxt.Arch.RegSize > ctxt.Arch.PtrSize {
-				fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0)
-				newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
-				newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(ctxt.Arch.PtrSize))
+			if d.arch.RegSize > d.arch.PtrSize {
+				fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0)
+				d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+				newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize))
 			}
 
-			newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(ctxt.Arch.RegSize), 0)
+			newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0)
 		})
 
 		// Construct hash<K,V>
-		dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
-			copychildren(ctxt, dwh, hash)
-			substitutetype(dwh, "buckets", defptrto(ctxt, dwhbs))
-			substitutetype(dwh, "oldbuckets", defptrto(ctxt, dwhbs))
+		dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
+			d.copychildren(ctxt, dwh, hash)
+			d.substitutetype(dwh, "buckets", d.defptrto(dwhbs))
+			d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs))
 			newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil)
 		})
 
 		// make map type a pointer to hash<K,V>
-		newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
+		d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
 	}
 }
 
-func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
-	sudog := walktypedef(findprotodie(ctxt, "type.runtime.sudog"))
-	waitq := walktypedef(findprotodie(ctxt, "type.runtime.waitq"))
-	hchan := walktypedef(findprotodie(ctxt, "type.runtime.hchan"))
+func (d *dwctxt2) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
+	sudog := walktypedef(d.findprotodie(ctxt, "type.runtime.sudog"))
+	waitq := walktypedef(d.findprotodie(ctxt, "type.runtime.waitq"))
+	hchan := walktypedef(d.findprotodie(ctxt, "type.runtime.hchan"))
 	if sudog == nil || waitq == nil || hchan == nil {
 		return
 	}
@@ -826,130 +952,103 @@
 		if die.Abbrev != dwarf.DW_ABRV_CHANTYPE {
 			continue
 		}
-		elemgotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol)
-		elemname := elemgotype.Name[5:]
-		elemtype := walksymtypedef(ctxt, defgotype(ctxt, elemgotype))
+		elemgotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+		tname := d.ldr.SymName(elemgotype)
+		elemname := tname[5:]
+		elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname)))
 
 		// sudog<T>
-		dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
-			copychildren(ctxt, dws, sudog)
-			substitutetype(dws, "elem", defptrto(ctxt, elemtype))
+		dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
+			d.copychildren(ctxt, dws, sudog)
+			d.substitutetype(dws, "elem", d.defptrto(elemtype))
 			newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil)
 		})
 
 		// waitq<T>
-		dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) {
+		dwws := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) {
 
-			copychildren(ctxt, dww, waitq)
-			substitutetype(dww, "first", defptrto(ctxt, dwss))
-			substitutetype(dww, "last", defptrto(ctxt, dwss))
+			d.copychildren(ctxt, dww, waitq)
+			d.substitutetype(dww, "first", d.defptrto(dwss))
+			d.substitutetype(dww, "last", d.defptrto(dwss))
 			newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil)
 		})
 
 		// hchan<T>
-		dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
-			copychildren(ctxt, dwh, hchan)
-			substitutetype(dwh, "recvq", dwws)
-			substitutetype(dwh, "sendq", dwws)
+		dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
+			d.copychildren(ctxt, dwh, hchan)
+			d.substitutetype(dwh, "recvq", dwws)
+			d.substitutetype(dwh, "sendq", dwws)
 			newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil)
 		})
 
-		newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
+		d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
 	}
 }
 
-func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *sym.Symbol) {
+func (d *dwctxt2) dwarfDefineGlobal(ctxt *Link, symIdx loader.Sym, str string, v int64, gotype loader.Sym) {
 	// Find a suitable CU DIE to include the global.
 	// One would think it's as simple as just looking at the unit, but that might
 	// not have any reachable code. So, we go to the runtime's CU if our unit
 	// isn't otherwise reachable.
-	var unit *sym.CompilationUnit
-	if s.Unit != nil {
-		unit = s.Unit
-	} else {
+	unit := d.ldr.SymUnit(symIdx)
+	if unit == nil {
 		unit = ctxt.runtimeCU
 	}
-	dv := newdie(ctxt, unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version))
-	newabslocexprattr(dv, v, s)
-	if !s.IsFileLocal() {
+	ver := d.ldr.SymVersion(symIdx)
+	dv := d.newdie(unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(ver))
+	d.newabslocexprattr(dv, v, symIdx)
+	if d.ldr.SymVersion(symIdx) < sym.SymVerStatic {
 		newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0)
 	}
-	dt := defgotype(ctxt, gotype)
-	newrefattr(dv, dwarf.DW_AT_type, dt)
-}
-
-// For use with pass.c::genasmsym
-func defdwsymb(ctxt *Link, s *sym.Symbol, str string, t SymbolType, v int64, gotype *sym.Symbol) {
-	if strings.HasPrefix(str, "go.string.") {
-		return
-	}
-	if strings.HasPrefix(str, "runtime.gcbits.") {
-		return
-	}
-
-	switch t {
-	case DataSym, BSSSym:
-		switch s.Type {
-		case sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS:
-			// ok
-		case sym.SRODATA:
-			if gotype != nil {
-				defgotype(ctxt, gotype)
-			}
-			return
-		default:
-			return
-		}
-		if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) {
-			return
-		}
-		dwarfDefineGlobal(ctxt, s, str, v, gotype)
-
-	case AutoSym, ParamSym, DeletedAutoSym:
-		defgotype(ctxt, gotype)
-	}
+	dt := d.defgotype(gotype)
+	d.newrefattr(dv, dwarf.DW_AT_type, dt)
 }
 
 // createUnitLength creates the initial length field with value v and update
 // offset of unit_length if needed.
-func createUnitLength(ctxt *Link, s *sym.Symbol, v uint64) {
-	if isDwarf64(ctxt) {
-		s.AddUint32(ctxt.Arch, 0xFFFFFFFF)
+func (d *dwctxt2) createUnitLength(su *loader.SymbolBuilder, v uint64) {
+	if isDwarf64(d.linkctxt) {
+		su.AddUint32(d.arch, 0xFFFFFFFF)
 	}
-	addDwarfAddrField(ctxt, s, v)
+	d.addDwarfAddrField(su, v)
 }
 
 // addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits.
-func addDwarfAddrField(ctxt *Link, s *sym.Symbol, v uint64) {
-	if isDwarf64(ctxt) {
-		s.AddUint(ctxt.Arch, v)
+func (d *dwctxt2) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) {
+	if isDwarf64(d.linkctxt) {
+		sb.AddUint(d.arch, v)
 	} else {
-		s.AddUint32(ctxt.Arch, uint32(v))
+		sb.AddUint32(d.arch, uint32(v))
 	}
 }
 
 // addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits.
-func addDwarfAddrRef(ctxt *Link, s *sym.Symbol, t *sym.Symbol) {
-	if isDwarf64(ctxt) {
-		adddwarfref(ctxt, s, t, 8)
+func (d *dwctxt2) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) {
+	if isDwarf64(d.linkctxt) {
+		d.adddwarfref(sb, t, 8)
 	} else {
-		adddwarfref(ctxt, s, t, 4)
+		d.adddwarfref(sb, t, 4)
 	}
 }
 
 // calcCompUnitRanges calculates the PC ranges of the compilation units.
-func calcCompUnitRanges(ctxt *Link) {
+func (d *dwctxt2) calcCompUnitRanges() {
 	var prevUnit *sym.CompilationUnit
-	for _, s := range ctxt.Textp {
-		if s.FuncInfo == nil {
+	for _, s := range d.linkctxt.Textp2 {
+		sym := loader.Sym(s)
+
+		fi := d.ldr.FuncInfo(sym)
+		if !fi.Valid() {
 			continue
 		}
+
 		// Skip linker-created functions (ex: runtime.addmoduledata), since they
 		// don't have DWARF to begin with.
-		if s.Unit == nil {
+		unit := d.ldr.SymUnit(sym)
+		if unit == nil {
 			continue
 		}
-		unit := s.Unit
+
 		// Update PC ranges.
 		//
 		// We don't simply compare the end of the previous
@@ -957,11 +1056,13 @@
 		// often a little padding between them. Instead, we
 		// only create boundaries between symbols from
 		// different units.
+		sval := d.ldr.SymValue(sym)
+		u0val := d.ldr.SymValue(loader.Sym(unit.Textp2[0]))
 		if prevUnit != unit {
-			unit.PCs = append(unit.PCs, dwarf.Range{Start: s.Value - unit.Textp[0].Value})
+			unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val})
 			prevUnit = unit
 		}
-		unit.PCs[len(unit.PCs)-1].End = s.Value - unit.Textp[0].Value + s.Size
+		unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym)))
 	}
 }
 
@@ -977,24 +1078,6 @@
 	die.Link = parent.Child
 }
 
-// If the pcln table contains runtime/proc.go, use that to set gdbscript path.
-func finddebugruntimepath(s *sym.Symbol) {
-	if gdbscript != "" {
-		return
-	}
-
-	for i := range s.FuncInfo.File {
-		f := s.FuncInfo.File[i]
-		// We can't use something that may be dead-code
-		// eliminated from a binary here. proc.go contains
-		// main and the scheduler, so it's not going anywhere.
-		if i := strings.Index(f.Name, "runtime/proc.go"); i >= 0 {
-			gdbscript = f.Name[:i] + "runtime/runtime-gdb.py"
-			break
-		}
-	}
-}
-
 /*
  * Generate a sequence of opcodes that is as short as possible.
  * See section 6.2.5
@@ -1020,115 +1103,134 @@
 	return "."
 }
 
-func importInfoSymbol(ctxt *Link, dsym *sym.Symbol) {
-	dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable
-	dsym.Type = sym.SDWARFINFO
-	for i := range dsym.R {
-		r := &dsym.R[i] // Copying sym.Reloc has measurable impact on performance
-		if r.Type == objabi.R_DWARFSECREF && r.Sym.Size == 0 {
-			n := nameFromDIESym(r.Sym)
-			defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0))
+func (d *dwctxt2) importInfoSymbol(ctxt *Link, dsym loader.Sym) {
+	d.ldr.SetAttrReachable(dsym, true)
+	d.ldr.SetAttrNotInSymbolTable(dsym, true)
+	if d.ldr.SymType(dsym) != sym.SDWARFINFO {
+		log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String())
+	}
+	drelocs := d.ldr.Relocs(dsym)
+	rslice := drelocs.ReadSyms(nil)
+	for i := 0; i < len(rslice); i++ {
+		r := &rslice[i]
+		if r.Type != objabi.R_DWARFSECREF {
+			continue
 		}
+		// If there is an entry for the symbol in our rtmap, then it
+		// means we've processed the type already, and can skip this one.
+		if _, ok := d.rtmap[r.Sym]; ok {
+			// type already generated
+			continue
+		}
+		// FIXME: is there a way we could avoid materializing the
+		// symbol name here?
+		sn := d.ldr.SymName(r.Sym)
+		tn := sn[len(dwarf.InfoPrefix):]
+		ts := d.ldr.Lookup("type."+tn, 0)
+		d.defgotype(ts)
 	}
 }
 
-func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
+func expandFile(fname string) string {
+	if strings.HasPrefix(fname, src.FileSymPrefix) {
+		fname = fname[len(src.FileSymPrefix):]
+	}
+	return expandGoroot(fname)
+}
 
-	var dwarfctxt dwarf.Context = dwctxt{ctxt}
+func expandFileSym(l *loader.Loader, fsym loader.Sym) string {
+	return expandFile(l.SymName(fsym))
+}
+
+func (d *dwctxt2) writelines(unit *sym.CompilationUnit, ls loader.Sym) {
+
 	is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles.
 
 	unitstart := int64(-1)
 	headerstart := int64(-1)
 	headerend := int64(-1)
 
-	newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, ls.Size, ls)
+	lsu := d.ldr.MakeSymbolUpdater(ls)
+	newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, lsu.Size(), dwSym(ls))
 
 	// Write .debug_line Line Number Program Header (sec 6.2.4)
 	// Fields marked with (*) must be changed for 64-bit dwarf
-	unitLengthOffset := ls.Size
-	createUnitLength(ctxt, ls, 0) // unit_length (*), filled in at end
-	unitstart = ls.Size
-	ls.AddUint16(ctxt.Arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
-	headerLengthOffset := ls.Size
-	addDwarfAddrField(ctxt, ls, 0) // header_length (*), filled in at end
-	headerstart = ls.Size
+	unitLengthOffset := lsu.Size()
+	d.createUnitLength(lsu, 0) // unit_length (*), filled in at end
+
+	unitstart = lsu.Size()
+	lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
+	headerLengthOffset := lsu.Size()
+	d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end
+	headerstart = lsu.Size()
 
 	// cpos == unitstart + 4 + 2 + 4
-	ls.AddUint8(1)                // minimum_instruction_length
-	ls.AddUint8(is_stmt)          // default_is_stmt
-	ls.AddUint8(LINE_BASE & 0xFF) // line_base
-	ls.AddUint8(LINE_RANGE)       // line_range
-	ls.AddUint8(OPCODE_BASE)      // opcode_base
-	ls.AddUint8(0)                // standard_opcode_lengths[1]
-	ls.AddUint8(1)                // standard_opcode_lengths[2]
-	ls.AddUint8(1)                // standard_opcode_lengths[3]
-	ls.AddUint8(1)                // standard_opcode_lengths[4]
-	ls.AddUint8(1)                // standard_opcode_lengths[5]
-	ls.AddUint8(0)                // standard_opcode_lengths[6]
-	ls.AddUint8(0)                // standard_opcode_lengths[7]
-	ls.AddUint8(0)                // standard_opcode_lengths[8]
-	ls.AddUint8(1)                // standard_opcode_lengths[9]
-	ls.AddUint8(0)                // standard_opcode_lengths[10]
-	ls.AddUint8(0)                // include_directories  (empty)
+	lsu.AddUint8(1)                // minimum_instruction_length
+	lsu.AddUint8(is_stmt)          // default_is_stmt
+	lsu.AddUint8(LINE_BASE & 0xFF) // line_base
+	lsu.AddUint8(LINE_RANGE)       // line_range
+	lsu.AddUint8(OPCODE_BASE)      // opcode_base
+	lsu.AddUint8(0)                // standard_opcode_lengths[1]
+	lsu.AddUint8(1)                // standard_opcode_lengths[2]
+	lsu.AddUint8(1)                // standard_opcode_lengths[3]
+	lsu.AddUint8(1)                // standard_opcode_lengths[4]
+	lsu.AddUint8(1)                // standard_opcode_lengths[5]
+	lsu.AddUint8(0)                // standard_opcode_lengths[6]
+	lsu.AddUint8(0)                // standard_opcode_lengths[7]
+	lsu.AddUint8(0)                // standard_opcode_lengths[8]
+	lsu.AddUint8(1)                // standard_opcode_lengths[9]
+	lsu.AddUint8(0)                // standard_opcode_lengths[10]
+	lsu.AddUint8(0)                // include_directories  (empty)
+
+	// Maps loader.Sym for file symbol to expanded filename.
+	expandedFiles := make(map[loader.Sym]string)
 
 	// Copy over the file table.
 	fileNums := make(map[string]int)
+	lsDwsym := dwSym(ls)
 	for i, name := range unit.DWARFFileTable {
-		if len(name) != 0 {
-			if strings.HasPrefix(name, src.FileSymPrefix) {
-				name = name[len(src.FileSymPrefix):]
-			}
-			name = expandGoroot(name)
-		} else {
-			// Can't have empty filenames, and having a unique filename is quite useful
-			// for debugging.
+		name := expandFile(name)
+		if len(name) == 0 {
+			// Can't have empty filenames, and having a unique
+			// filename is quite useful for debugging.
 			name = fmt.Sprintf("<missing>_%d", i)
 		}
 		fileNums[name] = i + 1
-		dwarfctxt.AddString(ls, name)
-		ls.AddUint8(0)
-		ls.AddUint8(0)
-		ls.AddUint8(0)
-	}
-	// Grab files for inlined functions.
-	// TODO: With difficulty, this could be moved into the compiler.
-	for _, s := range unit.Textp {
-		dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true)
-		for ri := 0; ri < len(dsym.R); ri++ {
-			r := &dsym.R[ri]
-			if r.Type != objabi.R_DWARFFILEREF {
-				continue
+		d.AddString(lsDwsym, name)
+		lsu.AddUint8(0)
+		lsu.AddUint8(0)
+		lsu.AddUint8(0)
+		if gdbscript == "" {
+			// We can't use something that may be dead-code
+			// eliminated from a binary here. proc.go contains
+			// main and the scheduler, so it's not going anywhere.
+			if i := strings.Index(name, "runtime/proc.go"); i >= 0 {
+				k := strings.Index(name, "runtime/proc.go")
+				gdbscript = name[:k] + "runtime/runtime-gdb.py"
 			}
-			name := r.Sym.Name
-			if _, ok := fileNums[name]; ok {
-				continue
-			}
-			fileNums[name] = len(fileNums) + 1
-			dwarfctxt.AddString(ls, name)
-			ls.AddUint8(0)
-			ls.AddUint8(0)
-			ls.AddUint8(0)
 		}
 	}
 
 	// 4 zeros: the string termination + 3 fields.
-	ls.AddUint8(0)
+	lsu.AddUint8(0)
 	// terminate file_names.
-	headerend = ls.Size
+	headerend = lsu.Size()
 
 	// Output the state machine for each function remaining.
 	var lastAddr int64
-	for _, s := range unit.Textp {
-		finddebugruntimepath(s)
+	rslice := []loader.Reloc{}
+	for _, s := range unit.Textp2 {
+		fnSym := loader.Sym(s)
 
 		// Set the PC.
-		ls.AddUint8(0)
-		dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize))
-		ls.AddUint8(dwarf.DW_LNE_set_address)
-		addr := ls.AddAddr(ctxt.Arch, s)
+		lsu.AddUint8(0)
+		dwarf.Uleb128put(d, lsDwsym, 1+int64(d.arch.PtrSize))
+		lsu.AddUint8(dwarf.DW_LNE_set_address)
+		addr := lsu.AddAddrPlus(d.arch, fnSym, 0)
 		// Make sure the units are sorted.
 		if addr < lastAddr {
-			Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr)
+			d.linkctxt.Errorf(fnSym, "address wasn't increasing %x < %x",
+				addr, lastAddr)
 		}
 		lastAddr = addr
 
@@ -1138,25 +1240,25 @@
 		// together rather then the append() below. This would allow us to have
 		// the compiler emit the DW_LNE_set_address and a rope data structure
 		// to concat them all together in the output.
-		lines := dwarfFuncSym(ctxt, s, dwarf.DebugLinesPrefix, false)
-		if lines != nil {
-			ls.P = append(ls.P, lines.P...)
+		_, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+		if lines != 0 {
+			lsu.AddBytes(d.ldr.Data(lines))
 		}
 	}
 
-	ls.AddUint8(0) // start extended opcode
-	dwarf.Uleb128put(dwarfctxt, ls, 1)
-	ls.AddUint8(dwarf.DW_LNE_end_sequence)
+	lsu.AddUint8(0) // start extended opcode
+	dwarf.Uleb128put(d, lsDwsym, 1)
+	lsu.AddUint8(dwarf.DW_LNE_end_sequence)
 
-	if ctxt.HeadType == objabi.Haix {
-		saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(ls.Size-unitLengthOffset))
+	if d.linkctxt.HeadType == objabi.Haix {
+		saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(lsu.Size()-unitLengthOffset))
 	}
-	if isDwarf64(ctxt) {
-		ls.SetUint(ctxt.Arch, unitLengthOffset+4, uint64(ls.Size-unitstart)) // +4 because of 0xFFFFFFFF
-		ls.SetUint(ctxt.Arch, headerLengthOffset, uint64(headerend-headerstart))
+	if isDwarf64(d.linkctxt) {
+		lsu.SetUint(d.arch, unitLengthOffset+4, uint64(lsu.Size()-unitstart)) // +4 because of 0xFFFFFFFF
+		lsu.SetUint(d.arch, headerLengthOffset, uint64(headerend-headerstart))
 	} else {
-		ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart))
-		ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart))
+		lsu.SetUint32(d.arch, unitLengthOffset, uint32(lsu.Size()-unitstart))
+		lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart))
 	}
 
 	// Process any R_DWARFFILEREF relocations, since we now know the
@@ -1166,56 +1268,80 @@
 	// DIE flavors (ex: variables) then those DIEs would need to
 	// be included below.
 	missing := make(map[int]interface{})
-	s := unit.Textp[0]
-	for _, f := range unit.FuncDIEs {
-		for ri := range f.R {
-			r := &f.R[ri]
+	for _, f := range unit.FuncDIEs2 {
+		fnSym := loader.Sym(f)
+
+		// Create a symbol updater prior to looking at the relocations
+		// on the DWARF subprogram DIE symbol. We need to do this here
+		// so that any modifications to the reloc slice will get
+		// stored in loader payload for the symbol (as opposed to a
+		// temporary slice of relocs read from the object file). Copy
+		// back relocations with updated addends.
+		fnu := d.ldr.MakeSymbolUpdater(fnSym)
+
+		relocs := d.ldr.Relocs(fnSym)
+		rslice = relocs.ReadAll(rslice)
+
+		for ri := range rslice {
+			r := &rslice[ri]
 			if r.Type != objabi.R_DWARFFILEREF {
 				continue
 			}
-			idx, ok := fileNums[r.Sym.Name]
+
+			fname, ok := expandedFiles[r.Sym]
+			if !ok {
+				fname = expandFileSym(d.ldr, r.Sym)
+				expandedFiles[r.Sym] = fname
+			}
+
+			idx, ok := fileNums[fname]
 			if ok {
 				if int(int32(idx)) != idx {
-					Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow")
+					d.linkctxt.Errorf(fnSym, "bad R_DWARFFILEREF relocation: file index overflow")
 				}
-				if r.Siz != 4 {
-					Errorf(f, "bad R_DWARFFILEREF relocation: has size %d, expected 4", r.Siz)
-				}
-				if r.Off < 0 || r.Off+4 > int32(len(f.P)) {
-					Errorf(f, "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d", r.Off, len(s.P))
-					continue
+				if r.Size != 4 {
+					d.linkctxt.Errorf(fnSym, "bad R_DWARFFILEREF relocation: has size %d, expected 4", r.Size)
 				}
 				if r.Add != 0 {
-					Errorf(f, "bad R_DWARFFILEREF relocation: addend not zero")
+					d.linkctxt.Errorf(fnSym, "bad R_DWARFFILEREF relocation: addend not zero")
 				}
-				r.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable
+				if r.Off < 0 || r.Off+4 > int32(len(fnu.Data())) {
+					d.linkctxt.Errorf(fnSym, "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d", r.Off, len(fnu.Data()))
+					continue
+				}
+
+				d.ldr.SetAttrReachable(r.Sym, true)
+				d.ldr.SetAttrNotInSymbolTable(r.Sym, true)
 				r.Add = int64(idx) // record the index in r.Add, we'll apply it in the reloc phase.
 			} else {
-				_, found := missing[int(r.Sym.Value)]
+				sv := d.ldr.SymValue(r.Sym)
+				_, found := missing[int(sv)]
 				if !found {
-					Errorf(f, "R_DWARFFILEREF relocation file missing: %v idx %d", r.Sym, r.Sym.Value)
-					missing[int(r.Sym.Value)] = nil
+					d.linkctxt.Errorf(fnSym, "R_DWARFFILEREF relocation file missing: %s idx %d symVal %d", d.ldr.SymName(r.Sym), r.Sym, sv)
+					missing[int(sv)] = nil
 				}
 			}
 		}
+		fnu.WriteRelocs(rslice)
 	}
 }
 
 // writepcranges generates the DW_AT_ranges table for compilation unit cu.
-func writepcranges(ctxt *Link, unit *sym.CompilationUnit, base *sym.Symbol, pcs []dwarf.Range, ranges *sym.Symbol) {
-	var dwarfctxt dwarf.Context = dwctxt{ctxt}
+func (d *dwctxt2) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, ranges loader.Sym) {
 
-	unitLengthOffset := ranges.Size
+	rsu := d.ldr.MakeSymbolUpdater(ranges)
+	rDwSym := dwSym(ranges)
+
+	unitLengthOffset := rsu.Size()
 
 	// Create PC ranges for this CU.
-	newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, ranges.Size, ranges)
-	newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, base.Value, base)
-	dwarf.PutBasedRanges(dwarfctxt, ranges, pcs)
+	newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym)
+	newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base))
+	dwarf.PutBasedRanges(d, rDwSym, pcs)
 
-	if ctxt.HeadType == objabi.Haix {
-		addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(ranges.Size-unitLengthOffset))
+	if d.linkctxt.HeadType == objabi.Haix {
+		addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rsu.Size()-unitLengthOffset))
 	}
-
 }
 
 /*
@@ -1246,84 +1372,91 @@
 	return b
 }
 
-func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
-	var dwarfctxt dwarf.Context = dwctxt{ctxt}
-	fs := ctxt.Syms.Lookup(".debug_frame", 0)
-	fs.Type = sym.SDWARFSECT
+func (d *dwctxt2) writeframes(syms []loader.Sym) []loader.Sym {
+	fs := d.ldr.AddExtSym(".debug_frame", 0)
+	fsd := dwSym(fs)
+	fsu := d.ldr.MakeSymbolUpdater(fs)
+	fsu.SetType(sym.SDWARFSECT)
 	syms = append(syms, fs)
+	isdw64 := isDwarf64(d.linkctxt)
+	haslr := haslinkregister(d.linkctxt)
 
 	// Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64
 	lengthFieldSize := int64(4)
-	if isDwarf64(ctxt) {
+	if isdw64 {
 		lengthFieldSize += 8
 	}
 
 	// Emit the CIE, Section 6.4.1
 	cieReserve := uint32(16)
-	if haslinkregister(ctxt) {
+	if haslr {
 		cieReserve = 32
 	}
-	if isDwarf64(ctxt) {
+	if isdw64 {
 		cieReserve += 4 // 4 bytes added for cid
 	}
-	createUnitLength(ctxt, fs, uint64(cieReserve))             // initial length, must be multiple of thearch.ptrsize
-	addDwarfAddrField(ctxt, fs, ^uint64(0))                    // cid
-	fs.AddUint8(3)                                             // dwarf version (appendix F)
-	fs.AddUint8(0)                                             // augmentation ""
-	dwarf.Uleb128put(dwarfctxt, fs, 1)                         // code_alignment_factor
-	dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor)       // all CFI offset calculations include multiplication with this factor
-	dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // return_address_register
+	d.createUnitLength(fsu, uint64(cieReserve))         // initial length, must be multiple of thearch.ptrsize
+	d.addDwarfAddrField(fsu, ^uint64(0))                // cid
+	fsu.AddUint8(3)                                     // dwarf version (appendix F)
+	fsu.AddUint8(0)                                     // augmentation ""
+	dwarf.Uleb128put(d, fsd, 1)                         // code_alignment_factor
+	dwarf.Sleb128put(d, fsd, dataAlignmentFactor)       // all CFI offset calculations include multiplication with this factor
+	dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register
 
-	fs.AddUint8(dwarf.DW_CFA_def_cfa)                          // Set the current frame address..
-	dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
-	if haslinkregister(ctxt) {
-		dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset.
+	fsu.AddUint8(dwarf.DW_CFA_def_cfa)                  // Set the current frame address..
+	dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
+	if haslr {
+		dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset.
 
-		fs.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
-		dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr))
+		fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
+		dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr))
 
-		fs.AddUint8(dwarf.DW_CFA_val_offset)                       // The previous value...
-		dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...of the platform's SP register...
-		dwarf.Uleb128put(dwarfctxt, fs, int64(0))                  // ...is CFA+0.
+		fsu.AddUint8(dwarf.DW_CFA_val_offset)               // The previous value...
+		dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register...
+		dwarf.Uleb128put(d, fsd, int64(0))                  // ...is CFA+0.
 	} else {
-		dwarf.Uleb128put(dwarfctxt, fs, int64(ctxt.Arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
+		dwarf.Uleb128put(d, fsd, int64(d.arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
 
-		fs.AddUint8(dwarf.DW_CFA_offset_extended)                                      // The previous value...
-		dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr))                     // ...of the return address...
-		dwarf.Uleb128put(dwarfctxt, fs, int64(-ctxt.Arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
+		fsu.AddUint8(dwarf.DW_CFA_offset_extended)                           // The previous value...
+		dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr))                  // ...of the return address...
+		dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
 	}
 
-	pad := int64(cieReserve) + lengthFieldSize - fs.Size
+	pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs)))
 
 	if pad < 0 {
 		Exitf("dwarf: cieReserve too small by %d bytes.", -pad)
 	}
 
-	fs.AddBytes(zeros[:pad])
+	fsu.AddBytes(zeros[:pad])
 
 	var deltaBuf []byte
-	pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
-	for _, s := range ctxt.Textp {
-		if s.FuncInfo == nil {
+	pcsp := obj.NewPCIter(uint32(d.arch.MinLC))
+	for _, s := range d.linkctxt.Textp2 {
+		fn := loader.Sym(s)
+		fi := d.ldr.FuncInfo(fn)
+		if !fi.Valid() {
 			continue
 		}
+		fpcsp := fi.Pcsp()
 
 		// Emit a FDE, Section 6.4.1.
 		// First build the section contents into a byte buffer.
 		deltaBuf = deltaBuf[:0]
-		if haslinkregister(ctxt) && s.Attr.TopFrame() {
+		if haslr && d.ldr.AttrTopFrame(fn) {
 			// Mark the link register as having an undefined value.
 			// This stops call stack unwinders progressing any further.
 			// TODO: similar mark on non-LR architectures.
 			deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined)
 			deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
 		}
-		for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() {
+
+		for pcsp.Init(fpcsp); !pcsp.Done; pcsp.Next() {
 			nextpc := pcsp.NextPC
 
 			// pciterinit goes up to the end of the function,
 			// but DWARF expects us to stop just before the end.
-			if int64(nextpc) == s.Size {
+			if int64(nextpc) == int64(len(d.ldr.Data(fn))) {
 				nextpc--
 				if nextpc < pcsp.PC {
 					continue
@@ -1331,12 +1464,12 @@
 			}
 
 			spdelta := int64(pcsp.Value)
-			if !haslinkregister(ctxt) {
+			if !haslr {
 				// Return address has been pushed onto stack.
-				spdelta += int64(ctxt.Arch.PtrSize)
+				spdelta += int64(d.arch.PtrSize)
 			}
 
-			if haslinkregister(ctxt) && !s.Attr.TopFrame() {
+			if haslr && !d.ldr.AttrTopFrame(fn) {
 				// TODO(bryanpkc): This is imprecise. In general, the instruction
 				// that stores the return address to the stack frame is not the
 				// same one that allocates the frame.
@@ -1354,9 +1487,9 @@
 				}
 			}
 
-			deltaBuf = appendPCDeltaCFA(ctxt.Arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta)
+			deltaBuf = appendPCDeltaCFA(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta)
 		}
-		pad := int(Rnd(int64(len(deltaBuf)), int64(ctxt.Arch.PtrSize))) - len(deltaBuf)
+		pad := int(Rnd(int64(len(deltaBuf)), int64(d.arch.PtrSize))) - len(deltaBuf)
 		deltaBuf = append(deltaBuf, zeros[:pad]...)
 
 		// Emit the FDE header, Section 6.4.1.
@@ -1365,48 +1498,61 @@
 		//	ptrsize: initial location
 		//	ptrsize: address range
 
-		fdeLength := uint64(4 + 2*ctxt.Arch.PtrSize + len(deltaBuf))
-		if isDwarf64(ctxt) {
+		fdeLength := uint64(4 + 2*d.arch.PtrSize + len(deltaBuf))
+		if isdw64 {
 			fdeLength += 4 // 4 bytes added for CIE pointer
 		}
-		createUnitLength(ctxt, fs, fdeLength)
+		d.createUnitLength(fsu, fdeLength)
 
-		if ctxt.LinkMode == LinkExternal {
-			addDwarfAddrRef(ctxt, fs, fs)
+		if d.linkctxt.LinkMode == LinkExternal {
+			d.addDwarfAddrRef(fsu, fs)
 		} else {
-			addDwarfAddrField(ctxt, fs, 0) // CIE offset
+			d.addDwarfAddrField(fsu, 0) // CIE offset
 		}
-		fs.AddAddr(ctxt.Arch, s)
-		fs.AddUintXX(ctxt.Arch, uint64(s.Size), ctxt.Arch.PtrSize) // address range
-		fs.AddBytes(deltaBuf)
+		fsu.AddAddrPlus(d.arch, s, 0)
+		fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range
+		fsu.AddBytes(deltaBuf)
 
-		if ctxt.HeadType == objabi.Haix {
-			addDwsectCUSize(".debug_frame", s.File, fdeLength+uint64(lengthFieldSize))
+		if d.linkctxt.HeadType == objabi.Haix {
+			addDwsectCUSize(".debug_frame", d.ldr.SymFile(fn), fdeLength+uint64(lengthFieldSize))
 		}
 	}
+
 	return syms
 }
 
 /*
  *  Walk DWarfDebugInfoEntries, and emit .debug_info
  */
+
 const (
 	COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
 )
 
-func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abbrevsym *sym.Symbol, pubNames, pubTypes *pubWriter) []*sym.Symbol {
-	infosec := ctxt.Syms.Lookup(".debug_info", 0)
-	infosec.Type = sym.SDWARFINFO
-	infosec.Attr |= sym.AttrReachable
-	syms = append(syms, infosec)
+// appendSyms appends the syms from 'src' into 'syms' and returns the
+// result. This can go away once we do away with sym.LoaderSym
+// entirely.
+func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym {
+	for _, s := range src {
+		syms = append(syms, loader.Sym(s))
+	}
+	return syms
+}
 
-	var dwarfctxt dwarf.Context = dwctxt{ctxt}
+func (d *dwctxt2) writeinfo(syms []loader.Sym, units []*sym.CompilationUnit, abbrevsym loader.Sym, pubNames, pubTypes *pubWriter2) []loader.Sym {
+
+	infosec := d.ldr.AddExtSym(".debug_info", 0)
+	disu := d.ldr.MakeSymbolUpdater(infosec)
+	disu.SetType(sym.SDWARFINFO)
+	d.ldr.SetAttrReachable(infosec, true)
+	syms = append(syms, infosec)
 
 	for _, u := range units {
 		compunit := u.DWInfo
-		s := dtolsym(compunit.Sym)
+		s := d.dtolsym(compunit.Sym)
+		su := d.ldr.MakeSymbolUpdater(s)
 
-		if len(u.Textp) == 0 && u.DWInfo.Child == nil {
+		if len(u.Textp2) == 0 && u.DWInfo.Child == nil {
 			continue
 		}
 
@@ -1416,59 +1562,62 @@
 		// Write .debug_info Compilation Unit Header (sec 7.5.1)
 		// Fields marked with (*) must be changed for 64-bit dwarf
 		// This must match COMPUNITHEADERSIZE above.
-		createUnitLength(ctxt, s, 0) // unit_length (*), will be filled in later.
-		s.AddUint16(ctxt.Arch, 4)    // dwarf version (appendix F)
+		d.createUnitLength(su, 0) // unit_length (*), will be filled in later.
+		su.AddUint16(d.arch, 4)   // dwarf version (appendix F)
 
 		// debug_abbrev_offset (*)
-		addDwarfAddrRef(ctxt, s, abbrevsym)
+		d.addDwarfAddrRef(su, abbrevsym)
 
-		s.AddUint8(uint8(ctxt.Arch.PtrSize)) // address_size
+		su.AddUint8(uint8(d.arch.PtrSize)) // address_size
 
-		dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev))
-		dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr)
+		ds := dwSym(s)
+		dwarf.Uleb128put(d, ds, int64(compunit.Abbrev))
+		dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr)
 
-		cu := []*sym.Symbol{s}
-		cu = append(cu, u.AbsFnDIEs...)
-		cu = append(cu, u.FuncDIEs...)
-		if u.Consts != nil {
-			cu = append(cu, u.Consts)
+		cu := []loader.Sym{s}
+		cu = appendSyms(cu, u.AbsFnDIEs2)
+		cu = appendSyms(cu, u.FuncDIEs2)
+		if u.Consts2 != 0 {
+			cu = append(cu, loader.Sym(u.Consts2))
 		}
 		var cusize int64
 		for _, child := range cu {
-			cusize += child.Size
+			cusize += int64(len(d.ldr.Data(child)))
 		}
 
 		for die := compunit.Child; die != nil; die = die.Link {
 			l := len(cu)
-			lastSymSz := cu[l-1].Size
-			cu = putdie(ctxt, dwarfctxt, cu, die)
+			lastSymSz := int64(len(d.ldr.Data(cu[l-1])))
+			cu = d.putdie(cu, die)
 			if ispubname(die) {
 				pubNames.add(die, cusize)
 			}
 			if ispubtype(die) {
 				pubTypes.add(die, cusize)
 			}
-			if lastSymSz != cu[l-1].Size {
+			if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) {
 				// putdie will sometimes append directly to the last symbol of the list
-				cusize = cusize - lastSymSz + cu[l-1].Size
+				cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1])))
 			}
 			for _, child := range cu[l:] {
-				cusize += child.Size
+				cusize += int64(len(d.ldr.Data(child)))
 			}
 		}
-		cu[len(cu)-1].AddUint8(0) // closes compilation unit DIE
+
+		culu := d.ldr.MakeSymbolUpdater(cu[len(cu)-1])
+		culu.AddUint8(0) // closes compilation unit DIE
 		cusize++
 
 		// Save size for AIX symbol table.
-		if ctxt.HeadType == objabi.Haix {
-			saveDwsectCUSize(".debug_info", getPkgFromCUSym(s), uint64(cusize))
+		if d.linkctxt.HeadType == objabi.Haix {
+			saveDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize))
 		}
-		if isDwarf64(ctxt) {
-			cusize -= 12                            // exclude the length field.
-			s.SetUint(ctxt.Arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF
+		if isDwarf64(d.linkctxt) {
+			cusize -= 12                          // exclude the length field.
+			su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF
 		} else {
 			cusize -= 4 // exclude the length field.
-			s.SetUint32(ctxt.Arch, 0, uint32(cusize))
+			su.SetUint32(d.arch, 0, uint32(cusize))
 		}
 		pubNames.endCompUnit(compunit, uint32(cusize)+4)
 		pubTypes.endCompUnit(compunit, uint32(cusize)+4)
@@ -1481,6 +1630,61 @@
  *  Emit .debug_pubnames/_types.  _info must have been written before,
  *  because we need die->offs and infoo/infosize;
  */
+
+type pubWriter2 struct {
+	d     *dwctxt2
+	s     loader.Sym
+	su    *loader.SymbolBuilder
+	sname string
+
+	sectionstart int64
+	culengthOff  int64
+}
+
+func newPubWriter2(d *dwctxt2, sname string) *pubWriter2 {
+	s := d.ldr.AddExtSym(sname, 0)
+	u := d.ldr.MakeSymbolUpdater(s)
+	u.SetType(sym.SDWARFSECT)
+	return &pubWriter2{d: d, s: s, su: u, sname: sname}
+}
+
+func (pw *pubWriter2) beginCompUnit(compunit *dwarf.DWDie) {
+	pw.sectionstart = pw.su.Size()
+
+	// Write .debug_pubnames/types	Header (sec 6.1.1)
+	pw.d.createUnitLength(pw.su, 0)                         // unit_length (*), will be filled in later.
+	pw.su.AddUint16(pw.d.arch, 2)                           // dwarf version (appendix F)
+	pw.d.addDwarfAddrRef(pw.su, pw.d.dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header)
+	pw.culengthOff = pw.su.Size()
+	pw.d.addDwarfAddrField(pw.su, uint64(0)) // debug_info_length, will be filled in later.
+}
+
+func (pw *pubWriter2) add(die *dwarf.DWDie, offset int64) {
+	dwa := getattr(die, dwarf.DW_AT_name)
+	name := dwa.Data.(string)
+	if pw.d.dtolsym(die.Sym) == 0 {
+		fmt.Println("Missing sym for ", name)
+	}
+	pw.d.addDwarfAddrField(pw.su, uint64(offset))
+	pw.su.Addstring(name)
+}
+
+func (pw *pubWriter2) endCompUnit(compunit *dwarf.DWDie, culength uint32) {
+	pw.d.addDwarfAddrField(pw.su, 0) // Null offset
+
+	// On AIX, save the current size of this compilation unit.
+	if pw.d.linkctxt.HeadType == objabi.Haix {
+		saveDwsectCUSize(pw.sname, pw.d.getPkgFromCUSym(pw.d.dtolsym(compunit.Sym)), uint64(pw.su.Size()-pw.sectionstart))
+	}
+	if isDwarf64(pw.d.linkctxt) {
+		pw.su.SetUint(pw.d.arch, pw.sectionstart+4, uint64(pw.su.Size()-pw.sectionstart)-12) // exclude the length field.
+		pw.su.SetUint(pw.d.arch, pw.culengthOff, uint64(culength))
+	} else {
+		pw.su.SetUint32(pw.d.arch, pw.sectionstart, uint32(pw.su.Size()-pw.sectionstart)-4) // exclude the length field.
+		pw.su.SetUint32(pw.d.arch, pw.culengthOff, culength)
+	}
+}
+
 func ispubname(die *dwarf.DWDie) bool {
 	switch die.Abbrev {
 	case dwarf.DW_ABRV_FUNCTION, dwarf.DW_ABRV_VARIABLE:
@@ -1495,65 +1699,12 @@
 	return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE
 }
 
-type pubWriter struct {
-	ctxt  *Link
-	s     *sym.Symbol
-	sname string
-
-	sectionstart int64
-	culengthOff  int64
-}
-
-func newPubWriter(ctxt *Link, sname string) *pubWriter {
-	s := ctxt.Syms.Lookup(sname, 0)
-	s.Type = sym.SDWARFSECT
-	return &pubWriter{ctxt: ctxt, s: s, sname: sname}
-}
-
-func (pw *pubWriter) beginCompUnit(compunit *dwarf.DWDie) {
-	pw.sectionstart = pw.s.Size
-
-	// Write .debug_pubnames/types	Header (sec 6.1.1)
-	createUnitLength(pw.ctxt, pw.s, 0)                    // unit_length (*), will be filled in later.
-	pw.s.AddUint16(pw.ctxt.Arch, 2)                       // dwarf version (appendix F)
-	addDwarfAddrRef(pw.ctxt, pw.s, dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header)
-	pw.culengthOff = pw.s.Size
-	addDwarfAddrField(pw.ctxt, pw.s, uint64(0)) // debug_info_length, will be filled in later.
-
-}
-
-func (pw *pubWriter) add(die *dwarf.DWDie, offset int64) {
-	dwa := getattr(die, dwarf.DW_AT_name)
-	name := dwa.Data.(string)
-	if die.Sym == nil {
-		fmt.Println("Missing sym for ", name)
-	}
-	addDwarfAddrField(pw.ctxt, pw.s, uint64(offset))
-	Addstring(pw.s, name)
-}
-
-func (pw *pubWriter) endCompUnit(compunit *dwarf.DWDie, culength uint32) {
-	addDwarfAddrField(pw.ctxt, pw.s, 0) // Null offset
-
-	// On AIX, save the current size of this compilation unit.
-	if pw.ctxt.HeadType == objabi.Haix {
-		saveDwsectCUSize(pw.sname, getPkgFromCUSym(dtolsym(compunit.Sym)), uint64(pw.s.Size-pw.sectionstart))
-	}
-	if isDwarf64(pw.ctxt) {
-		pw.s.SetUint(pw.ctxt.Arch, pw.sectionstart+4, uint64(pw.s.Size-pw.sectionstart)-12) // exclude the length field.
-		pw.s.SetUint(pw.ctxt.Arch, pw.culengthOff, uint64(culength))
-	} else {
-		pw.s.SetUint32(pw.ctxt.Arch, pw.sectionstart, uint32(pw.s.Size-pw.sectionstart)-4) // exclude the length field.
-		pw.s.SetUint32(pw.ctxt.Arch, pw.culengthOff, culength)
-	}
-}
-
-func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
+func (d *dwctxt2) writegdbscript(syms []loader.Sym) []loader.Sym {
 	// TODO (aix): make it available
-	if ctxt.HeadType == objabi.Haix {
+	if d.linkctxt.HeadType == objabi.Haix {
 		return syms
 	}
-	if ctxt.LinkMode == LinkExternal && ctxt.HeadType == objabi.Hwindows && ctxt.BuildMode == BuildModeCArchive {
+	if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.BuildMode == BuildModeCArchive {
 		// gcc on Windows places .debug_gdb_scripts in the wrong location, which
 		// causes the program not to run. See https://golang.org/issue/20183
 		// Non c-archives can avoid this issue via a linker script
@@ -1564,16 +1715,22 @@
 	}
 
 	if gdbscript != "" {
-		s := ctxt.Syms.Lookup(".debug_gdb_scripts", 0)
-		s.Type = sym.SDWARFSECT
-		syms = append(syms, s)
-		s.AddUint8(1) // magic 1 byte?
-		Addstring(s, gdbscript)
+		gs := d.ldr.AddExtSym(".debug_gdb_scripts", 0)
+		u := d.ldr.MakeSymbolUpdater(gs)
+		u.SetType(sym.SDWARFSECT)
+
+		syms = append(syms, gs)
+		u.AddUint8(1) // magic 1 byte?
+		u.Addstring(gdbscript)
 	}
 
 	return syms
+
 }
 
+// FIXME: might be worth looking replacing this map with a function
+// that switches based on symbol instead.
+
 var prototypedies map[string]*dwarf.DWDie
 
 func dwarfEnabled(ctxt *Link) bool {
@@ -1606,11 +1763,30 @@
 	return true
 }
 
+// mkBuiltinType populates the dwctxt2 sym lookup maps for the
+// newly created builtin type DIE 'typeDie'.
+func (d *dwctxt2) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie {
+	// create type DIE
+	die := d.newdie(&dwtypes, abrv, tname, 0)
+
+	// Look up type symbol.
+	gotype := d.lookupOrDiag("type." + tname)
+
+	// Map from die sym to type sym
+	ds := loader.Sym(die.Sym.(dwSym))
+	d.rtmap[ds] = gotype
+
+	// Map from type to def sym
+	d.tdmap[gotype] = ds
+
+	return die
+}
+
 // dwarfGenerateDebugInfo generated debug info entries for all types,
 // variables and functions in the program.
 // Along with dwarfGenerateDebugSyms they are the two main entry points into
 // dwarf generation: dwarfGenerateDebugInfo does all the work that should be
-// done before symbol names are mangled while dwarfgeneratedebugsyms does
+// done before symbol names are mangled while dwarfGenerateDebugSyms does
 // all the work that can only be done after addresses have been assigned to
 // text symbols.
 func dwarfGenerateDebugInfo(ctxt *Link) {
@@ -1618,25 +1794,29 @@
 		return
 	}
 
+	d := newdwctxt2(ctxt, true)
+
 	if ctxt.HeadType == objabi.Haix {
 		// Initial map used to store package size for each DWARF section.
 		dwsectCUSize = make(map[string]uint64)
 	}
 
-	// Forctxt.Diagnostic messages.
+	// For ctxt.Diagnostic messages.
 	newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes")
 
-	// Some types that must exist to define other ones.
-	newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0)
+	// Unspecified type. There are no references to this in the symbol table.
+	d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0)
 
-	newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0)
-	newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0)
-
-	die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size
+	// Some types that must exist to define other ones (uintptr in particular
+	// is needed for array size)
+	d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer")
+	die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr")
 	newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
-	newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(ctxt.Arch.PtrSize), 0)
+	newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0)
 	newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0)
-	newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, lookupOrDiag(ctxt, "type.uintptr"))
+	newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym(d.lookupOrDiag("type.uintptr")))
+
+	d.uintptrInfoSym = d.mustFind("uintptr")
 
 	// Prototypes needed for type synthesis.
 	prototypedies = map[string]*dwarf.DWDie{
@@ -1662,23 +1842,24 @@
 		"type.runtime.interfacetype",
 		"type.runtime.itab",
 		"type.runtime.imethod"} {
-		defgotype(ctxt, lookupOrDiag(ctxt, typ))
+		d.defgotype(d.lookupOrDiag(typ))
 	}
 
 	// fake root DIE for compile unit DIEs
 	var dwroot dwarf.DWDie
 	flagVariants := make(map[string]bool)
+	var relocs []loader.Reloc
 
 	for _, lib := range ctxt.Library {
-		consts := ctxt.Syms.ROLookup(dwarf.ConstInfoPrefix+lib.Pkg, 0)
+
+		consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0)
 		for _, unit := range lib.Units {
 			// We drop the constants into the first CU.
-			if consts != nil {
-				importInfoSymbol(ctxt, consts)
-				unit.Consts = consts
-				consts = nil
+			if consts != 0 {
+				unit.Consts2 = sym.LoaderSym(consts)
+				d.importInfoSymbol(ctxt, consts)
+				consts = 0
 			}
-
 			ctxt.compUnits = append(ctxt.compUnits, unit)
 
 			// We need at least one runtime unit.
@@ -1686,7 +1867,7 @@
 				ctxt.runtimeCU = unit
 			}
 
-			unit.DWInfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0)
+			unit.DWInfo = d.newdie(&dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0)
 			newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0)
 			// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
 			compDir := getCompilationDir()
@@ -1694,17 +1875,21 @@
 			// the linker directory. If we move CU construction into the
 			// compiler, this should happen naturally.
 			newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir)
-			producerExtra := ctxt.Syms.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0)
+
+			var peData []byte
+			if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 {
+				peData = d.ldr.Data(producerExtra)
+			}
 			producer := "Go cmd/compile " + objabi.Version
-			if len(producerExtra.P) > 0 {
+			if len(peData) > 0 {
 				// We put a semicolon before the flags to clearly
 				// separate them from the version, which can be long
 				// and have lots of weird things in it in development
 				// versions. We promise not to put a semicolon in the
 				// version, so it should be safe for readers to scan
 				// forward to the semicolon.
-				producer += "; " + string(producerExtra.P)
-				flagVariants[string(producerExtra.P)] = true
+				producer += "; " + string(peData)
+				flagVariants[string(peData)] = true
 			} else {
 				flagVariants[""] = true
 			}
@@ -1712,12 +1897,13 @@
 			newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer)
 
 			var pkgname string
-			if s := ctxt.Syms.ROLookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); s != nil {
-				pkgname = string(s.P)
+			if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 {
+				pnsData := d.ldr.Data(pnSymIdx)
+				pkgname = string(pnsData)
 			}
 			newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname)
 
-			if len(unit.Textp) == 0 {
+			if len(unit.Textp2) == 0 {
 				unit.DWInfo.Abbrev = dwarf.DW_ABRV_COMPUNIT_TEXTLESS
 			}
 
@@ -1725,36 +1911,48 @@
 			// referenced types, create the file table for debug_line, find all
 			// referenced abstract functions.
 			// Collect all debug_range symbols in unit.rangeSyms
-			for _, s := range unit.Textp { // textp has been dead-code-eliminated already.
-				dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, false)
-				dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable
-				dsym.Type = sym.SDWARFINFO
-				unit.FuncDIEs = append(unit.FuncDIEs, dsym)
+			for _, s := range unit.Textp2 { // textp2 has been dead-code-eliminated already.
+				fnSym := loader.Sym(s)
+				infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+				d.ldr.SetAttrNotInSymbolTable(infosym, true)
+				d.ldr.SetAttrReachable(infosym, true)
 
-				rangeSym := dwarfFuncSym(ctxt, s, dwarf.RangePrefix, false)
-				if rangeSym != nil && rangeSym.Size > 0 {
-					rangeSym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable
-					rangeSym.Type = sym.SDWARFRANGE
+				unit.FuncDIEs2 = append(unit.FuncDIEs2, sym.LoaderSym(infosym))
+				if rangesym != 0 {
+					rs := len(d.ldr.Data(rangesym))
+					d.ldr.SetAttrNotInSymbolTable(rangesym, true)
+					d.ldr.SetAttrReachable(rangesym, true)
 					if ctxt.HeadType == objabi.Haix {
-						addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rangeSym.Size))
+						addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rs))
 					}
-					unit.RangeSyms = append(unit.RangeSyms, rangeSym)
+					unit.RangeSyms2 = append(unit.RangeSyms2, sym.LoaderSym(rangesym))
 				}
 
-				for ri := 0; ri < len(dsym.R); ri++ {
-					r := &dsym.R[ri]
+				drelocs := d.ldr.Relocs(infosym)
+				relocs = drelocs.ReadSyms(relocs)
+				for ri := 0; ri < drelocs.Count; ri++ {
+					r := &relocs[ri]
 					if r.Type == objabi.R_DWARFSECREF {
 						rsym := r.Sym
-						if strings.HasPrefix(rsym.Name, dwarf.InfoPrefix) && strings.HasSuffix(rsym.Name, dwarf.AbstractFuncSuffix) && !rsym.Attr.OnList() {
-							// abstract function
-							rsym.Attr |= sym.AttrOnList
-							unit.AbsFnDIEs = append(unit.AbsFnDIEs, rsym)
-							importInfoSymbol(ctxt, rsym)
-						} else if rsym.Size == 0 {
-							// a type we do not have a DIE for
-							n := nameFromDIESym(rsym)
-							defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0))
+						rsn := d.ldr.SymName(rsym)
+						if len(rsn) == 0 {
+							continue
 						}
+						// NB: there should be a better way to do this that doesn't involve materializing the symbol name and doing string prefix+suffix checks.
+						if strings.HasPrefix(rsn, dwarf.InfoPrefix) && strings.HasSuffix(rsn, dwarf.AbstractFuncSuffix) && !d.ldr.AttrOnList(rsym) {
+							// abstract function
+							d.ldr.SetAttrOnList(rsym, true)
+							unit.AbsFnDIEs2 = append(unit.AbsFnDIEs2, sym.LoaderSym(rsym))
+							d.importInfoSymbol(ctxt, rsym)
+							continue
+						}
+						if _, ok := d.rtmap[rsym]; ok {
+							// type already generated
+							continue
+						}
+						tn := rsn[len(dwarf.InfoPrefix):]
+						ts := d.ldr.Lookup("type."+tn, 0)
+						d.defgotype(ts)
 					}
 				}
 			}
@@ -1769,19 +1967,56 @@
 	}
 
 	// Create DIEs for global variables and the types they use.
-	genasmsym(ctxt, defdwsymb)
+	// FIXME: ideally this should be done in the compiler, since
+	// for globals there isn't any abiguity about which package
+	// a global belongs to.
+	for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ {
+		if !d.ldr.AttrReachable(idx) ||
+			d.ldr.AttrNotInSymbolTable(idx) ||
+			d.ldr.SymVersion(idx) >= sym.SymVerStatic {
+			continue
+		}
+		t := d.ldr.SymType(idx)
+		switch t {
+		case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS:
+			// ok
+		default:
+			continue
+		}
+		// Skip things with no type
+		if d.ldr.SymGoType(idx) == 0 {
+			continue
+		}
+
+		sn := d.ldr.SymName(idx)
+		if ctxt.LinkMode != LinkExternal && isStaticTemp(sn) {
+			continue
+		}
+		if sn == "" {
+			// skip aux symbols
+			continue
+		}
+
+		// Create DIE for global.
+		sv := d.ldr.SymValue(idx)
+		gt := d.ldr.SymGoType(idx)
+		d.dwarfDefineGlobal(ctxt, idx, sn, sv, gt)
+	}
 
 	// Create DIEs for variable types indirectly referenced by function
 	// autos (which may not appear directly as param/var DIEs).
 	for _, lib := range ctxt.Library {
 		for _, unit := range lib.Units {
-			lists := [][]*sym.Symbol{unit.AbsFnDIEs, unit.FuncDIEs}
+			lists := [][]sym.LoaderSym{unit.AbsFnDIEs2, unit.FuncDIEs2}
 			for _, list := range lists {
 				for _, s := range list {
-					for i := 0; i < len(s.R); i++ {
-						r := &s.R[i]
+					symIdx := loader.Sym(s)
+					srelocs := d.ldr.Relocs(symIdx)
+					relocs = srelocs.ReadSyms(relocs)
+					for i := 0; i < len(relocs); i++ {
+						r := &relocs[i]
 						if r.Type == objabi.R_USETYPE {
-							defgotype(ctxt, r.Sym)
+							d.defgotype(r.Sym)
 						}
 					}
 				}
@@ -1789,76 +2024,117 @@
 		}
 	}
 
-	synthesizestringtypes(ctxt, dwtypes.Child)
-	synthesizeslicetypes(ctxt, dwtypes.Child)
-	synthesizemaptypes(ctxt, dwtypes.Child)
-	synthesizechantypes(ctxt, dwtypes.Child)
+	d.synthesizestringtypes(ctxt, dwtypes.Child)
+	d.synthesizeslicetypes(ctxt, dwtypes.Child)
+	d.synthesizemaptypes(ctxt, dwtypes.Child)
+	d.synthesizechantypes(ctxt, dwtypes.Child)
+
+	// NB: at this stage we have all the DIE objects constructed, but
+	// they have loader.Sym attributes and not sym.Symbol attributes.
+	// At the point when loadlibfull runs we will need to visit
+	// every DIE constructed and convert the symbols.
 }
 
 // dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc,
 // debug_pubnames and debug_pubtypes. It also writes out the debug_info
-// section using symbols generated in dwarfGenerateDebugInfo.
+// section using symbols generated in dwarfGenerateDebugInfo2.
 func dwarfGenerateDebugSyms(ctxt *Link) {
 	if !dwarfEnabled(ctxt) {
 		return
 	}
+	d := &dwctxt2{
+		linkctxt: ctxt,
+		ldr:      ctxt.loader,
+		arch:     ctxt.Arch,
+	}
+	d.dwarfGenerateDebugSyms()
+}
 
-	abbrev := writeabbrev(ctxt)
-	syms := []*sym.Symbol{abbrev}
+func (d *dwctxt2) dwarfGenerateDebugSyms() {
 
-	calcCompUnitRanges(ctxt)
-	sort.Sort(compilationUnitByStartPC(ctxt.compUnits))
+	// Hack: because the "wavefront" hasn't been pushed all the way
+	// up to dodata(), there will have been changes made to the sym.Symbol's
+	// that are not yet reflected in the loader. Call a temporary
+	// loader routine that copies any changes back.
+	// WARNING: changing a symbol's content will usually require
+	// calling the loader cloneToExternal method, meaning that there
+	// can be an increase in memory, so this is likely to mess up any
+	// benchmarking runs.
+	d.ldr.PropagateSymbolChangesBackToLoader()
+
+	abbrev := d.writeabbrev()
+	syms := []loader.Sym{abbrev}
+
+	d.calcCompUnitRanges()
+	sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits))
+
+	// Create .debug_line and .debug_ranges section symbols
+	debugLine := d.ldr.AddExtSym(".debug_line", 0)
+	dlu := d.ldr.MakeSymbolUpdater(debugLine)
+	dlu.SetType(sym.SDWARFSECT)
+	d.ldr.SetAttrReachable(debugLine, true)
+	syms = append(syms, debugLine)
+
+	debugRanges := d.ldr.AddExtSym(".debug_ranges", 0)
+	dru := d.ldr.MakeSymbolUpdater(debugRanges)
+	dru.SetType(sym.SDWARFRANGE)
+	d.ldr.SetAttrReachable(debugRanges, true)
 
 	// Write per-package line and range tables and start their CU DIEs.
-	debugLine := ctxt.Syms.Lookup(".debug_line", 0)
-	debugLine.Type = sym.SDWARFSECT
-	debugRanges := ctxt.Syms.Lookup(".debug_ranges", 0)
-	debugRanges.Type = sym.SDWARFRANGE
-	debugRanges.Attr |= sym.AttrReachable
-	syms = append(syms, debugLine)
-	for _, u := range ctxt.compUnits {
+	for _, u := range d.linkctxt.compUnits {
 		reversetree(&u.DWInfo.Child)
 		if u.DWInfo.Abbrev == dwarf.DW_ABRV_COMPUNIT_TEXTLESS {
 			continue
 		}
-		writelines(ctxt, u, debugLine)
-		writepcranges(ctxt, u, u.Textp[0], u.PCs, debugRanges)
+		d.writelines(u, debugLine)
+		base := loader.Sym(u.Textp2[0])
+		d.writepcranges(u, base, u.PCs, debugRanges)
 	}
 
 	// newdie adds DIEs to the *beginning* of the parent's DIE list.
 	// Now that we're done creating DIEs, reverse the trees so DIEs
 	// appear in the order they were created.
 	reversetree(&dwtypes.Child)
-	movetomodule(ctxt, &dwtypes)
+	movetomodule(d.linkctxt, &dwtypes)
 
-	pubNames := newPubWriter(ctxt, ".debug_pubnames")
-	pubTypes := newPubWriter(ctxt, ".debug_pubtypes")
+	pubNames := newPubWriter2(d, ".debug_pubnames")
+	pubTypes := newPubWriter2(d, ".debug_pubtypes")
 
 	// Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT
-	infosyms := writeinfo(ctxt, nil, ctxt.compUnits, abbrev, pubNames, pubTypes)
+	infosyms := d.writeinfo(nil, d.linkctxt.compUnits, abbrev, pubNames, pubTypes)
 
-	syms = writeframes(ctxt, syms)
+	syms = d.writeframes(syms)
 	syms = append(syms, pubNames.s, pubTypes.s)
-	syms = writegdbscript(ctxt, syms)
-	// Now we're done writing SDWARFSECT symbols, so we can write
+	syms = d.writegdbscript(syms)
+	// We are now done writing SDWARFSECT symbols, so we can write
 	// other SDWARF* symbols.
 	syms = append(syms, infosyms...)
-	syms = collectlocs(ctxt, syms, ctxt.compUnits)
+	syms = d.collectlocs(syms, d.linkctxt.compUnits)
 	syms = append(syms, debugRanges)
-	for _, unit := range ctxt.compUnits {
-		syms = append(syms, unit.RangeSyms...)
+	for _, unit := range d.linkctxt.compUnits {
+		for _, s := range unit.RangeSyms2 {
+			syms = append(syms, loader.Sym(s))
+		}
 	}
-	dwarfp = syms
+	dwarfp2 = syms
+	dwarfp = d.ldr.PropagateLoaderChangesToSymbols(dwarfp2, d.linkctxt.Syms)
 }
 
-func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) []*sym.Symbol {
+func (d *dwctxt2) collectlocs(syms []loader.Sym, units []*sym.CompilationUnit) []loader.Sym {
 	empty := true
+	rslice := []loader.Reloc{}
 	for _, u := range units {
-		for _, fn := range u.FuncDIEs {
-			for i := range fn.R {
-				reloc := &fn.R[i] // Copying sym.Reloc has measurable impact on performance
-				if reloc.Type == objabi.R_DWARFSECREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) {
-					reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable
+		for _, fn := range u.FuncDIEs2 {
+			relocs := d.ldr.Relocs(loader.Sym(fn))
+			rslice := relocs.ReadSyms(rslice)
+			for i := range rslice {
+				reloc := &rslice[i]
+				if reloc.Type != objabi.R_DWARFSECREF {
+					continue
+				}
+				if d.ldr.SymType(reloc.Sym) == sym.SDWARFLOC {
+					d.ldr.SetAttrReachable(reloc.Sym, true)
+					d.ldr.SetAttrNotInSymbolTable(reloc.Sym, true)
 					syms = append(syms, reloc.Sym)
 					empty = false
 					// One location list entry per function, but many relocations to it. Don't duplicate.
@@ -1867,152 +2143,44 @@
 			}
 		}
 	}
+
 	// Don't emit .debug_loc if it's empty -- it makes the ARM linker mad.
 	if !empty {
-		locsym := ctxt.Syms.Lookup(".debug_loc", 0)
-		locsym.Type = sym.SDWARFLOC
-		locsym.Attr |= sym.AttrReachable
+		locsym := d.ldr.AddExtSym(".debug_loc", 0)
+		u := d.ldr.MakeSymbolUpdater(locsym)
+		u.SetType(sym.SDWARFLOC)
+		d.ldr.SetAttrReachable(locsym, true)
 		syms = append(syms, locsym)
 	}
 	return syms
 }
 
-// Read a pointer-sized uint from the beginning of buf.
-func readPtr(ctxt *Link, buf []byte) uint64 {
-	switch ctxt.Arch.PtrSize {
-	case 4:
-		return uint64(ctxt.Arch.ByteOrder.Uint32(buf))
-	case 8:
-		return ctxt.Arch.ByteOrder.Uint64(buf)
-	default:
-		panic("unexpected pointer size")
-	}
-}
-
 /*
  *  Elf.
  */
-func dwarfaddshstrings(ctxt *Link, shstrtab *sym.Symbol) {
-	if *FlagW { // disable dwarf
-		return
-	}
-
-	secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"}
-	for _, sec := range secs {
-		Addstring(shstrtab, ".debug_"+sec)
-		if ctxt.LinkMode == LinkExternal {
-			Addstring(shstrtab, elfRelType+".debug_"+sec)
-		} else {
-			Addstring(shstrtab, ".zdebug_"+sec)
-		}
-	}
+func (d *dwctxt2) dwarfaddshstrings(ctxt *Link, shstrtab loader.Sym) {
+	panic("not yet implemented")
 }
 
 // Add section symbols for DWARF debug info.  This is called before
 // dwarfaddelfheaders.
-func dwarfaddelfsectionsyms(ctxt *Link) {
-	if *FlagW { // disable dwarf
-		return
-	}
-	if ctxt.LinkMode != LinkExternal {
-		return
-	}
-
-	s := ctxt.Syms.Lookup(".debug_info", 0)
-	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
-	s = ctxt.Syms.Lookup(".debug_abbrev", 0)
-	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
-	s = ctxt.Syms.Lookup(".debug_line", 0)
-	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
-	s = ctxt.Syms.Lookup(".debug_frame", 0)
-	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
-	s = ctxt.Syms.Lookup(".debug_loc", 0)
-	if s.Sect != nil {
-		putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
-	}
-	s = ctxt.Syms.Lookup(".debug_ranges", 0)
-	if s.Sect != nil {
-		putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
-	}
+func (d *dwctxt2) dwarfaddelfsectionsyms(ctxt *Link) {
+	panic("not yet implemented")
 }
 
 // dwarfcompress compresses the DWARF sections. Relocations are applied
 // on the fly. After this, dwarfp will contain a different (new) set of
 // symbols, and sections may have been replaced.
-func dwarfcompress(ctxt *Link) {
-	supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin
-	if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal {
-		return
-	}
-
-	var start int
-	var newDwarfp []*sym.Symbol
-	Segdwarf.Sections = Segdwarf.Sections[:0]
-	for i, s := range dwarfp {
-		// Find the boundaries between sections and compress
-		// the whole section once we've found the last of its
-		// symbols.
-		if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect {
-			s1 := compressSyms(ctxt, dwarfp[start:i+1])
-			if s1 == nil {
-				// Compression didn't help.
-				newDwarfp = append(newDwarfp, dwarfp[start:i+1]...)
-				Segdwarf.Sections = append(Segdwarf.Sections, s.Sect)
-			} else {
-				compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):]
-				sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04)
-				sect.Length = uint64(len(s1))
-				newSym := ctxt.Syms.Lookup(compressedSegName, 0)
-				newSym.P = s1
-				newSym.Size = int64(len(s1))
-				newSym.Sect = sect
-				newDwarfp = append(newDwarfp, newSym)
-			}
-			start = i + 1
-		}
-	}
-	dwarfp = newDwarfp
-	ctxt.relocbuf = nil // no longer needed, don't hold it live
-
-	// Re-compute the locations of the compressed DWARF symbols
-	// and sections, since the layout of these within the file is
-	// based on Section.Vaddr and Symbol.Value.
-	pos := Segdwarf.Vaddr
-	var prevSect *sym.Section
-	for _, s := range dwarfp {
-		s.Value = int64(pos)
-		if s.Sect != prevSect {
-			s.Sect.Vaddr = uint64(s.Value)
-			prevSect = s.Sect
-		}
-		if s.Sub != nil {
-			log.Fatalf("%s: unexpected sub-symbols", s)
-		}
-		pos += uint64(s.Size)
-		if ctxt.HeadType == objabi.Hwindows {
-			pos = uint64(Rnd(int64(pos), PEFILEALIGN))
-		}
-
-	}
-	Segdwarf.Length = pos - Segdwarf.Vaddr
+func (d *dwctxt2) dwarfcompress(ctxt *Link) {
+	panic("not yet implemented")
 }
 
-type compilationUnitByStartPC []*sym.CompilationUnit
-
-func (v compilationUnitByStartPC) Len() int      { return len(v) }
-func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
-
-func (v compilationUnitByStartPC) Less(i, j int) bool {
-	switch {
-	case len(v[i].Textp) == 0 && len(v[j].Textp) == 0:
-		return v[i].Lib.Pkg < v[j].Lib.Pkg
-	case len(v[i].Textp) != 0 && len(v[j].Textp) == 0:
-		return true
-	case len(v[i].Textp) == 0 && len(v[j].Textp) != 0:
-		return false
-	default:
-		return v[i].Textp[0].Value < v[j].Textp[0].Value
-	}
+// getPkgFromCUSym returns the package name for the compilation unit
+// represented by s.
+// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get
+// the package name.
+func (d *dwctxt2) getPkgFromCUSym(s loader.Sym) string {
+	return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.")
 }
 
 // On AIX, the symbol table needs to know where are the compilation units parts
@@ -2034,11 +2202,3 @@
 func addDwsectCUSize(sname string, pkgname string, size uint64) {
 	dwsectCUSize[sname+"."+pkgname] += size
 }
-
-// getPkgFromCUSym returns the package name for the compilation unit
-// represented by s.
-// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get
-// the package name.
-func getPkgFromCUSym(s *sym.Symbol) string {
-	return strings.TrimPrefix(s.Name, dwarf.InfoPrefix+".pkg.")
-}
diff --git a/src/cmd/link/internal/ld/dwarf2.go b/src/cmd/link/internal/ld/dwarf2.go
new file mode 100644
index 0000000..233cd6a
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf2.go
@@ -0,0 +1,172 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO/NICETOHAVE:
+//   - eliminate DW_CLS_ if not used
+//   - package info in compilation units
+//   - assign types to their packages
+//   - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg
+//     ptype struct '[]uint8' and qualifiers need to be quoted away
+//   - file:line info for variables
+//   - make strings a typedef so prettyprinters can see the underlying string type
+
+package ld
+
+import (
+	"cmd/internal/objabi"
+	"cmd/link/internal/loader"
+	"cmd/link/internal/sym"
+	"log"
+)
+
+func isDwarf64(ctxt *Link) bool {
+	return ctxt.HeadType == objabi.Haix
+}
+
+var dwarfp []*sym.Symbol
+
+/*
+ *  Elf.
+ */
+func dwarfaddshstrings(ctxt *Link, shstrtab *loader.SymbolBuilder) {
+	if *FlagW { // disable dwarf
+		return
+	}
+
+	secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"}
+	for _, sec := range secs {
+		shstrtab.Addstring(".debug_" + sec)
+		if ctxt.IsExternal() {
+			shstrtab.Addstring(elfRelType + ".debug_" + sec)
+		} else {
+			shstrtab.Addstring(".zdebug_" + sec)
+		}
+	}
+}
+
+// Add section symbols for DWARF debug info.  This is called before
+// dwarfaddelfheaders.
+func dwarfaddelfsectionsyms(ctxt *Link) {
+	if *FlagW { // disable dwarf
+		return
+	}
+	if ctxt.LinkMode != LinkExternal {
+		return
+	}
+
+	s := ctxt.Syms.Lookup(".debug_info", 0)
+	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+	s = ctxt.Syms.Lookup(".debug_abbrev", 0)
+	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+	s = ctxt.Syms.Lookup(".debug_line", 0)
+	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+	s = ctxt.Syms.Lookup(".debug_frame", 0)
+	putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+	s = ctxt.Syms.Lookup(".debug_loc", 0)
+	if s.Sect != nil {
+		putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+	}
+	s = ctxt.Syms.Lookup(".debug_ranges", 0)
+	if s.Sect != nil {
+		putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+	}
+}
+
+// dwarfcompress compresses the DWARF sections. Relocations are applied
+// on the fly. After this, dwarfp will contain a different (new) set of
+// symbols, and sections may have been replaced.
+func dwarfcompress(ctxt *Link) {
+	// compressedSect is a helper type for parallelizing compression.
+	type compressedSect struct {
+		index      int
+		compressed []byte
+		syms       []*sym.Symbol
+	}
+
+	supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin
+	if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal {
+		return
+	}
+
+	var start, compressedCount int
+	resChannel := make(chan compressedSect)
+	for i, s := range dwarfp {
+		// Find the boundaries between sections and compress
+		// the whole section once we've found the last of its
+		// symbols.
+		if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect {
+			go func(resIndex int, syms []*sym.Symbol) {
+				resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms}
+			}(compressedCount, dwarfp[start:i+1])
+			compressedCount++
+			start = i + 1
+		}
+	}
+	res := make([]compressedSect, compressedCount)
+	for ; compressedCount > 0; compressedCount-- {
+		r := <-resChannel
+		res[r.index] = r
+	}
+
+	var newDwarfp []*sym.Symbol
+	Segdwarf.Sections = Segdwarf.Sections[:0]
+	for _, z := range res {
+		s := z.syms[0]
+		if z.compressed == nil {
+			// Compression didn't help.
+			newDwarfp = append(newDwarfp, z.syms...)
+			Segdwarf.Sections = append(Segdwarf.Sections, s.Sect)
+		} else {
+			compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):]
+			sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04)
+			sect.Length = uint64(len(z.compressed))
+			newSym := ctxt.Syms.Lookup(compressedSegName, 0)
+			newSym.P = z.compressed
+			newSym.Size = int64(len(z.compressed))
+			newSym.Sect = sect
+			newDwarfp = append(newDwarfp, newSym)
+		}
+	}
+	dwarfp = newDwarfp
+
+	// Re-compute the locations of the compressed DWARF symbols
+	// and sections, since the layout of these within the file is
+	// based on Section.Vaddr and Symbol.Value.
+	pos := Segdwarf.Vaddr
+	var prevSect *sym.Section
+	for _, s := range dwarfp {
+		s.Value = int64(pos)
+		if s.Sect != prevSect {
+			s.Sect.Vaddr = uint64(s.Value)
+			prevSect = s.Sect
+		}
+		if s.Sub != nil {
+			log.Fatalf("%s: unexpected sub-symbols", s)
+		}
+		pos += uint64(s.Size)
+		if ctxt.HeadType == objabi.Hwindows {
+			pos = uint64(Rnd(int64(pos), PEFILEALIGN))
+		}
+
+	}
+	Segdwarf.Length = pos - Segdwarf.Vaddr
+}
+
+type compilationUnitByStartPC []*sym.CompilationUnit
+
+func (v compilationUnitByStartPC) Len() int      { return len(v) }
+func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
+
+func (v compilationUnitByStartPC) Less(i, j int) bool {
+	switch {
+	case len(v[i].Textp2) == 0 && len(v[j].Textp2) == 0:
+		return v[i].Lib.Pkg < v[j].Lib.Pkg
+	case len(v[i].Textp2) != 0 && len(v[j].Textp2) == 0:
+		return true
+	case len(v[i].Textp2) == 0 && len(v[j].Textp2) != 0:
+		return false
+	default:
+		return v[i].PCs[0].Start < v[j].PCs[0].Start
+	}
+}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index b7221f0..078b0a5 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -7,6 +7,7 @@
 import (
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"crypto/sha1"
 	"encoding/binary"
@@ -742,7 +743,40 @@
 	return h
 }
 
-func Elfwritedynent(ctxt *Link, s *sym.Symbol, tag int, val uint64) {
+func elfWriteDynEnt(arch *sys.Arch, s *sym.Symbol, tag int, val uint64) {
+	if elf64 {
+		s.AddUint64(arch, uint64(tag))
+		s.AddUint64(arch, val)
+	} else {
+		s.AddUint32(arch, uint32(tag))
+		s.AddUint32(arch, uint32(val))
+	}
+}
+
+func elfWriteDynEntSym(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) {
+	Elfwritedynentsymplus(arch, s, tag, t, 0)
+}
+
+func Elfwritedynentsymplus(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol, add int64) {
+	if elf64 {
+		s.AddUint64(arch, uint64(tag))
+	} else {
+		s.AddUint32(arch, uint32(tag))
+	}
+	s.AddAddrPlus(arch, t, add)
+}
+
+func elfWriteDynEntSymSize(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) {
+	if elf64 {
+		s.AddUint64(arch, uint64(tag))
+	} else {
+		s.AddUint32(arch, uint32(tag))
+	}
+	s.AddSize(arch, t)
+}
+
+// temporary
+func Elfwritedynent2(ctxt *Link, s *loader.SymbolBuilder, tag int, val uint64) {
 	if elf64 {
 		s.AddUint64(ctxt.Arch, uint64(tag))
 		s.AddUint64(ctxt.Arch, val)
@@ -752,11 +786,11 @@
 	}
 }
 
-func elfwritedynentsym(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) {
-	Elfwritedynentsymplus(ctxt, s, tag, t, 0)
+func elfwritedynentsym2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) {
+	Elfwritedynentsymplus2(ctxt, s, tag, t, 0)
 }
 
-func Elfwritedynentsymplus(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol, add int64) {
+func Elfwritedynentsymplus2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym, add int64) {
 	if elf64 {
 		s.AddUint64(ctxt.Arch, uint64(tag))
 	} else {
@@ -765,7 +799,7 @@
 	s.AddAddrPlus(ctxt.Arch, t, add)
 }
 
-func elfwritedynentsymsize(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) {
+func elfwritedynentsymsize2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) {
 	if elf64 {
 		s.AddUint64(ctxt.Arch, uint64(tag))
 	} else {
@@ -1121,23 +1155,23 @@
 	s = ctxt.Syms.Lookup(".dynamic", 0)
 	elfverneed = nfile
 	if elfverneed != 0 {
-		elfwritedynentsym(ctxt, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0))
-		Elfwritedynent(ctxt, s, DT_VERNEEDNUM, uint64(nfile))
-		elfwritedynentsym(ctxt, s, DT_VERSYM, ctxt.Syms.Lookup(".gnu.version", 0))
+		elfWriteDynEntSym(ctxt.Arch, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0))
+		elfWriteDynEnt(ctxt.Arch, s, DT_VERNEEDNUM, uint64(nfile))
+		elfWriteDynEntSym(ctxt.Arch, s, DT_VERSYM, ctxt.Syms.Lookup(".gnu.version", 0))
 	}
 
 	sy := ctxt.Syms.Lookup(elfRelType+".plt", 0)
 	if sy.Size > 0 {
 		if elfRelType == ".rela" {
-			Elfwritedynent(ctxt, s, DT_PLTREL, DT_RELA)
+			elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_RELA)
 		} else {
-			Elfwritedynent(ctxt, s, DT_PLTREL, DT_REL)
+			elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_REL)
 		}
-		elfwritedynentsymsize(ctxt, s, DT_PLTRELSZ, sy)
-		elfwritedynentsym(ctxt, s, DT_JMPREL, sy)
+		elfWriteDynEntSymSize(ctxt.Arch, s, DT_PLTRELSZ, sy)
+		elfWriteDynEntSym(ctxt.Arch, s, DT_JMPREL, sy)
 	}
 
-	Elfwritedynent(ctxt, s, DT_NULL, 0)
+	elfWriteDynEnt(ctxt.Arch, s, DT_NULL, 0)
 }
 
 func elfphload(seg *sym.Segment) *ElfPhdr {
@@ -1400,9 +1434,10 @@
 }
 
 func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
-	s := ctxt.Syms.Lookup(sectionName, 0)
-	s.Attr |= sym.AttrReachable
-	s.Type = sym.SELFROSECT
+	ldr := ctxt.loader
+	s := ldr.CreateSymForUpdate(sectionName, 0)
+	s.SetReachable(true)
+	s.SetType(sym.SELFROSECT)
 	// namesz
 	s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME)))
 	// descsz
@@ -1410,93 +1445,91 @@
 	// tag
 	s.AddUint32(ctxt.Arch, tag)
 	// name + padding
-	s.P = append(s.P, ELF_NOTE_GO_NAME...)
-	for len(s.P)%4 != 0 {
-		s.P = append(s.P, 0)
+	s.AddBytes(ELF_NOTE_GO_NAME)
+	for len(s.Data())%4 != 0 {
+		s.AddUint8(0)
 	}
 	// desc + padding
-	s.P = append(s.P, desc...)
-	for len(s.P)%4 != 0 {
-		s.P = append(s.P, 0)
+	s.AddBytes(desc)
+	for len(s.Data())%4 != 0 {
+		s.AddUint8(0)
 	}
-	s.Size = int64(len(s.P))
-	s.Align = 4
+	s.SetSize(int64(len(s.Data())))
+	s.SetAlign(4)
 }
 
 func (ctxt *Link) doelf() {
-	if !ctxt.IsELF {
-		return
-	}
+	ldr := ctxt.loader
 
 	/* predefine strings we need for section headers */
-	shstrtab := ctxt.Syms.Lookup(".shstrtab", 0)
+	shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0)
 
-	shstrtab.Type = sym.SELFROSECT
-	shstrtab.Attr |= sym.AttrReachable
+	shstrtab.SetType(sym.SELFROSECT)
+	shstrtab.SetReachable(true)
 
-	Addstring(shstrtab, "")
-	Addstring(shstrtab, ".text")
-	Addstring(shstrtab, ".noptrdata")
-	Addstring(shstrtab, ".data")
-	Addstring(shstrtab, ".bss")
-	Addstring(shstrtab, ".noptrbss")
-	Addstring(shstrtab, "__libfuzzer_extra_counters")
-	Addstring(shstrtab, ".go.buildinfo")
+	shstrtab.Addstring("")
+	shstrtab.Addstring(".text")
+	shstrtab.Addstring(".noptrdata")
+	shstrtab.Addstring(".data")
+	shstrtab.Addstring(".bss")
+	shstrtab.Addstring(".noptrbss")
+	shstrtab.Addstring("__libfuzzer_extra_counters")
+	shstrtab.Addstring(".go.buildinfo")
 
 	// generate .tbss section for dynamic internal linker or external
 	// linking, so that various binutils could correctly calculate
 	// PT_TLS size. See https://golang.org/issue/5200.
-	if !*FlagD || ctxt.LinkMode == LinkExternal {
-		Addstring(shstrtab, ".tbss")
+	if !*FlagD || ctxt.IsExternal() {
+		shstrtab.Addstring(".tbss")
 	}
-	if ctxt.HeadType == objabi.Hnetbsd {
-		Addstring(shstrtab, ".note.netbsd.ident")
+	if ctxt.IsNetbsd() {
+		shstrtab.Addstring(".note.netbsd.ident")
 	}
-	if ctxt.HeadType == objabi.Hopenbsd {
-		Addstring(shstrtab, ".note.openbsd.ident")
+	if ctxt.IsOpenbsd() {
+		shstrtab.Addstring(".note.openbsd.ident")
 	}
 	if len(buildinfo) > 0 {
-		Addstring(shstrtab, ".note.gnu.build-id")
+		shstrtab.Addstring(".note.gnu.build-id")
 	}
 	if *flagBuildid != "" {
-		Addstring(shstrtab, ".note.go.buildid")
+		shstrtab.Addstring(".note.go.buildid")
 	}
-	Addstring(shstrtab, ".elfdata")
-	Addstring(shstrtab, ".rodata")
+	shstrtab.Addstring(".elfdata")
+	shstrtab.Addstring(".rodata")
 	// See the comment about data.rel.ro.FOO section names in data.go.
 	relro_prefix := ""
 	if ctxt.UseRelro() {
-		Addstring(shstrtab, ".data.rel.ro")
+		shstrtab.Addstring(".data.rel.ro")
 		relro_prefix = ".data.rel.ro"
 	}
-	Addstring(shstrtab, relro_prefix+".typelink")
-	Addstring(shstrtab, relro_prefix+".itablink")
-	Addstring(shstrtab, relro_prefix+".gosymtab")
-	Addstring(shstrtab, relro_prefix+".gopclntab")
+	shstrtab.Addstring(relro_prefix + ".typelink")
+	shstrtab.Addstring(relro_prefix + ".itablink")
+	shstrtab.Addstring(relro_prefix + ".gosymtab")
+	shstrtab.Addstring(relro_prefix + ".gopclntab")
 
-	if ctxt.LinkMode == LinkExternal {
+	if ctxt.IsExternal() {
 		*FlagD = true
 
-		Addstring(shstrtab, elfRelType+".text")
-		Addstring(shstrtab, elfRelType+".rodata")
-		Addstring(shstrtab, elfRelType+relro_prefix+".typelink")
-		Addstring(shstrtab, elfRelType+relro_prefix+".itablink")
-		Addstring(shstrtab, elfRelType+relro_prefix+".gosymtab")
-		Addstring(shstrtab, elfRelType+relro_prefix+".gopclntab")
-		Addstring(shstrtab, elfRelType+".noptrdata")
-		Addstring(shstrtab, elfRelType+".data")
+		shstrtab.Addstring(elfRelType + ".text")
+		shstrtab.Addstring(elfRelType + ".rodata")
+		shstrtab.Addstring(elfRelType + relro_prefix + ".typelink")
+		shstrtab.Addstring(elfRelType + relro_prefix + ".itablink")
+		shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab")
+		shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab")
+		shstrtab.Addstring(elfRelType + ".noptrdata")
+		shstrtab.Addstring(elfRelType + ".data")
 		if ctxt.UseRelro() {
-			Addstring(shstrtab, elfRelType+".data.rel.ro")
+			shstrtab.Addstring(elfRelType + ".data.rel.ro")
 		}
-		Addstring(shstrtab, elfRelType+".go.buildinfo")
+		shstrtab.Addstring(elfRelType + ".go.buildinfo")
 
 		// add a .note.GNU-stack section to mark the stack as non-executable
-		Addstring(shstrtab, ".note.GNU-stack")
+		shstrtab.Addstring(".note.GNU-stack")
 
-		if ctxt.BuildMode == BuildModeShared {
-			Addstring(shstrtab, ".note.go.abihash")
-			Addstring(shstrtab, ".note.go.pkg-list")
-			Addstring(shstrtab, ".note.go.deps")
+		if ctxt.IsShared() {
+			shstrtab.Addstring(".note.go.abihash")
+			shstrtab.Addstring(".note.go.pkg-list")
+			shstrtab.Addstring(".note.go.deps")
 		}
 	}
 
@@ -1509,171 +1542,171 @@
 	}
 
 	if hasinitarr {
-		Addstring(shstrtab, ".init_array")
-		Addstring(shstrtab, elfRelType+".init_array")
+		shstrtab.Addstring(".init_array")
+		shstrtab.Addstring(elfRelType + ".init_array")
 	}
 
 	if !*FlagS {
-		Addstring(shstrtab, ".symtab")
-		Addstring(shstrtab, ".strtab")
+		shstrtab.Addstring(".symtab")
+		shstrtab.Addstring(".strtab")
 		dwarfaddshstrings(ctxt, shstrtab)
 	}
 
-	Addstring(shstrtab, ".shstrtab")
+	shstrtab.Addstring(".shstrtab")
 
 	if !*FlagD { /* -d suppresses dynamic loader format */
-		Addstring(shstrtab, ".interp")
-		Addstring(shstrtab, ".hash")
-		Addstring(shstrtab, ".got")
-		if ctxt.Arch.Family == sys.PPC64 {
-			Addstring(shstrtab, ".glink")
+		shstrtab.Addstring(".interp")
+		shstrtab.Addstring(".hash")
+		shstrtab.Addstring(".got")
+		if ctxt.IsPPC64() {
+			shstrtab.Addstring(".glink")
 		}
-		Addstring(shstrtab, ".got.plt")
-		Addstring(shstrtab, ".dynamic")
-		Addstring(shstrtab, ".dynsym")
-		Addstring(shstrtab, ".dynstr")
-		Addstring(shstrtab, elfRelType)
-		Addstring(shstrtab, elfRelType+".plt")
+		shstrtab.Addstring(".got.plt")
+		shstrtab.Addstring(".dynamic")
+		shstrtab.Addstring(".dynsym")
+		shstrtab.Addstring(".dynstr")
+		shstrtab.Addstring(elfRelType)
+		shstrtab.Addstring(elfRelType + ".plt")
 
-		Addstring(shstrtab, ".plt")
-		Addstring(shstrtab, ".gnu.version")
-		Addstring(shstrtab, ".gnu.version_r")
+		shstrtab.Addstring(".plt")
+		shstrtab.Addstring(".gnu.version")
+		shstrtab.Addstring(".gnu.version_r")
 
 		/* dynamic symbol table - first entry all zeros */
-		s := ctxt.Syms.Lookup(".dynsym", 0)
+		dynsym := ldr.CreateSymForUpdate(".dynsym", 0)
 
-		s.Type = sym.SELFROSECT
-		s.Attr |= sym.AttrReachable
+		dynsym.SetType(sym.SELFROSECT)
+		dynsym.SetReachable(true)
 		if elf64 {
-			s.Size += ELF64SYMSIZE
+			dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE)
 		} else {
-			s.Size += ELF32SYMSIZE
+			dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE)
 		}
 
 		/* dynamic string table */
-		s = ctxt.Syms.Lookup(".dynstr", 0)
+		dynstr := ldr.CreateSymForUpdate(".dynstr", 0)
 
-		s.Type = sym.SELFROSECT
-		s.Attr |= sym.AttrReachable
-		if s.Size == 0 {
-			Addstring(s, "")
+		dynstr.SetType(sym.SELFROSECT)
+		dynstr.SetReachable(true)
+		if dynstr.Size() == 0 {
+			dynstr.Addstring("")
 		}
-		dynstr := s
 
 		/* relocation table */
-		s = ctxt.Syms.Lookup(elfRelType, 0)
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFROSECT
+		s := ldr.CreateSymForUpdate(elfRelType, 0)
+		s.SetReachable(true)
+		s.SetType(sym.SELFROSECT)
 
 		/* global offset table */
-		s = ctxt.Syms.Lookup(".got", 0)
-
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFGOT // writable
+		got := ldr.CreateSymForUpdate(".got", 0)
+		got.SetReachable(true)
+		got.SetType(sym.SELFGOT) // writable
 
 		/* ppc64 glink resolver */
-		if ctxt.Arch.Family == sys.PPC64 {
-			s := ctxt.Syms.Lookup(".glink", 0)
-			s.Attr |= sym.AttrReachable
-			s.Type = sym.SELFRXSECT
+		if ctxt.IsPPC64() {
+			s := ldr.CreateSymForUpdate(".glink", 0)
+			s.SetReachable(true)
+			s.SetType(sym.SELFRXSECT)
 		}
 
 		/* hash */
-		s = ctxt.Syms.Lookup(".hash", 0)
+		hash := ldr.CreateSymForUpdate(".hash", 0)
+		hash.SetReachable(true)
+		hash.SetType(sym.SELFROSECT)
 
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFROSECT
+		gotplt := ldr.CreateSymForUpdate(".got.plt", 0)
+		gotplt.SetReachable(true)
+		gotplt.SetType(sym.SELFSECT) // writable
 
-		s = ctxt.Syms.Lookup(".got.plt", 0)
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFSECT // writable
-
-		s = ctxt.Syms.Lookup(".plt", 0)
-
-		s.Attr |= sym.AttrReachable
-		if ctxt.Arch.Family == sys.PPC64 {
+		plt := ldr.CreateSymForUpdate(".plt", 0)
+		plt.SetReachable(true)
+		if ctxt.IsPPC64() {
 			// In the ppc64 ABI, .plt is a data section
 			// written by the dynamic linker.
-			s.Type = sym.SELFSECT
+			plt.SetType(sym.SELFSECT)
 		} else {
-			s.Type = sym.SELFRXSECT
+			plt.SetType(sym.SELFRXSECT)
 		}
 
-		thearch.Elfsetupplt(ctxt)
+		s = ldr.CreateSymForUpdate(elfRelType+".plt", 0)
+		s.SetReachable(true)
+		s.SetType(sym.SELFROSECT)
 
-		s = ctxt.Syms.Lookup(elfRelType+".plt", 0)
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFROSECT
+		s = ldr.CreateSymForUpdate(".gnu.version", 0)
+		s.SetReachable(true)
+		s.SetType(sym.SELFROSECT)
 
-		s = ctxt.Syms.Lookup(".gnu.version", 0)
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFROSECT
-
-		s = ctxt.Syms.Lookup(".gnu.version_r", 0)
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFROSECT
+		s = ldr.CreateSymForUpdate(".gnu.version_r", 0)
+		s.SetReachable(true)
+		s.SetType(sym.SELFROSECT)
 
 		/* define dynamic elf table */
-		s = ctxt.Syms.Lookup(".dynamic", 0)
+		dynamic := ldr.CreateSymForUpdate(".dynamic", 0)
+		dynamic.SetReachable(true)
+		dynamic.SetType(sym.SELFSECT) // writable
 
-		s.Attr |= sym.AttrReachable
-		s.Type = sym.SELFSECT // writable
+		if ctxt.IsS390X() {
+			// S390X uses .got instead of .got.plt
+			gotplt = got
+		}
+		thearch.Elfsetupplt(ctxt, plt, gotplt, dynamic.Sym())
 
 		/*
 		 * .dynamic table
 		 */
-		elfwritedynentsym(ctxt, s, DT_HASH, ctxt.Syms.Lookup(".hash", 0))
+		elfwritedynentsym2(ctxt, dynamic, DT_HASH, hash.Sym())
 
-		elfwritedynentsym(ctxt, s, DT_SYMTAB, ctxt.Syms.Lookup(".dynsym", 0))
+		elfwritedynentsym2(ctxt, dynamic, DT_SYMTAB, dynsym.Sym())
 		if elf64 {
-			Elfwritedynent(ctxt, s, DT_SYMENT, ELF64SYMSIZE)
+			Elfwritedynent2(ctxt, dynamic, DT_SYMENT, ELF64SYMSIZE)
 		} else {
-			Elfwritedynent(ctxt, s, DT_SYMENT, ELF32SYMSIZE)
+			Elfwritedynent2(ctxt, dynamic, DT_SYMENT, ELF32SYMSIZE)
 		}
-		elfwritedynentsym(ctxt, s, DT_STRTAB, ctxt.Syms.Lookup(".dynstr", 0))
-		elfwritedynentsymsize(ctxt, s, DT_STRSZ, ctxt.Syms.Lookup(".dynstr", 0))
+		elfwritedynentsym2(ctxt, dynamic, DT_STRTAB, dynstr.Sym())
+		elfwritedynentsymsize2(ctxt, dynamic, DT_STRSZ, dynstr.Sym())
 		if elfRelType == ".rela" {
-			elfwritedynentsym(ctxt, s, DT_RELA, ctxt.Syms.Lookup(".rela", 0))
-			elfwritedynentsymsize(ctxt, s, DT_RELASZ, ctxt.Syms.Lookup(".rela", 0))
-			Elfwritedynent(ctxt, s, DT_RELAENT, ELF64RELASIZE)
+			rela := ldr.LookupOrCreateSym(".rela", 0)
+			elfwritedynentsym2(ctxt, dynamic, DT_RELA, rela)
+			elfwritedynentsymsize2(ctxt, dynamic, DT_RELASZ, rela)
+			Elfwritedynent2(ctxt, dynamic, DT_RELAENT, ELF64RELASIZE)
 		} else {
-			elfwritedynentsym(ctxt, s, DT_REL, ctxt.Syms.Lookup(".rel", 0))
-			elfwritedynentsymsize(ctxt, s, DT_RELSZ, ctxt.Syms.Lookup(".rel", 0))
-			Elfwritedynent(ctxt, s, DT_RELENT, ELF32RELSIZE)
+			rel := ldr.LookupOrCreateSym(".rel", 0)
+			elfwritedynentsym2(ctxt, dynamic, DT_REL, rel)
+			elfwritedynentsymsize2(ctxt, dynamic, DT_RELSZ, rel)
+			Elfwritedynent2(ctxt, dynamic, DT_RELENT, ELF32RELSIZE)
 		}
 
 		if rpath.val != "" {
-			Elfwritedynent(ctxt, s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val)))
+			Elfwritedynent2(ctxt, dynamic, DT_RUNPATH, uint64(dynstr.Addstring(rpath.val)))
 		}
 
-		if ctxt.Arch.Family == sys.PPC64 {
-			elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".plt", 0))
-		} else if ctxt.Arch.Family == sys.S390X {
-			elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got", 0))
+		if ctxt.IsPPC64() {
+			elfwritedynentsym2(ctxt, dynamic, DT_PLTGOT, plt.Sym())
 		} else {
-			elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got.plt", 0))
+			elfwritedynentsym2(ctxt, dynamic, DT_PLTGOT, gotplt.Sym())
 		}
 
-		if ctxt.Arch.Family == sys.PPC64 {
-			Elfwritedynent(ctxt, s, DT_PPC64_OPT, 0)
+		if ctxt.IsPPC64() {
+			Elfwritedynent2(ctxt, dynamic, DT_PPC64_OPT, 0)
 		}
 
 		// Solaris dynamic linker can't handle an empty .rela.plt if
 		// DT_JMPREL is emitted so we have to defer generation of DT_PLTREL,
 		// DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the
 		// size of .rel(a).plt section.
-		Elfwritedynent(ctxt, s, DT_DEBUG, 0)
+		Elfwritedynent2(ctxt, dynamic, DT_DEBUG, 0)
 	}
 
-	if ctxt.BuildMode == BuildModeShared {
+	if ctxt.IsShared() {
 		// The go.link.abihashbytes symbol will be pointed at the appropriate
 		// part of the .note.go.abihash section in data.go:func address().
-		s := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
-		s.Attr |= sym.AttrLocal
-		s.Type = sym.SRODATA
-		s.Attr |= sym.AttrSpecial
-		s.Attr |= sym.AttrReachable
-		s.Size = int64(sha1.Size)
+		s := ldr.LookupOrCreateSym("go.link.abihashbytes", 0)
+		sb := ldr.MakeSymbolUpdater(s)
+		ldr.SetAttrLocal(s, true)
+		sb.SetType(sym.SRODATA)
+		ldr.SetAttrSpecial(s, true)
+		sb.SetReachable(true)
+		sb.SetSize(sha1.Size)
 
 		sort.Sort(byPkg(ctxt.Library))
 		h := sha1.New()
@@ -2271,15 +2304,15 @@
 	}
 }
 
-func elfadddynsym(ctxt *Link, s *sym.Symbol) {
+func elfadddynsym(target *Target, syms *ArchSyms, s *sym.Symbol) {
 	if elf64 {
 		s.Dynid = int32(Nelfsym)
 		Nelfsym++
 
-		d := ctxt.Syms.Lookup(".dynsym", 0)
+		d := syms.DynSym
 
 		name := s.Extname()
-		d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
+		d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, name)))
 
 		/* type */
 		t := STB_GLOBAL << 4
@@ -2296,52 +2329,52 @@
 
 		/* section where symbol is defined */
 		if s.Type == sym.SDYNIMPORT {
-			d.AddUint16(ctxt.Arch, SHN_UNDEF)
+			d.AddUint16(target.Arch, SHN_UNDEF)
 		} else {
-			d.AddUint16(ctxt.Arch, 1)
+			d.AddUint16(target.Arch, 1)
 		}
 
 		/* value */
 		if s.Type == sym.SDYNIMPORT {
-			d.AddUint64(ctxt.Arch, 0)
+			d.AddUint64(target.Arch, 0)
 		} else {
-			d.AddAddr(ctxt.Arch, s)
+			d.AddAddr(target.Arch, s)
 		}
 
 		/* size of object */
-		d.AddUint64(ctxt.Arch, uint64(s.Size))
+		d.AddUint64(target.Arch, uint64(s.Size))
 
-		if ctxt.Arch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib() != "" && !seenlib[s.Dynimplib()] {
-			Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(ctxt.Syms.Lookup(".dynstr", 0), s.Dynimplib())))
+		if target.Arch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib() != "" && !seenlib[s.Dynimplib()] {
+			elfWriteDynEnt(target.Arch, syms.Dynamic, DT_NEEDED, uint64(Addstring(syms.DynStr, s.Dynimplib())))
 		}
 	} else {
 		s.Dynid = int32(Nelfsym)
 		Nelfsym++
 
-		d := ctxt.Syms.Lookup(".dynsym", 0)
+		d := syms.DynSym
 
 		/* name */
 		name := s.Extname()
 
-		d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
+		d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, name)))
 
 		/* value */
 		if s.Type == sym.SDYNIMPORT {
-			d.AddUint32(ctxt.Arch, 0)
+			d.AddUint32(target.Arch, 0)
 		} else {
-			d.AddAddr(ctxt.Arch, s)
+			d.AddAddr(target.Arch, s)
 		}
 
 		/* size of object */
-		d.AddUint32(ctxt.Arch, uint32(s.Size))
+		d.AddUint32(target.Arch, uint32(s.Size))
 
 		/* type */
 		t := STB_GLOBAL << 4
 
 		// TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386.
-		if ctxt.Arch.Family == sys.I386 && s.Attr.CgoExport() && s.Type == sym.STEXT {
+		if target.Arch.Family == sys.I386 && s.Attr.CgoExport() && s.Type == sym.STEXT {
 			t |= STT_FUNC
-		} else if ctxt.Arch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type == sym.STEXT {
+		} else if target.Arch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type == sym.STEXT {
 			t |= STT_FUNC
 		} else {
 			t |= STT_OBJECT
@@ -2351,9 +2384,9 @@
 
 		/* shndx */
 		if s.Type == sym.SDYNIMPORT {
-			d.AddUint16(ctxt.Arch, SHN_UNDEF)
+			d.AddUint16(target.Arch, SHN_UNDEF)
 		} else {
-			d.AddUint16(ctxt.Arch, 1)
+			d.AddUint16(target.Arch, 1)
 		}
 	}
 }
diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go
new file mode 100644
index 0000000..0cb0c5b
--- /dev/null
+++ b/src/cmd/link/internal/ld/errors.go
@@ -0,0 +1,62 @@
+// Copyright 2020 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.
+package ld
+
+import (
+	"cmd/internal/obj"
+	"cmd/link/internal/sym"
+	"sync"
+)
+
+type unresolvedSymKey struct {
+	from *sym.Symbol // Symbol that referenced unresolved "to"
+	to   *sym.Symbol // Unresolved symbol referenced by "from"
+}
+
+type lookupFn func(name string, version int) *sym.Symbol
+
+// ErrorReporter is used to make error reporting thread safe.
+type ErrorReporter struct {
+	unresOnce  sync.Once
+	unresSyms  map[unresolvedSymKey]bool
+	unresMutex sync.Mutex
+	lookup     lookupFn
+}
+
+// errorUnresolved prints unresolved symbol error for r.Sym that is referenced from s.
+func (reporter *ErrorReporter) errorUnresolved(s *sym.Symbol, r *sym.Reloc) {
+	reporter.unresOnce.Do(func() { reporter.unresSyms = make(map[unresolvedSymKey]bool) })
+
+	k := unresolvedSymKey{from: s, to: r.Sym}
+	reporter.unresMutex.Lock()
+	defer reporter.unresMutex.Unlock()
+	if !reporter.unresSyms[k] {
+		reporter.unresSyms[k] = true
+
+		// Try to find symbol under another ABI.
+		var reqABI, haveABI obj.ABI
+		haveABI = ^obj.ABI(0)
+		reqABI, ok := sym.VersionToABI(int(r.Sym.Version))
+		if ok {
+			for abi := obj.ABI(0); abi < obj.ABICount; abi++ {
+				v := sym.ABIToVersion(abi)
+				if v == -1 {
+					continue
+				}
+				if rs := reporter.lookup(r.Sym.Name, v); rs != nil && rs.Type != sym.Sxxx {
+					haveABI = abi
+				}
+			}
+		}
+
+		// Give a special error message for main symbol (see #24809).
+		if r.Sym.Name == "main.main" {
+			Errorf(s, "function main is undeclared in the main package")
+		} else if haveABI != ^obj.ABI(0) {
+			Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", r.Sym.Name, reqABI, haveABI)
+		} else {
+			Errorf(s, "relocation target %s not defined", r.Sym.Name)
+		}
+	}
+}
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index 21457fd..55d8265 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -10,6 +10,8 @@
 	"bytes"
 	"cmd/internal/bio"
 	"cmd/internal/objabi"
+	"cmd/internal/sys"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"encoding/json"
 	"fmt"
@@ -145,16 +147,14 @@
 		}
 	}
 
-	if *flagNewobj {
-		// Record the directives. We'll process them later after Symbols are created.
-		ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
-	} else {
-		setCgoAttr(ctxt, ctxt.Syms.Lookup, file, pkg, directives)
-	}
+	// Record the directives. We'll process them later after Symbols are created.
+	ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
 }
 
 // Set symbol attributes or flags based on cgo directives.
-func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) {
+// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
+func setCgoAttr(ctxt *Link, lookup func(string, int) loader.Sym, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
+	l := ctxt.loader
 	for _, f := range directives {
 		switch f[0] {
 		case "cgo_import_dynamic":
@@ -197,12 +197,16 @@
 				remote, q = remote[:i], remote[i+1:]
 			}
 			s := lookup(local, 0)
-			if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS || s.Type == sym.SHOSTOBJ {
-				s.SetDynimplib(lib)
-				s.SetExtname(remote)
-				s.SetDynimpvers(q)
-				if s.Type != sym.SHOSTOBJ {
-					s.Type = sym.SDYNIMPORT
+			st := l.SymType(s)
+			if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
+				l.SetSymDynimplib(s, lib)
+				l.SetSymExtname(s, remote)
+				l.SetSymDynimpvers(s, q)
+				if st != sym.SHOSTOBJ {
+					su := l.MakeSymbolUpdater(s)
+					su.SetType(sym.SDYNIMPORT)
+				} else {
+					hostObjSyms[s] = struct{}{}
 				}
 				havedynamic = 1
 			}
@@ -216,8 +220,10 @@
 			local := f[1]
 
 			s := lookup(local, 0)
-			s.Type = sym.SHOSTOBJ
-			s.Size = 0
+			su := l.MakeSymbolUpdater(s)
+			su.SetType(sym.SHOSTOBJ)
+			su.SetSize(0)
+			hostObjSyms[s] = struct{}{}
 			continue
 
 		case "cgo_export_static", "cgo_export_dynamic":
@@ -238,6 +244,10 @@
 			// yet know it's an alias).
 			s := lookup(local, 0)
 
+			if l.SymType(s) == sym.SHOSTOBJ {
+				hostObjSyms[s] = struct{}{}
+			}
+
 			switch ctxt.BuildMode {
 			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
 				if s == lookup("main", 0) {
@@ -247,24 +257,27 @@
 
 			// export overrides import, for openbsd/cgo.
 			// see issue 4878.
-			if s.Dynimplib() != "" {
-				s.ResetDyninfo()
-				s.SetExtname("")
-				s.Type = 0
+			if l.SymDynimplib(s) != "" {
+				l.SetSymDynimplib(s, "")
+				l.SetSymDynimpvers(s, "")
+				l.SetSymExtname(s, "")
+				var su *loader.SymbolBuilder
+				su = l.MakeSymbolUpdater(s)
+				su.SetType(0)
 			}
 
-			if !s.Attr.CgoExport() {
-				s.SetExtname(remote)
-			} else if s.Extname() != remote {
-				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote)
+			if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
+				l.SetSymExtname(s, remote)
+			} else if l.SymExtname(s) != remote {
+				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote)
 				nerrors++
 				return
 			}
 
 			if f[0] == "cgo_export_static" {
-				s.Attr |= sym.AttrCgoExportStatic
+				l.SetAttrCgoExportStatic(s, true)
 			} else {
-				s.Attr |= sym.AttrCgoExportDynamic
+				l.SetAttrCgoExportDynamic(s, true)
 			}
 			continue
 
@@ -295,6 +308,7 @@
 		fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
 		nerrors++
 	}
+	return
 }
 
 var seenlib = make(map[string]bool)
@@ -306,62 +320,62 @@
 	seenlib[lib] = true
 
 	if ctxt.IsELF {
-		s := ctxt.Syms.Lookup(".dynstr", 0)
+		s := ctxt.DynStr
 		if s.Size == 0 {
 			Addstring(s, "")
 		}
-		Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
+		elfWriteDynEnt(ctxt.Arch, ctxt.Dynamic, DT_NEEDED, uint64(Addstring(s, lib)))
 	} else {
 		Errorf(nil, "adddynlib: unsupported binary format")
 	}
 }
 
-func Adddynsym(ctxt *Link, s *sym.Symbol) {
-	if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal {
+func Adddynsym(target *Target, syms *ArchSyms, s *sym.Symbol) {
+	if s.Dynid >= 0 || target.LinkMode == LinkExternal {
 		return
 	}
 
-	if ctxt.IsELF {
-		elfadddynsym(ctxt, s)
-	} else if ctxt.HeadType == objabi.Hdarwin {
+	if target.IsELF {
+		elfadddynsym(target, syms, s)
+	} else if target.HeadType == objabi.Hdarwin {
 		Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname())
-	} else if ctxt.HeadType == objabi.Hwindows {
+	} else if target.HeadType == objabi.Hwindows {
 		// already taken care of
 	} else {
 		Errorf(s, "adddynsym: unsupported binary format")
 	}
 }
 
-func fieldtrack(ctxt *Link) {
-	// record field tracking references
+func fieldtrack(arch *sys.Arch, l *loader.Loader) {
 	var buf bytes.Buffer
-	for _, s := range ctxt.Syms.Allsym {
-		if strings.HasPrefix(s.Name, "go.track.") {
-			s.Attr |= sym.AttrSpecial // do not lay out in data segment
-			s.Attr |= sym.AttrNotInSymbolTable
-			if s.Attr.Reachable() {
-				buf.WriteString(s.Name[9:])
-				for p := ctxt.Reachparent[s]; p != nil; p = ctxt.Reachparent[p] {
+	for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
+		if name := l.SymName(i); strings.HasPrefix(name, "go.track.") {
+			bld := l.MakeSymbolUpdater(i)
+			bld.SetSpecial(true)
+			bld.SetNotInSymbolTable(true)
+			if bld.Reachable() {
+				buf.WriteString(name[9:])
+				for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
 					buf.WriteString("\t")
-					buf.WriteString(p.Name)
+					buf.WriteString(l.SymName(p))
 				}
 				buf.WriteString("\n")
-			}
 
-			s.Type = sym.SCONST
-			s.Value = 0
+				bld.SetType(sym.SCONST)
+				bld.SetValue(0)
+			}
 		}
 	}
-
 	if *flagFieldTrack == "" {
 		return
 	}
-	s := ctxt.Syms.ROLookup(*flagFieldTrack, 0)
-	if s == nil || !s.Attr.Reachable() {
+	s := l.Lookup(*flagFieldTrack, 0)
+	if s == 0 || !l.AttrReachable(s) {
 		return
 	}
-	s.Type = sym.SDATA
-	addstrdata(ctxt, *flagFieldTrack, buf.String())
+	bld := l.MakeSymbolUpdater(s)
+	bld.SetType(sym.SDATA)
+	addstrdata(arch, l, *flagFieldTrack, buf.String())
 }
 
 func (ctxt *Link) addexport() {
@@ -389,7 +403,7 @@
 	}
 
 	for _, exp := range dynexp {
-		Adddynsym(ctxt, exp)
+		Adddynsym(&ctxt.Target, &ctxt.ArchSyms, exp)
 	}
 	for _, lib := range dynlib {
 		adddynlib(ctxt, lib)
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index a4b4b60..d7d52a5 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -42,7 +42,6 @@
 	"cmd/link/internal/loadmacho"
 	"cmd/link/internal/loadpe"
 	"cmd/link/internal/loadxcoff"
-	"cmd/link/internal/objfile"
 	"cmd/link/internal/sym"
 	"crypto/sha1"
 	"debug/elf"
@@ -95,6 +94,40 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+type LookupFn func(name string, version int) *sym.Symbol
+
+// ArchSyms holds a number of architecture specific symbols used during
+// relocation.  Rather than allowing them universal access to all symbols,
+// we keep a subset for relocation application.
+type ArchSyms struct {
+	TOC    *sym.Symbol
+	DotTOC *sym.Symbol
+	GOT    *sym.Symbol
+	PLT    *sym.Symbol
+	GOTPLT *sym.Symbol
+
+	Tlsg      *sym.Symbol
+	Tlsoffset int
+
+	Dynamic *sym.Symbol
+	DynSym  *sym.Symbol
+	DynStr  *sym.Symbol
+}
+
+// setArchSyms sets up the ArchSyms structure, and must be called before
+// relocations are applied.
+func (ctxt *Link) setArchSyms() {
+	ctxt.TOC = ctxt.Syms.Lookup("TOC", 0)
+	ctxt.DotTOC = ctxt.Syms.Lookup(".TOC.", 0)
+	ctxt.GOT = ctxt.Syms.Lookup(".got", 0)
+	ctxt.PLT = ctxt.Syms.Lookup(".plt", 0)
+	ctxt.GOTPLT = ctxt.Syms.Lookup(".got.plt", 0)
+
+	ctxt.Dynamic = ctxt.Syms.Lookup(".dynamic", 0)
+	ctxt.DynSym = ctxt.Syms.Lookup(".dynsym", 0)
+	ctxt.DynStr = ctxt.Syms.Lookup(".dynstr", 0)
+}
+
 type Arch struct {
 	Funcalign      int
 	Maxalign       int
@@ -108,7 +141,7 @@
 	Openbsddynld   string
 	Dragonflydynld string
 	Solarisdynld   string
-	Adddynrel      func(*Link, *sym.Symbol, *sym.Reloc) bool
+	Adddynrel      func(*Link, *Target, *ArchSyms, *sym.Symbol, *sym.Reloc) bool
 	Archinit       func(*Link)
 	// Archreloc is an arch-specific hook that assists in
 	// relocation processing (invoked by 'relocsym'); it handles
@@ -119,7 +152,7 @@
 	// value is the appropriately relocated value (to be written back
 	// to the same spot in sym.P) and a boolean indicating
 	// success/failure (a failing value indicates a fatal error).
-	Archreloc func(link *Link, rel *sym.Reloc, sym *sym.Symbol,
+	Archreloc func(target *Target, syms *ArchSyms, rel *sym.Reloc, sym *sym.Symbol,
 		offset int64) (relocatedOffset int64, success bool)
 	// Archrelocvariant is a second arch-specific hook used for
 	// relocation processing; it handles relocations where r.Type is
@@ -129,7 +162,7 @@
 	// relocation applies, and "off" is the contents of the
 	// to-be-relocated data item (from sym.P). Return is an updated
 	// offset value.
-	Archrelocvariant func(link *Link, rel *sym.Reloc, sym *sym.Symbol,
+	Archrelocvariant func(target *Target, syms *ArchSyms, rel *sym.Reloc, sym *sym.Symbol,
 		offset int64) (relocatedOffset int64)
 	Trampoline func(*Link, *sym.Reloc, *sym.Symbol)
 
@@ -141,7 +174,7 @@
 	Asmb2 func(*Link)
 
 	Elfreloc1   func(*Link, *sym.Reloc, int64) bool
-	Elfsetupplt func(*Link)
+	Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
 	Gentext     func(*Link)
 	Machoreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool
 	PEreloc1    func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool
@@ -381,18 +414,16 @@
 }
 
 func (ctxt *Link) loadlib() {
-	if *flagNewobj {
-		var flags uint32
-		switch *FlagStrictDups {
-		case 0:
-			// nothing to do
-		case 1, 2:
-			flags = loader.FlagStrictDups
-		default:
-			log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
-		}
-		ctxt.loader = loader.NewLoader(flags)
+	var flags uint32
+	switch *FlagStrictDups {
+	case 0:
+		// nothing to do
+	case 1, 2:
+		flags = loader.FlagStrictDups
+	default:
+		log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
 	}
+	ctxt.loader = loader.NewLoader(flags, elfsetstring)
 
 	ctxt.cgo_export_static = make(map[string]bool)
 	ctxt.cgo_export_dynamic = make(map[string]bool)
@@ -423,35 +454,33 @@
 			loadobjfile(ctxt, lib)
 		}
 	}
+	// At this point, the Go objects are "preloaded". Not all the symbols are
+	// added to the symbol table (only defined package symbols are). Looking
+	// up symbol by name may not get expected result.
 
-	if *flagNewobj {
-		iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0
-		ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0
-	} else {
-		iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
-		ctxt.canUsePlugins = ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil
-	}
+	iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil
+	ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil
 
 	// We now have enough information to determine the link mode.
 	determineLinkMode(ctxt)
 
-	if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) {
+	if ctxt.LinkMode == LinkExternal && !iscgo && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) {
 		// This indicates a user requested -linkmode=external.
 		// The startup code uses an import of runtime/cgo to decide
 		// whether to initialize the TLS.  So give it one. This could
 		// be handled differently but it's an unusual case.
-		if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil {
-			if lib.Shlib != "" {
-				ldshlibsyms(ctxt, lib.Shlib)
-			} else {
-				if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
-					Exitf("cannot implicitly include runtime/cgo in a shared library")
-				}
-				loadobjfile(ctxt, lib)
+		if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" {
+			if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
+				Exitf("cannot implicitly include runtime/cgo in a shared library")
 			}
+			loadobjfile(ctxt, lib)
 		}
 	}
 
+	// Add non-package symbols and references of externally defined symbols.
+	ctxt.loader.LoadNonpkgSyms(ctxt.Syms)
+
+	// Load symbols from shared libraries, after all Go object symbols are loaded.
 	for _, lib := range ctxt.Library {
 		if lib.Shlib != "" {
 			if ctxt.Debugvlog > 1 {
@@ -461,63 +490,20 @@
 		}
 	}
 
-	if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
-		if *flagNewobj {
-			// In newobj mode, we typically create sym.Symbols later therefore
-			// also set cgo attributes later. However, for internal cgo linking,
-			// the host object loaders still work with sym.Symbols (for now),
-			// and they need cgo attributes set to work properly. So process
-			// them now.
-			lookup := func(name string, ver int) *sym.Symbol { return ctxt.loader.LookupOrCreate(name, ver, ctxt.Syms) }
-			for _, d := range ctxt.cgodata {
-				setCgoAttr(ctxt, lookup, d.file, d.pkg, d.directives)
-			}
-			ctxt.cgodata = nil
-		}
-
-		// Drop all the cgo_import_static declarations.
-		// Turns out we won't be needing them.
-		for _, s := range ctxt.Syms.Allsym {
-			if s.Type == sym.SHOSTOBJ {
-				// If a symbol was marked both
-				// cgo_import_static and cgo_import_dynamic,
-				// then we want to make it cgo_import_dynamic
-				// now.
-				if s.Extname() != "" && s.Dynimplib() != "" && !s.Attr.CgoExport() {
-					s.Type = sym.SDYNIMPORT
-				} else {
-					s.Type = 0
-				}
-			}
-		}
-	}
+	// Process cgo directives (has to be done before host object loading).
+	ctxt.loadcgodirectives()
 
 	// Conditionally load host objects, or setup for external linking.
 	hostobjs(ctxt)
 	hostlinksetup(ctxt)
 
-	if *flagNewobj {
-		// Add references of externally defined symbols.
-		ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms)
-	}
-
-	// Now that we know the link mode, set the dynexp list.
-	if !*flagNewobj { // set this later in newobj mode
-		setupdynexp(ctxt)
-	}
-
 	if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
 		// If we have any undefined symbols in external
 		// objects, try to read them from the libgcc file.
 		any := false
-		for _, s := range ctxt.Syms.Allsym {
-			for i := range s.R {
-				r := &s.R[i] // Copying sym.Reloc has measurable impact on performance
-				if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" {
-					any = true
-					break
-				}
-			}
+		undefs := ctxt.loader.UndefinedRelocTargets(1)
+		if len(undefs) > 0 {
+			any = true
 		}
 		if any {
 			if *flagLibGCC == "" {
@@ -561,9 +547,7 @@
 
 	importcycles()
 
-	if *flagNewobj {
-		strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
-	}
+	strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
 }
 
 // Set up dynexp list.
@@ -596,19 +580,50 @@
 	ctxt.cgo_export_dynamic = nil
 }
 
+// loadcgodirectives reads the previously discovered cgo directives, creating
+// symbols in preparation for host object loading or use later in the link.
+func (ctxt *Link) loadcgodirectives() {
+	l := ctxt.loader
+	hostObjSyms := make(map[loader.Sym]struct{})
+	for _, d := range ctxt.cgodata {
+		setCgoAttr(ctxt, ctxt.loader.LookupOrCreateSym, d.file, d.pkg, d.directives, hostObjSyms)
+	}
+	ctxt.cgodata = nil
+
+	if ctxt.LinkMode == LinkInternal {
+		// Drop all the cgo_import_static declarations.
+		// Turns out we won't be needing them.
+		for symIdx := range hostObjSyms {
+			if l.SymType(symIdx) == sym.SHOSTOBJ {
+				// If a symbol was marked both
+				// cgo_import_static and cgo_import_dynamic,
+				// then we want to make it cgo_import_dynamic
+				// now.
+				su := l.MakeSymbolUpdater(symIdx)
+				if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) {
+					su.SetType(sym.SDYNIMPORT)
+				} else {
+					su.SetType(0)
+				}
+			}
+		}
+	}
+}
+
 // Set up flags and special symbols depending on the platform build mode.
+// This version works with loader.Loader.
 func (ctxt *Link) linksetup() {
 	switch ctxt.BuildMode {
 	case BuildModeCShared, BuildModePlugin:
-		s := ctxt.Syms.Lookup("runtime.islibrary", 0)
-		s.Type = sym.SNOPTRDATA
-		s.Attr |= sym.AttrDuplicateOK
-		s.AddUint8(1)
+		symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0)
+		sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+		sb.SetType(sym.SNOPTRDATA)
+		sb.AddUint8(1)
 	case BuildModeCArchive:
-		s := ctxt.Syms.Lookup("runtime.isarchive", 0)
-		s.Type = sym.SNOPTRDATA
-		s.Attr |= sym.AttrDuplicateOK
-		s.AddUint8(1)
+		symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0)
+		sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+		sb.SetType(sym.SNOPTRDATA)
+		sb.AddUint8(1)
 	}
 
 	// Recalculate pe parameters now that we have ctxt.LinkMode set.
@@ -637,69 +652,77 @@
 	}
 
 	if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
-		toc := ctxt.Syms.Lookup(".TOC.", 0)
-		toc.Type = sym.SDYNIMPORT
+		toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0)
+		sb := ctxt.loader.MakeSymbolUpdater(toc)
+		sb.SetType(sym.SDYNIMPORT)
 	}
 
 	// The Android Q linker started to complain about underalignment of the our TLS
-	// section. We don't actually use the section on android, so dont't
+	// section. We don't actually use the section on android, so don't
 	// generate it.
 	if objabi.GOOS != "android" {
-		tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
+		tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0)
+		sb := ctxt.loader.MakeSymbolUpdater(tlsg)
 
 		// runtime.tlsg is used for external linking on platforms that do not define
 		// a variable to hold g in assembly (currently only intel).
-		if tlsg.Type == 0 {
-			tlsg.Type = sym.STLSBSS
-			tlsg.Size = int64(ctxt.Arch.PtrSize)
-		} else if tlsg.Type != sym.SDYNIMPORT {
-			Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type)
+		if sb.Type() == 0 {
+			sb.SetType(sym.STLSBSS)
+			sb.SetSize(int64(ctxt.Arch.PtrSize))
+		} else if sb.Type() != sym.SDYNIMPORT {
+			Errorf(nil, "runtime declared tlsg variable %v", sb.Type())
 		}
-		tlsg.Attr |= sym.AttrReachable
-		ctxt.Tlsg = tlsg
+		ctxt.loader.SetAttrReachable(tlsg, true)
+		ctxt.Tlsg2 = tlsg
 	}
 
-	var moduledata *sym.Symbol
+	var moduledata loader.Sym
+	var mdsb *loader.SymbolBuilder
 	if ctxt.BuildMode == BuildModePlugin {
-		moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
-		moduledata.Attr |= sym.AttrLocal
+		moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0)
+		mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+		ctxt.loader.SetAttrLocal(moduledata, true)
 	} else {
-		moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
+		moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0)
+		mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
 	}
-	if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT {
+	if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT {
 		// If the module (toolchain-speak for "executable or shared
 		// library") we are linking contains the runtime package, it
 		// will define the runtime.firstmoduledata symbol and we
 		// truncate it back to 0 bytes so we can define its entire
 		// contents in symtab.go:symtab().
-		moduledata.Size = 0
+		mdsb.SetSize(0)
 
 		// In addition, on ARM, the runtime depends on the linker
 		// recording the value of GOARM.
 		if ctxt.Arch.Family == sys.ARM {
-			s := ctxt.Syms.Lookup("runtime.goarm", 0)
-			s.Type = sym.SDATA
-			s.Size = 0
-			s.AddUint8(uint8(objabi.GOARM))
+			goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0)
+			sb := ctxt.loader.MakeSymbolUpdater(goarm)
+			sb.SetType(sym.SDATA)
+			sb.SetSize(0)
+			sb.AddUint8(uint8(objabi.GOARM))
 		}
 
 		if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
-			s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0)
-			s.Type = sym.SDATA
-			s.Size = 0
-			s.AddUint8(1)
+			fpe := ctxt.loader.LookupOrCreateSym("runtime.framepointer_enabled", 0)
+			sb := ctxt.loader.MakeSymbolUpdater(fpe)
+			sb.SetType(sym.SNOPTRDATA)
+			sb.SetSize(0)
+			sb.AddUint8(1)
 		}
 	} else {
 		// If OTOH the module does not contain the runtime package,
 		// create a local symbol for the moduledata.
-		moduledata = ctxt.Syms.Lookup("local.moduledata", 0)
-		moduledata.Attr |= sym.AttrLocal
+		moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0)
+		mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+		ctxt.loader.SetAttrLocal(moduledata, true)
 	}
 	// In all cases way we mark the moduledata as noptrdata to hide it from
 	// the GC.
-	moduledata.Type = sym.SNOPTRDATA
-	moduledata.Attr |= sym.AttrReachable
-	ctxt.Moduledata = moduledata
+	mdsb.SetType(sym.SNOPTRDATA)
+	ctxt.loader.SetAttrReachable(moduledata, true)
+	ctxt.Moduledata2 = moduledata
 
 	// If package versioning is required, generate a hash of the
 	// packages used in the link.
@@ -713,11 +736,25 @@
 
 	if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows {
 		if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() {
-			got := ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0)
-			got.Type = sym.SDYNIMPORT
-			got.Attr |= sym.AttrReachable
+			got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0)
+			sb := ctxt.loader.MakeSymbolUpdater(got)
+			sb.SetType(sym.SDYNIMPORT)
+			ctxt.loader.SetAttrReachable(got, true)
 		}
 	}
+
+	// DWARF-gen and other phases require that the unit Textp2 slices
+	// be populated, so that it can walk the functions in each unit.
+	// Call into the loader to do this (requires that we collect the
+	// set of internal libraries first). NB: might be simpler if we
+	// moved isRuntimeDepPkg to cmd/internal and then did the test in
+	// loader.AssignTextSymbolOrder.
+	ctxt.Library = postorder(ctxt.Library)
+	intlibs := []bool{}
+	for _, lib := range ctxt.Library {
+		intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg))
+	}
+	ctxt.Textp2 = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp2)
 }
 
 // mangleTypeSym shortens the names of symbols that represent Go types
@@ -741,7 +778,7 @@
 	for _, s := range ctxt.Syms.Allsym {
 		newName := typeSymbolMangle(s.Name)
 		if newName != s.Name {
-			ctxt.Syms.Rename(s.Name, newName, int(s.Version), ctxt.Reachparent)
+			ctxt.Syms.Rename(s.Name, newName, int(s.Version))
 		}
 	}
 }
@@ -1702,107 +1739,55 @@
 
 	magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
 	if magic == 0x7f454c46 { // \x7F E L F
-		if *flagNewobj {
-			ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				ehdr.flags = flags
-				ctxt.Textp = append(ctxt.Textp, textp...)
+		ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+			textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn, ehdr.flags)
+			if err != nil {
+				Errorf(nil, "%v", err)
+				return
 			}
-			return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
-		} else {
-			ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, flags, err := loadelf.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				ehdr.flags = flags
-				ctxt.Textp = append(ctxt.Textp, textp...)
-			}
-			return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
+			ehdr.flags = flags
+			ctxt.Textp2 = append(ctxt.Textp2, textp...)
 		}
+		return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
 	}
 
 	if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe {
-		if *flagNewobj {
-			ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				ctxt.Textp = append(ctxt.Textp, textp...)
+		ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+			textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn)
+			if err != nil {
+				Errorf(nil, "%v", err)
+				return
 			}
-			return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
-		} else {
-			ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, err := loadmacho.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				ctxt.Textp = append(ctxt.Textp, textp...)
-			}
-			return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
+			ctxt.Textp2 = append(ctxt.Textp2, textp...)
 		}
+		return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
 	}
 
 	if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 {
-		if *flagNewobj {
-			ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				if rsrc != nil {
-					setpersrc(ctxt, rsrc)
-				}
-				ctxt.Textp = append(ctxt.Textp, textp...)
+		ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+			textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn)
+			if err != nil {
+				Errorf(nil, "%v", err)
+				return
 			}
-			return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
-		} else {
-			ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, rsrc, err := loadpe.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				if rsrc != nil {
-					setpersrc(ctxt, rsrc)
-				}
-				ctxt.Textp = append(ctxt.Textp, textp...)
+			if rsrc != 0 {
+				setpersrc(ctxt, rsrc)
 			}
-			return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
+			ctxt.Textp2 = append(ctxt.Textp2, textp...)
 		}
+		return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
 	}
 
 	if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) {
-		if *flagNewobj {
-			ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				ctxt.Textp = append(ctxt.Textp, textp...)
+		ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+			textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn)
+			if err != nil {
+				Errorf(nil, "%v", err)
+				return
 			}
-			return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
-		} else {
-			ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
-				textp, err := loadxcoff.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
-				if err != nil {
-					Errorf(nil, "%v", err)
-					return
-				}
-				ctxt.Textp = append(ctxt.Textp, textp...)
-			}
-			return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
+			ctxt.Textp2 = append(ctxt.Textp2, textp...)
 		}
+		return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
 	}
 
 	/* check the header */
@@ -1887,24 +1872,7 @@
 	ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n
 	f.MustSeek(import1, 0)
 
-	flags := 0
-	switch *FlagStrictDups {
-	case 0:
-		break
-	case 1:
-		flags = objfile.StrictDupsWarnFlag
-	case 2:
-		flags = objfile.StrictDupsErrFlag
-	default:
-		log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
-	}
-	var c int
-	if *flagNewobj {
-		ctxt.loader.Preload(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
-	} else {
-		c = objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
-	}
-	strictDupMsgCount += c
+	ctxt.loader.Preload(ctxt.Syms, f, lib, unit, eof-f.Offset(), 0)
 	addImports(ctxt, lib, pn)
 	return nil
 }
@@ -2045,7 +2013,7 @@
 		Errorf(nil, "cannot read symbols from shared library: %s", libpath)
 		return
 	}
-	gcdataLocations := make(map[uint64]*sym.Symbol)
+	gcdataLocations := make(map[uint64]loader.Sym)
 	for _, elfsym := range syms {
 		if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
 			continue
@@ -2058,37 +2026,44 @@
 			ver = sym.SymVerABIInternal
 		}
 
-		var lsym *sym.Symbol
-		if *flagNewobj {
-			i := ctxt.loader.AddExtSym(elfsym.Name, ver)
-			if i == 0 {
-				continue
-			}
-			lsym = ctxt.Syms.Newsym(elfsym.Name, ver)
-			ctxt.loader.Syms[i] = lsym
-		} else {
-			lsym = ctxt.Syms.Lookup(elfsym.Name, ver)
-		}
-		// Because loadlib above loads all .a files before loading any shared
-		// libraries, any non-dynimport symbols we find that duplicate symbols
-		// already loaded should be ignored (the symbols from the .a files
-		// "win").
-		if lsym.Type != 0 && lsym.Type != sym.SDYNIMPORT {
+		l := ctxt.loader
+		s := l.LookupOrCreateSym(elfsym.Name, ver)
+
+		// Because loadlib above loads all .a files before loading
+		// any shared libraries, any non-dynimport symbols we find
+		// that duplicate symbols already loaded should be ignored
+		// (the symbols from the .a files "win").
+		if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT {
 			continue
 		}
-		lsym.Type = sym.SDYNIMPORT
-		lsym.SetElfType(elf.ST_TYPE(elfsym.Info))
-		lsym.Size = int64(elfsym.Size)
+		su := l.MakeSymbolUpdater(s)
+		su.SetType(sym.SDYNIMPORT)
+		l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info))
+		su.SetSize(int64(elfsym.Size))
 		if elfsym.Section != elf.SHN_UNDEF {
+			// If it's not undefined, mark the symbol as reachable
+			// so as to protect it from dead code elimination,
+			// even if there aren't any explicit references to it.
+			// Under the previous sym.Symbol based regime this
+			// wasn't necessary, but for the loader-based deadcode
+			// it is definitely needed.
+			//
+			// FIXME: have a more general/flexible mechanism for this?
+			//
+			l.SetAttrReachable(s, true)
+
 			// Set .File for the library that actually defines the symbol.
-			lsym.File = libpath
+			l.SetSymFile(s, libpath)
+
 			// The decodetype_* functions in decodetype.go need access to
 			// the type data.
-			if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") {
-				lsym.P = readelfsymboldata(ctxt, f, &elfsym)
-				gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = lsym
+			sname := l.SymName(s)
+			if strings.HasPrefix(sname, "type.") && !strings.HasPrefix(sname, "type..") {
+				su.SetData(readelfsymboldata(ctxt, f, &elfsym))
+				gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = s
 			}
 		}
+
 		// For function symbols, we don't know what ABI is
 		// available, so alias it under both ABIs.
 		//
@@ -2097,25 +2072,15 @@
 		// mangle Go function names in the .so to include the
 		// ABI.
 		if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
-			var alias *sym.Symbol
-			if *flagNewobj {
-				i := ctxt.loader.AddExtSym(elfsym.Name, sym.SymVerABIInternal)
-				if i == 0 {
-					continue
-				}
-				alias = ctxt.Syms.Newsym(elfsym.Name, sym.SymVerABIInternal)
-				ctxt.loader.Syms[i] = alias
-			} else {
-				alias = ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
-			}
-			if alias.Type != 0 {
+			alias := ctxt.loader.LookupOrCreateSym(elfsym.Name, sym.SymVerABIInternal)
+			if l.SymType(alias) != 0 {
 				continue
 			}
-			alias.Type = sym.SABIALIAS
-			alias.R = []sym.Reloc{{Sym: lsym}}
+			su := l.MakeSymbolUpdater(alias)
+			su.SetType(sym.SABIALIAS)
+			su.AddReloc(loader.Reloc{Sym: s})
 		}
 	}
-	gcdataAddresses := make(map[*sym.Symbol]uint64)
 	if ctxt.Arch.Family == sys.ARM64 {
 		for _, sect := range f.Sections {
 			if sect.Type == elf.SHT_RELA {
@@ -2133,15 +2098,12 @@
 					if t != elf.R_AARCH64_RELATIVE {
 						continue
 					}
-					if lsym, ok := gcdataLocations[rela.Off]; ok {
-						gcdataAddresses[lsym] = uint64(rela.Addend)
-					}
 				}
 			}
 		}
 	}
 
-	ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdataAddresses: gcdataAddresses})
+	ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
 }
 
 func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section {
@@ -2155,16 +2117,11 @@
 }
 
 type chain struct {
-	sym   *sym.Symbol
+	sym   loader.Sym
 	up    *chain
 	limit int // limit on entry to sym
 }
 
-var morestack *sym.Symbol
-
-// TODO: Record enough information in new object files to
-// allow stack checks here.
-
 func haslinkregister(ctxt *Link) bool {
 	return ctxt.FixedFrameSize() != 0
 }
@@ -2176,10 +2133,23 @@
 	return ctxt.Arch.RegSize
 }
 
-func (ctxt *Link) dostkcheck() {
-	var ch chain
+type stkChk struct {
+	ldr       *loader.Loader
+	ctxt      *Link
+	morestack loader.Sym
+	done      loader.Bitmap
+}
 
-	morestack = ctxt.Syms.Lookup("runtime.morestack", 0)
+// Walk the call tree and check that there is always enough stack space
+// for the call frames, especially for a chain of nosplit functions.
+func (ctxt *Link) dostkcheck() {
+	ldr := ctxt.loader
+	sc := stkChk{
+		ldr:       ldr,
+		ctxt:      ctxt,
+		morestack: ldr.Lookup("runtime.morestack", 0),
+		done:      loader.MakeBitmap(ldr.NSym()),
+	}
 
 	// Every splitting function ensures that there are at least StackLimit
 	// bytes available below SP when the splitting prologue finishes.
@@ -2188,8 +2158,7 @@
 	// Check that every function behaves correctly with this amount
 	// of stack, following direct calls in order to piece together chains
 	// of non-splitting functions.
-	ch.up = nil
-
+	var ch chain
 	ch.limit = objabi.StackLimit - callsize(ctxt)
 	if objabi.GOARCH == "arm64" {
 		// need extra 8 bytes below SP to save FP
@@ -2198,118 +2167,115 @@
 
 	// Check every function, but do the nosplit functions in a first pass,
 	// to make the printed failure chains as short as possible.
-	for _, s := range ctxt.Textp {
-		// runtime.racesymbolizethunk is called from gcc-compiled C
-		// code running on the operating system thread stack.
-		// It uses more than the usual amount of stack but that's okay.
-		if s.Name == "runtime.racesymbolizethunk" {
-			continue
-		}
-
-		if s.Attr.NoSplit() {
+	for _, s := range ctxt.Textp2 {
+		if ldr.IsNoSplit(s) {
 			ch.sym = s
-			stkcheck(ctxt, &ch, 0)
+			sc.check(&ch, 0)
 		}
 	}
 
-	for _, s := range ctxt.Textp {
-		if !s.Attr.NoSplit() {
+	for _, s := range ctxt.Textp2 {
+		if !ldr.IsNoSplit(s) {
 			ch.sym = s
-			stkcheck(ctxt, &ch, 0)
+			sc.check(&ch, 0)
 		}
 	}
 }
 
-func stkcheck(ctxt *Link, up *chain, depth int) int {
+func (sc *stkChk) check(up *chain, depth int) int {
 	limit := up.limit
 	s := up.sym
+	ldr := sc.ldr
+	ctxt := sc.ctxt
 
 	// Don't duplicate work: only need to consider each
 	// function at top of safe zone once.
 	top := limit == objabi.StackLimit-callsize(ctxt)
 	if top {
-		if s.Attr.StackCheck() {
+		if sc.done.Has(s) {
 			return 0
 		}
-		s.Attr |= sym.AttrStackCheck
+		sc.done.Set(s)
 	}
 
 	if depth > 500 {
-		Errorf(s, "nosplit stack check too deep")
-		stkbroke(ctxt, up, 0)
+		sc.ctxt.Errorf(s, "nosplit stack check too deep")
+		sc.broke(up, 0)
 		return -1
 	}
 
-	if s.Attr.External() || s.FuncInfo == nil {
+	if ldr.AttrExternal(s) {
 		// external function.
 		// should never be called directly.
 		// onlyctxt.Diagnose the direct caller.
 		// TODO(mwhudson): actually think about this.
 		// TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack.
 		// See the trampolines in src/runtime/sys_darwin_$ARCH.go.
-		if depth == 1 && s.Type != sym.SXREF && !ctxt.DynlinkingGo() &&
-			ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
-			//Errorf(s, "call to external function")
-		}
+		//if depth == 1 && ldr.SymType(s) != sym.SXREF && !ctxt.DynlinkingGo() &&
+		//	ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
+		//	Errorf(s, "call to external function")
+		//}
+		return -1
+	}
+	info := ldr.FuncInfo(s)
+	if !info.Valid() { // external function. see above.
 		return -1
 	}
 
 	if limit < 0 {
-		stkbroke(ctxt, up, limit)
+		sc.broke(up, limit)
 		return -1
 	}
 
 	// morestack looks like it calls functions,
 	// but it switches the stack pointer first.
-	if s == morestack {
+	if s == sc.morestack {
 		return 0
 	}
 
 	var ch chain
 	ch.up = up
 
-	if !s.Attr.NoSplit() {
+	if !ldr.IsNoSplit(s) {
 		// Ensure we have enough stack to call morestack.
 		ch.limit = limit - callsize(ctxt)
-		ch.sym = morestack
-		if stkcheck(ctxt, &ch, depth+1) < 0 {
+		ch.sym = sc.morestack
+		if sc.check(&ch, depth+1) < 0 {
 			return -1
 		}
 		if !top {
 			return 0
 		}
 		// Raise limit to allow frame.
-		locals := int32(0)
-		if s.FuncInfo != nil {
-			locals = s.FuncInfo.Locals
-		}
+		locals := info.Locals()
 		limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize())
 	}
 
 	// Walk through sp adjustments in function, consuming relocs.
-	ri := 0
-
-	endr := len(s.R)
+	relocs := ldr.Relocs(s)
 	var ch1 chain
 	pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
-	var r *sym.Reloc
-	for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() {
+	for pcsp.Init(info.Pcsp()); !pcsp.Done; pcsp.Next() {
 		// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
 
 		// Check stack size in effect for this span.
 		if int32(limit)-pcsp.Value < 0 {
-			stkbroke(ctxt, up, int(int32(limit)-pcsp.Value))
+			sc.broke(up, int(int32(limit)-pcsp.Value))
 			return -1
 		}
 
 		// Process calls in this span.
-		for ; ri < endr && uint32(s.R[ri].Off) < pcsp.NextPC; ri++ {
-			r = &s.R[ri]
+		for i := 0; i < relocs.Count; i++ {
+			r := relocs.At2(i)
+			if uint32(r.Off()) >= pcsp.NextPC {
+				break
+			}
+			t := r.Type()
 			switch {
-			case r.Type.IsDirectCall():
+			case t.IsDirectCall():
 				ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
-				ch.sym = r.Sym
-				if stkcheck(ctxt, &ch, depth+1) < 0 {
+				ch.sym = r.Sym()
+				if sc.check(&ch, depth+1) < 0 {
 					return -1
 				}
 
@@ -2317,13 +2283,13 @@
 			// so we have to make sure it can call morestack.
 			// Arrange the data structures to report both calls, so that
 			// if there is an error, stkprint shows all the steps involved.
-			case r.Type == objabi.R_CALLIND:
+			case t == objabi.R_CALLIND:
 				ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
-				ch.sym = nil
+				ch.sym = 0
 				ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue
 				ch1.up = &ch
-				ch1.sym = morestack
-				if stkcheck(ctxt, &ch1, depth+2) < 0 {
+				ch1.sym = sc.morestack
+				if sc.check(&ch1, depth+2) < 0 {
 					return -1
 				}
 			}
@@ -2333,17 +2299,18 @@
 	return 0
 }
 
-func stkbroke(ctxt *Link, ch *chain, limit int) {
-	Errorf(ch.sym, "nosplit stack overflow")
-	stkprint(ctxt, ch, limit)
+func (sc *stkChk) broke(ch *chain, limit int) {
+	sc.ctxt.Errorf(ch.sym, "nosplit stack overflow")
+	sc.print(ch, limit)
 }
 
-func stkprint(ctxt *Link, ch *chain, limit int) {
+func (sc *stkChk) print(ch *chain, limit int) {
+	ldr := sc.ldr
+	ctxt := sc.ctxt
 	var name string
-
-	if ch.sym != nil {
-		name = ch.sym.Name
-		if ch.sym.Attr.NoSplit() {
+	if ch.sym != 0 {
+		name = ldr.SymName(ch.sym)
+		if ldr.IsNoSplit(ch.sym) {
 			name += " (nosplit)"
 		}
 	} else {
@@ -2351,14 +2318,14 @@
 	}
 
 	if ch.up == nil {
-		// top of chain.  ch->sym != nil.
-		if ch.sym.Attr.NoSplit() {
+		// top of chain. ch.sym != 0.
+		if ldr.IsNoSplit(ch.sym) {
 			fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name)
 		} else {
 			fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name)
 		}
 	} else {
-		stkprint(ctxt, ch.up, ch.limit+callsize(ctxt))
+		sc.print(ch.up, ch.limit+callsize(ctxt))
 		if !haslinkregister(ctxt) {
 			fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name)
 		}
@@ -2706,34 +2673,19 @@
 	// Load full symbol contents, resolve indexed references.
 	ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms)
 
-	// Pull the symbols out.
-	ctxt.loader.ExtractSymbols(ctxt.Syms)
-
-	// Load cgo directives.
-	for _, d := range ctxt.cgodata {
-		setCgoAttr(ctxt, ctxt.Syms.Lookup, d.file, d.pkg, d.directives)
+	// Convert ctxt.Moduledata2 to ctxt.Moduledata, etc
+	if ctxt.Moduledata2 != 0 {
+		ctxt.Moduledata = ctxt.loader.Syms[ctxt.Moduledata2]
+		ctxt.Tlsg = ctxt.loader.Syms[ctxt.Tlsg2]
 	}
 
+	// Pull the symbols out.
+	ctxt.loader.ExtractSymbols(ctxt.Syms, ctxt.Reachparent)
+	ctxt.lookup = ctxt.Syms.ROLookup
+
 	setupdynexp(ctxt)
 
-	// Populate ctxt.Reachparent if appropriate.
-	if ctxt.Reachparent != nil {
-		for i := 0; i < len(ctxt.loader.Reachparent); i++ {
-			p := ctxt.loader.Reachparent[i]
-			if p == 0 {
-				continue
-			}
-			if p == loader.Sym(i) {
-				panic("self-cycle in reachparent")
-			}
-			sym := ctxt.loader.Syms[i]
-			psym := ctxt.loader.Syms[p]
-			ctxt.Reachparent[sym] = psym
-		}
-	}
-
-	// Drop the reference.
-	ctxt.loader = nil
+	// Drop the cgodata reference.
 	ctxt.cgodata = nil
 
 	addToTextp(ctxt)
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 124f7d9..e867857 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -32,7 +32,6 @@
 
 import (
 	"bufio"
-	"cmd/internal/obj"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/loader"
@@ -42,62 +41,51 @@
 )
 
 type Shlib struct {
-	Path            string
-	Hash            []byte
-	Deps            []string
-	File            *elf.File
-	gcdataAddresses map[*sym.Symbol]uint64
+	Path string
+	Hash []byte
+	Deps []string
+	File *elf.File
 }
 
 // Link holds the context for writing object code from a compiler
 // or for reading that input into the linker.
 type Link struct {
+	Target
+	ErrorReporter
+	ArchSyms
 	Out *OutBuf
 
 	Syms *sym.Symbols
 
-	Arch      *sys.Arch
 	Debugvlog int
 	Bso       *bufio.Writer
 
 	Loaded bool // set after all inputs have been loaded as symbols
 
-	IsELF    bool
-	HeadType objabi.HeadType
-
-	linkShared    bool // link against installed Go shared libraries
-	LinkMode      LinkMode
-	BuildMode     BuildMode
-	canUsePlugins bool // initialized when Loaded is set to true
 	compressDWARF bool
 
-	Tlsg         *sym.Symbol
+	Tlsg2        loader.Sym
 	Libdir       []string
 	Library      []*sym.Library
 	LibraryByPkg map[string]*sym.Library
 	Shlibs       []Shlib
-	Tlsoffset    int
 	Textp        []*sym.Symbol
+	Textp2       []loader.Sym
 	Filesyms     []*sym.Symbol
 	Moduledata   *sym.Symbol
+	Moduledata2  loader.Sym
 
 	PackageFile  map[string]string
 	PackageShlib map[string]string
 
 	tramps []*sym.Symbol // trampolines
 
-	// unresolvedSymSet is a set of erroneous unresolved references.
-	// Used to avoid duplicated error messages.
-	unresolvedSymSet map[unresolvedSymKey]bool
-
 	// Used to implement field tracking.
 	Reachparent map[*sym.Symbol]*sym.Symbol
 
 	compUnits []*sym.CompilationUnit // DWARF compilation units
 	runtimeCU *sym.CompilationUnit   // One of the runtime CUs, the last one seen.
 
-	relocbuf []byte // temporary buffer for applying relocations
-
 	loader  *loader.Loader
 	cgodata []cgodata // cgo directives to load, three strings are args for loadcgo
 
@@ -111,48 +99,6 @@
 	directives [][]string
 }
 
-type unresolvedSymKey struct {
-	from *sym.Symbol // Symbol that referenced unresolved "to"
-	to   *sym.Symbol // Unresolved symbol referenced by "from"
-}
-
-// ErrorUnresolved prints unresolved symbol error for r.Sym that is referenced from s.
-func (ctxt *Link) ErrorUnresolved(s *sym.Symbol, r *sym.Reloc) {
-	if ctxt.unresolvedSymSet == nil {
-		ctxt.unresolvedSymSet = make(map[unresolvedSymKey]bool)
-	}
-
-	k := unresolvedSymKey{from: s, to: r.Sym}
-	if !ctxt.unresolvedSymSet[k] {
-		ctxt.unresolvedSymSet[k] = true
-
-		// Try to find symbol under another ABI.
-		var reqABI, haveABI obj.ABI
-		haveABI = ^obj.ABI(0)
-		reqABI, ok := sym.VersionToABI(int(r.Sym.Version))
-		if ok {
-			for abi := obj.ABI(0); abi < obj.ABICount; abi++ {
-				v := sym.ABIToVersion(abi)
-				if v == -1 {
-					continue
-				}
-				if rs := ctxt.Syms.ROLookup(r.Sym.Name, v); rs != nil && rs.Type != sym.Sxxx {
-					haveABI = abi
-				}
-			}
-		}
-
-		// Give a special error message for main symbol (see #24809).
-		if r.Sym.Name == "main.main" {
-			Errorf(s, "function main is undeclared in the main package")
-		} else if haveABI != ^obj.ABI(0) {
-			Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", r.Sym.Name, reqABI, haveABI)
-		} else {
-			Errorf(s, "relocation target %s not defined", r.Sym.Name)
-		}
-	}
-}
-
 // The smallest possible offset from the hardware stack pointer to a local
 // variable on the stack. Architectures that use a link register save its value
 // on the stack in the function prologue and so always have a pointer between
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index e50eddd..3f45cc0 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -428,42 +428,49 @@
 	}
 
 	// empirically, string table must begin with " \x00".
-	s := ctxt.Syms.Lookup(".machosymstr", 0)
+	s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0)
+	sb := ctxt.loader.MakeSymbolUpdater(s)
 
-	s.Type = sym.SMACHOSYMSTR
-	s.Attr |= sym.AttrReachable
-	s.AddUint8(' ')
-	s.AddUint8('\x00')
+	sb.SetType(sym.SMACHOSYMSTR)
+	sb.SetReachable(true)
+	sb.AddUint8(' ')
+	sb.AddUint8('\x00')
 
-	s = ctxt.Syms.Lookup(".machosymtab", 0)
-	s.Type = sym.SMACHOSYMTAB
-	s.Attr |= sym.AttrReachable
+	s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0)
+	sb = ctxt.loader.MakeSymbolUpdater(s)
+	sb.SetType(sym.SMACHOSYMTAB)
+	sb.SetReachable(true)
 
-	if ctxt.LinkMode != LinkExternal {
-		s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub
-		s.Type = sym.SMACHOPLT
-		s.Attr |= sym.AttrReachable
+	if ctxt.IsInternal() {
+		s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub
+		sb = ctxt.loader.MakeSymbolUpdater(s)
+		sb.SetType(sym.SMACHOPLT)
+		sb.SetReachable(true)
 
-		s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr
-		s.Type = sym.SMACHOGOT
-		s.Attr |= sym.AttrReachable
-		s.Align = 4
+		s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr
+		sb = ctxt.loader.MakeSymbolUpdater(s)
+		sb.SetType(sym.SMACHOGOT)
+		sb.SetReachable(true)
+		sb.SetAlign(4)
 
-		s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt
-		s.Type = sym.SMACHOINDIRECTPLT
-		s.Attr |= sym.AttrReachable
+		s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt
+		sb = ctxt.loader.MakeSymbolUpdater(s)
+		sb.SetType(sym.SMACHOINDIRECTPLT)
+		sb.SetReachable(true)
 
-		s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got
-		s.Type = sym.SMACHOINDIRECTGOT
-		s.Attr |= sym.AttrReachable
+		s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got
+		sb = ctxt.loader.MakeSymbolUpdater(s)
+		sb.SetType(sym.SMACHOINDIRECTGOT)
+		sb.SetReachable(true)
 	}
 
 	// Add a dummy symbol that will become the __asm marker section.
-	if ctxt.LinkMode == LinkExternal {
-		s := ctxt.Syms.Lookup(".llvmasm", 0)
-		s.Type = sym.SMACHO
-		s.Attr |= sym.AttrReachable
-		s.AddUint8(0)
+	if ctxt.IsExternal() {
+		s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0)
+		sb = ctxt.loader.MakeSymbolUpdater(s)
+		sb.SetType(sym.SMACHO)
+		sb.SetReachable(true)
+		sb.AddUint8(0)
 	}
 }
 
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index 9f9395b..c3b7295 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -34,6 +34,7 @@
 	"bufio"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
+	"cmd/link/internal/benchmark"
 	"cmd/link/internal/sym"
 	"flag"
 	"log"
@@ -87,8 +88,6 @@
 	flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
 	FlagDebugTramp  = flag.Int("debugtramp", 0, "debug trampolines")
 	FlagStrictDups  = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
-	flagNewobj      = flag.Bool("newobj", false, "use new object file format")
-
 	FlagRound       = flag.Int("R", -1, "set address rounding `quantum`")
 	FlagTextAddr    = flag.Int64("T", -1, "set text segment `address`")
 	flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
@@ -96,6 +95,9 @@
 	cpuprofile     = flag.String("cpuprofile", "", "write cpu profile to `file`")
 	memprofile     = flag.String("memprofile", "", "write memory profile to `file`")
 	memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
+
+	benchmarkFlag     = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
+	benchmarkFileFlag = flag.String("benchmarkprofile", "", "set to enable per-phase pprof profiling")
 )
 
 // Main is the main entry point for the linker code.
@@ -170,13 +172,29 @@
 
 	interpreter = *flagInterpreter
 
+	// enable benchmarking
+	var bench *benchmark.Metrics
+	if len(*benchmarkFlag) != 0 {
+		if *benchmarkFlag == "mem" {
+			bench = benchmark.New(benchmark.GC, *benchmarkFileFlag)
+		} else if *benchmarkFlag == "cpu" {
+			bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag)
+		} else {
+			Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag)
+			usage()
+		}
+	}
+
+	bench.Start("libinit")
 	libinit(ctxt) // creates outfile
 
 	if ctxt.HeadType == objabi.Hunknown {
 		ctxt.HeadType.Set(objabi.GOOS)
 	}
 
+	bench.Start("computeTLSOffset")
 	ctxt.computeTLSOffset()
+	bench.Start("Archinit")
 	thearch.Archinit(ctxt)
 
 	if ctxt.linkShared && !ctxt.IsELF {
@@ -207,47 +225,83 @@
 	default:
 		addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
 	}
+	bench.Start("loadlib")
 	ctxt.loadlib()
 
+	bench.Start("deadcode")
 	deadcode(ctxt)
-	if *flagNewobj {
-		ctxt.loadlibfull() // XXX do it here for now
-	}
-	ctxt.linksetup()
-	ctxt.dostrdata()
 
-	dwarfGenerateDebugInfo(ctxt)
+	bench.Start("linksetup")
+	ctxt.linksetup()
+
+	bench.Start("dostrdata")
+	ctxt.dostrdata()
 	if objabi.Fieldtrack_enabled != 0 {
-		fieldtrack(ctxt)
+		bench.Start("fieldtrack")
+		fieldtrack(ctxt.Arch, ctxt.loader)
 	}
-	ctxt.mangleTypeSym()
+
+	bench.Start("dwarfGenerateDebugInfo")
+	dwarfGenerateDebugInfo(ctxt)
+
+	bench.Start("callgraph")
 	ctxt.callgraph()
 
-	ctxt.doelf()
-	if ctxt.HeadType == objabi.Hdarwin {
+	bench.Start("dostkcheck")
+	ctxt.dostkcheck()
+
+	if ctxt.IsELF {
+		bench.Start("doelf")
+		ctxt.doelf()
+	}
+	if ctxt.IsDarwin() {
+		bench.Start("domacho")
 		ctxt.domacho()
 	}
-	ctxt.dostkcheck()
-	if ctxt.HeadType == objabi.Hwindows {
+	if ctxt.IsWindows() {
+		bench.Start("dope")
 		ctxt.dope()
-		ctxt.windynrelocsyms()
 	}
-	if ctxt.HeadType == objabi.Haix {
+	bench.Start("loadlibfull")
+	ctxt.loadlibfull() // XXX do it here for now
+	if ctxt.IsAIX() {
+		bench.Start("doxcoff")
 		ctxt.doxcoff()
 	}
+	if ctxt.IsWindows() {
+		bench.Start("windynrelocsyms")
+		ctxt.windynrelocsyms()
+	}
 
+	bench.Start("mangleTypeSym")
+	ctxt.mangleTypeSym()
+
+	ctxt.setArchSyms()
+	bench.Start("addexport")
 	ctxt.addexport()
+	bench.Start("Gentext")
 	thearch.Gentext(ctxt) // trampolines, call stubs, etc.
+	bench.Start("textbuildid")
 	ctxt.textbuildid()
+	bench.Start("textaddress")
 	ctxt.textaddress()
+	bench.Start("pclntab")
 	ctxt.pclntab()
+	bench.Start("findfunctab")
 	ctxt.findfunctab()
+	bench.Start("typelink")
 	ctxt.typelink()
+	bench.Start("symtab")
 	ctxt.symtab()
+	bench.Start("buildinfo")
 	ctxt.buildinfo()
+	bench.Start("dodata")
 	ctxt.dodata()
+	bench.Start("address")
 	order := ctxt.address()
+	bench.Start("dwarfcompress")
 	dwarfcompress(ctxt)
+	bench.Start("layout")
 	filesize := ctxt.layout(order)
 
 	// Write out the output file.
@@ -266,25 +320,36 @@
 	if outputMmapped {
 		// Asmb will redirect symbols to the output file mmap, and relocations
 		// will be applied directly there.
+		bench.Start("Asmb")
 		thearch.Asmb(ctxt)
+		bench.Start("reloc")
 		ctxt.reloc()
+		bench.Start("Munmap")
 		ctxt.Out.Munmap()
 	} else {
 		// If we don't mmap, we need to apply relocations before
 		// writing out.
+		bench.Start("reloc")
 		ctxt.reloc()
+		bench.Start("Asmb")
 		thearch.Asmb(ctxt)
 	}
+	bench.Start("Asmb2")
 	thearch.Asmb2(ctxt)
 
+	bench.Start("undef")
 	ctxt.undef()
+	bench.Start("hostlink")
 	ctxt.hostlink()
 	if ctxt.Debugvlog != 0 {
 		ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym))
 		ctxt.Logf("%d liveness data\n", liveness)
 	}
+	bench.Start("Flush")
 	ctxt.Bso.Flush()
+	bench.Start("archive")
 	ctxt.archive()
+	bench.Report(os.Stdout)
 
 	errorexit()
 }
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index f775132..fdfb996 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -10,6 +10,7 @@
 import (
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/pe"
 	"encoding/binary"
@@ -237,7 +238,7 @@
 }
 
 type Imp struct {
-	s       *sym.Symbol
+	s       loader.Sym
 	off     uint64
 	next    *Imp
 	argsize int
@@ -252,13 +253,13 @@
 }
 
 var (
-	rsrcsym     *sym.Symbol
+	rsrcsym     loader.Sym
 	PESECTHEADR int32
 	PEFILEHEADR int32
 	pe64        int
 	dr          *Dll
-	dexport     [1024]*sym.Symbol
-	nexport     int
+
+	dexport = make([]loader.Sym, 0, 1024)
 )
 
 // peStringTable is a COFF string table.
@@ -957,8 +958,15 @@
 
 	if ctxt.LinkMode == LinkInternal {
 		// some mingw libs depend on this symbol, for example, FindPESectionByName
-		ctxt.xdefine("__image_base__", sym.SDATA, PEBASE)
-		ctxt.xdefine("_image_base__", sym.SDATA, PEBASE)
+		for _, name := range [2]string{"__image_base__", "_image_base__"} {
+			s := ctxt.loader.LookupOrCreateSym(name, 0)
+			sb := ctxt.loader.MakeSymbolUpdater(s)
+			sb.SetType(sym.SDATA)
+			sb.SetValue(PEBASE)
+			ctxt.loader.SetAttrReachable(s, true)
+			ctxt.loader.SetAttrSpecial(s, true)
+			ctxt.loader.SetAttrLocal(s, true)
+		}
 	}
 
 	HEADR = PEFILEHEADR
@@ -996,16 +1004,18 @@
 }
 
 func initdynimport(ctxt *Link) *Dll {
+	ldr := ctxt.loader
 	var d *Dll
 
 	dr = nil
 	var m *Imp
-	for _, s := range ctxt.Syms.Allsym {
-		if !s.Attr.Reachable() || s.Type != sym.SDYNIMPORT {
+	for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+		if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT {
 			continue
 		}
+		dynlib := ldr.SymDynimplib(s)
 		for d = dr; d != nil; d = d.next {
-			if d.name == s.Dynimplib() {
+			if d.name == dynlib {
 				m = new(Imp)
 				break
 			}
@@ -1013,7 +1023,7 @@
 
 		if d == nil {
 			d = new(Dll)
-			d.name = s.Dynimplib()
+			d.name = dynlib
 			d.next = dr
 			dr = d
 			m = new(Imp)
@@ -1024,15 +1034,15 @@
 		// of uinptrs this function consumes. Store the argsize and discard
 		// the %n suffix if any.
 		m.argsize = -1
-		extName := s.Extname()
+		extName := ldr.SymExtname(s)
 		if i := strings.IndexByte(extName, '%'); i >= 0 {
 			var err error
 			m.argsize, err = strconv.Atoi(extName[i+1:])
 			if err != nil {
-				Errorf(s, "failed to parse stdcall decoration: %v", err)
+				ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err)
 			}
 			m.argsize *= ctxt.Arch.PtrSize
-			s.SetExtname(extName[:i])
+			ldr.SetSymExtname(s, extName[:i])
 		}
 
 		m.s = s
@@ -1040,42 +1050,38 @@
 		d.ms = m
 	}
 
-	if ctxt.LinkMode == LinkExternal {
+	if ctxt.IsExternal() {
 		// Add real symbol name
 		for d := dr; d != nil; d = d.next {
 			for m = d.ms; m != nil; m = m.next {
-				m.s.Type = sym.SDATA
-				m.s.Grow(int64(ctxt.Arch.PtrSize))
-				dynName := m.s.Extname()
+				sb := ldr.MakeSymbolUpdater(m.s)
+				sb.SetType(sym.SDATA)
+				sb.Grow(int64(ctxt.Arch.PtrSize))
+				dynName := sb.Extname()
 				// only windows/386 requires stdcall decoration
-				if ctxt.Arch.Family == sys.I386 && m.argsize >= 0 {
+				if ctxt.Is386() && m.argsize >= 0 {
 					dynName += fmt.Sprintf("@%d", m.argsize)
 				}
-				dynSym := ctxt.Syms.Lookup(dynName, 0)
-				dynSym.Attr |= sym.AttrReachable
-				dynSym.Type = sym.SHOSTOBJ
-				r := m.s.AddRel()
-				r.Sym = dynSym
-				r.Off = 0
-				r.Siz = uint8(ctxt.Arch.PtrSize)
-				r.Type = objabi.R_ADDR
+				dynSym := ldr.CreateSymForUpdate(dynName, 0)
+				dynSym.SetReachable(true)
+				dynSym.SetType(sym.SHOSTOBJ)
+				sb.AddReloc(loader.Reloc{Sym: dynSym.Sym(), Type: objabi.R_ADDR, Off: 0, Size: uint8(ctxt.Arch.PtrSize)})
 			}
 		}
 	} else {
-		dynamic := ctxt.Syms.Lookup(".windynamic", 0)
-		dynamic.Attr |= sym.AttrReachable
-		dynamic.Type = sym.SWINDOWS
+		dynamic := ldr.CreateSymForUpdate(".windynamic", 0)
+		dynamic.SetReachable(true)
+		dynamic.SetType(sym.SWINDOWS)
 		for d := dr; d != nil; d = d.next {
 			for m = d.ms; m != nil; m = m.next {
-				m.s.Type = sym.SWINDOWS
-				m.s.Attr |= sym.AttrSubSymbol
-				m.s.Sub = dynamic.Sub
-				dynamic.Sub = m.s
-				m.s.Value = dynamic.Size
-				dynamic.Size += int64(ctxt.Arch.PtrSize)
+				sb := ldr.MakeSymbolUpdater(m.s)
+				sb.SetType(sym.SWINDOWS)
+				dynamic.PrependSub(m.s)
+				sb.SetValue(dynamic.Size())
+				dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
 			}
 
-			dynamic.Size += int64(ctxt.Arch.PtrSize)
+			dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
 		}
 	}
 
@@ -1095,6 +1101,7 @@
 }
 
 func addimports(ctxt *Link, datsect *peSection) {
+	ldr := ctxt.loader
 	startoff := ctxt.Out.Offset()
 	dynamic := ctxt.Syms.Lookup(".windynamic", 0)
 
@@ -1117,7 +1124,7 @@
 		for m := d.ms; m != nil; m = m.next {
 			m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff)
 			ctxt.Out.Write16(0) // hint
-			strput(ctxt.Out, m.s.Extname())
+			strput(ctxt.Out, ldr.SymExtname(m.s))
 		}
 	}
 
@@ -1198,36 +1205,31 @@
 	out.SeekSet(endoff)
 }
 
-type byExtname []*sym.Symbol
-
-func (s byExtname) Len() int           { return len(s) }
-func (s byExtname) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
-func (s byExtname) Less(i, j int) bool { return s[i].Extname() < s[j].Extname() }
-
 func initdynexport(ctxt *Link) {
-	nexport = 0
-	for _, s := range ctxt.Syms.Allsym {
-		if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() {
+	ldr := ctxt.loader
+	for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+		if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) {
 			continue
 		}
-		if nexport+1 > len(dexport) {
-			Errorf(s, "pe dynexport table is full")
+		if len(dexport)+1 > cap(dexport) {
+			ctxt.Errorf(s, "pe dynexport table is full")
 			errorexit()
 		}
 
-		dexport[nexport] = s
-		nexport++
+		dexport = append(dexport, s)
 	}
 
-	sort.Sort(byExtname(dexport[:nexport]))
+	sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) })
 }
 
 func addexports(ctxt *Link) {
+	ldr := ctxt.loader
 	var e IMAGE_EXPORT_DIRECTORY
 
+	nexport := len(dexport)
 	size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
-	for i := 0; i < nexport; i++ {
-		size += len(dexport[i].Extname()) + 1
+	for _, s := range dexport {
+		size += len(ldr.Syms[s].Extname()) + 1
 	}
 
 	if nexport == 0 {
@@ -1262,16 +1264,16 @@
 	binary.Write(out, binary.LittleEndian, &e)
 
 	// put EXPORT Address Table
-	for i := 0; i < nexport; i++ {
-		out.Write32(uint32(dexport[i].Value - PEBASE))
+	for _, s := range dexport {
+		out.Write32(uint32(ldr.Syms[s].Value - PEBASE))
 	}
 
 	// put EXPORT Name Pointer Table
 	v := int(e.Name + uint32(len(*flagOutfile)) + 1)
 
-	for i := 0; i < nexport; i++ {
+	for _, s := range dexport {
 		out.Write32(uint32(v))
-		v += len(dexport[i].Extname()) + 1
+		v += len(ldr.Syms[s].Extname()) + 1
 	}
 
 	// put EXPORT Ordinal Table
@@ -1282,8 +1284,9 @@
 	// put Names
 	out.WriteStringN(*flagOutfile, len(*flagOutfile)+1)
 
-	for i := 0; i < nexport; i++ {
-		out.WriteStringN(dexport[i].Extname(), len(dexport[i].Extname())+1)
+	for _, s := range dexport {
+		ss := ldr.Syms[s]
+		out.WriteStringN(ss.Extname(), len(ss.Extname())+1)
 	}
 	sect.pad(out, uint32(size))
 }
@@ -1455,27 +1458,30 @@
 	initdynexport(ctxt)
 }
 
-func setpersrc(ctxt *Link, sym *sym.Symbol) {
-	if rsrcsym != nil {
-		Errorf(sym, "too many .rsrc sections")
+func setpersrc(ctxt *Link, sym loader.Sym) {
+	if rsrcsym != 0 {
+		Errorf(nil, "too many .rsrc sections")
 	}
 
 	rsrcsym = sym
 }
 
 func addpersrc(ctxt *Link) {
-	if rsrcsym == nil {
+	if rsrcsym == 0 {
 		return
 	}
 
-	h := pefile.addSection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size))
+	data := ctxt.loader.Data(rsrcsym)
+	size := len(data)
+	h := pefile.addSection(".rsrc", size, size)
 	h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA
 	h.checkOffset(ctxt.Out.Offset())
 
 	// relocation
-	for ri := range rsrcsym.R {
-		r := &rsrcsym.R[ri]
-		p := rsrcsym.P[r.Off:]
+	relocs := ctxt.loader.Relocs(rsrcsym)
+	for i := 0; i < relocs.Count; i++ {
+		r := relocs.At(i)
+		p := data[r.Off:]
 		val := uint32(int64(h.virtualAddress) + r.Add)
 
 		// 32-bit little-endian
@@ -1486,8 +1492,8 @@
 		p[3] = byte(val >> 24)
 	}
 
-	ctxt.Out.Write(rsrcsym.P)
-	h.pad(ctxt.Out, uint32(rsrcsym.Size))
+	ctxt.Out.Write(data)
+	h.pad(ctxt.Out, uint32(size))
 
 	// update data directory
 	pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
index eb48ac8..62e6af2 100644
--- a/src/cmd/link/internal/ld/sym.go
+++ b/src/cmd/link/internal/ld/sym.go
@@ -40,9 +40,9 @@
 
 func linknew(arch *sys.Arch) *Link {
 	ctxt := &Link{
+		Target:       Target{Arch: arch},
 		Syms:         sym.NewSymbols(),
 		Out:          &OutBuf{arch: arch},
-		Arch:         arch,
 		LibraryByPkg: make(map[string]*sym.Library),
 	}
 
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index bba623e..b6734f6 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -326,12 +326,11 @@
 }
 
 func (ctxt *Link) symtab() {
-	switch ctxt.BuildMode {
-	case BuildModeCArchive, BuildModeCShared:
-		for _, s := range ctxt.Syms.Allsym {
-			// Create a new entry in the .init_array section that points to the
-			// library initializer function.
-			if s.Name == *flagEntrySymbol && ctxt.HeadType != objabi.Haix {
+	if ctxt.HeadType != objabi.Haix {
+		switch ctxt.BuildMode {
+		case BuildModeCArchive, BuildModeCShared:
+			s := ctxt.Syms.ROLookup(*flagEntrySymbol, sym.SymVerABI0)
+			if s != nil {
 				addinitarrdata(ctxt, s)
 			}
 		}
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
new file mode 100644
index 0000000..197c412
--- /dev/null
+++ b/src/cmd/link/internal/ld/target.go
@@ -0,0 +1,133 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+	"cmd/internal/objabi"
+	"cmd/internal/sys"
+	"encoding/binary"
+)
+
+// Target holds the configuration we're building for.
+type Target struct {
+	Arch *sys.Arch
+
+	HeadType objabi.HeadType
+
+	LinkMode  LinkMode
+	BuildMode BuildMode
+
+	linkShared    bool
+	canUsePlugins bool
+	IsELF         bool
+}
+
+//
+// Target type functions
+//
+
+func (t *Target) IsShared() bool {
+	return t.BuildMode == BuildModeShared
+}
+
+func (t *Target) IsPlugin() bool {
+	return t.BuildMode == BuildModePlugin
+}
+
+func (t *Target) IsInternal() bool {
+	return t.LinkMode == LinkInternal
+}
+
+func (t *Target) IsExternal() bool {
+	return t.LinkMode == LinkExternal
+}
+
+func (t *Target) IsPIE() bool {
+	return t.BuildMode == BuildModePIE
+}
+
+func (t *Target) IsSharedGoLink() bool {
+	return t.linkShared
+}
+
+func (t *Target) CanUsePlugins() bool {
+	return t.canUsePlugins
+}
+
+func (t *Target) IsElf() bool {
+	return t.IsELF
+}
+
+func (t *Target) IsDynlinkingGo() bool {
+	return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins()
+}
+
+//
+// Processor functions
+//
+
+func (t *Target) Is386() bool {
+	return t.Arch.Family == sys.I386
+}
+
+func (t *Target) IsARM() bool {
+	return t.Arch.Family == sys.ARM
+}
+
+func (t *Target) IsAMD64() bool {
+	return t.Arch.Family == sys.AMD64
+}
+
+func (t *Target) IsPPC64() bool {
+	return t.Arch.Family == sys.PPC64
+}
+
+func (t *Target) IsS390X() bool {
+	return t.Arch.Family == sys.S390X
+}
+
+//
+// OS Functions
+//
+
+func (t *Target) IsLinux() bool {
+	return t.HeadType == objabi.Hlinux
+}
+
+func (t *Target) IsDarwin() bool {
+	return t.HeadType == objabi.Hdarwin
+}
+
+func (t *Target) IsWindows() bool {
+	return t.HeadType == objabi.Hwindows
+}
+
+func (t *Target) IsPlan9() bool {
+	return t.HeadType == objabi.Hplan9
+}
+
+func (t *Target) IsAIX() bool {
+	return t.HeadType == objabi.Haix
+}
+
+func (t *Target) IsSolaris() bool {
+	return t.HeadType == objabi.Hsolaris
+}
+
+func (t *Target) IsNetbsd() bool {
+	return t.HeadType == objabi.Hnetbsd
+}
+
+func (t *Target) IsOpenbsd() bool {
+	return t.HeadType == objabi.Hopenbsd
+}
+
+//
+// MISC
+//
+
+func (t *Target) IsBigEndian() bool {
+	return t.Arch.ByteOrder == binary.BigEndian
+}
diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
index 9d236db..9f257b8 100644
--- a/src/cmd/link/internal/ld/util.go
+++ b/src/cmd/link/internal/ld/util.go
@@ -5,6 +5,7 @@
 package ld
 
 import (
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"encoding/binary"
 	"fmt"
@@ -38,6 +39,18 @@
 	Exit(2)
 }
 
+// afterErrorAction updates 'nerrors' on error and invokes exit or
+// panics in the proper circumstances.
+func afterErrorAction() {
+	nerrors++
+	if *flagH {
+		panic("error")
+	}
+	if nerrors > 20 {
+		Exitf("too many errors")
+	}
+}
+
 // Errorf logs an error message.
 //
 // If more than 20 errors have been printed, exit with an error.
@@ -50,13 +63,25 @@
 	}
 	format += "\n"
 	fmt.Fprintf(os.Stderr, format, args...)
-	nerrors++
-	if *flagH {
-		panic("error")
+	afterErrorAction()
+}
+
+// Errorf method logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) {
+	if s != 0 && ctxt.loader != nil {
+		sn := ctxt.loader.SymName(s)
+		format = sn + ": " + format
+	} else {
+		format = fmt.Sprintf("sym %d: %s", s, format)
 	}
-	if nerrors > 20 {
-		Exitf("too many errors")
-	}
+	format += "\n"
+	fmt.Fprintf(os.Stderr, format, args...)
+	afterErrorAction()
 }
 
 func artrim(x []byte) string {
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
index 8814bad..55a404c 100644
--- a/src/cmd/link/internal/ld/xcoff.go
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -1096,8 +1096,8 @@
 
 // Xcoffadddynrel adds a dynamic relocation in a XCOFF file.
 // This relocation will be made by the loader.
-func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
-	if ctxt.LinkMode == LinkExternal {
+func Xcoffadddynrel(target *Target, s *sym.Symbol, r *sym.Reloc) bool {
+	if target.IsExternal() {
 		return true
 	}
 	if s.Type <= sym.SPCLNTAB {
@@ -1186,7 +1186,7 @@
 	if ctxt.LinkMode == LinkExternal {
 		// Change rt0_go name to match name in runtime/cgo:main().
 		rt0 := ctxt.Syms.ROLookup("runtime.rt0_go", 0)
-		ctxt.Syms.Rename(rt0.Name, "runtime_rt0_go", 0, ctxt.Reachparent)
+		ctxt.Syms.Rename(rt0.Name, "runtime_rt0_go", 0)
 
 		for _, s := range ctxt.Syms.Allsym {
 			if !s.Attr.CgoExport() {
@@ -1198,7 +1198,7 @@
 				// On AIX, a exported function must have two symbols:
 				// - a .text symbol which must start with a ".".
 				// - a .data symbol which is a function descriptor.
-				ctxt.Syms.Rename(s.Name, "."+name, 0, ctxt.Reachparent)
+				ctxt.Syms.Rename(s.Name, "."+name, 0)
 
 				desc := ctxt.Syms.Lookup(name, 0)
 				desc.Type = sym.SNOPTRDATA
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
index 1962d76..2eabefd 100644
--- a/src/cmd/link/internal/loadelf/ldelf.go
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The Go Authors. All rights reserved.
+// Copyright 2019 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -270,19 +270,20 @@
 }
 
 type ElfSect struct {
-	name    string
-	nameoff uint32
-	type_   uint32
-	flags   uint64
-	addr    uint64
-	off     uint64
-	size    uint64
-	link    uint32
-	info    uint32
-	align   uint64
-	entsize uint64
-	base    []byte
-	sym     *sym.Symbol
+	name        string
+	nameoff     uint32
+	type_       uint32
+	flags       uint64
+	addr        uint64
+	off         uint64
+	size        uint64
+	link        uint32
+	info        uint32
+	align       uint64
+	entsize     uint64
+	base        []byte
+	readOnlyMem bool // Is this section in readonly memory?
+	sym         loader.Sym
 }
 
 type ElfObj struct {
@@ -320,7 +321,7 @@
 	type_ uint8
 	other uint8
 	shndx uint16
-	sym   *sym.Symbol
+	sym   loader.Sym
 }
 
 var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'}
@@ -452,32 +453,22 @@
 	return found, ehdrFlags, nil
 }
 
-func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
-	newSym := func(name string, version int) *sym.Symbol {
-		return l.Create(name, syms)
-	}
-	lookup := func(name string, version int) *sym.Symbol {
-		return l.LookupOrCreate(name, version, syms)
-	}
-	return load(arch, syms.IncVersion(), newSym, lookup, f, pkg, length, pn, flags)
-}
-
-func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
-	return load(arch, syms.IncVersion(), syms.Newsym, syms.Lookup, f, pkg, length, pn, flags)
-}
-
-type lookupFunc func(string, int) *sym.Symbol
-
-// load loads the ELF file pn from f.
-// Symbols are written into syms, and a slice of the text symbols is returned.
+// Load loads the ELF file pn from f.
+// Symbols are installed into the loader, and a slice of the text symbols is returned.
 //
 // On ARM systems, Load will attempt to determine what ELF header flags to
 // emit by scanning the attributes in the ELF file being loaded. The
 // parameter initEhdrFlags contains the current header flags for the output
 // object, and the returned ehdrFlags contains what this Load function computes.
 // TODO: find a better place for this logic.
-func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) {
-	errorf := func(str string, args ...interface{}) ([]*sym.Symbol, uint32, error) {
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) {
+	newSym := func(name string, version int) loader.Sym {
+		return l.CreateExtSym(name)
+	}
+	lookup := func(name string, version int) loader.Sym {
+		return l.LookupOrCreateSym(name, version)
+	}
+	errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) {
 		return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
 	}
 
@@ -610,7 +601,6 @@
 		sect := &elfobj.sect[i]
 		if is64 != 0 {
 			var b ElfSectBytes64
-
 			if err := binary.Read(f, e, &b); err != nil {
 				return errorf("malformed elf file: %v", err)
 			}
@@ -731,46 +721,47 @@
 		}
 		sectsymNames[name] = true
 
-		s := lookup(name, localSymVersion)
+		sb := l.MakeSymbolUpdater(lookup(name, localSymVersion))
 
 		switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) {
 		default:
 			return errorf("%s: unexpected flags for ELF section %s", pn, sect.name)
 
 		case ElfSectFlagAlloc:
-			s.Type = sym.SRODATA
+			sb.SetType(sym.SRODATA)
 
 		case ElfSectFlagAlloc + ElfSectFlagWrite:
 			if sect.type_ == ElfSectNobits {
-				s.Type = sym.SNOPTRBSS
+				sb.SetType(sym.SNOPTRBSS)
 			} else {
-				s.Type = sym.SNOPTRDATA
+				sb.SetType(sym.SNOPTRDATA)
 			}
 
 		case ElfSectFlagAlloc + ElfSectFlagExec:
-			s.Type = sym.STEXT
+			sb.SetType(sym.STEXT)
 		}
 
 		if sect.name == ".got" || sect.name == ".toc" {
-			s.Type = sym.SELFGOT
+			sb.SetType(sym.SELFGOT)
 		}
 		if sect.type_ == ElfSectProgbits {
-			s.P = sect.base
-			s.P = s.P[:sect.size]
+			sb.SetData(sect.base[:sect.size])
 		}
 
-		s.Size = int64(sect.size)
-		s.Align = int32(sect.align)
-		sect.sym = s
+		sb.SetSize(int64(sect.size))
+		sb.SetAlign(int32(sect.align))
+		sb.SetReadOnly(sect.readOnlyMem)
+
+		sect.sym = sb.Sym()
 	}
 
 	// enter sub-symbols into symbol table.
 	// symbol 0 is the null symbol.
-	symbols := make([]*sym.Symbol, elfobj.nsymtab)
+	symbols := make([]loader.Sym, elfobj.nsymtab)
 
 	for i := 1; i < elfobj.nsymtab; i++ {
 		var elfsym ElfSym
-		if err := readelfsym(newSym, lookup, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
+		if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
 			return errorf("%s: malformed elf file: %v", pn, err)
 		}
 		symbols[i] = elfsym.sym
@@ -778,12 +769,12 @@
 			continue
 		}
 		if elfsym.shndx == ElfSymShnCommon || elfsym.type_ == ElfSymTypeCommon {
-			s := elfsym.sym
-			if uint64(s.Size) < elfsym.size {
-				s.Size = int64(elfsym.size)
+			sb := l.MakeSymbolUpdater(elfsym.sym)
+			if uint64(sb.Size()) < elfsym.size {
+				sb.SetSize(int64(elfsym.size))
 			}
-			if s.Type == 0 || s.Type == sym.SXREF {
-				s.Type = sym.SNOPTRBSS
+			if sb.Type() == 0 || sb.Type() == sym.SXREF {
+				sb.SetType(sym.SNOPTRBSS)
 			}
 			continue
 		}
@@ -793,11 +784,11 @@
 		}
 
 		// even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols
-		if elfsym.sym == nil {
+		if elfsym.sym == 0 {
 			continue
 		}
 		sect = &elfobj.sect[elfsym.shndx]
-		if sect.sym == nil {
+		if sect.sym == 0 {
 			if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this
 				continue
 			}
@@ -822,36 +813,37 @@
 		}
 
 		s := elfsym.sym
-		if s.Outer != nil {
-			if s.Attr.DuplicateOK() {
+		if l.OuterSym(s) != 0 {
+			if l.AttrDuplicateOK(s) {
 				continue
 			}
-			return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name)
+			return errorf("duplicate symbol reference: %s in both %s and %s",
+				l.SymName(s), l.SymName(l.OuterSym(s)), l.SymName(sect.sym))
 		}
 
-		s.Sub = sect.sym.Sub
-		sect.sym.Sub = s
-		s.Type = sect.sym.Type
-		s.Attr |= sym.AttrSubSymbol
-		if !s.Attr.CgoExportDynamic() {
-			s.SetDynimplib("") // satisfy dynimport
+		sectsb := l.MakeSymbolUpdater(sect.sym)
+		sb := l.MakeSymbolUpdater(s)
+
+		sb.SetType(sectsb.Type())
+		sectsb.PrependSub(s)
+		if !l.AttrCgoExportDynamic(s) {
+			sb.SetDynimplib("") // satisfy dynimport
 		}
-		s.Value = int64(elfsym.value)
-		s.Size = int64(elfsym.size)
-		s.Outer = sect.sym
-		if sect.sym.Type == sym.STEXT {
-			if s.Attr.External() && !s.Attr.DuplicateOK() {
-				return errorf("%v: duplicate symbol definition", s)
+		sb.SetValue(int64(elfsym.value))
+		sb.SetSize(int64(elfsym.size))
+		if sectsb.Type() == sym.STEXT {
+			if l.AttrExternal(s) && !l.AttrDuplicateOK(s) {
+				return errorf("%s: duplicate symbol definition", sb.Name())
 			}
-			s.Attr |= sym.AttrExternal
+			l.SetAttrExternal(s, true)
 		}
 
 		if elfobj.machine == ElfMachPower64 {
 			flag := int(elfsym.other) >> 5
 			if 2 <= flag && flag <= 6 {
-				s.SetLocalentry(1 << uint(flag-2))
+				l.SetSymLocalentry(s, 1<<uint(flag-2))
 			} else if flag == 7 {
-				return errorf("%v: invalid sym.other 0x%x", s, elfsym.other)
+				return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other)
 			}
 		}
 	}
@@ -860,24 +852,27 @@
 	// This keeps textp in increasing address order.
 	for i := uint(0); i < elfobj.nsect; i++ {
 		s := elfobj.sect[i].sym
-		if s == nil {
+		if s == 0 {
 			continue
 		}
-		if s.Sub != nil {
-			s.Sub = sym.SortSub(s.Sub)
+		sb := l.MakeSymbolUpdater(s)
+		if l.SubSym(s) != 0 {
+			sb.SortSub()
 		}
-		if s.Type == sym.STEXT {
-			if s.Attr.OnList() {
-				return errorf("symbol %s listed multiple times", s.Name)
+		if sb.Type() == sym.STEXT {
+			if l.AttrOnList(s) {
+				return errorf("symbol %s listed multiple times",
+					l.SymName(s))
 			}
-			s.Attr |= sym.AttrOnList
+			l.SetAttrOnList(s, true)
 			textp = append(textp, s)
-			for s = s.Sub; s != nil; s = s.Sub {
-				if s.Attr.OnList() {
-					return errorf("symbol %s listed multiple times", s.Name)
+			for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) {
+				if l.AttrOnList(ss) {
+					return errorf("symbol %s listed multiple times",
+						l.SymName(ss))
 				}
-				s.Attr |= sym.AttrOnList
-				textp = append(textp, s)
+				l.SetAttrOnList(ss, true)
+				textp = append(textp, ss)
 			}
 		}
 	}
@@ -900,7 +895,7 @@
 			rela = 1
 		}
 		n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela))
-		r := make([]sym.Reloc, n)
+		r := make([]loader.Reloc, n)
 		p := rsect.base
 		for j := 0; j < n; j++ {
 			var add uint64
@@ -951,22 +946,22 @@
 			}
 
 			if symIdx == 0 { // absolute relocation, don't bother reading the null symbol
-				rp.Sym = nil
+				rp.Sym = 0
 			} else {
 				var elfsym ElfSym
-				if err := readelfsym(newSym, lookup, arch, elfobj, symIdx, &elfsym, 0, 0); err != nil {
+				if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil {
 					return errorf("malformed elf file: %v", err)
 				}
 				elfsym.sym = symbols[symIdx]
-				if elfsym.sym == nil {
-					return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, symIdx, elfsym.name, elfsym.shndx, elfsym.type_)
+				if elfsym.sym == 0 {
+					return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", l.SymName(sect.sym), j, int(symIdx), elfsym.name, elfsym.shndx, elfsym.type_)
 				}
 
 				rp.Sym = elfsym.sym
 			}
 
 			rp.Type = objabi.ElfRelocOffset + objabi.RelocType(relocType)
-			rp.Siz, err = relSize(arch, pn, uint32(relocType))
+			rp.Size, err = relSize(arch, pn, uint32(relocType))
 			if err != nil {
 				return nil, 0, err
 			}
@@ -974,30 +969,30 @@
 				rp.Add = int64(add)
 			} else {
 				// load addend from image
-				if rp.Siz == 4 {
+				if rp.Size == 4 {
 					rp.Add = int64(e.Uint32(sect.base[rp.Off:]))
-				} else if rp.Siz == 8 {
+				} else if rp.Size == 8 {
 					rp.Add = int64(e.Uint64(sect.base[rp.Off:]))
 				} else {
-					return errorf("invalid rela size %d", rp.Siz)
+					return errorf("invalid rela size %d", rp.Size)
 				}
 			}
 
-			if rp.Siz == 2 {
+			if rp.Size == 2 {
 				rp.Add = int64(int16(rp.Add))
 			}
-			if rp.Siz == 4 {
+			if rp.Size == 4 {
 				rp.Add = int64(int32(rp.Add))
 			}
 		}
 
 		//print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add);
-		sort.Sort(sym.RelocByOff(r[:n]))
+		sort.Sort(loader.RelocByOff(r[:n]))
 		// just in case
 
-		s := sect.sym
-		s.R = r
-		s.R = s.R[:n]
+		sb := l.MakeSymbolUpdater(sect.sym)
+		r = r[:n]
+		sb.SetRelocs(r)
 	}
 
 	return textp, ehdrFlags, nil
@@ -1022,16 +1017,16 @@
 		return err
 	}
 
-	sect.base = make([]byte, sect.size)
 	elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0)
-	if _, err := io.ReadFull(elfobj.f, sect.base); err != nil {
+	sect.base, sect.readOnlyMem, err = elfobj.f.Slice(uint64(sect.size))
+	if err != nil {
 		return fmt.Errorf("short read: %v", err)
 	}
 
 	return nil
 }
 
-func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
+func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
 	if i >= elfobj.nsymtab || i < 0 {
 		err = fmt.Errorf("invalid elf symbol index")
 		return err
@@ -1063,7 +1058,8 @@
 		elfsym.other = b.Other
 	}
 
-	var s *sym.Symbol
+	var s loader.Sym
+
 	if elfsym.name == "_GLOBAL_OFFSET_TABLE_" {
 		elfsym.name = ".got"
 	}
@@ -1090,8 +1086,12 @@
 				// TODO(minux): correctly handle __i686.get_pc_thunk.bx without
 				// set dupok generally. See https://golang.org/cl/5823055
 				// comment #5 for details.
-				if s != nil && elfsym.other == 2 {
-					s.Attr |= sym.AttrDuplicateOK | sym.AttrVisibilityHidden
+				if s != 0 && elfsym.other == 2 {
+					if !l.IsExternal(s) {
+						l.MakeSymbolUpdater(s)
+					}
+					l.SetAttrDuplicateOK(s, true)
+					l.SetAttrVisibilityHidden(s, true)
 				}
 			}
 
@@ -1107,9 +1107,8 @@
 				// so put it in the hash table.
 				if needSym != 0 {
 					s = lookup(elfsym.name, localSymVersion)
-					s.Attr |= sym.AttrVisibilityHidden
+					l.SetAttrVisibilityHidden(s, true)
 				}
-
 				break
 			}
 
@@ -1121,20 +1120,19 @@
 				// reduce mem use, but also (possibly) make it harder
 				// to debug problems.
 				s = newSym(elfsym.name, localSymVersion)
-
-				s.Attr |= sym.AttrVisibilityHidden
+				l.SetAttrVisibilityHidden(s, true)
 			}
 
 		case ElfSymBindWeak:
 			if needSym != 0 {
 				s = lookup(elfsym.name, 0)
 				if elfsym.other == 2 {
-					s.Attr |= sym.AttrVisibilityHidden
+					l.SetAttrVisibilityHidden(s, true)
 				}
 
 				// Allow weak symbols to be duplicated when already defined.
-				if s.Outer != nil {
-					s.Attr |= sym.AttrDuplicateOK
+				if l.OuterSym(s) != 0 {
+					l.SetAttrDuplicateOK(s, true)
 				}
 			}
 
@@ -1146,8 +1144,9 @@
 
 	// TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
 	// sense and should be removed when someone has thought about it properly.
-	if s != nil && s.Type == 0 && !s.Attr.VisibilityHidden() && elfsym.type_ != ElfSymTypeSection {
-		s.Type = sym.SXREF
+	if s != 0 && l.SymType(s) == 0 && !l.AttrVisibilityHidden(s) && elfsym.type_ != ElfSymTypeSection {
+		sb := l.MakeSymbolUpdater(s)
+		sb.SetType(sym.SXREF)
 	}
 	elfsym.sym = s
 
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 0adc395..8eb12c5 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -7,14 +7,15 @@
 import (
 	"bytes"
 	"cmd/internal/bio"
-	"cmd/internal/dwarf"
 	"cmd/internal/goobj2"
 	"cmd/internal/obj"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/sym"
+	"debug/elf"
 	"fmt"
 	"log"
+	"math/bits"
 	"os"
 	"sort"
 	"strconv"
@@ -35,8 +36,6 @@
 	li int      // local index of symbol whose relocs we're examining
 	r  *oReader // object reader for containing package
 	l  *Loader  // loader
-
-	ext *sym.Symbol // external symbol if not nil
 }
 
 // Reloc contains the payload for a specific relocation.
@@ -50,6 +49,17 @@
 	Sym  Sym              // global index of symbol the reloc addresses
 }
 
+// Reloc2 holds a "handle" to access a relocation record from an
+// object file.
+type Reloc2 struct {
+	*goobj2.Reloc2
+	r *oReader
+	l *Loader
+}
+
+func (rel Reloc2) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc2.Type()) }
+func (rel Reloc2) Sym() Sym               { return rel.l.resolve(rel.r, rel.Reloc2.Sym()) }
+
 // oReader is a wrapper type of obj.Reader, along with some
 // extra information.
 // TODO: rename to objReader once the old one is gone?
@@ -59,13 +69,24 @@
 	version   int    // version of static symbol
 	flags     uint32 // read from object file
 	pkgprefix string
-	rcache    []Sym // cache mapping local PkgNone symbol to resolved Sym
+	syms      []Sym  // Sym's global index, indexed by local index
+	ndef      int    // cache goobj2.Reader.NSym()
+	objidx    uint32 // index of this reader in the objs slice
 }
 
 type objIdx struct {
 	r *oReader
 	i Sym // start index
-	e Sym // end index
+}
+
+// objSym represents a symbol in an object file. It is a tuple of
+// the object and the symbol's local index.
+// For external symbols, r is l.extReader, s is its index into the
+// payload array.
+// {nil, 0} represents the nil symbol.
+type objSym struct {
+	r *oReader
+	s int // local index
 }
 
 type nameVer struct {
@@ -73,47 +94,133 @@
 	v    int
 }
 
-type bitmap []uint32
+type Bitmap []uint32
 
 // set the i-th bit.
-func (bm bitmap) Set(i Sym) {
+func (bm Bitmap) Set(i Sym) {
 	n, r := uint(i)/32, uint(i)%32
 	bm[n] |= 1 << r
 }
 
+// unset the i-th bit.
+func (bm Bitmap) Unset(i Sym) {
+	n, r := uint(i)/32, uint(i)%32
+	bm[n] &^= (1 << r)
+}
+
 // whether the i-th bit is set.
-func (bm bitmap) Has(i Sym) bool {
+func (bm Bitmap) Has(i Sym) bool {
 	n, r := uint(i)/32, uint(i)%32
 	return bm[n]&(1<<r) != 0
 }
 
-func makeBitmap(n int) bitmap {
-	return make(bitmap, (n+31)/32)
+// return current length of bitmap in bits.
+func (bm Bitmap) Len() int {
+	return len(bm) * 32
+}
+func MakeBitmap(n int) Bitmap {
+	return make(Bitmap, (n+31)/32)
+}
+
+// growBitmap insures that the specified bitmap has enough capacity,
+// reallocating (doubling the size) if needed.
+func growBitmap(reqLen int, b Bitmap) Bitmap {
+	curLen := b.Len()
+	if reqLen > curLen {
+		b = append(b, MakeBitmap(reqLen+1-curLen)...)
+	}
+	return b
 }
 
 // A Loader loads new object files and resolves indexed symbol references.
+//
+// Notes on the layout of global symbol index space:
+//
+// - Go object files are read before host object files; each Go object
+//   read adds its defined package symbols to the global index space.
+//   Nonpackage symbols are not yet added.
+//
+// - In loader.LoadNonpkgSyms, add non-package defined symbols and
+//   references in all object files to the global index space.
+//
+// - Host object file loading happens; the host object loader does a
+//   name/version lookup for each symbol it finds; this can wind up
+//   extending the external symbol index space range. The host object
+//   loader stores symbol payloads in loader.payloads using SymbolBuilder.
+//
+// - For now, in loader.LoadFull we convert all symbols (Go + external)
+//   to sym.Symbols.
+//
+// - At some point (when the wayfront is pushed through all of the
+//   linker), all external symbols will be payload-based, and we can
+//   get rid of the loader.Syms array.
+//
+// - Each symbol gets a unique global index. For duplicated and
+//   overwriting/overwritten symbols, the second (or later) appearance
+//   of the symbol gets the same global index as the first appearance.
 type Loader struct {
 	start       map[*oReader]Sym // map from object file to its start index
 	objs        []objIdx         // sorted by start index (i.e. objIdx.i)
-	max         Sym              // current max index
 	extStart    Sym              // from this index on, the symbols are externally defined
-	extSyms     []nameVer        // externally defined symbols
 	builtinSyms []Sym            // global index of builtin symbols
-	ocache      int              // index (into 'objs') of most recent lookup
+
+	objSyms []objSym // global index mapping to local index
 
 	symsByName    [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal
 	extStaticSyms map[nameVer]Sym   // externally defined static symbols, keyed by name
-	overwrite     map[Sym]Sym       // overwrite[i]=j if symbol j overwrites symbol i
+
+	extReader    *oReader // a dummy oReader, for external symbols
+	payloadBatch []extSymPayload
+	payloads     []*extSymPayload // contents of linker-materialized external syms
+	values       []int64          // symbol values, indexed by global sym index
 
 	itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
 
 	objByPkg map[string]*oReader // map package path to its Go object reader
 
-	Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now.
+	Syms     []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now.
+	symBatch []sym.Symbol  // batch of symbols.
 
 	anonVersion int // most recently assigned ext static sym pseudo-version
 
-	Reachable bitmap // bitmap of reachable symbols, indexed by global index
+	// Bitmaps and other side structures used to store data used to store
+	// symbol flags/attributes; these are to be accessed via the
+	// corresponding loader "AttrXXX" and "SetAttrXXX" methods. Please
+	// visit the comments on these methods for more details on the
+	// semantics / interpretation of the specific flags or attribute.
+	attrReachable        Bitmap // reachable symbols, indexed by global index
+	attrOnList           Bitmap // "on list" symbols, indexed by global index
+	attrLocal            Bitmap // "local" symbols, indexed by global index
+	attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by glob idx
+	attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index
+	attrDuplicateOK      Bitmap // dupOK symbols, indexed by ext sym index
+	attrShared           Bitmap // shared symbols, indexed by ext sym index
+	attrExternal         Bitmap // external symbols, indexed by ext sym index
+
+	attrReadOnly         map[Sym]bool     // readonly data for this sym
+	attrTopFrame         map[Sym]struct{} // top frame symbols
+	attrSpecial          map[Sym]struct{} // "special" frame symbols
+	attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
+	attrCgoExportStatic  map[Sym]struct{} // "cgo_export_static" symbols
+
+	// Outer and Sub relations for symbols.
+	// TODO: figure out whether it's more efficient to just have these
+	// as fields on extSymPayload (note that this won't be a viable
+	// strategy if somewhere in the linker we set sub/outer for a
+	// non-external sym).
+	outer map[Sym]Sym
+	sub   map[Sym]Sym
+
+	align map[Sym]int32 // stores alignment for symbols
+
+	dynimplib  map[Sym]string      // stores Dynimplib symbol attribute
+	dynimpvers map[Sym]string      // stores Dynimpvers symbol attribute
+	localentry map[Sym]uint8       // stores Localentry symbol attribute
+	extname    map[Sym]string      // stores Extname symbol attribute
+	elfType    map[Sym]elf.SymType // stores elf type symbol property
+	symFile    map[Sym]string      // stores file for shlib-derived syms
+	plt        map[Sym]int32       // stores dynimport for pe objects
+	got        map[Sym]int32       // stores got for pe objects
 
 	// Used to implement field tracking; created during deadcode if
 	// field tracking is enabled. Reachparent[K] contains the index of
@@ -125,6 +232,29 @@
 	flags uint32
 
 	strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
+
+	elfsetstring elfsetstringFunc
+}
+
+const (
+	pkgDef = iota
+	nonPkgDef
+	nonPkgRef
+)
+
+type elfsetstringFunc func(s *sym.Symbol, str string, off int)
+
+// extSymPayload holds the payload (data + relocations) for linker-synthesized
+// external symbols (note that symbol value is stored in a separate slice).
+type extSymPayload struct {
+	name   string // TODO: would this be better as offset into str table?
+	size   int64
+	ver    int
+	kind   sym.SymKind
+	objidx uint32 // index of original object if sym made by cloneToExternal
+	gotype Sym    // Gotype (0 if not present)
+	relocs []Reloc
+	data   []byte
 }
 
 const (
@@ -132,26 +262,39 @@
 	FlagStrictDups = 1 << iota
 )
 
-func NewLoader(flags uint32) *Loader {
+func NewLoader(flags uint32, elfsetstring elfsetstringFunc) *Loader {
 	nbuiltin := goobj2.NBuiltin()
 	return &Loader{
-		start:         make(map[*oReader]Sym),
-		objs:          []objIdx{{nil, 0, 0}},
-		symsByName:    [2]map[string]Sym{make(map[string]Sym), make(map[string]Sym)},
-		objByPkg:      make(map[string]*oReader),
-		overwrite:     make(map[Sym]Sym),
-		itablink:      make(map[Sym]struct{}),
-		extStaticSyms: make(map[nameVer]Sym),
-		builtinSyms:   make([]Sym, nbuiltin),
-		flags:         flags,
+		start:                make(map[*oReader]Sym),
+		objs:                 []objIdx{{}}, // reserve index 0 for nil symbol
+		objSyms:              []objSym{{}}, // reserve index 0 for nil symbol
+		extReader:            &oReader{},
+		symsByName:           [2]map[string]Sym{make(map[string]Sym, 100000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
+		objByPkg:             make(map[string]*oReader),
+		outer:                make(map[Sym]Sym),
+		sub:                  make(map[Sym]Sym),
+		align:                make(map[Sym]int32),
+		dynimplib:            make(map[Sym]string),
+		dynimpvers:           make(map[Sym]string),
+		localentry:           make(map[Sym]uint8),
+		extname:              make(map[Sym]string),
+		attrReadOnly:         make(map[Sym]bool),
+		elfType:              make(map[Sym]elf.SymType),
+		symFile:              make(map[Sym]string),
+		plt:                  make(map[Sym]int32),
+		got:                  make(map[Sym]int32),
+		attrTopFrame:         make(map[Sym]struct{}),
+		attrSpecial:          make(map[Sym]struct{}),
+		attrCgoExportDynamic: make(map[Sym]struct{}),
+		attrCgoExportStatic:  make(map[Sym]struct{}),
+		itablink:             make(map[Sym]struct{}),
+		extStaticSyms:        make(map[nameVer]Sym),
+		builtinSyms:          make([]Sym, nbuiltin),
+		flags:                flags,
+		elfsetstring:         elfsetstring,
 	}
 }
 
-// Return the start index in the global index space for a given object file.
-func (l *Loader) startIndex(r *oReader) Sym {
-	return l.start[r]
-}
-
 // Add object file r, return the start index.
 func (l *Loader) addObj(pkg string, r *oReader) Sym {
 	if _, ok := l.start[r]; ok {
@@ -161,89 +304,194 @@
 	if _, ok := l.objByPkg[pkg]; !ok {
 		l.objByPkg[pkg] = r
 	}
-	n := r.NSym() + r.NNonpkgdef()
-	i := l.max + 1
+	i := Sym(len(l.objSyms))
 	l.start[r] = i
-	l.objs = append(l.objs, objIdx{r, i, i + Sym(n) - 1})
-	l.max += Sym(n)
+	l.objs = append(l.objs, objIdx{r, i})
 	return i
 }
 
-// Add a symbol with a given index, return if it is added.
-func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ sym.SymKind) bool {
+// Add a symbol from an object file, return the global index and whether it is added.
+// If the symbol already exist, it returns the index of that symbol.
+func (l *Loader) AddSym(name string, ver int, r *oReader, li int, kind int, dupok bool, typ sym.SymKind) (Sym, bool) {
 	if l.extStart != 0 {
 		panic("AddSym called after AddExtSym is called")
 	}
+	i := Sym(len(l.objSyms))
+	addToGlobal := func() {
+		l.objSyms = append(l.objSyms, objSym{r, li})
+	}
+	if name == "" {
+		addToGlobal()
+		return i, true // unnamed aux symbol
+	}
 	if ver == r.version {
 		// Static symbol. Add its global index but don't
 		// add to name lookup table, as it cannot be
 		// referenced by name.
-		return true
+		addToGlobal()
+		return i, true
 	}
-	if oldi, ok := l.symsByName[ver][name]; ok {
-		if dupok {
-			if l.flags&FlagStrictDups != 0 {
-				l.checkdup(name, i, r, oldi)
-			}
-			return false
+	if kind == pkgDef {
+		// Defined package symbols cannot be dup to each other.
+		// We load all the package symbols first, so we don't need
+		// to check dup here.
+		// We still add it to the lookup table, as it may still be
+		// referenced by name (e.g. through linkname).
+		l.symsByName[ver][name] = i
+		addToGlobal()
+		return i, true
+	}
+
+	// Non-package (named) symbol. Check if it already exists.
+	oldi, existed := l.symsByName[ver][name]
+	if !existed {
+		l.symsByName[ver][name] = i
+		addToGlobal()
+		return i, true
+	}
+	// symbol already exists
+	if dupok {
+		if l.flags&FlagStrictDups != 0 {
+			l.checkdup(name, r, li, oldi)
 		}
-		oldr, li := l.toLocal(oldi)
-		oldsym := goobj2.Sym{}
-		oldsym.Read(oldr.Reader, oldr.SymOff(li))
-		if oldsym.Dupok() {
-			return false
+		return oldi, false
+	}
+	oldr, oldli := l.toLocal(oldi)
+	oldsym := goobj2.Sym{}
+	oldsym.ReadWithoutName(oldr.Reader, oldr.SymOff(oldli))
+	if oldsym.Dupok() {
+		return oldi, false
+	}
+	overwrite := r.DataSize(li) != 0
+	if overwrite {
+		// new symbol overwrites old symbol.
+		oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)]
+		if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) {
+			log.Fatalf("duplicated definition of symbol " + name)
 		}
-		overwrite := r.DataSize(int(i-l.startIndex(r))) != 0
-		if overwrite {
-			// new symbol overwrites old symbol.
-			oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)]
-			if !oldtyp.IsData() && r.DataSize(li) == 0 {
-				log.Fatalf("duplicated definition of symbol " + name)
-			}
-			l.overwrite[oldi] = i
-		} else {
-			// old symbol overwrites new symbol.
-			if typ != sym.SDATA && typ != sym.SNOPTRDATA && typ != sym.SBSS && typ != sym.SNOPTRBSS { // only allow overwriting data symbol
-				log.Fatalf("duplicated definition of symbol " + name)
-			}
-			l.overwrite[i] = oldi
-			return false
+		l.objSyms[oldi] = objSym{r, li}
+	} else {
+		// old symbol overwrites new symbol.
+		if !typ.IsData() { // only allow overwriting data symbol
+			log.Fatalf("duplicated definition of symbol " + name)
 		}
 	}
-	l.symsByName[ver][name] = i
-	return true
+	return oldi, true
+}
+
+// newExtSym creates a new external sym with the specified
+// name/version.
+func (l *Loader) newExtSym(name string, ver int) Sym {
+	i := Sym(len(l.objSyms))
+	if l.extStart == 0 {
+		l.extStart = i
+	}
+	l.growSyms(int(i))
+	pi := l.newPayload(name, ver)
+	l.objSyms = append(l.objSyms, objSym{l.extReader, int(pi)})
+	l.extReader.syms = append(l.extReader.syms, i)
+	return i
 }
 
 // Add an external symbol (without index). Return the index of newly added
 // symbol, or 0 if not added.
 func (l *Loader) AddExtSym(name string, ver int) Sym {
-	static := ver >= sym.SymVerStatic
-	if static {
-		if _, ok := l.extStaticSyms[nameVer{name, ver}]; ok {
-			return 0
-		}
-	} else {
-		if _, ok := l.symsByName[ver][name]; ok {
-			return 0
-		}
+	i := l.Lookup(name, ver)
+	if i != 0 {
+		return i
 	}
-	i := l.max + 1
+	i = l.newExtSym(name, ver)
+	static := ver >= sym.SymVerStatic || ver < 0
 	if static {
 		l.extStaticSyms[nameVer{name, ver}] = i
 	} else {
 		l.symsByName[ver][name] = i
 	}
-	l.max++
-	if l.extStart == 0 {
-		l.extStart = i
+	return i
+}
+
+// LookupOrCreateSym looks up the symbol with the specified name/version,
+// returning its Sym index if found. If the lookup fails, a new external
+// Sym will be created, entered into the lookup tables, and returned.
+func (l *Loader) LookupOrCreateSym(name string, ver int) Sym {
+	i := l.Lookup(name, ver)
+	if i != 0 {
+		return i
 	}
-	l.extSyms = append(l.extSyms, nameVer{name, ver})
-	l.growSyms(int(i))
+	i = l.newExtSym(name, ver)
+	static := ver >= sym.SymVerStatic || ver < 0
+	if static {
+		l.extStaticSyms[nameVer{name, ver}] = i
+	} else {
+		l.symsByName[ver][name] = i
+	}
 	return i
 }
 
 func (l *Loader) IsExternal(i Sym) bool {
-	return l.extStart != 0 && i >= l.extStart
+	r, _ := l.toLocal(i)
+	return l.isExtReader(r)
+}
+
+func (l *Loader) isExtReader(r *oReader) bool {
+	return r == l.extReader
+}
+
+// For external symbol, return its index in the payloads array.
+// XXX result is actually not a global index. We (ab)use the Sym type
+// so we don't need conversion for accessing bitmaps.
+func (l *Loader) extIndex(i Sym) Sym {
+	_, li := l.toLocal(i)
+	return Sym(li)
+}
+
+// Get a new payload for external symbol, return its index in
+// the payloads array.
+func (l *Loader) newPayload(name string, ver int) int {
+	pi := len(l.payloads)
+	pp := l.allocPayload()
+	pp.name = name
+	pp.ver = ver
+	l.payloads = append(l.payloads, pp)
+	l.growExtAttrBitmaps()
+	return pi
+}
+
+// getPayload returns a pointer to the extSymPayload struct for an
+// external symbol if the symbol has a payload. Will panic if the
+// symbol in question is bogus (zero or not an external sym).
+func (l *Loader) getPayload(i Sym) *extSymPayload {
+	if !l.IsExternal(i) {
+		panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
+	}
+	pi := l.extIndex(i)
+	return l.payloads[pi]
+}
+
+// allocPayload allocates a new payload.
+func (l *Loader) allocPayload() *extSymPayload {
+	batch := l.payloadBatch
+	if len(batch) == 0 {
+		batch = make([]extSymPayload, 1000)
+	}
+	p := &batch[0]
+	l.payloadBatch = batch[1:]
+	return p
+}
+
+func (ms *extSymPayload) Grow(siz int64) {
+	if int64(int(siz)) != siz {
+		log.Fatalf("symgrow size %d too long", siz)
+	}
+	if int64(len(ms.data)) >= siz {
+		return
+	}
+	if cap(ms.data) < int(siz) {
+		cl := len(ms.data)
+		ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...)
+		ms.data = ms.data[0:cl]
+	}
+	ms.data = ms.data[:siz]
 }
 
 // Ensure Syms slice has enough space.
@@ -253,57 +501,18 @@
 		return
 	}
 	l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...)
+	l.growValues(int(i) + 1)
+	l.growAttrBitmaps(int(i) + 1)
 }
 
 // Convert a local index to a global index.
 func (l *Loader) toGlobal(r *oReader, i int) Sym {
-	g := l.startIndex(r) + Sym(i)
-	if ov, ok := l.overwrite[g]; ok {
-		return ov
-	}
-	return g
+	return r.syms[i]
 }
 
 // Convert a global index to a local index.
 func (l *Loader) toLocal(i Sym) (*oReader, int) {
-	if ov, ok := l.overwrite[i]; ok {
-		i = ov
-	}
-	if l.IsExternal(i) {
-		return nil, int(i - l.extStart)
-	}
-	oc := l.ocache
-	if oc != 0 && i >= l.objs[oc].i && i <= l.objs[oc].e {
-		return l.objs[oc].r, int(i - l.objs[oc].i)
-	}
-	// Search for the local object holding index i.
-	// Below k is the first one that has its start index > i,
-	// so k-1 is the one we want.
-	k := sort.Search(len(l.objs), func(k int) bool {
-		return l.objs[k].i > i
-	})
-	l.ocache = k - 1
-	return l.objs[k-1].r, int(i - l.objs[k-1].i)
-}
-
-// rcacheGet checks for a valid entry for 's' in the readers cache,
-// where 's' is a local PkgIdxNone ref or def, or zero if
-// the cache is empty or doesn't contain a value for 's'.
-func (or *oReader) rcacheGet(symIdx uint32) Sym {
-	if len(or.rcache) > 0 {
-		return or.rcache[symIdx]
-	}
-	return 0
-}
-
-// rcacheSet installs a new entry in the oReader's PkgNone
-// resolver cache for the specified PkgIdxNone ref or def,
-// allocating a new cache if needed.
-func (or *oReader) rcacheSet(symIdx uint32, gsym Sym) {
-	if len(or.rcache) == 0 {
-		or.rcache = make([]Sym, or.NNonpkgdef()+or.NNonpkgref())
-	}
-	or.rcache[symIdx] = gsym
+	return l.objSyms[i].r, int(l.objSyms[i].s)
 }
 
 // Resolve a local symbol reference. Return global index.
@@ -311,25 +520,19 @@
 	var rr *oReader
 	switch p := s.PkgIdx; p {
 	case goobj2.PkgIdxInvalid:
+		// {0, X} with non-zero X is never a valid sym reference from a Go object.
+		// We steal this space for symbol references from external objects.
+		// In this case, X is just the global index.
+		if l.isExtReader(r) {
+			return Sym(s.SymIdx)
+		}
 		if s.SymIdx != 0 {
 			panic("bad sym ref")
 		}
 		return 0
 	case goobj2.PkgIdxNone:
-		// Check for cached version first
-		if cached := r.rcacheGet(s.SymIdx); cached != 0 {
-			return cached
-		}
-		// Resolve by name
-		i := int(s.SymIdx) + r.NSym()
-		osym := goobj2.Sym{}
-		osym.Read(r.Reader, r.SymOff(i))
-		name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
-		v := abiToVer(osym.ABI, r.version)
-		gsym := l.Lookup(name, v)
-		// Add to cache, then return.
-		r.rcacheSet(s.SymIdx, gsym)
-		return gsym
+		i := int(s.SymIdx) + r.ndef
+		return r.syms[i]
 	case goobj2.PkgIdxBuiltin:
 		return l.builtinSyms[s.SymIdx]
 	case goobj2.PkgIdxSelf:
@@ -355,44 +558,11 @@
 	return l.symsByName[ver][name]
 }
 
-// Returns whether i is a dup of another symbol, and i is not
-// "primary", i.e. Lookup i by name will not return i.
-func (l *Loader) IsDup(i Sym) bool {
-	if _, ok := l.overwrite[i]; ok {
-		return true
-	}
-	if l.IsExternal(i) {
-		return false
-	}
-	r, li := l.toLocal(i)
-	osym := goobj2.Sym{}
-	osym.Read(r.Reader, r.SymOff(li))
-	if !osym.Dupok() {
-		return false
-	}
-	if osym.Name == "" {
-		return false // Unnamed aux symbol cannot be dup.
-	}
-	if osym.ABI == goobj2.SymABIstatic {
-		return false // Static symbol cannot be dup.
-	}
-	name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
-	ver := abiToVer(osym.ABI, r.version)
-	return l.symsByName[ver][name] != i
-}
-
 // Check that duplicate symbols have same contents.
-func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) {
-	li := int(i - l.startIndex(r))
+func (l *Loader) checkdup(name string, r *oReader, li int, dup Sym) {
 	p := r.Data(li)
-	if strings.HasPrefix(name, "go.info.") {
-		p, _ = patchDWARFName1(p, r)
-	}
 	rdup, ldup := l.toLocal(dup)
 	pdup := rdup.Data(ldup)
-	if strings.HasPrefix(name, "go.info.") {
-		pdup, _ = patchDWARFName1(pdup, rdup)
-	}
 	if bytes.Equal(p, pdup) {
 		return
 	}
@@ -419,7 +589,7 @@
 
 // Number of total symbols.
 func (l *Loader) NSym() int {
-	return int(l.max + 1)
+	return len(l.objSyms)
 }
 
 // Number of defined Go symbols.
@@ -430,10 +600,8 @@
 // Returns the raw (unpatched) name of the i-th symbol.
 func (l *Loader) RawSymName(i Sym) string {
 	if l.IsExternal(i) {
-		if s := l.Syms[i]; s != nil {
-			return s.Name
-		}
-		return ""
+		pp := l.getPayload(i)
+		return pp.name
 	}
 	r, li := l.toLocal(i)
 	osym := goobj2.Sym{}
@@ -444,10 +612,8 @@
 // Returns the (patched) name of the i-th symbol.
 func (l *Loader) SymName(i Sym) string {
 	if l.IsExternal(i) {
-		if s := l.Syms[i]; s != nil {
-			return s.Name // external name should already be patched?
-		}
-		return ""
+		pp := l.getPayload(i)
+		return pp.name
 	}
 	r, li := l.toLocal(i)
 	osym := goobj2.Sym{}
@@ -455,17 +621,30 @@
 	return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
 }
 
+// Returns the version of the i-th symbol.
+func (l *Loader) SymVersion(i Sym) int {
+	if l.IsExternal(i) {
+		pp := l.getPayload(i)
+		return pp.ver
+	}
+	r, li := l.toLocal(i)
+	osym := goobj2.Sym{}
+	osym.ReadWithoutName(r.Reader, r.SymOff(li))
+	return int(abiToVer(osym.ABI, r.version))
+}
+
 // Returns the type of the i-th symbol.
 func (l *Loader) SymType(i Sym) sym.SymKind {
 	if l.IsExternal(i) {
-		if s := l.Syms[i]; s != nil {
-			return s.Type
+		pp := l.getPayload(i)
+		if pp != nil {
+			return pp.kind
 		}
 		return 0
 	}
 	r, li := l.toLocal(i)
 	osym := goobj2.Sym{}
-	osym.Read(r.Reader, r.SymOff(li))
+	osym.ReadWithoutName(r.Reader, r.SymOff(li))
 	return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
 }
 
@@ -477,15 +656,305 @@
 	}
 	r, li := l.toLocal(i)
 	osym := goobj2.Sym{}
-	osym.Read(r.Reader, r.SymOff(li))
+	osym.ReadFlag(r.Reader, r.SymOff(li))
 	return osym.Flag
 }
 
+// AttrReachable returns true for symbols that are transitively
+// referenced from the entry points. Unreachable symbols are not
+// written to the output.
+func (l *Loader) AttrReachable(i Sym) bool {
+	return l.attrReachable.Has(i)
+}
+
+// SetAttrReachable sets the reachability property for a symbol (see
+// AttrReachable).
+func (l *Loader) SetAttrReachable(i Sym, v bool) {
+	if v {
+		l.attrReachable.Set(i)
+	} else {
+		l.attrReachable.Unset(i)
+	}
+}
+
+// AttrOnList returns true for symbols that are on some list (such as
+// the list of all text symbols, or one of the lists of data symbols)
+// and is consulted to avoid bugs where a symbol is put on a list
+// twice.
+func (l *Loader) AttrOnList(i Sym) bool {
+	return l.attrOnList.Has(i)
+}
+
+// SetAttrOnList sets the "on list" property for a symbol (see
+// AttrOnList).
+func (l *Loader) SetAttrOnList(i Sym, v bool) {
+	if v {
+		l.attrOnList.Set(i)
+	} else {
+		l.attrOnList.Unset(i)
+	}
+}
+
+// AttrLocal returns true for symbols that are only visible within the
+// module (executable or shared library) being linked. This attribute
+// is applied to thunks and certain other linker-generated symbols.
+func (l *Loader) AttrLocal(i Sym) bool {
+	return l.attrLocal.Has(i)
+}
+
+// SetAttrLocal the "local" property for a symbol (see AttrLocal above).
+func (l *Loader) SetAttrLocal(i Sym, v bool) {
+	if v {
+		l.attrLocal.Set(i)
+	} else {
+		l.attrLocal.Unset(i)
+	}
+}
+
+// AttrNotInSymbolTable returns true for symbols that should not be
+// added to the symbol table of the final generated load module.
+func (l *Loader) AttrNotInSymbolTable(i Sym) bool {
+	return l.attrNotInSymbolTable.Has(i)
+}
+
+// SetAttrNotInSymbolTable the "not in symtab" property for a symbol
+// (see AttrNotInSymbolTable above).
+func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) {
+	if v {
+		l.attrNotInSymbolTable.Set(i)
+	} else {
+		l.attrNotInSymbolTable.Unset(i)
+	}
+}
+
+// AttrVisibilityHidden symbols returns true for ELF symbols with
+// visibility set to STV_HIDDEN. They become local symbols in
+// the final executable. Only relevant when internally linking
+// on an ELF platform.
+func (l *Loader) AttrVisibilityHidden(i Sym) bool {
+	if !l.IsExternal(i) {
+		return false
+	}
+	return l.attrVisibilityHidden.Has(l.extIndex(i))
+}
+
+// SetAttrVisibilityHidden sets the "hidden visibility" property for a
+// symbol (see AttrVisibilityHidden).
+func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) {
+	if !l.IsExternal(i) {
+		panic("tried to set visibility attr on non-external symbol")
+	}
+	if v {
+		l.attrVisibilityHidden.Set(l.extIndex(i))
+	} else {
+		l.attrVisibilityHidden.Unset(l.extIndex(i))
+	}
+}
+
+// AttrDuplicateOK returns true for a symbol that can be present in
+// multiple object files.
+func (l *Loader) AttrDuplicateOK(i Sym) bool {
+	if !l.IsExternal(i) {
+		// TODO: if this path winds up being taken frequently, it
+		// might make more sense to copy the flag value out of the object
+		// into a larger bitmap during preload.
+		r, li := l.toLocal(i)
+		osym := goobj2.Sym{}
+		osym.ReadFlag(r.Reader, r.SymOff(li))
+		return osym.Dupok()
+	}
+	return l.attrDuplicateOK.Has(l.extIndex(i))
+}
+
+// SetAttrDuplicateOK sets the "duplicate OK" property for an external
+// symbol (see AttrDuplicateOK).
+func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) {
+	if !l.IsExternal(i) {
+		panic("tried to set dupok attr on non-external symbol")
+	}
+	if v {
+		l.attrDuplicateOK.Set(l.extIndex(i))
+	} else {
+		l.attrDuplicateOK.Unset(l.extIndex(i))
+	}
+}
+
+// AttrShared returns true for symbols compiled with the -shared option.
+func (l *Loader) AttrShared(i Sym) bool {
+	if !l.IsExternal(i) {
+		// TODO: if this path winds up being taken frequently, it
+		// might make more sense to copy the flag value out of the
+		// object into a larger bitmap during preload.
+		r, _ := l.toLocal(i)
+		return (r.Flags() & goobj2.ObjFlagShared) != 0
+	}
+	return l.attrShared.Has(l.extIndex(i))
+}
+
+// SetAttrShared sets the "shared" property for an external
+// symbol (see AttrShared).
+func (l *Loader) SetAttrShared(i Sym, v bool) {
+	if !l.IsExternal(i) {
+		panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i)))
+	}
+	if v {
+		l.attrShared.Set(l.extIndex(i))
+	} else {
+		l.attrShared.Unset(l.extIndex(i))
+	}
+}
+
+// AttrExternal returns true for function symbols loaded from host
+// object files.
+func (l *Loader) AttrExternal(i Sym) bool {
+	if !l.IsExternal(i) {
+		return false
+	}
+	return l.attrExternal.Has(l.extIndex(i))
+}
+
+// SetAttrExternal sets the "external" property for an host object
+// symbol (see AttrExternal).
+func (l *Loader) SetAttrExternal(i Sym, v bool) {
+	if !l.IsExternal(i) {
+		panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.RawSymName(i)))
+	}
+	if v {
+		l.attrExternal.Set(l.extIndex(i))
+	} else {
+		l.attrExternal.Unset(l.extIndex(i))
+	}
+}
+
+// AttrTopFrame returns true for a function symbol that is an entry
+// point, meaning that unwinders should stop when they hit this
+// function.
+func (l *Loader) AttrTopFrame(i Sym) bool {
+	_, ok := l.attrTopFrame[i]
+	return ok
+}
+
+// SetAttrTopFrame sets the "top frame" property for a symbol (see
+// AttrTopFrame).
+func (l *Loader) SetAttrTopFrame(i Sym, v bool) {
+	if v {
+		l.attrTopFrame[i] = struct{}{}
+	} else {
+		delete(l.attrTopFrame, i)
+	}
+}
+
+// AttrSpecial returns true for a symbols that do not have their
+// address (i.e. Value) computed by the usual mechanism of
+// data.go:dodata() & data.go:address().
+func (l *Loader) AttrSpecial(i Sym) bool {
+	_, ok := l.attrSpecial[i]
+	return ok
+}
+
+// SetAttrSpecial sets the "special" property for a symbol (see
+// AttrSpecial).
+func (l *Loader) SetAttrSpecial(i Sym, v bool) {
+	if v {
+		l.attrSpecial[i] = struct{}{}
+	} else {
+		delete(l.attrSpecial, i)
+	}
+}
+
+// AttrCgoExportDynamic returns true for a symbol that has been
+// specially marked via the "cgo_export_dynamic" compiler directive
+// written by cgo (in response to //export directives in the source).
+func (l *Loader) AttrCgoExportDynamic(i Sym) bool {
+	_, ok := l.attrCgoExportDynamic[i]
+	return ok
+}
+
+// SetAttrCgoExportDynamic sets the "cgo_export_dynamic" for a symbol
+// (see AttrCgoExportDynamic).
+func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) {
+	if v {
+		l.attrCgoExportDynamic[i] = struct{}{}
+	} else {
+		delete(l.attrCgoExportDynamic, i)
+	}
+}
+
+// AttrCgoExportStatic returns true for a symbol that has been
+// specially marked via the "cgo_export_static" directive
+// written by cgo.
+func (l *Loader) AttrCgoExportStatic(i Sym) bool {
+	_, ok := l.attrCgoExportStatic[i]
+	return ok
+}
+
+// SetAttrCgoExportStatic sets the "cgo_export_dynamic" for a symbol
+// (see AttrCgoExportStatic).
+func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
+	if v {
+		l.attrCgoExportStatic[i] = struct{}{}
+	} else {
+		delete(l.attrCgoExportStatic, i)
+	}
+}
+
+// AttrReadOnly returns true for a symbol whose underlying data
+// is stored via a read-only mmap.
+func (l *Loader) AttrReadOnly(i Sym) bool {
+	if v, ok := l.attrReadOnly[i]; ok {
+		return v
+	}
+	if l.IsExternal(i) {
+		return false
+	}
+	r, _ := l.toLocal(i)
+	return r.ReadOnly()
+}
+
+// SetAttrReadOnly sets the "cgo_export_dynamic" for a symbol
+// (see AttrReadOnly).
+func (l *Loader) SetAttrReadOnly(i Sym, v bool) {
+	l.attrReadOnly[i] = v
+}
+
+// AttrSubSymbol returns true for symbols that are listed as a
+// sub-symbol of some other outer symbol. The sub/outer mechanism is
+// used when loading host objects (sections from the host object
+// become regular linker symbols and symbols go on the Sub list of
+// their section) and for constructing the global offset table when
+// internally linking a dynamic executable.
+func (l *Loader) AttrSubSymbol(i Sym) bool {
+	// we don't explicitly store this attribute any more -- return
+	// a value based on the sub-symbol setting.
+	return l.OuterSym(i) != 0
+}
+
+// AttrContainer returns true for symbols that are listed as a
+// sub-symbol of some other outer symbol. The sub/outer mechanism is
+// used when loading host objects (sections from the host object
+// become regular linker symbols and symbols go on the Sub list of
+// their section) and for constructing the global offset table when
+// internally linking a dynamic executable.
+func (l *Loader) AttrContainer(i Sym) bool {
+	// we don't explicitly store this attribute any more -- return
+	// a value based on the sub-symbol setting.
+	return l.SubSym(i) != 0
+}
+
+// Note that we don't have SetAttrSubSymbol' or 'SetAttrContainer' methods
+// in the loader; clients should just use methods like PrependSub
+// to establish these relationships
+
 // Returns whether the i-th symbol has ReflectMethod attribute set.
 func (l *Loader) IsReflectMethod(i Sym) bool {
 	return l.SymAttr(i)&goobj2.SymFlagReflectMethod != 0
 }
 
+// Returns whether the i-th symbol is nosplit.
+func (l *Loader) IsNoSplit(i Sym) bool {
+	return l.SymAttr(i)&goobj2.SymFlagNoSplit != 0
+}
+
 // Returns whether this is a Go type symbol.
 func (l *Loader) IsGoType(i Sym) bool {
 	return l.SymAttr(i)&goobj2.SymFlagGoType != 0
@@ -499,11 +968,30 @@
 	return false
 }
 
+// growValues grows the slice used to store symbol values.
+func (l *Loader) growValues(reqLen int) {
+	curLen := len(l.values)
+	if reqLen > curLen {
+		l.values = append(l.values, make([]int64, reqLen+1-curLen)...)
+	}
+}
+
+// SymValue returns the value of the i-th symbol. i is global index.
+func (l *Loader) SymValue(i Sym) int64 {
+	return l.values[i]
+}
+
+// SetSymValue sets the value of the i-th symbol. i is global index.
+func (l *Loader) SetSymValue(i Sym, val int64) {
+	l.values[i] = val
+}
+
 // Returns the symbol content of the i-th symbol. i is global index.
 func (l *Loader) Data(i Sym) []byte {
 	if l.IsExternal(i) {
-		if s := l.Syms[i]; s != nil {
-			return s.P
+		pp := l.getPayload(i)
+		if pp != nil {
+			return pp.data
 		}
 		return nil
 	}
@@ -511,6 +999,243 @@
 	return r.Data(li)
 }
 
+// SymAlign returns the alignment for a symbol.
+func (l *Loader) SymAlign(i Sym) int32 {
+	// If an alignment has been recorded, return that.
+	if align, ok := l.align[i]; ok {
+		return align
+	}
+	// TODO: would it make sense to return an arch-specific
+	// alignment depending on section type? E.g. STEXT => 32,
+	// SDATA => 1, etc?
+	return 0
+}
+
+// SetSymAlign sets the alignment for a symbol.
+func (l *Loader) SetSymAlign(i Sym, align int32) {
+	// reject bad synbols
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol index in SetSymAlign")
+	}
+	// Reject nonsense alignments.
+	// TODO: do we need this?
+	if align < 0 {
+		panic("bad alignment value")
+	}
+	if align == 0 {
+		delete(l.align, i)
+	} else {
+		// Alignment should be a power of 2.
+		if bits.OnesCount32(uint32(align)) != 1 {
+			panic("bad alignment value")
+		}
+		l.align[i] = align
+	}
+}
+
+// SymDynImplib returns the "dynimplib" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimplib(i Sym) string {
+	return l.dynimplib[i]
+}
+
+// SetSymDynimplib sets the "dynimplib" attribute for a symbol.
+func (l *Loader) SetSymDynimplib(i Sym, value string) {
+	// reject bad symbols
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol index in SetDynimplib")
+	}
+	if value == "" {
+		delete(l.dynimplib, i)
+	} else {
+		l.dynimplib[i] = value
+	}
+}
+
+// SymDynimpvers returns the "dynimpvers" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimpvers(i Sym) string {
+	return l.dynimpvers[i]
+}
+
+// SetSymDynimpvers sets the "dynimpvers" attribute for a symbol.
+func (l *Loader) SetSymDynimpvers(i Sym, value string) {
+	// reject bad symbols
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol index in SetDynimpvers")
+	}
+	if value == "" {
+		delete(l.dynimpvers, i)
+	} else {
+		l.dynimpvers[i] = value
+	}
+}
+
+// SymExtname returns the "extname" value for the specified
+// symbol.
+func (l *Loader) SymExtname(i Sym) string {
+	if s, ok := l.extname[i]; ok {
+		return s
+	}
+	return l.SymName(i)
+}
+
+// SetSymExtname sets the  "extname" attribute for a symbol.
+func (l *Loader) SetSymExtname(i Sym, value string) {
+	// reject bad symbols
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol index in SetExtname")
+	}
+	if value == "" {
+		delete(l.extname, i)
+	} else {
+		l.extname[i] = value
+	}
+}
+
+// SymElfType returns the previously recorded ELF type for a symbol
+// (used only for symbols read from shared libraries by ldshlibsyms).
+// It is not set for symbols defined by the packages being linked or
+// by symbols read by ldelf (and so is left as elf.STT_NOTYPE).
+func (l *Loader) SymElfType(i Sym) elf.SymType {
+	if et, ok := l.elfType[i]; ok {
+		return et
+	}
+	return elf.STT_NOTYPE
+}
+
+// SetSymElfType sets the elf type attribute for a symbol.
+func (l *Loader) SetSymElfType(i Sym, et elf.SymType) {
+	// reject bad symbols
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol index in SetSymElfType")
+	}
+	if et == elf.STT_NOTYPE {
+		delete(l.elfType, i)
+	} else {
+		l.elfType[i] = et
+	}
+}
+
+// SetPlt sets the plt value for pe symbols.
+func (l *Loader) SetPlt(i Sym, v int32) {
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol for SetPlt")
+	}
+	if v == 0 {
+		delete(l.plt, i)
+	} else {
+		l.plt[i] = v
+	}
+}
+
+// SetGot sets the got value for pe symbols.
+func (l *Loader) SetGot(i Sym, v int32) {
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol for SetPlt")
+	}
+	if v == 0 {
+		delete(l.got, i)
+	} else {
+		l.got[i] = v
+	}
+}
+
+// SymGoType returns the 'Gotype' property for a given symbol (set by
+// the Go compiler for variable symbols). This version relies on
+// reading aux symbols for the target sym -- it could be that a faster
+// approach would be to check for gotype during preload and copy the
+// results in to a map (might want to try this at some point and see
+// if it helps speed things up).
+func (l *Loader) SymGoType(i Sym) Sym {
+	if l.IsExternal(i) {
+		pp := l.getPayload(i)
+		return pp.gotype
+	}
+	r, li := l.toLocal(i)
+	naux := r.NAux(li)
+	for j := 0; j < naux; j++ {
+		a := goobj2.Aux{}
+		a.Read(r.Reader, r.AuxOff(li, j))
+		switch a.Type {
+		case goobj2.AuxGotype:
+			return l.resolve(r, a.Sym)
+		}
+	}
+	return 0
+}
+
+// SymUnit returns the compilation unit for a given symbol (which will
+// typically be nil for external or linker-manufactured symbols).
+func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit {
+	if l.IsExternal(i) {
+		pp := l.getPayload(i)
+		if pp.objidx != 0 {
+			r := l.objs[pp.objidx].r
+			return r.unit
+		}
+		return nil
+	}
+	r, _ := l.toLocal(i)
+	return r.unit
+}
+
+// SymFile returns the file for a symbol, which is normally the
+// package the symbol came from (for regular compiler-generated Go
+// symbols), but in the case of building with "-linkshared" (when a
+// symbol is read from a a shared library), will hold the library
+// name.
+func (l *Loader) SymFile(i Sym) string {
+	if l.IsExternal(i) {
+		if f, ok := l.symFile[i]; ok {
+			return f
+		}
+		pp := l.getPayload(i)
+		if pp.objidx != 0 {
+			r := l.objs[pp.objidx].r
+			return r.unit.Lib.File
+		}
+		return ""
+	}
+	r, _ := l.toLocal(i)
+	return r.unit.Lib.File
+}
+
+// SetSymFile sets the file attribute for a symbol. This is
+// needed mainly for external symbols, specifically those imported
+// from shared libraries.
+func (l *Loader) SetSymFile(i Sym, file string) {
+	// reject bad symbols
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol index in SetSymFile")
+	}
+	if !l.IsExternal(i) {
+		panic("can't set file for non-external sym")
+	}
+	l.symFile[i] = file
+}
+
+// SymLocalentry returns the "local entry" value for the specified
+// symbol.
+func (l *Loader) SymLocalentry(i Sym) uint8 {
+	return l.localentry[i]
+}
+
+// SetSymExtname sets the "extname" attribute for a symbol.
+func (l *Loader) SetSymLocalentry(i Sym, value uint8) {
+	// reject bad symbols
+	if i >= Sym(len(l.objSyms)) || i == 0 {
+		panic("bad symbol index in SetExtname")
+	}
+	if value == 0 {
+		delete(l.localentry, i)
+	} else {
+		l.localentry[i] = value
+	}
+}
+
 // Returns the number of aux symbols given a global index.
 func (l *Loader) NAux(i Sym) int {
 	if l.IsExternal(i) {
@@ -532,6 +1257,56 @@
 	return l.resolve(r, a.Sym)
 }
 
+// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF
+// symbols associated with a given function symbol.  Prior to the
+// introduction of the loader, this was done purely using name
+// lookups, e.f. for function with name XYZ we would then look up
+// go.info.XYZ, etc.
+// FIXME: once all of dwarfgen is converted over to the loader,
+// it would save some space to make these aux symbols nameless.
+func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) {
+	if l.SymType(fnSymIdx) != sym.STEXT {
+		log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
+	}
+	if l.IsExternal(fnSymIdx) {
+		// Current expectation is that any external function will
+		// not have auxsyms.
+		return
+	}
+	naux := l.NAux(fnSymIdx)
+	if naux == 0 {
+		return
+	}
+	r, li := l.toLocal(fnSymIdx)
+	for i := 0; i < naux; i++ {
+		a := goobj2.Aux{}
+		a.Read(r.Reader, r.AuxOff(li, i))
+		switch a.Type {
+		case goobj2.AuxDwarfInfo:
+			auxDwarfInfo = l.resolve(r, a.Sym)
+			if l.SymType(auxDwarfInfo) != sym.SDWARFINFO {
+				panic("aux dwarf info sym with wrong type")
+			}
+		case goobj2.AuxDwarfLoc:
+			auxDwarfLoc = l.resolve(r, a.Sym)
+			if l.SymType(auxDwarfLoc) != sym.SDWARFLOC {
+				panic("aux dwarf loc sym with wrong type")
+			}
+		case goobj2.AuxDwarfRanges:
+			auxDwarfRanges = l.resolve(r, a.Sym)
+			if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE {
+				panic("aux dwarf ranges sym with wrong type")
+			}
+		case goobj2.AuxDwarfLines:
+			auxDwarfLines = l.resolve(r, a.Sym)
+			if l.SymType(auxDwarfLines) != sym.SDWARFLINES {
+				panic("aux dwarf lines sym with wrong type")
+			}
+		}
+	}
+	return
+}
+
 // ReadAuxSyms reads the aux symbol ids for the specified symbol into the
 // slice passed as a parameter. If the slice capacity is not large enough, a new
 // larger slice will be allocated. Final slice is returned.
@@ -550,51 +1325,123 @@
 	dst = dst[:0]
 
 	r, li := l.toLocal(symIdx)
+	a := goobj2.Aux{}
 	for i := 0; i < naux; i++ {
-		a := goobj2.Aux{}
-		a.Read(r.Reader, r.AuxOff(li, i))
+		a.ReadSym(r.Reader, r.AuxOff(li, i))
 		dst = append(dst, l.resolve(r, a.Sym))
 	}
 
 	return dst
 }
 
+// PrependSub prepends 'sub' onto the sub list for outer symbol 'outer'.
+// Will panic if 'sub' already has an outer sym or sub sym.
+// FIXME: should this be instead a method on SymbolBuilder?
+func (l *Loader) PrependSub(outer Sym, sub Sym) {
+	// NB: this presupposes that an outer sym can't be a sub symbol of
+	// some other outer-outer sym (I'm assuming this is true, but I
+	// haven't tested exhaustively).
+	if l.OuterSym(outer) != 0 {
+		panic("outer has outer itself")
+	}
+	if l.SubSym(sub) != 0 {
+		panic("sub set for subsym")
+	}
+	if l.OuterSym(sub) != 0 {
+		panic("outer already set for subsym")
+	}
+	l.sub[sub] = l.sub[outer]
+	l.sub[outer] = sub
+	l.outer[sub] = outer
+}
+
 // OuterSym gets the outer symbol for host object loaded symbols.
 func (l *Loader) OuterSym(i Sym) Sym {
-	sym := l.Syms[i]
-	if sym != nil && sym.Outer != nil {
-		outer := sym.Outer
-		return l.Lookup(outer.Name, int(outer.Version))
-	}
-	return 0
+	// FIXME: add check for isExternal?
+	return l.outer[i]
 }
 
 // SubSym gets the subsymbol for host object loaded symbols.
 func (l *Loader) SubSym(i Sym) Sym {
-	sym := l.Syms[i]
-	if sym != nil && sym.Sub != nil {
-		sub := sym.Sub
-		return l.Lookup(sub.Name, int(sub.Version))
-	}
-	return 0
+	// NB: note -- no check for l.isExternal(), since I am pretty sure
+	// that later phases in the linker set subsym for "type." syms
+	return l.sub[i]
 }
 
-// Initialize Reachable bitmap for running deadcode pass.
+// Initialize Reachable bitmap and its siblings for running deadcode pass.
 func (l *Loader) InitReachable() {
-	l.Reachable = makeBitmap(l.NSym())
+	l.growAttrBitmaps(l.NSym() + 1)
+}
+
+type symWithVal struct {
+	s Sym
+	v int64
+}
+type bySymValue []symWithVal
+
+func (s bySymValue) Len() int           { return len(s) }
+func (s bySymValue) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v }
+
+// SortSub walks through the sub-symbols for 's' and sorts them
+// in place by increasing value. Return value is the new
+// sub symbol for the specified outer symbol.
+func (l *Loader) SortSub(s Sym) Sym {
+
+	if s == 0 || l.sub[s] == 0 {
+		return s
+	}
+
+	// Sort symbols using a slice first. Use a stable sort on the off
+	// chance that there's more than once symbol with the same value,
+	// so as to preserve reproducible builds.
+	sl := []symWithVal{}
+	for ss := l.sub[s]; ss != 0; ss = l.sub[ss] {
+		sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)})
+	}
+	sort.Stable(bySymValue(sl))
+
+	// Then apply any changes needed to the sub map.
+	ns := Sym(0)
+	for i := len(sl) - 1; i >= 0; i-- {
+		s := sl[i].s
+		l.sub[s] = ns
+		ns = s
+	}
+
+	// Update sub for outer symbol, then return
+	l.sub[s] = sl[0].s
+	return sl[0].s
+}
+
+// Insure that reachable bitmap and its siblings have enough size.
+func (l *Loader) growAttrBitmaps(reqLen int) {
+	if reqLen > l.attrReachable.Len() {
+		// These are indexed by global symbol
+		l.attrReachable = growBitmap(reqLen, l.attrReachable)
+		l.attrOnList = growBitmap(reqLen, l.attrOnList)
+		l.attrLocal = growBitmap(reqLen, l.attrLocal)
+		l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable)
+	}
+	l.growExtAttrBitmaps()
+}
+
+func (l *Loader) growExtAttrBitmaps() {
+	// These are indexed by external symbol index (e.g. l.extIndex(i))
+	extReqLen := len(l.payloads)
+	if extReqLen > l.attrVisibilityHidden.Len() {
+		l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden)
+		l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK)
+		l.attrShared = growBitmap(extReqLen, l.attrShared)
+		l.attrExternal = growBitmap(extReqLen, l.attrExternal)
+	}
 }
 
 // At method returns the j-th reloc for a global symbol.
 func (relocs *Relocs) At(j int) Reloc {
-	if relocs.ext != nil {
-		rel := &relocs.ext.R[j]
-		return Reloc{
-			Off:  rel.Off,
-			Size: rel.Siz,
-			Type: rel.Type,
-			Add:  rel.Add,
-			Sym:  relocs.l.Lookup(rel.Sym.Name, int(rel.Sym.Version)),
-		}
+	if relocs.l.isExtReader(relocs.r) {
+		pp := relocs.l.payloads[relocs.li]
+		return pp.relocs[j]
 	}
 	rel := goobj2.Reloc{}
 	rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
@@ -608,10 +1455,35 @@
 	}
 }
 
+func (relocs *Relocs) At2(j int) Reloc2 {
+	if relocs.l.isExtReader(relocs.r) {
+		pp := relocs.l.payloads[relocs.li]
+		r := pp.relocs[j]
+		// XXX populate a goobj2.Reloc from external reloc record.
+		// Ugly. Maybe we just want to use this format to store the
+		// reloc record in the first place?
+		var b goobj2.Reloc2
+		b.Set(r.Off, r.Size, uint8(r.Type), r.Add, goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(r.Sym)})
+		return Reloc2{&b, relocs.r, relocs.l}
+	}
+	return Reloc2{relocs.r.Reloc2(relocs.li, j), relocs.r, relocs.l}
+}
+
 // ReadAll method reads all relocations for a symbol into the
 // specified slice. If the slice capacity is not large enough, a new
 // larger slice will be allocated. Final slice is returned.
 func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
+	return relocs.readAll(dst, false)
+}
+
+// ReadSyms method reads all relocation target symbols and reloc types
+// for a symbol into the specified slice. It is like ReadAll but only
+// fill in the Sym and Type fields.
+func (relocs *Relocs) ReadSyms(dst []Reloc) []Reloc {
+	return relocs.readAll(dst, true)
+}
+
+func (relocs *Relocs) readAll(dst []Reloc, onlySymType bool) []Reloc {
 	if relocs.Count == 0 {
 		return dst[:0]
 	}
@@ -621,25 +1493,20 @@
 	}
 	dst = dst[:0]
 
-	if relocs.ext != nil {
-		for i := 0; i < relocs.Count; i++ {
-			erel := &relocs.ext.R[i]
-			rel := Reloc{
-				Off:  erel.Off,
-				Size: erel.Siz,
-				Type: erel.Type,
-				Add:  erel.Add,
-				Sym:  relocs.l.Lookup(erel.Sym.Name, int(erel.Sym.Version)),
-			}
-			dst = append(dst, rel)
-		}
+	if relocs.l.isExtReader(relocs.r) {
+		pp := relocs.l.payloads[relocs.li]
+		dst = append(dst, pp.relocs...)
 		return dst
 	}
 
 	off := relocs.r.RelocOff(relocs.li, 0)
+	rel := goobj2.Reloc{}
 	for i := 0; i < relocs.Count; i++ {
-		rel := goobj2.Reloc{}
-		rel.Read(relocs.r.Reader, off)
+		if onlySymType {
+			rel.ReadSymType(relocs.r.Reader, off)
+		} else {
+			rel.Read(relocs.r.Reader, off)
+		}
 		off += uint32(rel.Size())
 		target := relocs.l.resolve(relocs.r, rel.Sym)
 		dst = append(dst, Reloc{
@@ -655,29 +1522,79 @@
 
 // Relocs returns a Relocs object for the given global sym.
 func (l *Loader) Relocs(i Sym) Relocs {
-	if l.IsExternal(i) {
-		if s := l.Syms[i]; s != nil {
-			return Relocs{Count: len(s.R), l: l, ext: s}
-		}
-		return Relocs{}
-	}
 	r, li := l.toLocal(i)
+	if r == nil {
+		panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i))
+	}
 	return l.relocs(r, li)
 }
 
 // Relocs returns a Relocs object given a local sym index and reader.
 func (l *Loader) relocs(r *oReader, li int) Relocs {
+	var n int
+	if l.isExtReader(r) {
+		pp := l.payloads[li]
+		n = len(pp.relocs)
+	} else {
+		n = r.NReloc(li)
+	}
 	return Relocs{
-		Count: r.NReloc(li),
+		Count: n,
 		li:    li,
 		r:     r,
 		l:     l,
 	}
 }
 
-// Preload a package: add autolibs, add symbols to the symbol table.
-// Does not read symbol data yet.
-func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
+// RelocByOff implements sort.Interface for sorting relocations by offset.
+
+type RelocByOff []Reloc
+
+func (x RelocByOff) Len() int           { return len(x) }
+func (x RelocByOff) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
+func (x RelocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
+
+// FuncInfo provides hooks to access goobj2.FuncInfo in the objects.
+type FuncInfo struct {
+	l    *Loader
+	r    *oReader
+	data []byte
+}
+
+func (fi *FuncInfo) Valid() bool { return fi.r != nil }
+
+func (fi *FuncInfo) Locals() int {
+	return int((*goobj2.FuncInfo)(nil).ReadLocals(fi.data))
+}
+
+func (fi *FuncInfo) Pcsp() []byte {
+	pcsp, end := (*goobj2.FuncInfo)(nil).ReadPcsp(fi.data)
+	return fi.r.BytesAt(fi.r.PcdataBase()+pcsp, int(end-pcsp))
+}
+
+// TODO: more accessors.
+
+func (l *Loader) FuncInfo(i Sym) FuncInfo {
+	if l.IsExternal(i) {
+		return FuncInfo{}
+	}
+	r, li := l.toLocal(i)
+	n := r.NAux(li)
+	for j := 0; j < n; j++ {
+		a := goobj2.Aux{}
+		a.Read(r.Reader, r.AuxOff(li, j))
+		if a.Type == goobj2.AuxFuncInfo {
+			b := r.Data(int(a.Sym.SymIdx))
+			return FuncInfo{l, r, b}
+		}
+	}
+	return FuncInfo{}
+}
+
+// Preload a package: add autolibs, add defined package symbols to the symbol table.
+// Does not add non-package symbols yet, which will be done in LoadNonpkgSyms.
+// Does not read symbol data.
+func (l *Loader) Preload(syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, flags int) {
 	roObject, readonly, err := f.Slice(uint64(length))
 	if err != nil {
 		log.Fatal("cannot read object file:", err)
@@ -688,7 +1605,9 @@
 	}
 	localSymVersion := syms.IncVersion()
 	pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
-	or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, nil}
+	ndef := r.NSym()
+	nnonpkgdef := r.NNonpkgdef()
+	or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, make([]Sym, ndef+nnonpkgdef+r.NNonpkgref()), ndef, uint32(len(l.objs))}
 
 	// Autolib
 	lib.ImportStrings = append(lib.ImportStrings, r.Autolib()...)
@@ -700,51 +1619,80 @@
 		unit.DWARFFileTable[i] = r.DwarfFile(i)
 	}
 
-	istart := l.addObj(lib.Pkg, or)
-
-	ndef := r.NSym()
-	nnonpkgdef := r.NNonpkgdef()
-	for i, n := 0, ndef+nnonpkgdef; i < n; i++ {
-		osym := goobj2.Sym{}
-		osym.Read(r, r.SymOff(i))
-		name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1)
-		if name == "" {
-			continue // don't add unnamed aux symbol
-		}
-		v := abiToVer(osym.ABI, localSymVersion)
-		dupok := osym.Dupok()
-		added := l.AddSym(name, v, istart+Sym(i), or, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
-		if added && strings.HasPrefix(name, "go.itablink.") {
-			l.itablink[istart+Sym(i)] = struct{}{}
-		}
-		if added && strings.HasPrefix(name, "runtime.") {
-			if bi := goobj2.BuiltinIdx(name, v); bi != -1 {
-				// This is a definition of a builtin symbol. Record where it is.
-				l.builtinSyms[bi] = istart + Sym(i)
-			}
-		}
-	}
+	l.addObj(lib.Pkg, or)
+	l.preloadSyms(or, pkgDef)
 
 	// The caller expects us consuming all the data
 	f.MustSeek(length, os.SEEK_CUR)
 }
 
-// Make sure referenced symbols are added. Most of them should already be added.
-// This should only be needed for referenced external symbols.
-func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) {
-	for _, o := range l.objs[1:] {
-		loadObjRefs(l, o.r, arch, syms)
+// Preload symbols of given kind from an object.
+func (l *Loader) preloadSyms(r *oReader, kind int) {
+	ndef := r.NSym()
+	nnonpkgdef := r.NNonpkgdef()
+	var start, end int
+	switch kind {
+	case pkgDef:
+		start = 0
+		end = ndef
+	case nonPkgDef:
+		start = ndef
+		end = ndef + nnonpkgdef
+	default:
+		panic("preloadSyms: bad kind")
+	}
+	l.growSyms(len(l.objSyms) + end - start)
+	l.growAttrBitmaps(len(l.objSyms) + end - start)
+	for i := start; i < end; i++ {
+		osym := goobj2.Sym{}
+		osym.Read(r.Reader, r.SymOff(i))
+		name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+		v := abiToVer(osym.ABI, r.version)
+		dupok := osym.Dupok()
+		gi, added := l.AddSym(name, v, r, i, kind, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
+		r.syms[i] = gi
+		if !added {
+			continue
+		}
+		if osym.TopFrame() {
+			l.SetAttrTopFrame(gi, true)
+		}
+		if strings.HasPrefix(name, "go.itablink.") {
+			l.itablink[gi] = struct{}{}
+		}
+		if strings.HasPrefix(name, "runtime.") {
+			if bi := goobj2.BuiltinIdx(name, v); bi != -1 {
+				// This is a definition of a builtin symbol. Record where it is.
+				l.builtinSyms[bi] = gi
+			}
+		}
+		if strings.HasPrefix(name, "go.string.") ||
+			strings.HasPrefix(name, "gclocals·") ||
+			strings.HasPrefix(name, "runtime.gcbits.") {
+			l.SetAttrNotInSymbolTable(gi, true)
+		}
 	}
 }
 
-func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) {
+// Add non-package symbols and references to external symbols (which are always
+// named).
+func (l *Loader) LoadNonpkgSyms(syms *sym.Symbols) {
+	for _, o := range l.objs[1:] {
+		l.preloadSyms(o.r, nonPkgDef)
+	}
+	for _, o := range l.objs[1:] {
+		loadObjRefs(l, o.r, syms)
+	}
+}
+
+func loadObjRefs(l *Loader, r *oReader, syms *sym.Symbols) {
 	ndef := r.NSym() + r.NNonpkgdef()
 	for i, n := 0, r.NNonpkgref(); i < n; i++ {
 		osym := goobj2.Sym{}
 		osym.Read(r.Reader, r.SymOff(ndef+i))
 		name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
 		v := abiToVer(osym.ABI, r.version)
-		l.AddExtSym(name, v)
+		r.syms[ndef+i] = l.AddExtSym(name, v)
 	}
 }
 
@@ -794,22 +1742,65 @@
 		nr += loadObjSyms(l, syms, o.r)
 	}
 
+	// Make a first pass through the external symbols, making
+	// sure that each external symbol has a non-nil entry in
+	// l.Syms (note that relocations and symbol content will
+	// be copied in a later loop).
+	toConvert := make([]Sym, 0, len(l.payloads))
+	for _, i := range l.extReader.syms {
+		sname := l.RawSymName(i)
+		if !l.attrReachable.Has(i) && !strings.HasPrefix(sname, "gofile..") { // XXX file symbols are used but not marked
+			continue
+		}
+		pp := l.getPayload(i)
+		nr += len(pp.relocs)
+		// create and install the sym.Symbol here so that l.Syms will
+		// be fully populated when we do relocation processing and
+		// outer/sub processing below. Note that once we do this,
+		// we'll need to get at the payload for a symbol with direct
+		// reference to l.payloads[] as opposed to calling l.getPayload().
+		s := l.allocSym(sname, 0)
+		l.installSym(i, s)
+		toConvert = append(toConvert, i)
+	}
+
 	// allocate a single large slab of relocations for all live symbols
 	l.relocBatch = make([]sym.Reloc, nr)
 
-	// external symbols
-	for i := l.extStart; i <= l.max; i++ {
-		if s := l.Syms[i]; s != nil {
-			s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
-			continue // already loaded from external object
+	// convert payload-based external symbols into sym.Symbol-based
+	for _, i := range toConvert {
+
+		// Copy kind/size/value etc.
+		pp := l.payloads[l.extIndex(i)]
+		s := l.Syms[i]
+		s.Version = int16(pp.ver)
+		s.Type = pp.kind
+		s.Size = pp.size
+		s.Value = l.SymValue(i)
+		if pp.gotype != 0 {
+			s.Gotype = l.Syms[pp.gotype]
 		}
-		nv := l.extSyms[i-l.extStart]
-		if l.Reachable.Has(i) || strings.HasPrefix(nv.name, "gofile..") { // XXX file symbols are used but not marked
-			s := syms.Newsym(nv.name, nv.v)
-			preprocess(arch, s)
-			s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
-			l.Syms[i] = s
+		s.Value = l.values[i]
+		if f, ok := l.symFile[i]; ok {
+			s.File = f
+		} else if pp.objidx != 0 {
+			s.File = l.objs[pp.objidx].r.unit.Lib.File
 		}
+
+		// Copy relocations
+		batch := l.relocBatch
+		s.R = batch[:len(pp.relocs):len(pp.relocs)]
+		l.relocBatch = batch[len(pp.relocs):]
+		l.convertRelocations(pp.relocs, s, false)
+
+		// Copy data
+		s.P = pp.data
+
+		// Transfer over attributes.
+		l.migrateAttributes(i, s)
+
+		// Preprocess symbol. May set 'AttrLocal'.
+		preprocess(arch, s)
 	}
 
 	// load contents of defined symbols
@@ -817,11 +1808,15 @@
 		loadObjFull(l, o.r)
 	}
 
+	// Note: resolution of ABI aliases is now also handled in
+	// loader.convertRelocations, so once the host object loaders move
+	// completely to loader.Sym, we can remove the code below.
+
 	// Resolve ABI aliases for external symbols. This is only
 	// needed for internal cgo linking.
 	// (The old code does this in deadcode, but deadcode2 doesn't
 	// do this.)
-	for i := l.extStart; i <= l.max; i++ {
+	for _, i := range l.extReader.syms {
 		if s := l.Syms[i]; s != nil && s.Attr.Reachable() {
 			for ri := range s.R {
 				r := &s.R[ri]
@@ -833,40 +1828,283 @@
 	}
 }
 
-// ExtractSymbols grabs the symbols out of the loader for work that hasn't been
-// ported to the new symbol type.
-func (l *Loader) ExtractSymbols(syms *sym.Symbols) {
-	// Nil out overwritten symbols.
-	// Overwritten Go symbols aren't a problem (as they're lazy loaded), but
-	// symbols loaded from host object loaders are fully loaded, and we might
-	// have multiple symbols with the same name. This loop nils them out.
-	for oldI := range l.overwrite {
-		l.Syms[oldI] = nil
+// PropagateSymbolChangesBackToLoader is a temporary shim function
+// that copies over a given sym.Symbol into the equivalent representation
+// in the loader world. The intent is to enable converting a given
+// linker phase/pass from dealing with sym.Symbol's to a modernized
+// pass that works with loader.Sym, in cases where the "loader.Sym
+// wavefront" has not yet reached the pass in question. For such work
+// the recipe is to first call PropagateSymbolChangesBackToLoader(),
+// then exexute the pass working with the loader, then call
+// PropagateLoaderChangesToSymbols to copy the changes made by the
+// pass back to the sym.Symbol world.
+func (l *Loader) PropagateSymbolChangesBackToLoader() {
+
+	// For the moment we only copy symbol values, and we don't touch
+	// any new sym.Symbols created since loadlibfull() was run. This
+	// seems to be what's needed for DWARF gen.
+	for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
+		s := l.Syms[i]
+		if s != nil {
+			if s.Value != l.SymValue(i) {
+				l.SetSymValue(i, s.Value)
+			}
+		}
+	}
+}
+
+// PropagateLoaderChangesToSymbols is a temporary shim function that
+// takes a list of loader.Sym symbols and works to copy their contents
+// and attributes over to a corresponding sym.Symbol. See the
+// PropagateSymbolChangesBackToLoader header comment for more info.
+//
+// WARNING: this function is brittle and depends heavily on loader
+// implementation. A key problem with doing this is that as things
+// stand at the moment, some sym.Symbol contents/attributes are
+// populated only when converting from loader.Sym to sym.Symbol
+// in loadlibfull, meaning if we may wipe out some information
+// when copying back.
+
+func (l *Loader) PropagateLoaderChangesToSymbols(toconvert []Sym, syms *sym.Symbols) []*sym.Symbol {
+
+	result := []*sym.Symbol{}
+	relocfixup := []Sym{}
+
+	// Note: this loop needs to allow for the possibility that we may
+	// see "new" symbols on the 'toconvert' list that come from object
+	// files (for example, DWARF location lists), as opposed to just
+	// newly manufactured symbols (ex: DWARF section symbols such as
+	// ".debug_info").  This means that we have to be careful not to
+	// stomp on sym.Symbol attributes/content that was set up in
+	// in loadlibfull().
+
+	// Also note that in order for the relocation fixup to work, we
+	// have to do this in two passes -- one pass to create the symbols,
+	// and then a second fix up the relocations once all necessary
+	// sym.Symbols are created.
+
+	// First pass, symbol creation and symbol data fixup.
+	anonVerReplacement := syms.IncVersion()
+	rslice := []Reloc{}
+	for _, cand := range toconvert {
+
+		sn := l.SymName(cand)
+		sv := l.SymVersion(cand)
+		st := l.SymType(cand)
+		if sv < 0 {
+			sv = anonVerReplacement
+		}
+
+		s := l.Syms[cand]
+
+		isnew := false
+		if sn == "" {
+			// Don't install anonymous symbols in the lookup tab.
+			if s == nil {
+				s = l.allocSym(sn, sv)
+				l.installSym(cand, s)
+			}
+			isnew = true
+		} else {
+			if s != nil {
+				// Already have a symbol for this -- it must be
+				// something that was previously processed by
+				// loadObjFull. Note that the symbol in question may
+				// or may not be in the name lookup map.
+			} else {
+				isnew = true
+				s = syms.Lookup(sn, sv)
+			}
+		}
+		result = append(result, s)
+
+		// Always copy these from new to old.
+		s.Value = l.SymValue(cand)
+		s.Type = st
+
+		// If the data for a symbol has increased in size, make sure
+		// we bring the new content across.
+		relfix := isnew
+		if isnew || len(l.Data(cand)) > len(s.P) {
+			s.P = l.Data(cand)
+			s.Size = int64(len(s.P))
+			relfix = true
+		}
+
+		// For 'new' symbols, copy other content (such as Gotype,
+		// sym file, relocations, etc).
+		if isnew {
+			if gt := l.SymGoType(cand); gt != 0 {
+				s.Gotype = l.Syms[gt]
+			}
+			if f, ok := l.symFile[cand]; ok {
+				s.File = f
+			} else {
+				r, _ := l.toLocal(cand)
+				if r != nil && r != l.extReader {
+					s.File = l.SymFile(cand)
+				}
+			}
+		}
+
+		// If this symbol has any DWARF file relocations, we need to
+		// make sure that the relocations are copied back over, since
+		// DWARF-gen alters the offset values for these relocs. Also:
+		// if this is an info symbol and it refers to a previously
+		// unseen range/loc symbol, we'll need to fix up relocations
+		// for it as well.
+		relocs := l.Relocs(cand)
+		rslice = relocs.ReadSyms(rslice)
+		for ri := range rslice {
+			if rslice[ri].Type == objabi.R_DWARFFILEREF {
+				relfix = true
+				break
+			}
+			if st != sym.SDWARFINFO {
+				continue
+			}
+			rst := l.SymType(rslice[ri].Sym)
+			if rst == sym.SDWARFRANGE || rst == sym.SDWARFLOC {
+				relfix = true
+				break
+			}
+		}
+
+		if relfix {
+			relocfixup = append(relocfixup, cand)
+		}
+
+		// If new symbol, call a helper to migrate attributes.
+		// Otherwise touch only not-in-symbol-table, since there are
+		// some attrs that are only set up at the point where we
+		// convert loader.Sym to sym.Symbol.
+		if isnew {
+			l.migrateAttributes(cand, s)
+		} else {
+			if l.AttrNotInSymbolTable(cand) {
+				s.Attr.Set(sym.AttrNotInSymbolTable, true)
+			}
+		}
 	}
 
-	// Add symbols to the ctxt.Syms lookup table. This explicitly
-	// skips things created via loader.Create (marked with versions
-	// less than zero), since if we tried to add these we'd wind up
-	// with collisions. Along the way, update the version from the
-	// negative anon version to something larger than sym.SymVerStatic
-	// (needed so that sym.symbol.IsFileLocal() works properly).
+	// Second pass to fix up relocations.
+	for _, cand := range relocfixup {
+		s := l.Syms[cand]
+		relocs := l.Relocs(cand)
+		rslice = relocs.ReadAll(rslice)
+		s.R = make([]sym.Reloc, len(rslice))
+		l.convertRelocations(rslice, s, true)
+	}
+
+	return result
+}
+
+// ExtractSymbols grabs the symbols out of the loader for work that hasn't been
+// ported to the new symbol type.
+func (l *Loader) ExtractSymbols(syms *sym.Symbols, rp map[*sym.Symbol]*sym.Symbol) {
+	// Add symbols to the ctxt.Syms lookup table. This explicitly skips things
+	// created via loader.Create (marked with versions less than zero), since
+	// if we tried to add these we'd wind up with collisions. We do, however,
+	// add these symbols to the list of global symbols so that other future
+	// steps (like pclntab generation) can find these symbols if neceassary.
+	// Along the way, update the version from the negative anon version to
+	// something larger than sym.SymVerStatic (needed so that
+	// sym.symbol.IsFileLocal() works properly).
 	anonVerReplacement := syms.IncVersion()
 	for _, s := range l.Syms {
 		if s == nil {
 			continue
 		}
-		if s.Name != "" && s.Version >= 0 {
-			syms.Add(s)
-		}
+		syms.Allsym = append(syms.Allsym, s) // XXX still add to Allsym for now, as there are code looping through Allsym
 		if s.Version < 0 {
 			s.Version = int16(anonVerReplacement)
 		}
 	}
+
+	for i, s := range l.Reachparent {
+		if i == 0 {
+			continue
+		}
+		rp[l.Syms[i]] = l.Syms[s]
+	}
+
+	// Provide lookup functions for sym.Symbols.
+	syms.Lookup = func(name string, ver int) *sym.Symbol {
+		i := l.LookupOrCreateSym(name, ver)
+		if s := l.Syms[i]; s != nil {
+			return s
+		}
+		s := l.allocSym(name, ver)
+		l.installSym(i, s)
+		syms.Allsym = append(syms.Allsym, s) // XXX see above
+		return s
+	}
+	syms.ROLookup = func(name string, ver int) *sym.Symbol {
+		i := l.Lookup(name, ver)
+		return l.Syms[i]
+	}
+	syms.Rename = func(old, new string, ver int) {
+		// annoying... maybe there is a better way to do this
+		if ver >= 2 {
+			panic("cannot rename static symbol")
+		}
+		i := l.Lookup(old, ver)
+		s := l.Syms[i]
+		s.Name = new
+		if s.Extname() == old {
+			s.SetExtname(new)
+		}
+		delete(l.symsByName[ver], old)
+
+		// This mirrors the old code. But I'm not sure if the logic of
+		// handling dup in the old code actually works, or necessary.
+		dupi := l.symsByName[ver][new]
+		dup := l.Syms[dupi]
+		if dup == nil {
+			l.symsByName[ver][new] = i
+		} else {
+			if s.Type == 0 {
+				dup.Attr |= s.Attr
+				*s = *dup
+			} else if dup.Type == 0 {
+				s.Attr |= dup.Attr
+				*dup = *s
+				l.symsByName[ver][new] = i
+			}
+		}
+	}
+}
+
+// allocSym allocates a new symbol backing.
+func (l *Loader) allocSym(name string, version int) *sym.Symbol {
+	batch := l.symBatch
+	if len(batch) == 0 {
+		batch = make([]sym.Symbol, 1000)
+	}
+	s := &batch[0]
+	l.symBatch = batch[1:]
+
+	s.Dynid = -1
+	s.Name = name
+	s.Version = int16(version)
+
+	return s
+}
+
+// installSym sets the underlying sym.Symbol for the specified sym index.
+func (l *Loader) installSym(i Sym, s *sym.Symbol) {
+	if s == nil {
+		panic("installSym nil symbol")
+	}
+	if l.Syms[i] != nil {
+		panic("sym already present in installSym")
+	}
+	l.Syms[i] = s
 }
 
 // addNewSym adds a new sym.Symbol to the i-th index in the list of symbols.
-func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
-	s := syms.Newsym(name, ver)
+func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
+	s := l.allocSym(name, ver)
 	if s.Type != 0 && s.Type != sym.SXREF {
 		fmt.Println("symbol already processed:", unit.Lib, i, s)
 		panic("symbol already processed")
@@ -877,7 +2115,7 @@
 	s.Type = t
 	s.Unit = unit
 	l.growSyms(int(i))
-	l.Syms[i] = s
+	l.installSym(i, s)
 	return s
 }
 
@@ -885,16 +2123,11 @@
 // object corresponding to object reader "r". Return value is the
 // number of sym.Reloc entries required for all the new symbols.
 func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
-	istart := l.startIndex(r)
 	nr := 0
-
 	for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
-		// If it's been previously loaded in host object loading, we don't need to do it again.
-		if s := l.Syms[istart+Sym(i)]; s != nil {
-			// Mark symbol as reachable as it wasn't marked as such before.
-			s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
-			nr += r.NReloc(i)
-			continue
+		gi := r.syms[i]
+		if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+			continue // come from a different object
 		}
 		osym := goobj2.Sym{}
 		osym.Read(r.Reader, r.SymOff(i))
@@ -903,10 +2136,6 @@
 			continue
 		}
 		ver := abiToVer(osym.ABI, r.version)
-		if osym.ABI != goobj2.SymABIstatic && l.symsByName[ver][name] != istart+Sym(i) {
-			continue
-		}
-
 		t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
 		if t == sym.SXREF {
 			log.Fatalf("bad sxref")
@@ -914,15 +2143,14 @@
 		if t == 0 {
 			log.Fatalf("missing type for %s in %s", name, r.unit.Lib)
 		}
-		if !l.Reachable.Has(istart+Sym(i)) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" {
+		if !l.attrReachable.Has(gi) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" {
 			// No need to load unreachable symbols.
-			// XXX some type symbol's content may be needed in DWARF code, but they are not marked.
 			// XXX reference to runtime.addmoduledata may be generated later by the linker in plugin mode.
 			continue
 		}
 
-		s := l.addNewSym(istart+Sym(i), syms, name, ver, r.unit, t)
-		s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
+		s := l.addNewSym(gi, name, ver, r.unit, t)
+		l.migrateAttributes(gi, s)
 		nr += r.NReloc(i)
 	}
 	return nr
@@ -947,85 +2175,155 @@
 	fdOff   uint32 // number of int64's needed in all Funcdataoff slices
 }
 
-// LoadSymbol loads a single symbol by name.
-// This function should only be used by the host object loaders.
-// NB: This function does NOT set the symbol as reachable.
-func (l *Loader) LoadSymbol(name string, version int, syms *sym.Symbols) *sym.Symbol {
-	global := l.Lookup(name, version)
-
-	// If we're already loaded, bail.
-	if global != 0 && int(global) < len(l.Syms) && l.Syms[global] != nil {
-		return l.Syms[global]
+// cloneToExternal takes the existing object file symbol (symIdx)
+// and creates a new external symbol payload that is a clone with
+// respect to name, version, type, relocations, etc. The idea here
+// is that if the linker decides it wants to update the contents of
+// a symbol originally discovered as part of an object file, it's
+// easier to do this if we make the updates to an external symbol
+// payload.
+// XXX maybe rename? makeExtPayload?
+func (l *Loader) cloneToExternal(symIdx Sym) {
+	if l.IsExternal(symIdx) {
+		panic("sym is already external, no need for clone")
 	}
+	l.growSyms(int(symIdx))
 
-	// Read the symbol.
-	r, i := l.toLocal(global)
-	istart := l.startIndex(r)
-
+	// Read the particulars from object.
 	osym := goobj2.Sym{}
-	osym.Read(r.Reader, r.SymOff(int(i)))
-	if l.symsByName[version][name] != istart+Sym(i) {
-		return nil
+	r, li := l.toLocal(symIdx)
+	osym.Read(r.Reader, r.SymOff(li))
+	sname := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+	sver := abiToVer(osym.ABI, r.version)
+	skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+
+	// Create new symbol, update version and kind.
+	pi := l.newPayload(sname, sver)
+	pp := l.payloads[pi]
+	pp.kind = skind
+	pp.ver = sver
+	pp.size = int64(osym.Siz)
+	pp.objidx = r.objidx
+
+	// If this is a def, then copy the guts. We expect this case
+	// to be very rare (one case it may come up is with -X).
+	if li < (r.NSym() + r.NNonpkgdef()) {
+
+		// Copy relocations
+		relocs := l.Relocs(symIdx)
+		pp.relocs = relocs.ReadAll(nil)
+
+		// Copy data
+		pp.data = r.Data(li)
 	}
 
-	return l.addNewSym(istart+Sym(i), syms, name, version, r.unit, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
+	// If we're overriding a data symbol, collect the associated
+	// Gotype, so as to propagate it to the new symbol.
+	naux := r.NAux(li)
+	for j := 0; j < naux; j++ {
+		a := goobj2.Aux{}
+		a.Read(r.Reader, r.AuxOff(li, j))
+		switch a.Type {
+		case goobj2.AuxGotype:
+			pp.gotype = l.resolve(r, a.Sym)
+		default:
+			log.Fatalf("internal error: cloneToExternal applied to %s symbol %s with non-gotype aux data %d", skind.String(), sname, a.Type)
+		}
+	}
+
+	// Install new payload to global index space.
+	// (This needs to happen at the end, as the accessors above
+	// need to access the old symbol content.)
+	l.objSyms[symIdx] = objSym{l.extReader, pi}
+	l.extReader.syms = append(l.extReader.syms, symIdx)
 }
 
-// LookupOrCreate looks up a symbol by name, and creates one if not found.
-// Either way, it will also create a sym.Symbol for it, if not already.
-// This should only be called when interacting with parts of the linker
-// that still works on sym.Symbols (i.e. internal cgo linking, for now).
-func (l *Loader) LookupOrCreate(name string, version int, syms *sym.Symbols) *sym.Symbol {
-	i := l.Lookup(name, version)
-	if i != 0 {
-		// symbol exists
-		if int(i) < len(l.Syms) && l.Syms[i] != nil {
-			return l.Syms[i] // already loaded
-		}
-		if l.IsExternal(i) {
-			panic("Can't load an external symbol.")
-		}
-		return l.LoadSymbol(name, version, syms)
-	}
-	i = l.AddExtSym(name, version)
-	s := syms.Newsym(name, version)
-	l.Syms[i] = s
-	return s
+// copyAttributes copies over all of the attributes of symbol 'src' to
+// symbol 'dst'. The assumption is that 'dst' is an external symbol.
+func (l *Loader) copyAttributes(src Sym, dst Sym) {
+	l.SetAttrReachable(dst, l.AttrReachable(src))
+	l.SetAttrOnList(dst, l.AttrOnList(src))
+	l.SetAttrLocal(dst, l.AttrLocal(src))
+	l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src))
+	l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
+	l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
+	l.SetAttrShared(dst, l.AttrShared(src))
+	l.SetAttrExternal(dst, l.AttrExternal(src))
+	l.SetAttrTopFrame(dst, l.AttrTopFrame(src))
+	l.SetAttrSpecial(dst, l.AttrSpecial(src))
+	l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
+	l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
+	l.SetAttrReadOnly(dst, l.AttrReadOnly(src))
 }
 
-// Create creates a symbol with the specified name, returning a
-// sym.Symbol object for it. This method is intended for static/hidden
-// symbols discovered while loading host objects. We can see more than
-// one instance of a given static symbol with the same name/version,
-// so we can't add them to the lookup tables "as is". Instead assign
-// them fictitious (unique) versions, starting at -1 and decreasing by
-// one for each newly created symbol, and record them in the
-// extStaticSyms hash.
-func (l *Loader) Create(name string, syms *sym.Symbols) *sym.Symbol {
-	i := l.max + 1
-	l.max++
-	if l.extStart == 0 {
-		l.extStart = i
+// migrateAttributes copies over all of the attributes of symbol 'src' to
+// sym.Symbol 'dst'.
+func (l *Loader) migrateAttributes(src Sym, dst *sym.Symbol) {
+	dst.Attr.Set(sym.AttrReachable, l.AttrReachable(src))
+	dst.Attr.Set(sym.AttrOnList, l.AttrOnList(src))
+	dst.Attr.Set(sym.AttrLocal, l.AttrLocal(src))
+	dst.Attr.Set(sym.AttrNotInSymbolTable, l.AttrNotInSymbolTable(src))
+	dst.Attr.Set(sym.AttrNoSplit, l.IsNoSplit(src))
+	dst.Attr.Set(sym.AttrVisibilityHidden, l.AttrVisibilityHidden(src))
+	dst.Attr.Set(sym.AttrDuplicateOK, l.AttrDuplicateOK(src))
+	dst.Attr.Set(sym.AttrShared, l.AttrShared(src))
+	dst.Attr.Set(sym.AttrExternal, l.AttrExternal(src))
+	dst.Attr.Set(sym.AttrTopFrame, l.AttrTopFrame(src))
+	dst.Attr.Set(sym.AttrSpecial, l.AttrSpecial(src))
+	dst.Attr.Set(sym.AttrCgoExportDynamic, l.AttrCgoExportDynamic(src))
+	dst.Attr.Set(sym.AttrCgoExportStatic, l.AttrCgoExportStatic(src))
+	dst.Attr.Set(sym.AttrReadOnly, l.AttrReadOnly(src))
+
+	// Convert outer/sub relationships
+	if outer, ok := l.outer[src]; ok {
+		dst.Outer = l.Syms[outer]
+	}
+	if sub, ok := l.sub[src]; ok {
+		dst.Sub = l.Syms[sub]
 	}
 
+	// Set sub-symbol attribute. FIXME: would be better to do away
+	// with this and just use l.OuterSymbol() != 0 elsewhere within
+	// the linker.
+	dst.Attr.Set(sym.AttrSubSymbol, dst.Outer != nil)
+
+	// Copy over dynimplib, dynimpvers, extname.
+	if l.SymExtname(src) != "" {
+		dst.SetExtname(l.SymExtname(src))
+	}
+	if l.SymDynimplib(src) != "" {
+		dst.SetDynimplib(l.SymDynimplib(src))
+	}
+	if l.SymDynimpvers(src) != "" {
+		dst.SetDynimpvers(l.SymDynimpvers(src))
+	}
+
+	// Copy ELF type if set.
+	if et, ok := l.elfType[src]; ok {
+		dst.SetElfType(et)
+	}
+
+	// Copy pe objects values if set.
+	if plt, ok := l.plt[src]; ok {
+		dst.SetPlt(plt)
+	}
+	if got, ok := l.got[src]; ok {
+		dst.SetGot(got)
+	}
+}
+
+// CreateExtSym creates a new external symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateExtSym(name string) Sym {
 	// Assign a new unique negative version -- this is to mark the
 	// symbol so that it can be skipped when ExtractSymbols is adding
 	// ext syms to the sym.Symbols hash.
 	l.anonVersion--
-	ver := l.anonVersion
-	l.extSyms = append(l.extSyms, nameVer{name, ver})
-	l.growSyms(int(i))
-	s := syms.Newsym(name, ver)
-	l.Syms[i] = s
-	l.extStaticSyms[nameVer{name, ver}] = i
-
-	return s
+	return l.newExtSym(name, l.anonVersion)
 }
 
 func loadObjFull(l *Loader, r *oReader) {
 	lib := r.unit.Lib
-	istart := l.startIndex(r)
-
 	resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
 		i := l.resolve(r, s)
 		return l.Syms[i]
@@ -1037,36 +2335,37 @@
 	pcdataBase := r.PcdataBase()
 	rslice := []Reloc{}
 	for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
-		osym := goobj2.Sym{}
-		osym.Read(r.Reader, r.SymOff(i))
-		name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
-		if name == "" {
-			continue
-		}
-		ver := abiToVer(osym.ABI, r.version)
-		dupok := osym.Dupok()
-		if dupok {
-			if dupsym := l.symsByName[ver][name]; dupsym != istart+Sym(i) {
-				if l.Reachable.Has(dupsym) {
-					// A dupok symbol is resolved to another package. We still need
-					// to record its presence in the current package, as the trampoline
-					// pass expects packages are laid out in dependency order.
-					s := l.Syms[dupsym]
-					if s.Type == sym.STEXT {
-						lib.DupTextSyms = append(lib.DupTextSyms, s)
-					}
-				}
-				continue
-			}
+		// A symbol may be a dup or overwritten. In this case, its
+		// content will actually be provided by a different object
+		// (to which its global index points). Skip those symbols.
+		gi := l.toGlobal(r, i)
+		var isdup bool
+		if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+			isdup = true
 		}
 
-		s := l.Syms[istart+Sym(i)]
-		if s == nil {
+		osym := goobj2.Sym{}
+		osym.ReadWithoutName(r.Reader, r.SymOff(i))
+		dupok := osym.Dupok()
+		if dupok && isdup {
+			if l.attrReachable.Has(gi) {
+				// A dupok symbol is resolved to another package. We still need
+				// to record its presence in the current package, as the trampoline
+				// pass expects packages are laid out in dependency order.
+				s := l.Syms[gi]
+				if s.Type == sym.STEXT {
+					lib.DupTextSyms = append(lib.DupTextSyms, s)
+				}
+			}
 			continue
 		}
-		if s.Name != name { // Sanity check. We can remove it in the final version.
-			fmt.Println("name mismatch:", lib, i, s.Name, name)
-			panic("name mismatch")
+
+		if isdup {
+			continue // come from a different object
+		}
+		s := l.Syms[gi]
+		if s == nil {
+			continue
 		}
 
 		local := osym.Local()
@@ -1083,35 +2382,7 @@
 		batch := l.relocBatch
 		s.R = batch[:relocs.Count:relocs.Count]
 		l.relocBatch = batch[relocs.Count:]
-		for j := range s.R {
-			r := rslice[j]
-			rs := r.Sym
-			sz := r.Size
-			rt := r.Type
-			if rt == objabi.R_METHODOFF {
-				if l.Reachable.Has(rs) {
-					rt = objabi.R_ADDROFF
-				} else {
-					sz = 0
-					rs = 0
-				}
-			}
-			if rt == objabi.R_WEAKADDROFF && !l.Reachable.Has(rs) {
-				rs = 0
-				sz = 0
-			}
-			if rs != 0 && l.SymType(rs) == sym.SABIALIAS {
-				rsrelocs := l.Relocs(rs)
-				rs = rsrelocs.At(0).Sym
-			}
-			s.R[j] = sym.Reloc{
-				Off:  r.Off,
-				Siz:  sz,
-				Type: rt,
-				Add:  r.Add,
-				Sym:  l.Syms[rs],
-			}
-		}
+		l.convertRelocations(rslice, s, false)
 
 		// Aux symbol info
 		isym := -1
@@ -1149,14 +2420,6 @@
 		s.Attr.Set(sym.AttrLocal, local)
 		s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
 
-		if s.Type == sym.SDWARFINFO {
-			// For DWARF symbols, replace `"".` to actual package prefix
-			// in the symbol content.
-			// TODO: maybe we should do this in the compiler and get rid
-			// of this.
-			patchDWARFName(s, r)
-		}
-
 		if s.Type != sym.STEXT {
 			continue
 		}
@@ -1203,7 +2466,7 @@
 		info := goobj2.FuncInfo{}
 		info.Read(b)
 
-		if info.NoSplit != 0 {
+		if osym.NoSplit() {
 			s.Attr |= sym.AttrNoSplit
 		}
 		if osym.ReflectMethod() {
@@ -1283,43 +2546,168 @@
 	}
 }
 
-var emptyPkg = []byte(`"".`)
-
-func patchDWARFName1(p []byte, r *oReader) ([]byte, int) {
-	// This is kind of ugly. Really the package name should not
-	// even be included here.
-	if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION {
-		return p, -1
-	}
-	e := bytes.IndexByte(p, 0)
-	if e == -1 {
-		return p, -1
-	}
-	if !bytes.Contains(p[:e], emptyPkg) {
-		return p, -1
-	}
-	pkgprefix := []byte(r.pkgprefix)
-	patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1)
-	return append(patched, p[e:]...), e
-}
-
-func patchDWARFName(s *sym.Symbol, r *oReader) {
-	patched, e := patchDWARFName1(s.P, r)
-	if e == -1 {
-		return
-	}
-	s.P = patched
-	s.Attr.Set(sym.AttrReadOnly, false)
-	delta := int64(len(s.P)) - s.Size
-	s.Size = int64(len(s.P))
-	for i := range s.R {
-		r := &s.R[i]
-		if r.Off > int32(e) {
-			r.Off += int32(delta)
+// convertRelocations takes a vector of loader.Reloc relocations and
+// translates them into an equivalent set of sym.Reloc relocations on
+// the symbol "dst", performing fixups along the way for ABI aliases,
+// etc. It is assumed that the caller has pre-allocated the dst symbol
+// relocations slice. If 'strict' is set, then this method will
+// panic if it finds a relocation targeting a nil symbol.
+func (l *Loader) convertRelocations(src []Reloc, dst *sym.Symbol, strict bool) {
+	for j := range dst.R {
+		r := src[j]
+		rs := r.Sym
+		sz := r.Size
+		rt := r.Type
+		if rt == objabi.R_METHODOFF {
+			if l.attrReachable.Has(rs) {
+				rt = objabi.R_ADDROFF
+			} else {
+				sz = 0
+				rs = 0
+			}
+		}
+		if rt == objabi.R_WEAKADDROFF && !l.attrReachable.Has(rs) {
+			rs = 0
+			sz = 0
+		}
+		if rs != 0 && l.Syms[rs] != nil && l.Syms[rs].Type == sym.SABIALIAS {
+			rsrelocs := l.Relocs(rs)
+			rs = rsrelocs.At(0).Sym
+		}
+		if strict && rs != 0 && l.Syms[rs] == nil && rt != objabi.R_USETYPE {
+			panic("nil reloc target in convertRelocations")
+		}
+		dst.R[j] = sym.Reloc{
+			Off:  r.Off,
+			Siz:  sz,
+			Type: rt,
+			Add:  r.Add,
+			Sym:  l.Syms[rs],
 		}
 	}
 }
 
+// UndefinedRelocTargets iterates through the global symbol index
+// space, looking for symbols with relocations targeting undefined
+// references. The linker's loadlib method uses this to determine if
+// there are unresolved references to functions in system libraries
+// (for example, libgcc.a), presumably due to CGO code. Return
+// value is a list of loader.Sym's corresponding to the undefined
+// cross-refs. The "limit" param controls the maximum number of
+// results returned; if "limit" is -1, then all undefs are returned.
+func (l *Loader) UndefinedRelocTargets(limit int) []Sym {
+	result := []Sym{}
+	rslice := []Reloc{}
+	for si := Sym(1); si < Sym(len(l.objSyms)); si++ {
+		relocs := l.Relocs(si)
+		rslice = relocs.ReadSyms(rslice)
+		for ri := 0; ri < relocs.Count; ri++ {
+			r := &rslice[ri]
+			if r.Sym != 0 && l.SymType(r.Sym) == sym.SXREF && l.RawSymName(r.Sym) != ".got" {
+				result = append(result, r.Sym)
+				if limit != -1 && len(result) >= limit {
+					break
+				}
+			}
+		}
+	}
+	return result
+}
+
+// AssignTextSymbolOrder populates the Textp2 slices within each
+// library and compilation unit, insuring that packages are laid down
+// in dependency order (internal first, then everything else). Return value
+// is a slice of all text syms.
+func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym {
+
+	// Library Textp2 lists should be empty at this point.
+	for _, lib := range libs {
+		if len(lib.Textp2) != 0 {
+			panic("expected empty Textp2 slice for library")
+		}
+		if len(lib.DupTextSyms2) != 0 {
+			panic("expected empty DupTextSyms2 slice for library")
+		}
+	}
+
+	// Used to record which dupok symbol we've assigned to a unit.
+	// Can't use the onlist attribute here because it will need to
+	// clear for the later assignment of the sym.Symbol to a unit.
+	// NB: we can convert to using onList once we no longer have to
+	// call the regular addToTextp.
+	assignedToUnit := MakeBitmap(l.NSym() + 1)
+
+	// Start off textp2 with reachable external syms.
+	textp2 := []Sym{}
+	for _, sym := range extsyms {
+		if !l.attrReachable.Has(sym) {
+			continue
+		}
+		textp2 = append(textp2, sym)
+	}
+
+	// Walk through all text symbols from Go object files and append
+	// them to their corresponding library's textp2 list.
+	for _, o := range l.objs[1:] {
+		r := o.r
+		lib := r.unit.Lib
+		for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+			gi := l.toGlobal(r, i)
+			if !l.attrReachable.Has(gi) {
+				continue
+			}
+			osym := goobj2.Sym{}
+			osym.ReadWithoutName(r.Reader, r.SymOff(i))
+			st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+			if st != sym.STEXT {
+				continue
+			}
+			dupok := osym.Dupok()
+			if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+				// A dupok text symbol is resolved to another package.
+				// We still need to record its presence in the current
+				// package, as the trampoline pass expects packages
+				// are laid out in dependency order.
+				lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi))
+				continue // symbol in different object
+			}
+			if dupok {
+				lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi))
+			}
+
+			lib.Textp2 = append(lib.Textp2, sym.LoaderSym(gi))
+		}
+	}
+
+	// Now redo the assignment of text symbols to libs/units.
+	for _, doInternal := range [2]bool{true, false} {
+		for idx, lib := range libs {
+			if intlibs[idx] != doInternal {
+				continue
+			}
+			libtextp2 := []sym.LoaderSym{}
+			lists := [2][]sym.LoaderSym{lib.Textp2, lib.DupTextSyms2}
+			for _, list := range lists {
+				for _, s := range list {
+					sym := Sym(s)
+					if l.attrReachable.Has(sym) && !assignedToUnit.Has(sym) {
+						libtextp2 = append(libtextp2, s)
+						textp2 = append(textp2, sym)
+						unit := l.SymUnit(sym)
+						if unit != nil {
+							unit.Textp2 = append(unit.Textp2, s)
+							assignedToUnit.Set(sym)
+						}
+					}
+				}
+			}
+			lib.Textp2 = libtextp2
+		}
+	}
+
+	return textp2
+}
+
 // For debugging.
 func (l *Loader) Dump() {
 	fmt.Println("objs")
@@ -1328,18 +2716,24 @@
 			fmt.Println(obj.i, obj.r.unit.Lib)
 		}
 	}
+	fmt.Println("extStart:", l.extStart)
+	fmt.Println("Nsyms:", len(l.objSyms))
 	fmt.Println("syms")
-	for i, s := range l.Syms {
-		if i == 0 {
-			continue
+	for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
+		pi := interface{}("")
+		if l.IsExternal(i) {
+			pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
+		}
+		var s *sym.Symbol
+		if int(i) < len(l.Syms) {
+			s = l.Syms[i]
 		}
 		if s != nil {
-			fmt.Println(i, s, s.Type)
+			fmt.Println(i, s, s.Type, pi)
 		} else {
-			fmt.Println(i, l.SymName(Sym(i)), "<not loaded>")
+			fmt.Println(i, l.SymName(i), "<not loaded>", pi)
 		}
 	}
-	fmt.Println("overwrite:", l.overwrite)
 	fmt.Println("symsByName")
 	for name, i := range l.symsByName[0] {
 		fmt.Println(i, name, 0)
@@ -1347,4 +2741,9 @@
 	for name, i := range l.symsByName[1] {
 		fmt.Println(i, name, 1)
 	}
+	fmt.Println("payloads:")
+	for i := range l.payloads {
+		pp := l.payloads[i]
+		fmt.Println(i, pp.name, pp.ver, pp.kind)
+	}
 }
diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go
new file mode 100644
index 0000000..0503f66
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader_test.go
@@ -0,0 +1,441 @@
+// 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.
+
+package loader
+
+import (
+	"bytes"
+	"cmd/internal/objabi"
+	"cmd/internal/sys"
+	"cmd/link/internal/sym"
+	"fmt"
+	"testing"
+)
+
+// dummyAddSym adds the named symbol to the loader as if it had been
+// read from a Go object file. Note that it allocates a global
+// index without creating an associated object reader, so one can't
+// do anything interesting with this symbol (such as look at its
+// data or relocations).
+func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
+	idx := len(ldr.objSyms)
+	s, ok := ldr.AddSym(name, 0, or, idx, nonPkgDef, false, sym.SRODATA)
+	if !ok {
+		t.Errorf("AddrSym failed for '" + name + "'")
+	}
+	return s
+}
+
+func TestAddMaterializedSymbol(t *testing.T) {
+	edummy := func(s *sym.Symbol, str string, off int) {}
+	ldr := NewLoader(0, edummy)
+	dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+	or := &dummyOreader
+
+	// Create some syms from a dummy object file symbol to get things going.
+	ts1 := addDummyObjSym(t, ldr, or, "type.uint8")
+	ts2 := addDummyObjSym(t, ldr, or, "mumble")
+	ts3 := addDummyObjSym(t, ldr, or, "type.string")
+
+	// Create some external symbols.
+	es1 := ldr.AddExtSym("extnew1", 0)
+	if es1 == 0 {
+		t.Fatalf("AddExtSym failed for extnew1")
+	}
+	es1x := ldr.AddExtSym("extnew1", 0)
+	if es1x != es1 {
+		t.Fatalf("AddExtSym lookup: expected %d got %d for second lookup", es1, es1x)
+	}
+	es2 := ldr.AddExtSym("go.info.type.uint8", 0)
+	if es2 == 0 {
+		t.Fatalf("AddExtSym failed for go.info.type.uint8")
+	}
+	// Create a nameless symbol
+	es3 := ldr.CreateExtSym("")
+	if es3 == 0 {
+		t.Fatalf("CreateExtSym failed for nameless sym")
+	}
+
+	// Grab symbol builder pointers
+	sb1 := ldr.MakeSymbolUpdater(es1)
+	sb2 := ldr.MakeSymbolUpdater(es2)
+	sb3 := ldr.MakeSymbolUpdater(es3)
+
+	// Suppose we create some more symbols, which triggers a grow.
+	// Make sure the symbol builder's payload pointer is valid,
+	// even across a grow.
+	ldr.growSyms(9999)
+
+	// Check get/set symbol type
+	es3typ := sb3.Type()
+	if es3typ != sym.Sxxx {
+		t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ)
+	}
+	sb3.SetType(sym.SRODATA)
+	es3typ = sb3.Type()
+	if es3typ != sym.SRODATA {
+		t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+	}
+	es3typ = ldr.SymType(es3)
+	if es3typ != sym.SRODATA {
+		t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+	}
+
+	// New symbols should not initially be reachable.
+	if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) {
+		t.Errorf("newly materialized symbols should not be reachable")
+	}
+
+	// ... however it should be possible to set/unset their reachability.
+	ldr.SetAttrReachable(es3, true)
+	if !ldr.AttrReachable(es3) {
+		t.Errorf("expected reachable symbol after update")
+	}
+	ldr.SetAttrReachable(es3, false)
+	if ldr.AttrReachable(es3) {
+		t.Errorf("expected unreachable symbol after update")
+	}
+
+	// Test expansion of attr bitmaps
+	for idx := 0; idx < 36; idx++ {
+		es := ldr.AddExtSym(fmt.Sprintf("zext%d", idx), 0)
+		if ldr.AttrOnList(es) {
+			t.Errorf("expected OnList after creation")
+		}
+		ldr.SetAttrOnList(es, true)
+		if !ldr.AttrOnList(es) {
+			t.Errorf("expected !OnList after update")
+		}
+		if ldr.AttrDuplicateOK(es) {
+			t.Errorf("expected DupOK after creation")
+		}
+		ldr.SetAttrDuplicateOK(es, true)
+		if !ldr.AttrDuplicateOK(es) {
+			t.Errorf("expected !DupOK after update")
+		}
+	}
+
+	sb1 = ldr.MakeSymbolUpdater(es1)
+	sb2 = ldr.MakeSymbolUpdater(es2)
+
+	// Get/set a few other attributes
+	if ldr.AttrVisibilityHidden(es3) {
+		t.Errorf("expected initially not hidden")
+	}
+	ldr.SetAttrVisibilityHidden(es3, true)
+	if !ldr.AttrVisibilityHidden(es3) {
+		t.Errorf("expected hidden after update")
+	}
+
+	// Test get/set symbol value.
+	toTest := []Sym{ts2, es3}
+	for i, s := range toTest {
+		if v := ldr.SymValue(s); v != 0 {
+			t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v)
+		}
+		nv := int64(i + 101)
+		ldr.SetSymValue(s, nv)
+		if v := ldr.SymValue(s); v != nv {
+			t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v)
+		}
+	}
+
+	// Check/set alignment
+	es3al := ldr.SymAlign(es3)
+	if es3al != 0 {
+		t.Errorf("SymAlign(es3): expected 0, got %d", es3al)
+	}
+	ldr.SetSymAlign(es3, 128)
+	es3al = ldr.SymAlign(es3)
+	if es3al != 128 {
+		t.Errorf("SymAlign(es3): expected 128, got %d", es3al)
+	}
+
+	// Add some relocations to the new symbols.
+	r1 := Reloc{0, 1, objabi.R_ADDR, 0, ts1}
+	r2 := Reloc{3, 8, objabi.R_CALL, 0, ts2}
+	r3 := Reloc{7, 1, objabi.R_USETYPE, 0, ts3}
+	sb1.AddReloc(r1)
+	sb1.AddReloc(r2)
+	sb2.AddReloc(r3)
+
+	// Add some data to the symbols.
+	d1 := []byte{1, 2, 3}
+	d2 := []byte{4, 5, 6, 7}
+	sb1.AddBytes(d1)
+	sb2.AddBytes(d2)
+
+	// Now invoke the usual loader interfaces to make sure
+	// we're getting the right things back for these symbols.
+	// First relocations...
+	expRel := [][]Reloc{[]Reloc{r1, r2}, []Reloc{r3}}
+	for k, sb := range []*SymbolBuilder{sb1, sb2} {
+		rsl := sb.Relocs()
+		exp := expRel[k]
+		if !sameRelocSlice(rsl, exp) {
+			t.Errorf("expected relocs %v, got %v", exp, rsl)
+		}
+		relocs := ldr.Relocs(sb.Sym())
+		r0 := relocs.At(0)
+		if r0 != exp[0] {
+			t.Errorf("expected reloc %v, got %v", exp[0], r0)
+		}
+	}
+
+	// ... then data.
+	dat := sb2.Data()
+	if bytes.Compare(dat, d2) != 0 {
+		t.Errorf("expected es2 data %v, got %v", d2, dat)
+	}
+
+	// Nameless symbol should still be nameless.
+	es3name := ldr.RawSymName(es3)
+	if "" != es3name {
+		t.Errorf("expected es3 name of '', got '%s'", es3name)
+	}
+
+	// Read value of materialized symbol.
+	es1val := sb1.Value()
+	if 0 != es1val {
+		t.Errorf("expected es1 value of 0, got %v", es1val)
+	}
+
+	// Test other misc methods
+	irm := ldr.IsReflectMethod(es1)
+	if 0 != es1val {
+		t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm)
+	}
+
+	// Writing data to a materialized symbol should mark it reachable.
+	if !sb1.Reachable() || !sb2.Reachable() {
+		t.Fatalf("written-to materialized symbols should be reachable")
+	}
+}
+
+func sameRelocSlice(s1 []Reloc, s2 []Reloc) bool {
+	if len(s1) != len(s2) {
+		return false
+	}
+	for i := 0; i < len(s1); i++ {
+		if s1[i] != s2[i] {
+			return false
+		}
+	}
+	return true
+}
+
+type addFunc func(l *Loader, s Sym, s2 Sym) Sym
+
+func TestAddDataMethods(t *testing.T) {
+	edummy := func(s *sym.Symbol, str string, off int) {}
+	ldr := NewLoader(0, edummy)
+	dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+	or := &dummyOreader
+
+	// Populate loader with some symbols.
+	addDummyObjSym(t, ldr, or, "type.uint8")
+	ldr.AddExtSym("hello", 0)
+
+	arch := sys.ArchAMD64
+	var testpoints = []struct {
+		which       string
+		addDataFunc addFunc
+		expData     []byte
+		expKind     sym.SymKind
+		expRel      []Reloc
+	}{
+		{
+			which: "AddUint8",
+			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+				sb := l.MakeSymbolUpdater(s)
+				sb.AddUint8('a')
+				return s
+			},
+			expData: []byte{'a'},
+			expKind: sym.SDATA,
+		},
+		{
+			which: "AddUintXX",
+			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+				sb := l.MakeSymbolUpdater(s)
+				sb.AddUintXX(arch, 25185, 2)
+				return s
+			},
+			expData: []byte{'a', 'b'},
+			expKind: sym.SDATA,
+		},
+		{
+			which: "SetUint8",
+			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+				sb := l.MakeSymbolUpdater(s)
+				sb.AddUint8('a')
+				sb.AddUint8('b')
+				sb.SetUint8(arch, 1, 'c')
+				return s
+			},
+			expData: []byte{'a', 'c'},
+			expKind: sym.SDATA,
+		},
+		{
+			which: "AddString",
+			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+				sb := l.MakeSymbolUpdater(s)
+				sb.Addstring("hello")
+				return s
+			},
+			expData: []byte{'h', 'e', 'l', 'l', 'o', 0},
+			expKind: sym.SNOPTRDATA,
+		},
+		{
+			which: "AddAddrPlus",
+			addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+				sb := l.MakeSymbolUpdater(s)
+				sb.AddAddrPlus(arch, s2, 3)
+				return s
+			},
+			expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+			expKind: sym.SDATA,
+			expRel:  []Reloc{Reloc{Type: objabi.R_ADDR, Size: 8, Add: 3, Sym: 6}},
+		},
+		{
+			which: "AddAddrPlus4",
+			addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+				sb := l.MakeSymbolUpdater(s)
+				sb.AddAddrPlus4(arch, s2, 3)
+				return s
+			},
+			expData: []byte{0, 0, 0, 0},
+			expKind: sym.SDATA,
+			expRel:  []Reloc{Reloc{Type: objabi.R_ADDR, Size: 4, Add: 3, Sym: 7}},
+		},
+		{
+			which: "AddCURelativeAddrPlus",
+			addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+				sb := l.MakeSymbolUpdater(s)
+				sb.AddCURelativeAddrPlus(arch, s2, 7)
+				return s
+			},
+			expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+			expKind: sym.SDATA,
+			expRel:  []Reloc{Reloc{Type: objabi.R_ADDRCUOFF, Size: 8, Add: 7, Sym: 8}},
+		},
+	}
+
+	var pmi Sym
+	for k, tp := range testpoints {
+		name := fmt.Sprintf("new%d", k+1)
+		mi := ldr.AddExtSym(name, 0)
+		if mi == 0 {
+			t.Fatalf("AddExtSym failed for '" + name + "'")
+		}
+		mi = tp.addDataFunc(ldr, mi, pmi)
+		if ldr.SymType(mi) != tp.expKind {
+			t.Errorf("testing Loader.%s: expected kind %s got %s",
+				tp.which, tp.expKind, ldr.SymType(mi))
+		}
+		if bytes.Compare(ldr.Data(mi), tp.expData) != 0 {
+			t.Errorf("testing Loader.%s: expected data %v got %v",
+				tp.which, tp.expData, ldr.Data(mi))
+		}
+		if !ldr.AttrReachable(mi) {
+			t.Fatalf("testing Loader.%s: sym updated should be reachable", tp.which)
+		}
+		relocs := ldr.Relocs(mi)
+		rsl := relocs.ReadAll(nil)
+		if !sameRelocSlice(rsl, tp.expRel) {
+			t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v",
+				tp.which, rsl, tp.expRel)
+		}
+		pmi = mi
+	}
+}
+
+func TestOuterSub(t *testing.T) {
+	edummy := func(s *sym.Symbol, str string, off int) {}
+	ldr := NewLoader(0, edummy)
+	dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+	or := &dummyOreader
+
+	// Populate loader with some symbols.
+	addDummyObjSym(t, ldr, or, "type.uint8")
+	es1 := ldr.AddExtSym("outer", 0)
+	es2 := ldr.AddExtSym("sub1", 0)
+	es3 := ldr.AddExtSym("sub2", 0)
+	es4 := ldr.AddExtSym("sub3", 0)
+	es5 := ldr.AddExtSym("sub4", 0)
+	es6 := ldr.AddExtSym("sub5", 0)
+
+	// Should not have an outer sym initially
+	if ldr.OuterSym(es1) != 0 {
+		t.Errorf("es1 outer sym set ")
+	}
+	if ldr.SubSym(es2) != 0 {
+		t.Errorf("es2 outer sym set ")
+	}
+
+	// Establish first outer/sub relationship
+	ldr.PrependSub(es1, es2)
+	if ldr.OuterSym(es1) != 0 {
+		t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+	}
+	if ldr.OuterSym(es2) != es1 {
+		t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+	}
+	if ldr.SubSym(es1) != es2 {
+		t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2)
+	}
+	if ldr.SubSym(es2) != 0 {
+		t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0)
+	}
+
+	// Establish second outer/sub relationship
+	ldr.PrependSub(es1, es3)
+	if ldr.OuterSym(es1) != 0 {
+		t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+	}
+	if ldr.OuterSym(es2) != es1 {
+		t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+	}
+	if ldr.OuterSym(es3) != es1 {
+		t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1)
+	}
+	if ldr.SubSym(es1) != es3 {
+		t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3)
+	}
+	if ldr.SubSym(es3) != es2 {
+		t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2)
+	}
+
+	// Some more
+	ldr.PrependSub(es1, es4)
+	ldr.PrependSub(es1, es5)
+	ldr.PrependSub(es1, es6)
+
+	// Set values.
+	ldr.SetSymValue(es2, 7)
+	ldr.SetSymValue(es3, 1)
+	ldr.SetSymValue(es4, 13)
+	ldr.SetSymValue(es5, 101)
+	ldr.SetSymValue(es6, 3)
+
+	// Sort
+	news := ldr.SortSub(es1)
+	if news != es3 {
+		t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3)
+	}
+	pv := int64(-1)
+	count := 0
+	for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) {
+		v := ldr.SymValue(ss)
+		if v <= pv {
+			t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d",
+				ss, v, pv)
+		}
+		pv = v
+		count++
+	}
+	if count != 5 {
+		t.Errorf("expected %d in sub list got %d", 5, count)
+	}
+}
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
new file mode 100644
index 0000000..aeaec8b
--- /dev/null
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -0,0 +1,330 @@
+// 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.
+
+package loader
+
+import (
+	"cmd/internal/objabi"
+	"cmd/internal/sys"
+	"cmd/link/internal/sym"
+	"fmt"
+)
+
+// SymbolBuilder is a helper designed to help with the construction
+// of new symbol contents.
+type SymbolBuilder struct {
+	*extSymPayload         // points to payload being updated
+	symIdx         Sym     // index of symbol being updated/constructed
+	l              *Loader // loader
+}
+
+// MakeSymbolBuilder creates a symbol builder for use in constructing
+// an entirely new symbol.
+func (l *Loader) MakeSymbolBuilder(name string) *SymbolBuilder {
+	// for now assume that any new sym is intended to be static
+	symIdx := l.CreateExtSym(name)
+	if l.Syms[symIdx] != nil {
+		panic("can't build if sym.Symbol already present")
+	}
+	sb := &SymbolBuilder{l: l, symIdx: symIdx}
+	sb.extSymPayload = l.getPayload(symIdx)
+	return sb
+}
+
+// MakeSymbolUpdater creates a symbol builder helper for an existing
+// symbol 'symIdx'. If 'symIdx' is not an external symbol, then create
+// a clone of it (copy name, properties, etc) fix things up so that
+// the lookup tables and caches point to the new version, not the old
+// version.
+func (l *Loader) MakeSymbolUpdater(symIdx Sym) *SymbolBuilder {
+	if symIdx == 0 {
+		panic("can't update the null symbol")
+	}
+	if !l.IsExternal(symIdx) {
+		// Create a clone with the same name/version/kind etc.
+		l.cloneToExternal(symIdx)
+	}
+	// Now that we're doing phase 2 DWARF generation using the loader
+	// but before the wavefront has reached dodata(), we can't have this
+	// assertion here. Commented out for now.
+	if false {
+		if l.Syms[symIdx] != nil {
+			panic(fmt.Sprintf("can't build if sym.Symbol %q already present", l.RawSymName(symIdx)))
+		}
+	}
+
+	// Construct updater and return.
+	sb := &SymbolBuilder{l: l, symIdx: symIdx}
+	sb.extSymPayload = l.getPayload(symIdx)
+	return sb
+}
+
+// CreateSymForUpdate creates a symbol with given name and version,
+// returns a CreateSymForUpdate for update. If the symbol already
+// exists, it will update in-place.
+func (l *Loader) CreateSymForUpdate(name string, version int) *SymbolBuilder {
+	return l.MakeSymbolUpdater(l.LookupOrCreateSym(name, version))
+}
+
+// Getters for properties of the symbol we're working on.
+
+func (sb *SymbolBuilder) Sym() Sym               { return sb.symIdx }
+func (sb *SymbolBuilder) Name() string           { return sb.name }
+func (sb *SymbolBuilder) Version() int           { return sb.ver }
+func (sb *SymbolBuilder) Type() sym.SymKind      { return sb.kind }
+func (sb *SymbolBuilder) Size() int64            { return sb.size }
+func (sb *SymbolBuilder) Data() []byte           { return sb.data }
+func (sb *SymbolBuilder) Value() int64           { return sb.l.SymValue(sb.symIdx) }
+func (sb *SymbolBuilder) Align() int32           { return sb.l.SymAlign(sb.symIdx) }
+func (sb *SymbolBuilder) Localentry() uint8      { return sb.l.SymLocalentry(sb.symIdx) }
+func (sb *SymbolBuilder) OnList() bool           { return sb.l.AttrOnList(sb.symIdx) }
+func (sb *SymbolBuilder) External() bool         { return sb.l.AttrExternal(sb.symIdx) }
+func (sb *SymbolBuilder) Extname() string        { return sb.l.SymExtname(sb.symIdx) }
+func (sb *SymbolBuilder) CgoExportDynamic() bool { return sb.l.AttrCgoExportDynamic(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimplib() string      { return sb.l.SymDynimplib(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimpvers() string     { return sb.l.SymDynimpvers(sb.symIdx) }
+func (sb *SymbolBuilder) SubSym() Sym            { return sb.l.SubSym(sb.symIdx) }
+func (sb *SymbolBuilder) GoType() Sym            { return sb.l.SymGoType(sb.symIdx) }
+
+// Setters for symbol properties.
+
+func (sb *SymbolBuilder) SetType(kind sym.SymKind)   { sb.kind = kind }
+func (sb *SymbolBuilder) SetSize(size int64)         { sb.size = size }
+func (sb *SymbolBuilder) SetData(data []byte)        { sb.data = data }
+func (sb *SymbolBuilder) SetOnList(v bool)           { sb.l.SetAttrOnList(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetExternal(v bool)         { sb.l.SetAttrExternal(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetValue(v int64)           { sb.l.SetSymValue(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetAlign(align int32)       { sb.l.SetSymAlign(sb.symIdx, align) }
+func (sb *SymbolBuilder) SetLocalentry(value uint8)  { sb.l.SetSymLocalentry(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetExtname(value string)    { sb.l.SetSymExtname(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimplib(value string)  { sb.l.SetSymDynimplib(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetPlt(value int32)         { sb.l.SetPlt(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetGot(value int32)         { sb.l.SetGot(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetSpecial(value bool)      { sb.l.SetAttrSpecial(sb.symIdx, value) }
+
+func (sb *SymbolBuilder) SetNotInSymbolTable(value bool) {
+	sb.l.SetAttrNotInSymbolTable(sb.symIdx, value)
+}
+
+func (sb *SymbolBuilder) AddBytes(data []byte) {
+	sb.setReachable()
+	if sb.kind == 0 {
+		sb.kind = sym.SDATA
+	}
+	sb.data = append(sb.data, data...)
+	sb.size = int64(len(sb.data))
+}
+
+func (sb *SymbolBuilder) Relocs() []Reloc {
+	return sb.relocs
+}
+
+func (sb *SymbolBuilder) SetRelocs(rslice []Reloc) {
+	sb.relocs = rslice
+}
+
+func (sb *SymbolBuilder) WriteRelocs(rslice []Reloc) {
+	if len(sb.relocs) != len(rslice) {
+		panic("src/dest length mismatch")
+	}
+	copy(sb.relocs, rslice)
+}
+
+func (sb *SymbolBuilder) AddReloc(r Reloc) {
+	sb.relocs = append(sb.relocs, r)
+}
+
+func (sb *SymbolBuilder) Reachable() bool {
+	return sb.l.AttrReachable(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReachable(v bool) {
+	sb.l.SetAttrReachable(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) setReachable() {
+	sb.SetReachable(true)
+}
+
+func (sb *SymbolBuilder) ReadOnly() bool {
+	return sb.l.AttrReadOnly(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReadOnly(v bool) {
+	sb.l.SetAttrReadOnly(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) DuplicateOK() bool {
+	return sb.l.AttrDuplicateOK(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetDuplicateOK(v bool) {
+	sb.l.SetAttrDuplicateOK(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) Outer() Sym {
+	return sb.l.OuterSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) Sub() Sym {
+	return sb.l.SubSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SortSub() {
+	sb.l.SortSub(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) PrependSub(sub Sym) {
+	sb.l.PrependSub(sb.symIdx, sub)
+}
+
+func (sb *SymbolBuilder) AddUint8(v uint8) int64 {
+	off := sb.size
+	if sb.kind == 0 {
+		sb.kind = sym.SDATA
+	}
+	sb.setReachable()
+	sb.size++
+	sb.data = append(sb.data, v)
+	return off
+}
+
+func (sb *SymbolBuilder) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 {
+	off := sb.size
+	sb.setReachable()
+	sb.setUintXX(arch, off, v, int64(wid))
+	return off
+}
+
+func (sb *SymbolBuilder) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 {
+	if sb.kind == 0 {
+		sb.kind = sym.SDATA
+	}
+	if sb.size < off+wid {
+		sb.size = off + wid
+		sb.Grow(sb.size)
+	}
+
+	switch wid {
+	case 1:
+		sb.data[off] = uint8(v)
+	case 2:
+		arch.ByteOrder.PutUint16(sb.data[off:], uint16(v))
+	case 4:
+		arch.ByteOrder.PutUint32(sb.data[off:], uint32(v))
+	case 8:
+		arch.ByteOrder.PutUint64(sb.data[off:], v)
+	}
+
+	return off + wid
+}
+
+func (sb *SymbolBuilder) AddUint16(arch *sys.Arch, v uint16) int64 {
+	return sb.AddUintXX(arch, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) AddUint32(arch *sys.Arch, v uint32) int64 {
+	return sb.AddUintXX(arch, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) AddUint64(arch *sys.Arch, v uint64) int64 {
+	return sb.AddUintXX(arch, v, 8)
+}
+
+func (sb *SymbolBuilder) AddUint(arch *sys.Arch, v uint64) int64 {
+	return sb.AddUintXX(arch, v, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) SetUint8(arch *sys.Arch, r int64, v uint8) int64 {
+	sb.setReachable()
+	return sb.setUintXX(arch, r, uint64(v), 1)
+}
+
+func (sb *SymbolBuilder) SetUint16(arch *sys.Arch, r int64, v uint16) int64 {
+	sb.setReachable()
+	return sb.setUintXX(arch, r, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) SetUint32(arch *sys.Arch, r int64, v uint32) int64 {
+	sb.setReachable()
+	return sb.setUintXX(arch, r, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 {
+	sb.setReachable()
+	return sb.setUintXX(arch, r, v, int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) Addstring(str string) int64 {
+	sb.setReachable()
+	if sb.kind == 0 {
+		sb.kind = sym.SNOPTRDATA
+	}
+	r := sb.size
+	if sb.name == ".shstrtab" {
+		// FIXME: find a better mechanism for this
+		sb.l.elfsetstring(nil, str, int(r))
+	}
+	sb.data = append(sb.data, str...)
+	sb.data = append(sb.data, 0)
+	sb.size = int64(len(sb.data))
+	return r
+}
+
+func (sb *SymbolBuilder) addRel() *Reloc {
+	sb.relocs = append(sb.relocs, Reloc{})
+	return &sb.relocs[len(sb.relocs)-1]
+}
+
+func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+	if sb.kind == 0 {
+		sb.kind = sym.SDATA
+	}
+	i := sb.size
+
+	sb.size += int64(rsize)
+	sb.Grow(sb.size)
+
+	r := sb.addRel()
+	r.Sym = tgt
+	r.Off = int32(i)
+	r.Size = uint8(rsize)
+	r.Type = typ
+	r.Add = add
+
+	return i + int64(r.Size)
+}
+
+// Add a symbol reference (relocation) with given type, addend, and size
+// (the most generic form).
+func (sb *SymbolBuilder) AddSymRef(arch *sys.Arch, tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+	sb.setReachable()
+	return sb.addSymRef(tgt, add, typ, rsize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+	sb.setReachable()
+	return sb.addSymRef(tgt, add, objabi.R_ADDR, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus4(arch *sys.Arch, tgt Sym, add int64) int64 {
+	sb.setReachable()
+	return sb.addSymRef(tgt, add, objabi.R_ADDR, 4)
+}
+
+func (sb *SymbolBuilder) AddPCRelPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+	sb.setReachable()
+	return sb.addSymRef(tgt, add, objabi.R_PCREL, 4)
+}
+
+func (sb *SymbolBuilder) AddCURelativeAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+	sb.setReachable()
+	return sb.addSymRef(tgt, add, objabi.R_ADDRCUOFF, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 {
+	sb.setReachable()
+	return sb.addSymRef(tgt, 0, objabi.R_SIZE, arch.PtrSize)
+}
diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go
index 85a1ebc..d1ff82f 100644
--- a/src/cmd/link/internal/loadmacho/ldmacho.go
+++ b/src/cmd/link/internal/loadmacho/ldmacho.go
@@ -14,7 +14,6 @@
 	"cmd/link/internal/sym"
 	"encoding/binary"
 	"fmt"
-	"io"
 	"sort"
 )
 
@@ -101,7 +100,7 @@
 	flags   uint32
 	res1    uint32
 	res2    uint32
-	sym     *sym.Symbol
+	sym     loader.Sym
 	rel     []ldMachoRel
 }
 
@@ -132,7 +131,7 @@
 	desc    uint16
 	kind    int8
 	value   uint64
-	sym     *sym.Symbol
+	sym     loader.Sym
 }
 
 type ldMachoDysymtab struct {
@@ -320,10 +319,9 @@
 		return 0
 	}
 	rel := make([]ldMachoRel, sect.nreloc)
-	n := int(sect.nreloc * 8)
-	buf := make([]byte, n)
 	m.f.MustSeek(m.base+int64(sect.reloff), 0)
-	if _, err := io.ReadFull(m.f, buf); err != nil {
+	buf, _, err := m.f.Slice(uint64(sect.nreloc * 8))
+	if err != nil {
 		return -1
 	}
 	for i := uint32(0); i < sect.nreloc; i++ {
@@ -364,10 +362,9 @@
 
 func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int {
 	n := int(d.nindirectsyms)
-
-	p := make([]byte, n*4)
 	m.f.MustSeek(m.base+int64(d.indirectsymoff), 0)
-	if _, err := io.ReadFull(m.f, p); err != nil {
+	p, _, err := m.f.Slice(uint64(n * 4))
+	if err != nil {
 		return -1
 	}
 
@@ -383,9 +380,9 @@
 		return 0
 	}
 
-	strbuf := make([]byte, symtab.strsize)
 	m.f.MustSeek(m.base+int64(symtab.stroff), 0)
-	if _, err := io.ReadFull(m.f, strbuf); err != nil {
+	strbuf, _, err := m.f.Slice(uint64(symtab.strsize))
+	if err != nil {
 		return -1
 	}
 
@@ -394,9 +391,9 @@
 		symsize = 16
 	}
 	n := int(symtab.nsym * uint32(symsize))
-	symbuf := make([]byte, n)
 	m.f.MustSeek(m.base+int64(symtab.symoff), 0)
-	if _, err := io.ReadFull(m.f, symbuf); err != nil {
+	symbuf, _, err := m.f.Slice(uint64(n))
+	if err != nil {
 		return -1
 	}
 	sym := make([]ldMachoSym, symtab.nsym)
@@ -424,28 +421,17 @@
 	return 0
 }
 
-func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) ([]*sym.Symbol, error) {
-	newSym := func(name string, version int) *sym.Symbol {
-		return l.LookupOrCreate(name, version, syms)
-	}
-	return load(arch, syms.IncVersion(), newSym, f, pkg, length, pn)
-}
-
-func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
-	return load(arch, syms.IncVersion(), syms.Lookup, f, pkg, length, pn)
-}
-
-// load the Mach-O file pn from f.
+// Load the Mach-O file pn from f.
 // Symbols are written into syms, and a slice of the text symbols is returned.
-func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Symbol, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
-	errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+	errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
 		return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
 	}
 
 	base := f.Offset()
 
-	var hdr [7 * 4]uint8
-	if _, err := io.ReadFull(f, hdr[:]); err != nil {
+	hdr, _, err := f.Slice(7 * 4)
+	if err != nil {
 		return errorf("reading hdr: %v", err)
 	}
 
@@ -499,8 +485,8 @@
 	}
 
 	m.cmd = make([]ldMachoCmd, ncmd)
-	cmdp := make([]byte, cmdsz)
-	if _, err := io.ReadFull(f, cmdp); err != nil {
+	cmdp, _, err := f.Slice(uint64(cmdsz))
+	if err != nil {
 		return errorf("reading cmds: %v", err)
 	}
 
@@ -559,8 +545,8 @@
 	}
 
 	f.MustSeek(m.base+int64(c.seg.fileoff), 0)
-	dat := make([]byte, c.seg.filesz)
-	if _, err := io.ReadFull(f, dat); err != nil {
+	dat, readOnly, err := f.Slice(uint64(c.seg.filesz))
+	if err != nil {
 		return errorf("cannot load object data: %v", err)
 	}
 
@@ -573,30 +559,32 @@
 			continue
 		}
 		name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
-		s := lookup(name, localSymVersion)
-		if s.Type != 0 {
+		s := l.LookupOrCreateSym(name, localSymVersion)
+		bld := l.MakeSymbolUpdater(s)
+		if bld.Type() != 0 {
 			return errorf("duplicate %s/%s", sect.segname, sect.name)
 		}
 
 		if sect.flags&0xff == 1 { // S_ZEROFILL
-			s.P = make([]byte, sect.size)
+			bld.SetData(make([]byte, sect.size))
 		} else {
-			s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size]
+			bld.SetReadOnly(readOnly)
+			bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size])
 		}
-		s.Size = int64(len(s.P))
+		bld.SetSize(int64(len(bld.Data())))
 
 		if sect.segname == "__TEXT" {
 			if sect.name == "__text" {
-				s.Type = sym.STEXT
+				bld.SetType(sym.STEXT)
 			} else {
-				s.Type = sym.SRODATA
+				bld.SetType(sym.SRODATA)
 			}
 		} else {
 			if sect.name == "__bss" {
-				s.Type = sym.SNOPTRBSS
-				s.P = s.P[:0]
+				bld.SetType(sym.SNOPTRBSS)
+				bld.SetData(nil)
 			} else {
-				s.Type = sym.SNOPTRDATA
+				bld.SetType(sym.SNOPTRDATA)
 			}
 		}
 
@@ -621,12 +609,12 @@
 		if machsym.type_&N_EXT == 0 {
 			v = localSymVersion
 		}
-		s := lookup(name, v)
+		s := l.LookupOrCreateSym(name, v)
 		if machsym.type_&N_EXT == 0 {
-			s.Attr |= sym.AttrDuplicateOK
+			l.SetAttrDuplicateOK(s, true)
 		}
 		if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
-			s.Attr |= sym.AttrDuplicateOK
+			l.SetAttrDuplicateOK(s, true)
 		}
 		machsym.sym = s
 		if machsym.sectnum == 0 { // undefined
@@ -637,35 +625,32 @@
 		}
 
 		sect := &c.seg.sect[machsym.sectnum-1]
+		bld := l.MakeSymbolUpdater(s)
 		outer := sect.sym
-		if outer == nil {
+		if outer == 0 {
 			continue // ignore reference to invalid section
 		}
 
-		if s.Outer != nil {
-			if s.Attr.DuplicateOK() {
+		if osym := l.OuterSym(s); osym != 0 {
+			if l.AttrDuplicateOK(s) {
 				continue
 			}
-			return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name)
+			return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym))
 		}
 
-		s.Type = outer.Type
-		s.Attr |= sym.AttrSubSymbol
-		s.Sub = outer.Sub
-		outer.Sub = s
-		s.Outer = outer
-		s.Value = int64(machsym.value - sect.addr)
-		if !s.Attr.CgoExportDynamic() {
-			s.SetDynimplib("") // satisfy dynimport
+		bld.SetType(l.SymType(outer))
+		l.PrependSub(outer, s)
+
+		bld.SetValue(int64(machsym.value - sect.addr))
+		if !l.AttrCgoExportDynamic(s) {
+			bld.SetDynimplib("") // satisfy dynimport
 		}
-		if outer.Type == sym.STEXT {
-			if s.Attr.External() && !s.Attr.DuplicateOK() {
+		if l.SymType(outer) == sym.STEXT {
+			if bld.External() && !bld.DuplicateOK() {
 				return errorf("%v: duplicate symbol definition", s)
 			}
-			s.Attr |= sym.AttrExternal
+			bld.SetExternal(true)
 		}
-
-		machsym.sym = s
 	}
 
 	// Sort outer lists by address, adding to textp.
@@ -673,33 +658,37 @@
 	for i := 0; uint32(i) < c.seg.nsect; i++ {
 		sect := &c.seg.sect[i]
 		s := sect.sym
-		if s == nil {
+		if s == 0 {
 			continue
 		}
-		if s.Sub != nil {
-			s.Sub = sym.SortSub(s.Sub)
+		bld := l.MakeSymbolUpdater(s)
+		if bld.SubSym() != 0 {
+
+			bld.SortSub()
 
 			// assign sizes, now that we know symbols in sorted order.
-			for s1 := s.Sub; s1 != nil; s1 = s1.Sub {
-				if s1.Sub != nil {
-					s1.Size = s1.Sub.Value - s1.Value
+			for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+				s1Bld := l.MakeSymbolUpdater(s1)
+				if sub := l.SubSym(s1); sub != 0 {
+					s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1))
 				} else {
-					s1.Size = s.Value + s.Size - s1.Value
+					dlen := int64(len(l.Data(s)))
+					s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1))
 				}
 			}
 		}
 
-		if s.Type == sym.STEXT {
-			if s.Attr.OnList() {
-				return errorf("symbol %s listed multiple times", s.Name)
+		if bld.Type() == sym.STEXT {
+			if bld.OnList() {
+				return errorf("symbol %s listed multiple times", bld.Name())
 			}
-			s.Attr |= sym.AttrOnList
+			bld.SetOnList(true)
 			textp = append(textp, s)
-			for s1 := s.Sub; s1 != nil; s1 = s1.Sub {
-				if s1.Attr.OnList() {
-					return errorf("symbol %s listed multiple times", s1.Name)
+			for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+				if l.AttrOnList(s1) {
+					return errorf("symbol %s listed multiple times", l.RawSymName(s1))
 				}
-				s1.Attr |= sym.AttrOnList
+				l.SetAttrOnList(s1, true)
 				textp = append(textp, s1)
 			}
 		}
@@ -709,14 +698,14 @@
 	for i := 0; uint32(i) < c.seg.nsect; i++ {
 		sect := &c.seg.sect[i]
 		s := sect.sym
-		if s == nil {
+		if s == 0 {
 			continue
 		}
 		macholoadrel(m, sect)
 		if sect.rel == nil {
 			continue
 		}
-		r := make([]sym.Reloc, sect.nreloc)
+		r := make([]loader.Reloc, sect.nreloc)
 		rpi := 0
 	Reloc:
 		for j := uint32(0); j < sect.nreloc; j++ {
@@ -741,7 +730,7 @@
 					return errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_))
 				}
 
-				rp.Siz = rel.length
+				rp.Size = rel.length
 				rp.Off = int32(rel.addr)
 
 				// NOTE(rsc): I haven't worked out why (really when)
@@ -765,7 +754,7 @@
 				for k := 0; uint32(k) < c.seg.nsect; k++ {
 					ks := &c.seg.sect[k]
 					if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size {
-						if ks.sym != nil {
+						if ks.sym != 0 {
 							rp.Sym = ks.sym
 							rp.Add += int64(uint64(rel.value) - ks.addr)
 						} else if ks.segname == "__IMPORT" && ks.name == "__pointers" {
@@ -805,11 +794,12 @@
 				return errorf("unsupported scattered relocation: invalid address %#x", rel.addr)
 			}
 
-			rp.Siz = rel.length
+			rp.Size = rel.length
 			rp.Type = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
 			rp.Off = int32(rel.addr)
 
 			// Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
+			p := l.Data(s)
 			if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
 				// Calculate the addend as the offset into the section.
 				//
@@ -828,9 +818,9 @@
 				// [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
 				secaddr := c.seg.sect[rel.symnum-1].addr
 
-				rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr)
+				rp.Add = int64(uint64(int64(int32(e.Uint32(p[rp.Off:])))+int64(rp.Off)+4) - secaddr)
 			} else {
-				rp.Add = int64(int32(e.Uint32(s.P[rp.Off:])))
+				rp.Add = int64(int32(e.Uint32(p[rp.Off:])))
 			}
 
 			// An unsigned internal relocation has a value offset
@@ -844,7 +834,7 @@
 			// it *is* the PC being subtracted. Use that to make
 			// it match our version of PC-relative.
 			if rel.pcrel != 0 && arch.Family == sys.I386 {
-				rp.Add += int64(rp.Off) + int64(rp.Siz)
+				rp.Add += int64(rp.Off) + int64(rp.Size)
 			}
 			if rel.extrn == 0 {
 				if rel.symnum < 1 || rel.symnum > c.seg.nsect {
@@ -852,7 +842,7 @@
 				}
 
 				rp.Sym = c.seg.sect[rel.symnum-1].sym
-				if rp.Sym == nil {
+				if rp.Sym == 0 {
 					return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
 				}
 
@@ -874,9 +864,9 @@
 			rpi++
 		}
 
-		sort.Sort(sym.RelocByOff(r[:rpi]))
-		s.R = r
-		s.R = s.R[:rpi]
+		sort.Sort(loader.RelocByOff(r[:rpi]))
+		sb := l.MakeSymbolUpdater(sect.sym)
+		sb.SetRelocs(r[:rpi])
 	}
 
 	return textp, nil
diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
index 8b6aac3..88819f3 100644
--- a/src/cmd/link/internal/loadpe/ldpe.go
+++ b/src/cmd/link/internal/loadpe/ldpe.go
@@ -145,22 +145,26 @@
 	return n, nil
 }
 
-func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
-	lookup := func(name string, version int) *sym.Symbol {
-		return l.LookupOrCreate(name, version, syms)
+// makeUpdater creates a loader.SymbolBuilder if one hasn't been created previously.
+// We use this to lazily make SymbolBuilders as we don't always need a builder, and creating them for all symbols might be an error.
+func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loader.SymbolBuilder {
+	if bld != nil {
+		return bld
 	}
-	return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
+	bld = l.MakeSymbolUpdater(s)
+	return bld
 }
 
-func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
-	return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
-}
-
-// load loads the PE file pn from input.
+// Load loads the PE file pn from input.
 // Symbols are written into syms, and a slice of the text symbols is returned.
 // If an .rsrc section is found, its symbol is returned as rsrc.
-func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
-	sectsyms := make(map[*pe.Section]*sym.Symbol)
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc loader.Sym, err error) {
+	lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) {
+		s := l.LookupOrCreateSym(name, version)
+		sb := l.MakeSymbolUpdater(s)
+		return sb, s
+	}
+	sectsyms := make(map[*pe.Section]loader.Sym)
 	sectdata := make(map[*pe.Section][]byte)
 
 	// Some input files are archives containing multiple of
@@ -172,7 +176,7 @@
 	// TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
 	f, err := pe.NewFile(sr)
 	if err != nil {
-		return nil, nil, err
+		return nil, 0, err
 	}
 	defer f.Close()
 
@@ -191,34 +195,34 @@
 		}
 
 		name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
-		s := lookup(name, localSymVersion)
+		bld, s := lookup(name, localSymVersion)
 
 		switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
 		case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
-			s.Type = sym.SRODATA
+			bld.SetType(sym.SRODATA)
 
 		case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss
-			s.Type = sym.SNOPTRBSS
+			bld.SetType(sym.SNOPTRBSS)
 
 		case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data
-			s.Type = sym.SNOPTRDATA
+			bld.SetType(sym.SNOPTRDATA)
 
 		case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text
-			s.Type = sym.STEXT
+			bld.SetType(sym.STEXT)
 
 		default:
-			return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
+			return nil, 0, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
 		}
 
-		if s.Type != sym.SNOPTRBSS {
+		if bld.Type() != sym.SNOPTRBSS {
 			data, err := sect.Data()
 			if err != nil {
-				return nil, nil, err
+				return nil, 0, err
 			}
 			sectdata[sect] = data
-			s.P = data
+			bld.SetData(data)
 		}
-		s.Size = int64(sect.Size)
+		bld.SetSize(int64(sect.Size))
 		sectsyms[sect] = s
 		if sect.Name == ".rsrc" {
 			rsrc = s
@@ -242,35 +246,35 @@
 			continue
 		}
 
-		rs := make([]sym.Reloc, rsect.NumberOfRelocations)
+		rs := make([]loader.Reloc, rsect.NumberOfRelocations)
 		for j, r := range rsect.Relocs {
 			rp := &rs[j]
 			if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
-				return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
+				return nil, 0, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
 			}
 			pesym := &f.COFFSymbols[r.SymbolTableIndex]
-			gosym, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
+			_, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
 			if err != nil {
-				return nil, nil, err
+				return nil, 0, err
 			}
-			if gosym == nil {
+			if gosym == 0 {
 				name, err := pesym.FullName(f.StringTable)
 				if err != nil {
 					name = string(pesym.Name[:])
 				}
-				return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
+				return nil, 0, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
 			}
 
 			rp.Sym = gosym
-			rp.Siz = 4
+			rp.Size = 4
 			rp.Off = int32(r.VirtualAddress)
 			switch arch.Family {
 			default:
-				return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
+				return nil, 0, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
 			case sys.I386, sys.AMD64:
 				switch r.Type {
 				default:
-					return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
+					return nil, 0, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
 
 				case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
 					IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
@@ -286,7 +290,7 @@
 					rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
 
 				case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
-					rp.Siz = 8
+					rp.Size = 8
 
 					rp.Type = objabi.R_ADDR
 
@@ -297,7 +301,7 @@
 			case sys.ARM:
 				switch r.Type {
 				default:
-					return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
+					return nil, 0, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
 
 				case IMAGE_REL_ARM_SECREL:
 					rp.Type = objabi.R_PCREL
@@ -324,11 +328,10 @@
 			}
 		}
 
-		sort.Sort(sym.RelocByOff(rs[:rsect.NumberOfRelocations]))
+		sort.Sort(loader.RelocByOff(rs[:rsect.NumberOfRelocations]))
 
-		s := sectsyms[rsect]
-		s.R = rs
-		s.R = s.R[:rsect.NumberOfRelocations]
+		bld := l.MakeSymbolUpdater(sectsyms[rsect])
+		bld.SetRelocs(rs[:rsect.NumberOfRelocations])
 	}
 
 	// enter sub-symbols into symbol table.
@@ -339,7 +342,7 @@
 
 		name, err := pesym.FullName(f.StringTable)
 		if err != nil {
-			return nil, nil, err
+			return nil, 0, err
 		}
 		if name == "" {
 			continue
@@ -361,54 +364,56 @@
 			}
 		}
 
-		s, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
+		bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
 		if err != nil {
-			return nil, nil, err
+			return nil, 0, err
 		}
 
 		if pesym.SectionNumber == 0 { // extern
-			if s.Type == sym.SDYNIMPORT {
-				s.SetPlt(-2) // flag for dynimport in PE object files.
+			if l.SymType(s) == sym.SDYNIMPORT {
+				bld = makeUpdater(l, bld, s)
+				bld.SetPlt(-2) // flag for dynimport in PE object files.
 			}
-			if s.Type == sym.SXREF && pesym.Value > 0 { // global data
-				s.Type = sym.SNOPTRDATA
-				s.Size = int64(pesym.Value)
+			if l.SymType(s) == sym.SXREF && pesym.Value > 0 { // global data
+				bld = makeUpdater(l, bld, s)
+				bld.SetType(sym.SNOPTRDATA)
+				bld.SetSize(int64(pesym.Value))
 			}
 
 			continue
 		} else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
 			sect = f.Sections[pesym.SectionNumber-1]
 			if _, found := sectsyms[sect]; !found {
-				return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
+				return nil, 0, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
 			}
 		} else {
-			return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
+			return nil, 0, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
 		}
 
 		if sect == nil {
-			return nil, rsrc, nil
+			return nil, 0, nil
 		}
 
-		if s.Outer != nil {
-			if s.Attr.DuplicateOK() {
+		if l.OuterSym(s) != 0 {
+			if l.AttrDuplicateOK(s) {
 				continue
 			}
-			return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sectsyms[sect].Name)
+			outerName := l.SymName(l.OuterSym(s))
+			sectName := l.SymName(sectsyms[sect])
+			return nil, 0, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
 		}
 
+		bld = makeUpdater(l, bld, s)
 		sectsym := sectsyms[sect]
-		s.Sub = sectsym.Sub
-		sectsym.Sub = s
-		s.Type = sectsym.Type
-		s.Attr |= sym.AttrSubSymbol
-		s.Value = int64(pesym.Value)
-		s.Size = 4
-		s.Outer = sectsym
-		if sectsym.Type == sym.STEXT {
-			if s.Attr.External() && !s.Attr.DuplicateOK() {
-				return nil, nil, fmt.Errorf("%s: duplicate symbol definition", s.Name)
+		bld.SetType(l.SymType(sectsym))
+		l.PrependSub(sectsym, s)
+		bld.SetValue(int64(pesym.Value))
+		bld.SetSize(4)
+		if l.SymType(sectsym) == sym.STEXT {
+			if bld.External() && !bld.DuplicateOK() {
+				return nil, 0, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
 			}
-			s.Attr |= sym.AttrExternal
+			bld.SetExternal(true)
 		}
 	}
 
@@ -416,23 +421,16 @@
 	// This keeps textp in increasing address order.
 	for _, sect := range f.Sections {
 		s := sectsyms[sect]
-		if s == nil {
+		if s == 0 {
 			continue
 		}
-		if s.Sub != nil {
-			s.Sub = sym.SortSub(s.Sub)
-		}
-		if s.Type == sym.STEXT {
-			if s.Attr.OnList() {
-				return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name)
-			}
-			s.Attr |= sym.AttrOnList
-			textp = append(textp, s)
-			for s = s.Sub; s != nil; s = s.Sub {
-				if s.Attr.OnList() {
-					return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name)
+		l.SortSub(s)
+		if l.SymType(s) == sym.STEXT {
+			for ; s != 0; s = l.SubSym(s) {
+				if l.AttrOnList(s) {
+					return nil, 0, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
 				}
-				s.Attr |= sym.AttrOnList
+				l.SetAttrOnList(s, true)
 				textp = append(textp, s)
 			}
 		}
@@ -445,14 +443,14 @@
 	return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
 }
 
-func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) {
+func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader.Sym, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]loader.Sym, localSymVersion int) (*loader.SymbolBuilder, loader.Sym, error) {
 	symname, err := pesym.FullName(f.StringTable)
 	if err != nil {
-		return nil, err
+		return nil, 0, err
 	}
 	var name string
 	if issect(pesym) {
-		name = sectsyms[f.Sections[pesym.SectionNumber-1]].Name
+		name = l.SymName(sectsyms[f.Sections[pesym.SectionNumber-1]])
 	} else {
 		name = symname
 		switch arch.Family {
@@ -483,10 +481,11 @@
 		name = name[:i]
 	}
 
-	var s *sym.Symbol
+	var s loader.Sym
+	var bld *loader.SymbolBuilder
 	switch pesym.Type {
 	default:
-		return nil, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)
+		return nil, 0, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)
 
 	case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
 		switch pesym.StorageClass {
@@ -495,19 +494,22 @@
 
 		case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
 			s = lookup(name, localSymVersion)
-			s.Attr |= sym.AttrDuplicateOK
+			bld = makeUpdater(l, bld, s)
+			bld.SetDuplicateOK(true)
 
 		default:
-			return nil, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass)
+			return nil, 0, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass)
 		}
 	}
 
-	if s != nil && s.Type == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
-		s.Type = sym.SXREF
+	if s != 0 && l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
+		bld = makeUpdater(l, bld, s)
+		bld.SetType(sym.SXREF)
 	}
 	if strings.HasPrefix(symname, "__imp_") {
-		s.SetGot(-2) // flag for __imp_
+		bld = makeUpdater(l, bld, s)
+		bld.SetGot(-2) // flag for __imp_
 	}
 
-	return s, nil
+	return bld, s, nil
 }
diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go
index 759b176..906e871 100644
--- a/src/cmd/link/internal/loadxcoff/ldxcoff.go
+++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go
@@ -19,7 +19,7 @@
 // ldSection is an XCOFF section with its symbols.
 type ldSection struct {
 	xcoff.Section
-	sym *sym.Symbol
+	sym loader.Sym
 }
 
 // TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf
@@ -39,23 +39,10 @@
 	return n, nil
 }
 
-// Load loads xcoff files with the indexed object files.
-func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
-	lookup := func(name string, version int) *sym.Symbol {
-		return l.LookupOrCreate(name, version, syms)
-	}
-	return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
-}
-
-// LoadOld uses the old version of object loading.
-func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
-	return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
-}
-
 // loads the Xcoff file pn from f.
-// Symbols are written into syms, and a slice of the text symbols is returned.
-func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
-	errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
+// Symbols are written into loader, and a slice of the text symbols is returned.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+	errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
 		return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
 	}
 
@@ -75,34 +62,35 @@
 		lds := new(ldSection)
 		lds.Section = *sect
 		name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
-		s := lookup(name, localSymVersion)
+		symbol := l.LookupOrCreateSym(name, localSymVersion)
+		s := l.MakeSymbolUpdater(symbol)
 
 		switch lds.Type {
 		default:
 			return errorf("unrecognized section type 0x%x", lds.Type)
 		case xcoff.STYP_TEXT:
-			s.Type = sym.STEXT
+			s.SetType(sym.STEXT)
 		case xcoff.STYP_DATA:
-			s.Type = sym.SNOPTRDATA
+			s.SetType(sym.SNOPTRDATA)
 		case xcoff.STYP_BSS:
-			s.Type = sym.SNOPTRBSS
+			s.SetType(sym.SNOPTRBSS)
 		}
 
-		s.Size = int64(lds.Size)
-		if s.Type != sym.SNOPTRBSS {
+		s.SetSize(int64(lds.Size))
+		if s.Type() != sym.SNOPTRBSS {
 			data, err := lds.Section.Data()
 			if err != nil {
 				return nil, err
 			}
-			s.P = data
+			s.SetData(data)
 		}
 
-		lds.sym = s
+		lds.sym = symbol
 		ldSections = append(ldSections, lds)
 	}
 
 	// sx = symbol from file
-	// s = symbol for syms
+	// s = symbol for loader
 	for _, sx := range f.Symbols {
 		// get symbol type
 		stype, errmsg := getSymbolType(f, sx)
@@ -113,14 +101,14 @@
 			continue
 		}
 
-		s := lookup(sx.Name, 0)
+		s := l.LookupOrCreateSym(sx.Name, 0)
 
 		// Text symbol
-		if s.Type == sym.STEXT {
-			if s.Attr.OnList() {
-				return errorf("symbol %s listed multiple times", s.Name)
+		if l.SymType(s) == sym.STEXT {
+			if l.AttrOnList(s) {
+				return errorf("symbol %s listed multiple times", l.SymName(s))
 			}
-			s.Attr |= sym.AttrOnList
+			l.SetAttrOnList(s, true)
 			textp = append(textp, s)
 		}
 	}
@@ -131,11 +119,11 @@
 		if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA {
 			continue
 		}
-		rs := make([]sym.Reloc, sect.Nreloc)
+		rs := make([]loader.Reloc, sect.Nreloc)
 		for i, rx := range sect.Relocs {
 			r := &rs[i]
 
-			r.Sym = lookup(rx.Symbol.Name, 0)
+			r.Sym = l.LookupOrCreateSym(rx.Symbol.Name, 0)
 			if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
 				return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
 			}
@@ -149,27 +137,26 @@
 				if rx.Length != 64 {
 					return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length)
 				}
-				r.Siz = 8
+				r.Size = 8
 				r.Type = objabi.R_CONST
 				r.Add = int64(rx.Symbol.Value)
 
 			case xcoff.R_RBR:
-				r.Siz = 4
+				r.Size = 4
 				r.Type = objabi.R_CALLPOWER
 				r.Add = 0 //
 
 			}
 		}
-		s := sect.sym
-		s.R = rs
-		s.R = s.R[:sect.Nreloc]
+		bld := l.MakeSymbolUpdater(sect.sym)
+		bld.SetRelocs(rs[:sect.Nreloc])
 	}
 	return textp, nil
 
 }
 
 // Convert symbol xcoff type to sym.SymKind
-// Returns nil if this shouldn't be added into syms (like .file or .dw symbols )
+// Returns nil if this shouldn't be added into loader (like .file or .dw symbols )
 func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) {
 	// .file symbol
 	if s.SectionNumber == -2 {
diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go
index 16c94c1..c2cabc8 100644
--- a/src/cmd/link/internal/mips/asm.go
+++ b/src/cmd/link/internal/mips/asm.go
@@ -34,6 +34,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"fmt"
@@ -44,7 +45,7 @@
 	return
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	log.Fatalf("adddynrel not implemented")
 	return false
 }
@@ -74,7 +75,7 @@
 	return true
 }
 
-func elfsetupplt(ctxt *ld.Link) {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
 	return
 }
 
@@ -96,8 +97,8 @@
 	}
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+	if target.IsExternal() {
 		switch r.Type {
 		default:
 			return val, false
@@ -116,12 +117,12 @@
 				ld.Errorf(s, "missing section for %s", rs.Name)
 			}
 			r.Xsym = rs
-			return applyrel(ctxt.Arch, r, s, val, r.Xadd), true
+			return applyrel(target.Arch, r, s, val, r.Xadd), true
 		case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS:
 			r.Done = false
 			r.Xsym = r.Sym
 			r.Xadd = r.Add
-			return applyrel(ctxt.Arch, r, s, val, r.Add), true
+			return applyrel(target.Arch, r, s, val, r.Add), true
 		}
 	}
 
@@ -129,10 +130,10 @@
 	case objabi.R_CONST:
 		return r.Add, true
 	case objabi.R_GOTOFF:
-		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
 	case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
 		t := ld.Symaddr(r.Sym) + r.Add
-		return applyrel(ctxt.Arch, r, s, val, t), true
+		return applyrel(target.Arch, r, s, val, t), true
 	case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
 		t := ld.Symaddr(r.Sym) + r.Add
 
@@ -145,20 +146,20 @@
 			ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
 		}
 
-		return applyrel(ctxt.Arch, r, s, val, t), true
+		return applyrel(target.Arch, r, s, val, t), true
 	case objabi.R_ADDRMIPSTLS:
 		// thread pointer is at 0x7000 offset from the start of TLS data area
 		t := ld.Symaddr(r.Sym) + r.Add - 0x7000
 		if t < -32768 || t >= 32678 {
 			ld.Errorf(s, "TLS offset out of range %d", t)
 		}
-		return applyrel(ctxt.Arch, r, s, val, t), true
+		return applyrel(target.Arch, r, s, val, t), true
 	}
 
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	return -1
 }
 
diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go
index 5c6fef9..a6abec1 100644
--- a/src/cmd/link/internal/mips64/asm.go
+++ b/src/cmd/link/internal/mips64/asm.go
@@ -34,6 +34,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"fmt"
@@ -42,7 +43,7 @@
 
 func gentext(ctxt *ld.Link) {}
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	log.Fatalf("adddynrel not implemented")
 	return false
 }
@@ -91,7 +92,7 @@
 	return true
 }
 
-func elfsetupplt(ctxt *ld.Link) {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
 	return
 }
 
@@ -99,8 +100,8 @@
 	return false
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+	if target.IsExternal() {
 		switch r.Type {
 		default:
 			return val, false
@@ -136,11 +137,11 @@
 	case objabi.R_CONST:
 		return r.Add, true
 	case objabi.R_GOTOFF:
-		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
 	case objabi.R_ADDRMIPS,
 		objabi.R_ADDRMIPSU:
 		t := ld.Symaddr(r.Sym) + r.Add
-		o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+		o1 := target.Arch.ByteOrder.Uint32(s.P[r.Off:])
 		if r.Type == objabi.R_ADDRMIPS {
 			return int64(o1&0xffff0000 | uint32(t)&0xffff), true
 		}
@@ -151,20 +152,20 @@
 		if t < -32768 || t >= 32678 {
 			ld.Errorf(s, "TLS offset out of range %d", t)
 		}
-		o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+		o1 := target.Arch.ByteOrder.Uint32(s.P[r.Off:])
 		return int64(o1&0xffff0000 | uint32(t)&0xffff), true
 	case objabi.R_CALLMIPS,
 		objabi.R_JMPMIPS:
 		// Low 26 bits = (S + A) >> 2
 		t := ld.Symaddr(r.Sym) + r.Add
-		o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+		o1 := target.Arch.ByteOrder.Uint32(s.P[r.Off:])
 		return int64(o1&0xfc000000 | uint32(t>>2)&^0xfc000000), true
 	}
 
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	return -1
 }
 
diff --git a/src/cmd/link/internal/objfile/objfile.go b/src/cmd/link/internal/objfile/objfile.go
deleted file mode 100644
index a15d3c3..0000000
--- a/src/cmd/link/internal/objfile/objfile.go
+++ /dev/null
@@ -1,659 +0,0 @@
-// Copyright 2013 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.
-
-// Package objfile reads Go object files for the Go linker, cmd/link.
-//
-// This package is similar to cmd/internal/objfile which also reads
-// Go object files.
-package objfile
-
-import (
-	"bufio"
-	"bytes"
-	"cmd/internal/bio"
-	"cmd/internal/dwarf"
-	"cmd/internal/obj"
-	"cmd/internal/objabi"
-	"cmd/internal/sys"
-	"cmd/link/internal/sym"
-	"fmt"
-	"io"
-	"log"
-	"os"
-	"strconv"
-	"strings"
-	"unsafe"
-)
-
-const (
-	startmagic = "\x00go114ld"
-	endmagic   = "\xffgo114ld"
-)
-
-var emptyPkg = []byte(`"".`)
-
-// objReader reads Go object files.
-type objReader struct {
-	rd              *bio.Reader
-	arch            *sys.Arch
-	syms            *sym.Symbols
-	lib             *sym.Library
-	unit            *sym.CompilationUnit
-	pn              string
-	dupSym          *sym.Symbol
-	localSymVersion int
-	flags           int
-	strictDupMsgs   int
-	dataSize        int
-
-	// rdBuf is used by readString and readSymName as scratch for reading strings.
-	rdBuf []byte
-
-	// List of symbol references for the file being read.
-	refs        []*sym.Symbol
-	data        []byte
-	reloc       []sym.Reloc
-	pcdata      []sym.Pcdata
-	funcdata    []*sym.Symbol
-	funcdataoff []int64
-	file        []*sym.Symbol
-	pkgpref     string // objabi.PathToPrefix(r.lib.Pkg) + "."
-
-	roObject []byte // from read-only mmap of object file (may be nil)
-	roOffset int64  // offset into readonly object data examined so far
-
-	dataReadOnly bool // whether data is backed by read-only memory
-}
-
-// Flags to enable optional behavior during object loading/reading.
-
-const (
-	NoFlag int = iota
-
-	// Sanity-check duplicate symbol contents, issuing warning
-	// when duplicates have different lengths or contents.
-	StrictDupsWarnFlag
-
-	// Similar to StrictDupsWarnFlag, but issue fatal error.
-	StrictDupsErrFlag
-)
-
-// Load loads an object file f into library lib.
-// The symbols loaded are added to syms.
-func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) int {
-	start := f.Offset()
-	roObject := f.SliceRO(uint64(length))
-	if roObject != nil {
-		f.MustSeek(int64(-length), os.SEEK_CUR)
-	}
-	r := &objReader{
-		rd:              f,
-		lib:             lib,
-		unit:            unit,
-		arch:            arch,
-		syms:            syms,
-		pn:              pn,
-		dupSym:          &sym.Symbol{Name: ".dup"},
-		localSymVersion: syms.IncVersion(),
-		flags:           flags,
-		roObject:        roObject,
-		pkgpref:         objabi.PathToPrefix(lib.Pkg) + ".",
-	}
-	r.loadObjFile()
-	if roObject != nil {
-		if r.roOffset != length {
-			log.Fatalf("%s: unexpected end at %d, want %d", pn, r.roOffset, start+length)
-		}
-		r.rd.MustSeek(int64(length), os.SEEK_CUR)
-	} else if f.Offset() != start+length {
-		log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length)
-	}
-	return r.strictDupMsgs
-}
-
-func (r *objReader) loadObjFile() {
-	// Magic header
-	var buf [8]uint8
-	r.readFull(buf[:])
-	if string(buf[:]) != startmagic {
-		log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", r.pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
-	}
-
-	// Version
-	c, err := r.readByte()
-	if err != nil || c != 1 {
-		log.Fatalf("%s: invalid file version number %d", r.pn, c)
-	}
-
-	// Autolib
-	for {
-		lib := r.readString()
-		if lib == "" {
-			break
-		}
-		r.lib.ImportStrings = append(r.lib.ImportStrings, lib)
-	}
-
-	// DWARF strings
-	count := r.readInt()
-	r.unit.DWARFFileTable = make([]string, count)
-	for i := 0; i < count; i++ {
-		// TODO: This should probably be a call to mkROString.
-		r.unit.DWARFFileTable[i] = r.readString()
-	}
-
-	// Symbol references
-	r.refs = []*sym.Symbol{nil} // zeroth ref is nil
-	for {
-		c, err := r.peek(1)
-		if err != nil {
-			log.Fatalf("%s: peeking: %v", r.pn, err)
-		}
-		if c[0] == 0xff {
-			r.readByte()
-			break
-		}
-		r.readRef()
-	}
-
-	// Lengths
-	r.readSlices()
-
-	// Data section
-	err = r.readDataSection()
-	if err != nil {
-		log.Fatalf("%s: error reading %s", r.pn, err)
-	}
-
-	// Defined symbols
-	for {
-		c, err := r.peek(1)
-		if err != nil {
-			log.Fatalf("%s: peeking: %v", r.pn, err)
-		}
-		if c[0] == 0xff {
-			break
-		}
-		r.readSym()
-	}
-
-	// Magic footer
-	buf = [8]uint8{}
-	r.readFull(buf[:])
-	if string(buf[:]) != endmagic {
-		log.Fatalf("%s: invalid file end", r.pn)
-	}
-}
-
-func (r *objReader) readSlices() {
-	r.dataSize = r.readInt()
-	n := r.readInt()
-	r.reloc = make([]sym.Reloc, n)
-	n = r.readInt()
-	r.pcdata = make([]sym.Pcdata, n)
-	_ = r.readInt() // TODO: remove on next object file rev (autom count)
-	n = r.readInt()
-	r.funcdata = make([]*sym.Symbol, n)
-	r.funcdataoff = make([]int64, n)
-	n = r.readInt()
-	r.file = make([]*sym.Symbol, n)
-}
-
-func (r *objReader) readDataSection() (err error) {
-	if r.roObject != nil {
-		r.data, r.dataReadOnly, err =
-			r.roObject[r.roOffset:r.roOffset+int64(r.dataSize)], true, nil
-		r.roOffset += int64(r.dataSize)
-		return
-	}
-	r.data, r.dataReadOnly, err = r.rd.Slice(uint64(r.dataSize))
-	return
-}
-
-// Symbols are prefixed so their content doesn't get confused with the magic footer.
-const symPrefix = 0xfe
-
-func (r *objReader) readSym() {
-	var c byte
-	var err error
-	if c, err = r.readByte(); c != symPrefix || err != nil {
-		log.Fatalln("readSym out of sync")
-	}
-	if c, err = r.readByte(); err != nil {
-		log.Fatalln("error reading input: ", err)
-	}
-	t := sym.AbiSymKindToSymKind[c]
-	s := r.readSymIndex()
-	flags := r.readInt()
-	dupok := flags&1 != 0
-	local := flags&2 != 0
-	makeTypelink := flags&4 != 0
-	size := r.readInt()
-	typ := r.readSymIndex()
-	data := r.readData()
-	nreloc := r.readInt()
-	isdup := false
-
-	var dup *sym.Symbol
-	if s.Type != 0 && s.Type != sym.SXREF {
-		if (t == sym.SDATA || t == sym.SBSS || t == sym.SNOPTRBSS) && len(data) == 0 && nreloc == 0 {
-			if s.Size < int64(size) {
-				s.Size = int64(size)
-			}
-			if typ != nil && s.Gotype == nil {
-				s.Gotype = typ
-			}
-			return
-		}
-
-		if (s.Type == sym.SDATA || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 {
-			goto overwrite
-		}
-		if s.Type != sym.SBSS && s.Type != sym.SNOPTRBSS && !dupok && !s.Attr.DuplicateOK() {
-			log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, r.pn)
-		}
-		if len(s.P) > 0 {
-			dup = s
-			s = r.dupSym
-			isdup = true
-		}
-	}
-
-overwrite:
-	s.File = r.pkgpref[:len(r.pkgpref)-1]
-	s.Unit = r.unit
-	if dupok {
-		s.Attr |= sym.AttrDuplicateOK
-	}
-	if t == sym.SXREF {
-		log.Fatalf("bad sxref")
-	}
-	if t == 0 {
-		log.Fatalf("missing type for %s in %s", s.Name, r.pn)
-	}
-	if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
-		t = s.Type
-	}
-	s.Type = t
-	if s.Size < int64(size) {
-		s.Size = int64(size)
-	}
-	s.Attr.Set(sym.AttrLocal, local)
-	s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
-	if typ != nil {
-		s.Gotype = typ
-	}
-	if isdup && typ != nil { // if bss sym defined multiple times, take type from any one def
-		dup.Gotype = typ
-	}
-	s.P = data
-	s.Attr.Set(sym.AttrReadOnly, r.dataReadOnly)
-	if nreloc > 0 {
-		s.R = r.reloc[:nreloc:nreloc]
-		if !isdup {
-			r.reloc = r.reloc[nreloc:]
-		}
-
-		for i := 0; i < nreloc; i++ {
-			s.R[i] = sym.Reloc{
-				Off:  r.readInt32(),
-				Siz:  r.readUint8(),
-				Type: objabi.RelocType(r.readInt32()),
-				Add:  r.readInt64(),
-				Sym:  r.readSymIndex(),
-			}
-		}
-	}
-
-	if s.Type == sym.STEXT {
-		s.FuncInfo = new(sym.FuncInfo)
-		pc := s.FuncInfo
-
-		pc.Args = r.readInt32()
-		pc.Locals = r.readInt32()
-		if r.readUint8() != 0 {
-			s.Attr |= sym.AttrNoSplit
-		}
-		flags := r.readInt()
-		if flags&(1<<2) != 0 {
-			s.Attr |= sym.AttrReflectMethod
-		}
-		if flags&(1<<3) != 0 {
-			s.Attr |= sym.AttrShared
-		}
-		if flags&(1<<4) != 0 {
-			s.Attr |= sym.AttrTopFrame
-		}
-		n := r.readInt()
-		if n != 0 {
-			log.Fatalf("stale object file: autom count nonzero")
-		}
-
-		pc.Pcsp.P = r.readData()
-		pc.Pcfile.P = r.readData()
-		pc.Pcline.P = r.readData()
-		pc.Pcinline.P = r.readData()
-		n = r.readInt()
-		pc.Pcdata = r.pcdata[:n:n]
-		if !isdup {
-			r.pcdata = r.pcdata[n:]
-		}
-		for i := 0; i < n; i++ {
-			pc.Pcdata[i].P = r.readData()
-		}
-		n = r.readInt()
-		pc.Funcdata = r.funcdata[:n:n]
-		pc.Funcdataoff = r.funcdataoff[:n:n]
-		if !isdup {
-			r.funcdata = r.funcdata[n:]
-			r.funcdataoff = r.funcdataoff[n:]
-		}
-		for i := 0; i < n; i++ {
-			pc.Funcdata[i] = r.readSymIndex()
-		}
-		for i := 0; i < n; i++ {
-			pc.Funcdataoff[i] = r.readInt64()
-		}
-		n = r.readInt()
-		pc.File = r.file[:n:n]
-		if !isdup {
-			r.file = r.file[n:]
-		}
-		for i := 0; i < n; i++ {
-			pc.File[i] = r.readSymIndex()
-		}
-		n = r.readInt()
-		pc.InlTree = make([]sym.InlinedCall, n)
-		for i := 0; i < n; i++ {
-			pc.InlTree[i].Parent = r.readInt32()
-			pc.InlTree[i].File = r.readSymIndex()
-			pc.InlTree[i].Line = r.readInt32()
-			pc.InlTree[i].Func = r.readSymIndex().Name
-			pc.InlTree[i].ParentPC = r.readInt32()
-		}
-
-		if !dupok {
-			if s.Attr.OnList() {
-				log.Fatalf("symbol %s listed multiple times", s.Name)
-			}
-			s.Attr |= sym.AttrOnList
-			r.lib.Textp = append(r.lib.Textp, s)
-		} else {
-			// there may ba a dup in another package
-			// put into a temp list and add to text later
-			if !isdup {
-				r.lib.DupTextSyms = append(r.lib.DupTextSyms, s)
-			} else {
-				r.lib.DupTextSyms = append(r.lib.DupTextSyms, dup)
-			}
-		}
-	}
-	if s.Type == sym.SDWARFINFO {
-		r.patchDWARFName(s)
-	}
-
-	if isdup && r.flags&(StrictDupsWarnFlag|StrictDupsErrFlag) != 0 {
-		// Compare the just-read symbol with the previously read
-		// symbol of the same name, verifying that they have the same
-		// payload. If not, issue a warning and possibly an error.
-		if !bytes.Equal(s.P, dup.P) {
-			reason := "same length but different contents"
-			if len(s.P) != len(dup.P) {
-				reason = fmt.Sprintf("new length %d != old length %d",
-					len(data), len(dup.P))
-			}
-			fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.lib, dup, dup.Unit.Lib, reason)
-
-			// For the moment, whitelist DWARF subprogram DIEs for
-			// auto-generated wrapper functions. What seems to happen
-			// here is that we get different line numbers on formal
-			// params; I am guessing that the pos is being inherited
-			// from the spot where the wrapper is needed.
-			whitelist := (strings.HasPrefix(dup.Name, "go.info.go.interface") ||
-				strings.HasPrefix(dup.Name, "go.info.go.builtin") ||
-				strings.HasPrefix(dup.Name, "go.isstmt.go.builtin") ||
-				strings.HasPrefix(dup.Name, "go.debuglines"))
-			if !whitelist {
-				r.strictDupMsgs++
-			}
-		}
-	}
-}
-
-func (r *objReader) patchDWARFName(s *sym.Symbol) {
-	// This is kind of ugly. Really the package name should not
-	// even be included here.
-	if s.Size < 1 || s.P[0] != dwarf.DW_ABRV_FUNCTION {
-		return
-	}
-	e := bytes.IndexByte(s.P, 0)
-	if e == -1 {
-		return
-	}
-	p := bytes.Index(s.P[:e], emptyPkg)
-	if p == -1 {
-		return
-	}
-	pkgprefix := []byte(r.pkgpref)
-	patched := bytes.Replace(s.P[:e], emptyPkg, pkgprefix, -1)
-
-	s.P = append(patched, s.P[e:]...)
-	delta := int64(len(s.P)) - s.Size
-	s.Size = int64(len(s.P))
-	for i := range s.R {
-		r := &s.R[i]
-		if r.Off > int32(e) {
-			r.Off += int32(delta)
-		}
-	}
-}
-
-func (r *objReader) readFull(b []byte) {
-	if r.roObject != nil {
-		copy(b, r.roObject[r.roOffset:])
-		r.roOffset += int64(len(b))
-		return
-	}
-	_, err := io.ReadFull(r.rd, b)
-	if err != nil {
-		log.Fatalf("%s: error reading %s", r.pn, err)
-	}
-}
-
-func (r *objReader) readByte() (byte, error) {
-	if r.roObject != nil {
-		b := r.roObject[r.roOffset]
-		r.roOffset++
-		return b, nil
-	}
-	return r.rd.ReadByte()
-}
-
-func (r *objReader) peek(n int) ([]byte, error) {
-	if r.roObject != nil {
-		return r.roObject[r.roOffset : r.roOffset+int64(n)], nil
-	}
-	return r.rd.Peek(n)
-}
-
-func (r *objReader) readRef() {
-	if c, err := r.readByte(); c != symPrefix || err != nil {
-		log.Fatalf("readSym out of sync")
-	}
-	name := r.readSymName()
-	var v int
-	if abi := r.readInt(); abi == -1 {
-		// Static
-		v = r.localSymVersion
-	} else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
-		// Note that data symbols are "ABI0", which maps to version 0.
-		v = abiver
-	} else {
-		log.Fatalf("invalid symbol ABI for %q: %d", name, abi)
-	}
-	s := r.syms.Lookup(name, v)
-	r.refs = append(r.refs, s)
-
-	if s == nil || v == r.localSymVersion {
-		return
-	}
-	if s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
-		x, err := strconv.ParseUint(s.Name[5:], 16, 64)
-		if err != nil {
-			log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
-		}
-		s.Type = sym.SRODATA
-		s.Attr |= sym.AttrLocal
-		switch s.Name[:5] {
-		case "$f32.":
-			if uint64(uint32(x)) != x {
-				log.Panicf("$-symbol %s too large: %d", s.Name, x)
-			}
-			s.AddUint32(r.arch, uint32(x))
-		case "$f64.", "$i64.":
-			s.AddUint64(r.arch, x)
-		default:
-			log.Panicf("unrecognized $-symbol: %s", s.Name)
-		}
-		s.Attr.Set(sym.AttrReachable, false)
-	}
-	if strings.HasPrefix(s.Name, "runtime.gcbits.") {
-		s.Attr |= sym.AttrLocal
-	}
-}
-
-func (r *objReader) readInt64() int64 {
-	uv := uint64(0)
-	for shift := uint(0); ; shift += 7 {
-		if shift >= 64 {
-			log.Fatalf("corrupt input")
-		}
-		c, err := r.readByte()
-		if err != nil {
-			log.Fatalln("error reading input: ", err)
-		}
-		uv |= uint64(c&0x7F) << shift
-		if c&0x80 == 0 {
-			break
-		}
-	}
-
-	return int64(uv>>1) ^ (int64(uv<<63) >> 63)
-}
-
-func (r *objReader) readInt() int {
-	n := r.readInt64()
-	if int64(int(n)) != n {
-		log.Panicf("%v out of range for int", n)
-	}
-	return int(n)
-}
-
-func (r *objReader) readInt32() int32 {
-	n := r.readInt64()
-	if int64(int32(n)) != n {
-		log.Panicf("%v out of range for int32", n)
-	}
-	return int32(n)
-}
-
-func (r *objReader) readInt16() int16 {
-	n := r.readInt64()
-	if int64(int16(n)) != n {
-		log.Panicf("%v out of range for int16", n)
-	}
-	return int16(n)
-}
-
-func (r *objReader) readUint8() uint8 {
-	n := r.readInt64()
-	if int64(uint8(n)) != n {
-		log.Panicf("%v out of range for uint8", n)
-	}
-	return uint8(n)
-}
-
-func (r *objReader) readString() string {
-	n := r.readInt()
-	if cap(r.rdBuf) < n {
-		r.rdBuf = make([]byte, 2*n)
-	}
-	r.readFull(r.rdBuf[:n])
-	return string(r.rdBuf[:n])
-}
-
-func (r *objReader) readData() []byte {
-	n := r.readInt()
-	p := r.data[:n:n]
-	r.data = r.data[n:]
-	return p
-}
-
-type stringHeader struct {
-	str unsafe.Pointer
-	len int
-}
-
-func mkROString(rodata []byte) string {
-	if len(rodata) == 0 {
-		return ""
-	}
-	ss := stringHeader{str: unsafe.Pointer(&rodata[0]), len: len(rodata)}
-	s := *(*string)(unsafe.Pointer(&ss))
-	return s
-}
-
-// readSymName reads a symbol name, replacing all "". with pkg.
-func (r *objReader) readSymName() string {
-	n := r.readInt()
-	if n == 0 {
-		r.readInt64()
-		return ""
-	}
-	if cap(r.rdBuf) < n {
-		r.rdBuf = make([]byte, 2*n)
-	}
-	sOffset := r.roOffset
-	origName, err := r.peek(n)
-	if err == bufio.ErrBufferFull {
-		// Long symbol names are rare but exist. One source is type
-		// symbols for types with long string forms. See #15104.
-		origName = make([]byte, n)
-		r.readFull(origName)
-	} else if err != nil {
-		log.Fatalf("%s: error reading symbol: %v", r.pn, err)
-	}
-	adjName := r.rdBuf[:0]
-	nPkgRefs := 0
-	for {
-		i := bytes.Index(origName, emptyPkg)
-		if i == -1 {
-			var s string
-			if r.roObject != nil && nPkgRefs == 0 {
-				s = mkROString(r.roObject[sOffset : sOffset+int64(n)])
-			} else {
-				s = string(append(adjName, origName...))
-			}
-			// Read past the peeked origName, now that we're done with it,
-			// using the rfBuf (also no longer used) as the scratch space.
-			// TODO: use bufio.Reader.Discard if available instead?
-			if err == nil {
-				r.readFull(r.rdBuf[:n])
-			}
-			r.rdBuf = adjName[:0] // in case 2*n wasn't enough
-			return s
-		}
-		nPkgRefs++
-		adjName = append(adjName, origName[:i]...)
-		adjName = append(adjName, r.pkgpref[:len(r.pkgpref)-1]...)
-		adjName = append(adjName, '.')
-		origName = origName[i+len(emptyPkg):]
-	}
-}
-
-// Reads the index of a symbol reference and resolves it to a symbol
-func (r *objReader) readSymIndex() *sym.Symbol {
-	i := r.readInt()
-	return r.refs[i]
-}
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index 9fbcff5..d0993e3 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -34,6 +34,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"encoding/binary"
@@ -262,11 +263,11 @@
 	stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	if ctxt.IsELF {
 		return addelfdynrel(ctxt, s, r)
 	} else if ctxt.HeadType == objabi.Haix {
-		return ld.Xcoffadddynrel(ctxt, s, r)
+		return ld.Xcoffadddynrel(&ctxt.Target, s, r)
 	}
 	return false
 }
@@ -313,7 +314,7 @@
 		r.Type = objabi.R_ADDR
 		if targ.Type == sym.SDYNIMPORT {
 			// These happen in .toc sections
-			ld.Adddynsym(ctxt, targ)
+			ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, targ)
 
 			rela := ctxt.Syms.Lookup(".rela", 0)
 			rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
@@ -498,14 +499,13 @@
 	return true
 }
 
-func elfsetupplt(ctxt *ld.Link) {
-	plt := ctxt.Syms.Lookup(".plt", 0)
-	if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+	if plt.Size() == 0 {
 		// The dynamic linker stores the address of the
 		// dynamic resolver and the DSO identifier in the two
 		// doublewords at the beginning of the .plt section
 		// before the PLT array. Reserve space for these.
-		plt.Size = 16
+		plt.SetSize(16)
 	}
 }
 
@@ -513,31 +513,13 @@
 	return false
 }
 
-// Return the value of .TOC. for symbol s
-func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 {
-	var toc *sym.Symbol
-
-	if s.Outer != nil {
-		toc = ctxt.Syms.ROLookup(".TOC.", int(s.Outer.Version))
-	} else {
-		toc = ctxt.Syms.ROLookup(".TOC.", int(s.Version))
-	}
-
-	if toc == nil {
-		ld.Errorf(s, "TOC-relative relocation in object without .TOC.")
-		return 0
-	}
-
-	return toc.Value
-}
-
 // archreloctoc relocates a TOC relative symbol.
 // If the symbol pointed by this TOC relative symbol is in .data or .bss, the
 // default load instruction can be changed to an addi instruction and the
 // symbol address can be used directly.
 // This code is for AIX only.
-func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
-	if ctxt.HeadType == objabi.Hlinux {
+func archreloctoc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
+	if target.IsLinux() {
 		ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name)
 	}
 	var o1, o2 uint32
@@ -555,13 +537,13 @@
 		ld.Errorf(s, "archreloctoc called for a symbol without TOC anchor")
 	}
 
-	if ctxt.LinkMode == ld.LinkInternal && tarSym != nil && tarSym.Attr.Reachable() && (tarSym.Sect.Seg == &ld.Segdata) {
-		t = ld.Symaddr(tarSym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value
+	if target.IsInternal() && tarSym != nil && tarSym.Attr.Reachable() && (tarSym.Sect.Seg == &ld.Segdata) {
+		t = ld.Symaddr(tarSym) + r.Add - syms.TOC.Value
 		// change ld to addi in the second instruction
 		o2 = (o2 & 0x03FF0000) | 0xE<<26
 		useAddi = true
 	} else {
-		t = ld.Symaddr(r.Sym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value
+		t = ld.Symaddr(r.Sym) + r.Add - syms.TOC.Value
 	}
 
 	if t != int64(int32(t)) {
@@ -593,12 +575,12 @@
 
 // archrelocaddr relocates a symbol address.
 // This code is for AIX only.
-func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
-	if ctxt.HeadType == objabi.Haix {
+func archrelocaddr(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
+	if target.IsAIX() {
 		ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name)
 	}
 	var o1, o2 uint32
-	if ctxt.Arch.ByteOrder == binary.BigEndian {
+	if target.IsBigEndian() {
 		o1 = uint32(val >> 32)
 		o2 = uint32(val)
 	} else {
@@ -635,7 +617,7 @@
 		return -1
 	}
 
-	if ctxt.Arch.ByteOrder == binary.BigEndian {
+	if target.IsBigEndian() {
 		return int64(o1)<<32 | int64(o2)
 	}
 	return int64(o2)<<32 | int64(o1)
@@ -770,13 +752,13 @@
 	ctxt.Arch.ByteOrder.PutUint32(tramp.P[12:], o4)
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+	if target.IsExternal() {
 		// On AIX, relocations (except TLS ones) must be also done to the
 		// value with the current addresses.
 		switch r.Type {
 		default:
-			if ctxt.HeadType != objabi.Haix {
+			if target.IsAIX() {
 				return val, false
 			}
 		case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE:
@@ -806,14 +788,14 @@
 			}
 			r.Xsym = rs
 
-			if ctxt.HeadType != objabi.Haix {
+			if !target.IsAIX() {
 				return val, true
 			}
 		case objabi.R_CALLPOWER:
 			r.Done = false
 			r.Xsym = r.Sym
 			r.Xadd = r.Add
-			if ctxt.HeadType != objabi.Haix {
+			if !target.IsAIX() {
 				return val, true
 			}
 		}
@@ -823,11 +805,11 @@
 	case objabi.R_CONST:
 		return r.Add, true
 	case objabi.R_GOTOFF:
-		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
 	case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS:
-		return archreloctoc(ctxt, r, s, val), true
+		return archreloctoc(target, syms, r, s, val), true
 	case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS:
-		return archrelocaddr(ctxt, r, s, val), true
+		return archrelocaddr(target, syms, r, s, val), true
 	case objabi.R_CALLPOWER:
 		// Bits 6 through 29 = (S + A - P) >> 2
 
@@ -843,7 +825,7 @@
 		}
 		return val | int64(uint32(t)&^0xfc000003), true
 	case objabi.R_POWER_TOC: // S + A - .TOC.
-		return ld.Symaddr(r.Sym) + r.Add - symtoc(ctxt, s), true
+		return ld.Symaddr(r.Sym) + r.Add - syms.DotTOC.Value, true
 
 	case objabi.R_POWER_TLS_LE:
 		// The thread pointer points 0x7000 bytes after the start of the
@@ -851,7 +833,7 @@
 		// Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI
 		// Specification".
 		v := r.Sym.Value - 0x7000
-		if ctxt.HeadType == objabi.Haix {
+		if target.IsAIX() {
 			// On AIX, the thread pointer points 0x7800 bytes after
 			// the TLS.
 			v -= 0x800
@@ -865,7 +847,7 @@
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	switch r.Variant & sym.RV_TYPE_MASK {
 	default:
 		ld.Errorf(s, "unexpected relocation variant %d", r.Variant)
@@ -879,7 +861,7 @@
 			// Whether to check for signed or unsigned
 			// overflow depends on the instruction
 			var o1 uint32
-			if ctxt.Arch.ByteOrder == binary.BigEndian {
+			if target.IsBigEndian() {
 				o1 = binary.BigEndian.Uint32(s.P[r.Off-2:])
 			} else {
 				o1 = binary.LittleEndian.Uint32(s.P[r.Off:])
@@ -913,7 +895,7 @@
 			// Whether to check for signed or unsigned
 			// overflow depends on the instruction
 			var o1 uint32
-			if ctxt.Arch.ByteOrder == binary.BigEndian {
+			if target.IsBigEndian() {
 				o1 = binary.BigEndian.Uint32(s.P[r.Off-2:])
 			} else {
 				o1 = binary.LittleEndian.Uint32(s.P[r.Off:])
@@ -937,7 +919,7 @@
 
 	case sym.RV_POWER_DS:
 		var o1 uint32
-		if ctxt.Arch.ByteOrder == binary.BigEndian {
+		if target.IsBigEndian() {
 			o1 = uint32(binary.BigEndian.Uint16(s.P[r.Off:]))
 		} else {
 			o1 = uint32(binary.LittleEndian.Uint16(s.P[r.Off:]))
@@ -961,13 +943,13 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 
 	if ctxt.IsELF {
 		plt := ctxt.Syms.Lookup(".plt", 0)
 		rela := ctxt.Syms.Lookup(".rela.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt(ctxt)
+			panic("plt is not set up")
 		}
 
 		// Create the glink resolver if necessary
@@ -1056,7 +1038,7 @@
 	// before the first symbol resolver stub.
 	s := ctxt.Syms.Lookup(".dynamic", 0)
 
-	ld.Elfwritedynentsymplus(ctxt, s, ld.DT_PPC64_GLINK, glink, glink.Size-32)
+	ld.Elfwritedynentsymplus(ctxt.Arch, s, ld.DT_PPC64_GLINK, glink, glink.Size-32)
 
 	return glink
 }
diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go
index b089728..bd8380b 100644
--- a/src/cmd/link/internal/riscv64/asm.go
+++ b/src/cmd/link/internal/riscv64/asm.go
@@ -9,6 +9,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"fmt"
 	"log"
@@ -17,11 +18,11 @@
 func gentext(ctxt *ld.Link) {
 }
 
-func adddynrela(ctxt *ld.Link, rel *sym.Symbol, s *sym.Symbol, r *sym.Reloc) {
+func adddynrela(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, rel *sym.Symbol, s *sym.Symbol, r *sym.Reloc) {
 	log.Fatalf("adddynrela not implemented")
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	log.Fatalf("adddynrel not implemented")
 	return false
 }
@@ -31,7 +32,7 @@
 	return false
 }
 
-func elfsetupplt(ctxt *ld.Link) {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
 	log.Fatalf("elfsetuplt")
 }
 
@@ -40,7 +41,7 @@
 	return false
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
 	switch r.Type {
 	case objabi.R_CALLRISCV:
 		// Nothing to do.
@@ -91,7 +92,7 @@
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	log.Fatalf("archrelocvariant")
 	return -1
 }
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
index 94a5a2f..57437f2 100644
--- a/src/cmd/link/internal/s390x/asm.go
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -34,6 +34,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"fmt"
@@ -104,7 +105,7 @@
 	initarray_entry.AddAddr(ctxt.Arch, initfunc)
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	targ := r.Sym
 	r.InitExt()
 
@@ -333,10 +334,8 @@
 	return true
 }
 
-func elfsetupplt(ctxt *ld.Link) {
-	plt := ctxt.Syms.Lookup(".plt", 0)
-	got := ctxt.Syms.Lookup(".got", 0)
-	if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+	if plt.Size() == 0 {
 		// stg     %r1,56(%r15)
 		plt.AddUint8(0xe3)
 		plt.AddUint8(0x10)
@@ -347,7 +346,7 @@
 		// larl    %r1,_GLOBAL_OFFSET_TABLE_
 		plt.AddUint8(0xc0)
 		plt.AddUint8(0x10)
-		plt.AddPCRelPlus(ctxt.Arch, got, 6)
+		plt.AddSymRef(ctxt.Arch, got.Sym(), 6, objabi.R_PCRELDBL, 4)
 		// mvc     48(8,%r15),8(%r1)
 		plt.AddUint8(0xd2)
 		plt.AddUint8(0x07)
@@ -376,7 +375,7 @@
 		plt.AddUint8(0x00)
 
 		// assume got->size == 0 too
-		got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+		got.AddAddrPlus(ctxt.Arch, dynamic, 0)
 
 		got.AddUint64(ctxt.Arch, 0)
 		got.AddUint64(ctxt.Arch, 0)
@@ -387,8 +386,8 @@
 	return false
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+	if target.IsExternal() {
 		return val, false
 	}
 
@@ -396,13 +395,13 @@
 	case objabi.R_CONST:
 		return r.Add, true
 	case objabi.R_GOTOFF:
-		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
 	}
 
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	switch r.Variant & sym.RV_TYPE_MASK {
 	default:
 		ld.Errorf(s, "unexpected relocation variant %d", r.Variant)
@@ -424,14 +423,14 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 
 	if ctxt.IsELF {
 		plt := ctxt.Syms.Lookup(".plt", 0)
 		got := ctxt.Syms.Lookup(".got", 0)
 		rela := ctxt.Syms.Lookup(".rela.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt(ctxt)
+			panic("plt is not set up")
 		}
 		// larl    %r1,_GLOBAL_OFFSET_TABLE_+index
 
@@ -487,7 +486,7 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 	got := ctxt.Syms.Lookup(".got", 0)
 	s.SetGot(int32(got.Size))
 	got.AddUint64(ctxt.Arch, 0)
diff --git a/src/cmd/link/internal/sym/compilation_unit.go b/src/cmd/link/internal/sym/compilation_unit.go
index 02fb0cf..b8b6845 100644
--- a/src/cmd/link/internal/sym/compilation_unit.go
+++ b/src/cmd/link/internal/sym/compilation_unit.go
@@ -6,6 +6,10 @@
 
 import "cmd/internal/dwarf"
 
+// LoaderSym holds a loader.Sym value. We can't refer to this
+// type from the sym package since loader imports sym.
+type LoaderSym int
+
 // CompilationUnit is an abstraction used by DWARF to represent a chunk of
 // debug-related data. We create a CompilationUnit per Object file in a
 // library (so, one for all the Go code, one for each assembly file, etc.).
@@ -20,4 +24,10 @@
 	RangeSyms      []*Symbol     // Symbols for debug_range
 	Textp          []*Symbol     // Text symbols in this CU
 	DWARFFileTable []string      // The file table used to generate the .debug_lines
+
+	Consts2    LoaderSym   // Package constants DIEs (loader)
+	FuncDIEs2  []LoaderSym // Function DIE subtrees (loader)
+	AbsFnDIEs2 []LoaderSym // Abstract function DIE subtrees (loader)
+	RangeSyms2 []LoaderSym // Symbols for debug_range (loader)
+	Textp2     []LoaderSym // Text symbols in this CU (loader)
 }
diff --git a/src/cmd/link/internal/sym/library.go b/src/cmd/link/internal/sym/library.go
index 4f2023b..21568df 100644
--- a/src/cmd/link/internal/sym/library.go
+++ b/src/cmd/link/internal/sym/library.go
@@ -18,6 +18,9 @@
 	Main          bool
 	Safe          bool
 	Units         []*CompilationUnit
+
+	Textp2       []LoaderSym // text syms defined in this library
+	DupTextSyms2 []LoaderSym // dupok text syms defined in this library
 }
 
 func (l Library) String() string {
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
index e9819a0..1fee966 100644
--- a/src/cmd/link/internal/sym/symbol.go
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -103,6 +103,10 @@
 	return s.Size
 }
 
+func (s *Symbol) Length(dwarfContext interface{}) int64 {
+	return s.Size
+}
+
 func (s *Symbol) Grow(siz int64) {
 	if int64(int(siz)) != siz {
 		log.Fatalf("symgrow size %d too long", siz)
diff --git a/src/cmd/link/internal/sym/symbols.go b/src/cmd/link/internal/sym/symbols.go
index e772496..e64779d 100644
--- a/src/cmd/link/internal/sym/symbols.go
+++ b/src/cmd/link/internal/sym/symbols.go
@@ -31,105 +31,34 @@
 package sym
 
 type Symbols struct {
-	symbolBatch []Symbol
-
 	// Symbol lookup based on name and indexed by version.
-	hash []map[string]*Symbol
+	versions int
 
 	Allsym []*Symbol
+
+	// Provided by the loader
+
+	// Look up the symbol with the given name and version, creating the
+	// symbol if it is not found.
+	Lookup func(name string, v int) *Symbol
+
+	// Look up the symbol with the given name and version, returning nil
+	// if it is not found.
+	ROLookup func(name string, v int) *Symbol
+
+	// Rename renames a symbol.
+	Rename func(old, new string, v int)
 }
 
 func NewSymbols() *Symbols {
-	hash := make([]map[string]*Symbol, SymVerStatic)
-	// Preallocate about 2mb for hash of non static symbols
-	hash[0] = make(map[string]*Symbol, 100000)
-	// And another 1mb for internal ABI text symbols.
-	hash[SymVerABIInternal] = make(map[string]*Symbol, 50000)
 	return &Symbols{
-		hash:   hash,
-		Allsym: make([]*Symbol, 0, 100000),
+		versions: SymVerStatic,
+		Allsym:   make([]*Symbol, 0, 100000),
 	}
 }
 
-func (syms *Symbols) Newsym(name string, v int) *Symbol {
-	batch := syms.symbolBatch
-	if len(batch) == 0 {
-		batch = make([]Symbol, 1000)
-	}
-	s := &batch[0]
-	syms.symbolBatch = batch[1:]
-
-	s.Dynid = -1
-	s.Name = name
-	s.Version = int16(v)
-	syms.Allsym = append(syms.Allsym, s)
-
-	return s
-}
-
-// Look up the symbol with the given name and version, creating the
-// symbol if it is not found.
-func (syms *Symbols) Lookup(name string, v int) *Symbol {
-	m := syms.hash[v]
-	s := m[name]
-	if s != nil {
-		return s
-	}
-	s = syms.Newsym(name, v)
-	m[name] = s
-	return s
-}
-
-// Look up the symbol with the given name and version, returning nil
-// if it is not found.
-func (syms *Symbols) ROLookup(name string, v int) *Symbol {
-	return syms.hash[v][name]
-}
-
-// Add an existing symbol to the symbol table.
-func (syms *Symbols) Add(s *Symbol) {
-	name := s.Name
-	v := int(s.Version)
-	m := syms.hash[v]
-	if _, ok := m[name]; ok {
-		panic(name + " already added")
-	}
-	m[name] = s
-}
-
 // Allocate a new version (i.e. symbol namespace).
 func (syms *Symbols) IncVersion() int {
-	syms.hash = append(syms.hash, make(map[string]*Symbol))
-	return len(syms.hash) - 1
-}
-
-// Rename renames a symbol.
-func (syms *Symbols) Rename(old, new string, v int, reachparent map[*Symbol]*Symbol) {
-	s := syms.hash[v][old]
-	oldExtName := s.Extname()
-	s.Name = new
-	if oldExtName == old {
-		s.SetExtname(new)
-	}
-	delete(syms.hash[v], old)
-
-	dup := syms.hash[v][new]
-	if dup == nil {
-		syms.hash[v][new] = s
-	} else {
-		if s.Type == 0 {
-			dup.Attr |= s.Attr
-			if s.Attr.Reachable() && reachparent != nil {
-				reachparent[dup] = reachparent[s]
-			}
-			*s = *dup
-		} else if dup.Type == 0 {
-			s.Attr |= dup.Attr
-			if dup.Attr.Reachable() && reachparent != nil {
-				reachparent[s] = reachparent[dup]
-			}
-			*dup = *s
-			syms.hash[v][new] = s
-		}
-	}
+	syms.versions++
+	return syms.versions - 1
 }
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
index 3fe36db..1e407d0 100644
--- a/src/cmd/link/internal/x86/asm.go
+++ b/src/cmd/link/internal/x86/asm.go
@@ -34,6 +34,7 @@
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
+	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"log"
@@ -167,7 +168,7 @@
 	initarray_entry.AddAddr(ctxt.Arch, initfunc)
 }
 
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(ctxt *ld.Link, target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
 	targ := r.Sym
 
 	switch r.Type {
@@ -312,7 +313,7 @@
 			break
 		}
 		if ctxt.IsELF {
-			ld.Adddynsym(ctxt, targ)
+			ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, targ)
 			rel := ctxt.Syms.Lookup(".rel", 0)
 			rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
 			rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_386_32)))
@@ -332,7 +333,7 @@
 			// just in case the C code assigns to the variable,
 			// and of course it only works for single pointers,
 			// but we only need to support cgo and that's all it needs.
-			ld.Adddynsym(ctxt, targ)
+			ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, targ)
 
 			got := ctxt.Syms.Lookup(".got", 0)
 			s.Type = got.Type
@@ -492,46 +493,44 @@
 	return true
 }
 
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+	if target.IsExternal() {
 		return val, false
 	}
 	switch r.Type {
 	case objabi.R_CONST:
 		return r.Add, true
 	case objabi.R_GOTOFF:
-		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+		return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
 	}
 
 	return val, false
 }
 
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return t
 }
 
-func elfsetupplt(ctxt *ld.Link) {
-	plt := ctxt.Syms.Lookup(".plt", 0)
-	got := ctxt.Syms.Lookup(".got.plt", 0)
-	if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+	if plt.Size() == 0 {
 		// pushl got+4
 		plt.AddUint8(0xff)
 
 		plt.AddUint8(0x35)
-		plt.AddAddrPlus(ctxt.Arch, got, 4)
+		plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4)
 
 		// jmp *got+8
 		plt.AddUint8(0xff)
 
 		plt.AddUint8(0x25)
-		plt.AddAddrPlus(ctxt.Arch, got, 8)
+		plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8)
 
 		// zero pad
 		plt.AddUint32(ctxt.Arch, 0)
 
 		// assume got->size == 0 too
-		got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+		got.AddAddrPlus(ctxt.Arch, dynamic, 0)
 
 		got.AddUint32(ctxt.Arch, 0)
 		got.AddUint32(ctxt.Arch, 0)
@@ -543,14 +542,14 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 
 	if ctxt.IsELF {
 		plt := ctxt.Syms.Lookup(".plt", 0)
 		got := ctxt.Syms.Lookup(".got.plt", 0)
 		rel := ctxt.Syms.Lookup(".rel.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt(ctxt)
+			panic("plt is not set up")
 		}
 
 		// jmpq *got+size
@@ -603,7 +602,7 @@
 		return
 	}
 
-	ld.Adddynsym(ctxt, s)
+	ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
 	got := ctxt.Syms.Lookup(".got", 0)
 	s.SetGot(int32(got.Size))
 	got.AddUint32(ctxt.Arch, 0)