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

Clean merge.

Change-Id: If9bfb0f27f41563fd5d386de9c1081542c3ce498
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index 5fe3fd9..fad87b2 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -23,6 +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")
 )
 
 var (
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index fc6acc7..6b0a609 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -40,18 +40,18 @@
 	}
 	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()
 
 	architecture.Init(ctxt)
 
 	// Create object file, write header.
-	out, err := os.Create(*flags.OutputFile)
+	buf, err := bio.Create(*flags.OutputFile)
 	if err != nil {
 		log.Fatal(err)
 	}
-	defer bio.MustClose(out)
-	buf := bufio.NewWriter(bio.MustWriter(out))
+	defer buf.Close()
 
 	if !*flags.SymABIs {
 		fmt.Fprintf(buf, "go object %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version)
@@ -83,6 +83,7 @@
 		}
 	}
 	if ok && !*flags.SymABIs {
+		ctxt.NumberSyms(true)
 		obj.WriteObjFile(ctxt, buf, "")
 	}
 	if !ok || diag {
@@ -91,9 +92,8 @@
 		} else {
 			log.Print("assembly failed")
 		}
-		out.Close()
+		buf.Close()
 		os.Remove(*flags.OutputFile)
 		os.Exit(1)
 	}
-	buf.Flush()
 }
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index 54b87ab..9125f67 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -203,6 +203,7 @@
 	"bufio"
 	"bytes"
 	"cmd/compile/internal/types"
+	"cmd/internal/goobj2"
 	"cmd/internal/src"
 	"encoding/binary"
 	"fmt"
@@ -945,10 +946,12 @@
 
 func (w *exportWriter) varExt(n *Node) {
 	w.linkname(n.Sym)
+	w.symIdx(n.Sym)
 }
 
 func (w *exportWriter) funcExt(n *Node) {
 	w.linkname(n.Sym)
+	w.symIdx(n.Sym)
 
 	// Escape analysis.
 	for _, fs := range types.RecvsParams {
@@ -987,6 +990,17 @@
 	w.string(s.Linkname)
 }
 
+func (w *exportWriter) symIdx(s *types.Sym) {
+	if Ctxt.Flag_newobj {
+		lsym := s.Linksym()
+		if lsym.PkgIdx > goobj2.PkgIdxSelf || lsym.PkgIdx == goobj2.PkgIdxInvalid || s.Linkname != "" {
+			w.int64(-1)
+		} else {
+			w.int64(int64(lsym.SymIdx))
+		}
+	}
+}
+
 // Inline bodies.
 
 func (w *exportWriter) stmtList(list Nodes) {
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index 64c554d..824648a 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -10,6 +10,7 @@
 import (
 	"cmd/compile/internal/types"
 	"cmd/internal/bio"
+	"cmd/internal/obj"
 	"cmd/internal/src"
 	"encoding/binary"
 	"fmt"
@@ -651,10 +652,12 @@
 
 func (r *importReader) varExt(n *Node) {
 	r.linkname(n.Sym)
+	r.symIdx(n.Sym)
 }
 
 func (r *importReader) funcExt(n *Node) {
 	r.linkname(n.Sym)
+	r.symIdx(n.Sym)
 
 	// Escape analysis.
 	for _, fs := range types.RecvsParams {
@@ -683,6 +686,20 @@
 	s.Linkname = r.string()
 }
 
+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)
+		}
+	}
+}
+
 func (r *importReader) doInline(n *Node) {
 	if len(n.Func.Inl.Body) != 0 {
 		Fatalf("%v already has inline body", n)
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 05aac9e..9e8abbc 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -263,12 +263,14 @@
 	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")
+
 	objabi.Flagparse(usage)
 
 	// 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")
+	recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "newobj")
 
 	if smallFrames {
 		maxStackVarSize = 128 * 1024
@@ -722,6 +724,8 @@
 
 	// Write object data to disk.
 	timings.Start("be", "dumpobj")
+	dumpdata()
+	Ctxt.NumberSyms(false)
 	dumpobj()
 	if asmhdr != "" {
 		dumpasmhdr()
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index be13b27..ae0fc1d 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -111,21 +111,7 @@
 	dumpexport(bout)
 }
 
-func dumpLinkerObj(bout *bio.Writer) {
-	printObjHeader(bout)
-
-	if len(pragcgobuf) != 0 {
-		// write empty export section; must be before cgo section
-		fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
-		fmt.Fprintf(bout, "\n$$  // cgo\n")
-		if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
-			Fatalf("serializing pragcgobuf: %v", err)
-		}
-		fmt.Fprintf(bout, "\n$$\n\n")
-	}
-
-	fmt.Fprintf(bout, "\n!\n")
-
+func dumpdata() {
 	externs := len(externdcl)
 
 	dumpglobls()
@@ -163,8 +149,24 @@
 	}
 
 	addGCLocals()
+}
 
-	obj.WriteObjFile(Ctxt, bout.Writer, myimportpath)
+func dumpLinkerObj(bout *bio.Writer) {
+	printObjHeader(bout)
+
+	if len(pragcgobuf) != 0 {
+		// write empty export section; must be before cgo section
+		fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
+		fmt.Fprintf(bout, "\n$$  // cgo\n")
+		if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
+			Fatalf("serializing pragcgobuf: %v", err)
+		}
+		fmt.Fprintf(bout, "\n$$\n\n")
+	}
+
+	fmt.Fprintf(bout, "\n!\n")
+
+	obj.WriteObjFile(Ctxt, bout, myimportpath)
 }
 
 func addptabs() {
diff --git a/src/cmd/compile/internal/types/sym.go b/src/cmd/compile/internal/types/sym.go
index c9dd9f3..07bce4d 100644
--- a/src/cmd/compile/internal/types/sym.go
+++ b/src/cmd/compile/internal/types/sym.go
@@ -80,11 +80,18 @@
 	if sym == nil {
 		return nil
 	}
+	initPkg := func(r *obj.LSym) {
+		if sym.Linkname != "" {
+			r.Pkg = "_"
+		} else {
+			r.Pkg = sym.Pkg.Prefix
+		}
+	}
 	if sym.Func() {
 		// This is a function symbol. Mark it as "internal ABI".
-		return Ctxt.LookupABI(sym.LinksymName(), obj.ABIInternal)
+		return Ctxt.LookupABIInit(sym.LinksymName(), obj.ABIInternal, initPkg)
 	}
-	return Ctxt.Lookup(sym.LinksymName())
+	return Ctxt.LookupInit(sym.LinksymName(), initPkg)
 }
 
 // Less reports whether symbol a is ordered before symbol b.
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index f27ea17..e85dd9a 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -54,6 +54,7 @@
 	"cmd/internal/gcprog",
 	"cmd/internal/dwarf",
 	"cmd/internal/edit",
+	"cmd/internal/goobj2",
 	"cmd/internal/objabi",
 	"cmd/internal/obj",
 	"cmd/internal/obj/arm",
diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go
index 0c70b8c..2a3afff 100644
--- a/src/cmd/internal/goobj/read.go
+++ b/src/cmd/internal/goobj/read.go
@@ -503,6 +503,11 @@
 	// TODO: extract OS + build ID if/when we need it
 
 	r.readFull(r.tmp[:8])
+	if bytes.Equal(r.tmp[:8], []byte("\x00go114LD")) {
+		r.offset -= 8
+		r.readNew()
+		return nil
+	}
 	if !bytes.Equal(r.tmp[:8], []byte("\x00go114ld")) {
 		return r.error(errCorruptObject)
 	}
diff --git a/src/cmd/internal/goobj/readnew.go b/src/cmd/internal/goobj/readnew.go
new file mode 100644
index 0000000..b4b8469
--- /dev/null
+++ b/src/cmd/internal/goobj/readnew.go
@@ -0,0 +1,168 @@
+// 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 goobj
+
+import (
+	"cmd/internal/goobj2"
+	"cmd/internal/objabi"
+	"fmt"
+	"strings"
+)
+
+// Read object file in new format. For now we still fill
+// the data to the current goobj API.
+func (r *objReader) readNew() {
+	start := uint32(r.offset)
+	rr := goobj2.NewReader(r.f, start)
+	if rr == nil {
+		panic("cannot read object file")
+	}
+
+	// Imports
+	pkglist := rr.Pkglist()
+	r.p.Imports = pkglist[1:] // index 0 is a dummy invalid package
+
+	abiToVer := func(abi uint16) int64 {
+		var vers int64
+		if abi == goobj2.SymABIstatic {
+			// Static symbol
+			vers = r.p.MaxVersion
+		}
+		return vers
+	}
+
+	resolveSymRef := func(s goobj2.SymRef) SymID {
+		var i int
+		switch p := s.PkgIdx; p {
+		case goobj2.PkgIdxInvalid:
+			if s.SymIdx != 0 {
+				panic("bad sym ref")
+			}
+			return SymID{}
+		case goobj2.PkgIdxNone:
+			i = int(s.SymIdx) + rr.NSym()
+		case goobj2.PkgIdxBuiltin:
+			panic("PkgIdxBuiltin is unused")
+		case goobj2.PkgIdxSelf:
+			i = int(s.SymIdx)
+		default:
+			pkg := pkglist[p]
+			return SymID{fmt.Sprintf("%s.<#%d>", pkg, s.SymIdx), 0}
+		}
+		sym := goobj2.Sym{}
+		sym.Read(rr, rr.SymOff(i))
+		return SymID{sym.Name, abiToVer(sym.ABI)}
+	}
+
+	// Read things for the current goobj API for now.
+
+	// Symbols
+	pcdataBase := start + rr.PcdataBase()
+	n := rr.NSym() + rr.NNonpkgdef() + rr.NNonpkgref()
+	ndef := rr.NSym() + rr.NNonpkgdef()
+	for i := 0; i < n; i++ {
+		osym := goobj2.Sym{}
+		osym.Read(rr, rr.SymOff(i))
+		if osym.Name == "" {
+			continue // not a real symbol
+		}
+		// In a symbol name in an object file, "". denotes the
+		// prefix for the package in which the object file has been found.
+		// Expand it.
+		name := strings.ReplaceAll(osym.Name, `"".`, r.pkgprefix)
+		symID := SymID{Name: name, Version: abiToVer(osym.ABI)}
+		r.p.SymRefs = append(r.p.SymRefs, symID)
+
+		if i >= ndef {
+			continue // not a defined symbol from here
+		}
+
+		// Symbol data
+		dataOff := rr.DataOff(i)
+		siz := int64(rr.DataSize(i))
+
+		sym := Sym{
+			SymID: symID,
+			Kind:  objabi.SymKind(osym.Type),
+			DupOK: osym.Flag&goobj2.SymFlagDupok != 0,
+			Size:  int64(osym.Siz),
+			Data:  Data{int64(start + dataOff), siz},
+		}
+		r.p.Syms = append(r.p.Syms, &sym)
+
+		// Reloc
+		nreloc := rr.NReloc(i)
+		sym.Reloc = make([]Reloc, nreloc)
+		for j := 0; j < nreloc; j++ {
+			rel := goobj2.Reloc{}
+			rel.Read(rr, rr.RelocOff(i, j))
+			sym.Reloc[j] = Reloc{
+				Offset: int64(rel.Off),
+				Size:   int64(rel.Siz),
+				Type:   objabi.RelocType(rel.Type),
+				Add:    rel.Add,
+				Sym:    resolveSymRef(rel.Sym),
+			}
+		}
+
+		// Aux symbol info
+		isym := -1
+		funcdata := make([]goobj2.SymRef, 0, 4)
+		naux := rr.NAux(i)
+		for j := 0; j < naux; j++ {
+			a := goobj2.Aux{}
+			a.Read(rr, rr.AuxOff(i, j))
+			switch a.Type {
+			case goobj2.AuxGotype:
+				sym.Type = resolveSymRef(a.Sym)
+			case goobj2.AuxFuncInfo:
+				if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
+					panic("funcinfo symbol not defined in current package")
+				}
+				isym = int(a.Sym.SymIdx)
+			case goobj2.AuxFuncdata:
+				funcdata = append(funcdata, a.Sym)
+			default:
+				panic("unknown aux type")
+			}
+		}
+
+		// Symbol Info
+		if isym == -1 {
+			continue
+		}
+		b := rr.BytesAt(rr.DataOff(isym), rr.DataSize(isym))
+		info := goobj2.FuncInfo{}
+		info.Read(b)
+
+		info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
+		f := &Func{
+			Args:     int64(info.Args),
+			Frame:    int64(info.Locals),
+			NoSplit:  info.NoSplit != 0,
+			Leaf:     info.Flags&goobj2.FuncFlagLeaf != 0,
+			TopFrame: info.Flags&goobj2.FuncFlagTopFrame != 0,
+			PCSP:     Data{int64(pcdataBase + info.Pcsp), int64(info.Pcfile - info.Pcsp)},
+			PCFile:   Data{int64(pcdataBase + info.Pcfile), int64(info.Pcline - info.Pcfile)},
+			PCLine:   Data{int64(pcdataBase + info.Pcline), int64(info.Pcinline - info.Pcline)},
+			PCInline: Data{int64(pcdataBase + info.Pcinline), int64(info.Pcdata[0] - info.Pcinline)},
+			PCData:   make([]Data, len(info.Pcdata)-1), // -1 as we appended one above
+			FuncData: make([]FuncData, len(info.Funcdataoff)),
+			File:     make([]string, len(info.File)),
+		}
+		sym.Func = f
+		for k := range f.PCData {
+			f.PCData[k] = Data{int64(pcdataBase + info.Pcdata[k]), int64(info.Pcdata[k+1] - info.Pcdata[k])}
+		}
+		for k := range f.FuncData {
+			symID := resolveSymRef(funcdata[k])
+			f.FuncData[k] = FuncData{symID, int64(info.Funcdataoff[k])}
+		}
+		for k := range f.File {
+			symID := resolveSymRef(info.File[k])
+			f.File[k] = symID.Name
+		}
+	}
+}
diff --git a/src/cmd/internal/goobj2/funcinfo.go b/src/cmd/internal/goobj2/funcinfo.go
new file mode 100644
index 0000000..5938b5f
--- /dev/null
+++ b/src/cmd/internal/goobj2/funcinfo.go
@@ -0,0 +1,114 @@
+// 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 goobj2
+
+import (
+	"bytes"
+	"encoding/binary"
+)
+
+// FuncInfo is serialized as a symbol (aux symbol). The symbol data is
+// the binary encoding of the struct below.
+//
+// TODO: make each pcdata a separate symbol?
+type FuncInfo struct {
+	NoSplit uint8
+	Flags   uint8
+
+	Args   uint32
+	Locals uint32
+
+	Pcsp        uint32
+	Pcfile      uint32
+	Pcline      uint32
+	Pcinline    uint32
+	Pcdata      []uint32
+	PcdataEnd   uint32
+	Funcdataoff []uint32
+	File        []SymRef // TODO: just use string?
+
+	// TODO: InlTree
+}
+
+const (
+	FuncFlagLeaf = 1 << iota
+	FuncFlagCFunc
+	FuncFlagReflectMethod
+	FuncFlagShared // This is really silly
+	FuncFlagTopFrame
+)
+
+func (a *FuncInfo) Write(w *bytes.Buffer) {
+	w.WriteByte(a.NoSplit)
+	w.WriteByte(a.Flags)
+
+	var b [4]byte
+	writeUint32 := func(x uint32) {
+		binary.LittleEndian.PutUint32(b[:], x)
+		w.Write(b[:])
+	}
+
+	writeUint32(a.Args)
+	writeUint32(a.Locals)
+
+	writeUint32(a.Pcsp)
+	writeUint32(a.Pcfile)
+	writeUint32(a.Pcline)
+	writeUint32(a.Pcinline)
+	writeUint32(uint32(len(a.Pcdata)))
+	for _, x := range a.Pcdata {
+		writeUint32(x)
+	}
+	writeUint32(a.PcdataEnd)
+	writeUint32(uint32(len(a.Funcdataoff)))
+	for _, x := range a.Funcdataoff {
+		writeUint32(x)
+	}
+	writeUint32(uint32(len(a.File)))
+	for _, f := range a.File {
+		writeUint32(f.PkgIdx)
+		writeUint32(f.SymIdx)
+	}
+
+	// TODO: InlTree
+}
+
+func (a *FuncInfo) Read(b []byte) {
+	a.NoSplit = b[0]
+	a.Flags = b[1]
+	b = b[2:]
+
+	readUint32 := func() uint32 {
+		x := binary.LittleEndian.Uint32(b)
+		b = b[4:]
+		return x
+	}
+
+	a.Args = readUint32()
+	a.Locals = readUint32()
+
+	a.Pcsp = readUint32()
+	a.Pcfile = readUint32()
+	a.Pcline = readUint32()
+	a.Pcinline = readUint32()
+	pcdatalen := readUint32()
+	a.Pcdata = make([]uint32, pcdatalen)
+	for i := range a.Pcdata {
+		a.Pcdata[i] = readUint32()
+	}
+	a.PcdataEnd = readUint32()
+	funcdataofflen := readUint32()
+	a.Funcdataoff = make([]uint32, funcdataofflen)
+	for i := range a.Funcdataoff {
+		a.Funcdataoff[i] = readUint32()
+	}
+	filelen := readUint32()
+	a.File = make([]SymRef, filelen)
+	for i := range a.File {
+		a.File[i] = SymRef{readUint32(), readUint32()}
+	}
+
+	// TODO: InlTree
+}
diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go
new file mode 100644
index 0000000..b5cc0d7
--- /dev/null
+++ b/src/cmd/internal/goobj2/objfile.go
@@ -0,0 +1,568 @@
+// 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.
+
+// Go new object file format, reading and writing.
+
+package goobj2 // TODO: replace the goobj package?
+
+import (
+	"bytes"
+	"cmd/internal/bio"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"io"
+	"unsafe"
+)
+
+// New object file format.
+//
+//    Header struct {
+//       Magic   [...]byte   // "\x00go114LD"
+//       // TODO: Fingerprint
+//       Offsets [...]uint32 // byte offset of each block below
+//    }
+//
+//    Strings [...]struct {
+//       Len  uint32
+//       Data [...]byte
+//    }
+//
+//    PkgIndex [...]stringOff // TODO: add fingerprints
+//
+//    DwarfFiles [...]stringOff // XXX as a separate block for now
+//
+//    SymbolDefs [...]struct {
+//       Name stringOff
+//       ABI  uint16
+//       Type uint8
+//       Flag uint8
+//       Size uint32
+//    }
+//    NonPkgDefs [...]struct { // non-pkg symbol definitions
+//       ... // same as SymbolDefs
+//    }
+//    NonPkgRefs [...]struct { // non-pkg symbol references
+//       ... // same as SymbolDefs
+//    }
+//
+//    RelocIndex [...]uint32 // index to Relocs
+//    AuxIndex   [...]uint32 // index to Aux
+//    DataIndex  [...]uint32 // offset to Data
+//
+//    Relocs [...]struct {
+//       Off  int32
+//       Size uint8
+//       Type uint8
+//       Add  int64
+//       Sym  symRef
+//    }
+//
+//    Aux [...]struct {
+//       Type uint8
+//       Sym  symRef
+//    }
+//
+//    Data   [...]byte
+//    Pcdata [...]byte
+//
+// stringOff is a uint32 (?) offset that points to the corresponding
+// string, which is a uint32 length followed by that number of bytes.
+//
+// symRef is struct { PkgIdx, SymIdx uint32 }.
+//
+// Slice type (e.g. []symRef) is encoded as a length prefix (uint32)
+// followed by that number of elements.
+//
+// The types below correspond to the encoded data structure in the
+// object file.
+
+// Symbol indexing.
+//
+// Each symbol is referenced with a pair of indices, { PkgIdx, SymIdx },
+// as the symRef struct above.
+//
+// PkgIdx is either a predeclared index (see PkgIdxNone below) or
+// an index of an imported package. For the latter case, PkgIdx is the
+// index of the package in the PkgIndex array. 0 is an invalid index.
+//
+// SymIdx is the index of the symbol in the given package.
+// - If PkgIdx is PkgIdxSelf, SymIdx is the index of the symbol in the
+//   SymbolDefs array.
+// - If PkgIdx is PkgIdxNone, SymIdx is the index of the symbol in the
+//   NonPkgDefs array (could natually overflow to NonPkgRefs array).
+// - Otherwise, SymIdx is the index of the symbol in some other package's
+//   SymbolDefs array.
+//
+// {0, 0} represents a nil symbol. Otherwise PkgIdx should not be 0.
+//
+// RelocIndex, AuxIndex, and DataIndex contains indices/offsets to
+// Relocs/Aux/Data blocks, one element per symbol, first for all the
+// defined symbols, then all the defined non-package symbols, in the
+// same order of SymbolDefs/NonPkgDefs arrays. For N total defined
+// symbols, the array is of length N+1. The last element is the total
+// number of relocations (aux symbols, data blocks, etc.).
+//
+// They can be accessed by index. For the i-th symbol, its relocations
+// are the RelocIndex[i]-th (inclusive) to RelocIndex[i+1]-th (exclusive)
+// elements in the Relocs array. Aux/Data are likewise. (The index is
+// 0-based.)
+
+// Auxiliary symbols.
+//
+// Each symbol may (or may not) be associated with a number of auxiliary
+// symbols. They are described in the Aux block. See Aux struct below.
+// Currently a symbol's Gotype and FuncInfo are auxiliary symbols. We
+// may make use of aux symbols in more cases, e.g. DWARF symbols.
+
+// Package Index.
+const (
+	PkgIdxNone    = (1<<31 - 1) - iota // Non-package symbols
+	PkgIdxBuiltin                      // Predefined symbols // TODO: not used for now, we could use it for compiler-generated symbols like runtime.newobject
+	PkgIdxSelf                         // Symbols defined in the current package
+	PkgIdxInvalid = 0
+	// The index of other referenced packages starts from 1.
+)
+
+// Blocks
+const (
+	BlkPkgIdx = iota
+	BlkDwarfFile
+	BlkSymdef
+	BlkNonpkgdef
+	BlkNonpkgref
+	BlkRelocIdx
+	BlkAuxIdx
+	BlkDataIdx
+	BlkReloc
+	BlkAux
+	BlkData
+	BlkPcdata
+	NBlk
+)
+
+// File header.
+// TODO: probably no need to export this.
+type Header struct {
+	Magic   string
+	Offsets [NBlk]uint32
+}
+
+const Magic = "\x00go114LD"
+
+func (h *Header) Write(w *Writer) {
+	w.RawString(h.Magic)
+	for _, x := range h.Offsets {
+		w.Uint32(x)
+	}
+}
+
+func (h *Header) Read(r *Reader) error {
+	b := r.BytesAt(0, len(Magic))
+	h.Magic = string(b)
+	if h.Magic != Magic {
+		return errors.New("wrong magic, not a Go object file")
+	}
+	off := uint32(len(h.Magic))
+	for i := range h.Offsets {
+		h.Offsets[i] = r.uint32At(off)
+		off += 4
+	}
+	return nil
+}
+
+func (h *Header) Size() int {
+	return len(h.Magic) + 4*len(h.Offsets)
+}
+
+// Symbol definition.
+type Sym struct {
+	Name string
+	ABI  uint16
+	Type uint8
+	Flag uint8
+	Siz  uint32
+}
+
+const SymABIstatic = ^uint16(0)
+
+const (
+	SymFlagDupok = 1 << iota
+	SymFlagLocal
+	SymFlagTypelink
+)
+
+func (s *Sym) Write(w *Writer) {
+	w.StringRef(s.Name)
+	w.Uint16(s.ABI)
+	w.Uint8(s.Type)
+	w.Uint8(s.Flag)
+	w.Uint32(s.Siz)
+}
+
+func (s *Sym) Read(r *Reader, off uint32) {
+	s.Name = r.StringRef(off)
+	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) Size() int {
+	return 4 + 2 + 1 + 1 + 4
+}
+
+// Symbol reference.
+type SymRef struct {
+	PkgIdx uint32
+	SymIdx uint32
+}
+
+func (s *SymRef) Write(w *Writer) {
+	w.Uint32(s.PkgIdx)
+	w.Uint32(s.SymIdx)
+}
+
+func (s *SymRef) Read(r *Reader, off uint32) {
+	s.PkgIdx = r.uint32At(off)
+	s.SymIdx = r.uint32At(off + 4)
+}
+
+func (s *SymRef) Size() int {
+	return 4 + 4
+}
+
+// Relocation.
+type Reloc struct {
+	Off  int32
+	Siz  uint8
+	Type uint8
+	Add  int64
+	Sym  SymRef
+}
+
+func (r *Reloc) Write(w *Writer) {
+	w.Uint32(uint32(r.Off))
+	w.Uint8(r.Siz)
+	w.Uint8(r.Type)
+	w.Uint64(uint64(r.Add))
+	r.Sym.Write(w)
+}
+
+func (o *Reloc) Read(r *Reader, off uint32) {
+	o.Off = r.int32At(off)
+	o.Siz = r.uint8At(off + 4)
+	o.Type = r.uint8At(off + 5)
+	o.Add = r.int64At(off + 6)
+	o.Sym.Read(r, off+14)
+}
+
+func (r *Reloc) Size() int {
+	return 4 + 1 + 1 + 8 + r.Sym.Size()
+}
+
+// Aux symbol info.
+type Aux struct {
+	Type uint8
+	Sym  SymRef
+}
+
+// Aux Type
+const (
+	AuxGotype = iota
+	AuxFuncInfo
+	AuxFuncdata
+
+	// TODO: more. DWARF? Pcdata?
+)
+
+func (a *Aux) Write(w *Writer) {
+	w.Uint8(a.Type)
+	a.Sym.Write(w)
+}
+
+func (a *Aux) Read(r *Reader, off uint32) {
+	a.Type = r.uint8At(off)
+	a.Sym.Read(r, off+1)
+}
+
+func (a *Aux) Size() int {
+	return 1 + a.Sym.Size()
+}
+
+type Writer struct {
+	wr        *bio.Writer
+	stringMap map[string]uint32
+	off       uint32 // running offset
+}
+
+func NewWriter(wr *bio.Writer) *Writer {
+	return &Writer{wr: wr, stringMap: make(map[string]uint32)}
+}
+
+func (w *Writer) AddString(s string) {
+	if _, ok := w.stringMap[s]; ok {
+		return
+	}
+	w.stringMap[s] = w.off
+	w.Uint32(uint32(len(s)))
+	w.RawString(s)
+}
+
+func (w *Writer) StringRef(s string) {
+	off, ok := w.stringMap[s]
+	if !ok {
+		panic(fmt.Sprintf("writeStringRef: string not added: %q", s))
+	}
+	w.Uint32(off)
+}
+
+func (w *Writer) RawString(s string) {
+	w.wr.WriteString(s)
+	w.off += uint32(len(s))
+}
+
+func (w *Writer) Bytes(s []byte) {
+	w.wr.Write(s)
+	w.off += uint32(len(s))
+}
+
+func (w *Writer) Uint64(x uint64) {
+	var b [8]byte
+	binary.LittleEndian.PutUint64(b[:], x)
+	w.wr.Write(b[:])
+	w.off += 8
+}
+
+func (w *Writer) Uint32(x uint32) {
+	var b [4]byte
+	binary.LittleEndian.PutUint32(b[:], x)
+	w.wr.Write(b[:])
+	w.off += 4
+}
+
+func (w *Writer) Uint16(x uint16) {
+	var b [2]byte
+	binary.LittleEndian.PutUint16(b[:], x)
+	w.wr.Write(b[:])
+	w.off += 2
+}
+
+func (w *Writer) Uint8(x uint8) {
+	w.wr.WriteByte(x)
+	w.off++
+}
+
+func (w *Writer) Offset() uint32 {
+	return w.off
+}
+
+type Reader struct {
+	b        []byte // mmapped bytes, if not nil
+	readonly bool   // whether b is backed with read-only memory
+
+	rd    io.ReaderAt
+	start uint32
+	h     Header // keep block offsets
+}
+
+func NewReader(rd io.ReaderAt, off uint32) *Reader {
+	r := &Reader{rd: rd, start: off}
+	err := r.h.Read(r)
+	if err != nil {
+		return nil
+	}
+	return r
+}
+
+func NewReaderFromBytes(b []byte, readonly bool) *Reader {
+	r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), start: 0}
+	err := r.h.Read(r)
+	if err != nil {
+		return nil
+	}
+	return r
+}
+
+func (r *Reader) BytesAt(off uint32, len int) []byte {
+	if len == 0 {
+		return nil
+	}
+	if r.b != nil {
+		end := int(off) + len
+		return r.b[int(off):end:end]
+	}
+	b := make([]byte, len)
+	_, err := r.rd.ReadAt(b, int64(r.start+off))
+	if err != nil {
+		panic("corrupted input")
+	}
+	return b
+}
+
+func (r *Reader) uint64At(off uint32) uint64 {
+	b := r.BytesAt(off, 8)
+	return binary.LittleEndian.Uint64(b)
+}
+
+func (r *Reader) int64At(off uint32) int64 {
+	return int64(r.uint64At(off))
+}
+
+func (r *Reader) uint32At(off uint32) uint32 {
+	b := r.BytesAt(off, 4)
+	return binary.LittleEndian.Uint32(b)
+}
+
+func (r *Reader) int32At(off uint32) int32 {
+	return int32(r.uint32At(off))
+}
+
+func (r *Reader) uint16At(off uint32) uint16 {
+	b := r.BytesAt(off, 2)
+	return binary.LittleEndian.Uint16(b)
+}
+
+func (r *Reader) uint8At(off uint32) uint8 {
+	b := r.BytesAt(off, 1)
+	return b[0]
+}
+
+func (r *Reader) StringAt(off uint32) string {
+	l := r.uint32At(off)
+	if r.b != nil {
+		b := r.b[off+4 : off+4+l]
+		if r.readonly {
+			return toString(b) // backed by RO memory, ok to make unsafe string
+		}
+		return string(b)
+	}
+	b := make([]byte, l)
+	n, err := r.rd.ReadAt(b, int64(r.start+off+4))
+	if n != int(l) || err != nil {
+		panic("corrupted input")
+	}
+	return string(b)
+}
+
+func toString(b []byte) string {
+	type stringHeader struct {
+		str unsafe.Pointer
+		len int
+	}
+
+	if len(b) == 0 {
+		return ""
+	}
+	ss := stringHeader{str: unsafe.Pointer(&b[0]), len: len(b)}
+	s := *(*string)(unsafe.Pointer(&ss))
+	return s
+}
+
+func (r *Reader) StringRef(off uint32) string {
+	return r.StringAt(r.uint32At(off))
+}
+
+func (r *Reader) Pkglist() []string {
+	n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / 4
+	s := make([]string, n)
+	for i := range s {
+		off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
+		s[i] = r.StringRef(off)
+	}
+	return s
+}
+
+func (r *Reader) NPkg() int {
+	return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / 4
+}
+
+func (r *Reader) Pkg(i int) string {
+	off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
+	return r.StringRef(off)
+}
+
+func (r *Reader) NDwarfFile() int {
+	return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / 4
+}
+
+func (r *Reader) DwarfFile(i int) string {
+	off := r.h.Offsets[BlkDwarfFile] + uint32(i)*4
+	return r.StringRef(off)
+}
+
+func (r *Reader) NSym() int {
+	symsiz := (&Sym{}).Size()
+	return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / symsiz
+}
+
+func (r *Reader) NNonpkgdef() int {
+	symsiz := (&Sym{}).Size()
+	return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / symsiz
+}
+
+func (r *Reader) NNonpkgref() int {
+	symsiz := (&Sym{}).Size()
+	return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / symsiz
+}
+
+// SymOff returns the offset of the i-th symbol.
+func (r *Reader) SymOff(i int) uint32 {
+	symsiz := (&Sym{}).Size()
+	return r.h.Offsets[BlkSymdef] + uint32(i*symsiz)
+}
+
+// NReloc returns the number of relocations of the i-th symbol.
+func (r *Reader) NReloc(i int) int {
+	relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
+	return int(r.uint32At(relocIdxOff+4) - r.uint32At(relocIdxOff))
+}
+
+// RelocOff returns the offset of the j-th relocation of the i-th symbol.
+func (r *Reader) RelocOff(i int, j int) uint32 {
+	relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
+	relocIdx := r.uint32At(relocIdxOff)
+	relocsiz := (&Reloc{}).Size()
+	return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz)
+}
+
+// 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)
+	return int(r.uint32At(auxIdxOff+4) - r.uint32At(auxIdxOff))
+}
+
+// AuxOff returns the offset of the j-th aux symbol of the i-th symbol.
+func (r *Reader) AuxOff(i int, j int) uint32 {
+	auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4)
+	auxIdx := r.uint32At(auxIdxOff)
+	auxsiz := (&Aux{}).Size()
+	return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(auxsiz)
+}
+
+// DataOff returns the offset of the i-th symbol's data.
+func (r *Reader) DataOff(i int) uint32 {
+	dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4)
+	return r.h.Offsets[BlkData] + r.uint32At(dataIdxOff)
+}
+
+// DataSize returns the size of the i-th symbol's data.
+func (r *Reader) DataSize(i int) int {
+	return int(r.DataOff(i+1) - r.DataOff(i))
+}
+
+// Data returns the i-th symbol's data.
+func (r *Reader) Data(i int) []byte {
+	return r.BytesAt(r.DataOff(i), r.DataSize(i))
+}
+
+// AuxDataBase returns the base offset of the aux data block.
+func (r *Reader) PcdataBase() uint32 {
+	return r.h.Offsets[BlkPcdata]
+}
+
+// ReadOnly returns whether r.BytesAt returns read-only bytes.
+func (r *Reader) ReadOnly() bool {
+	return r.readonly
+}
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 1c101bf..2c106ba 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -388,6 +388,10 @@
 	R      []Reloc
 
 	Func *FuncInfo
+
+	Pkg    string
+	PkgIdx int32
+	SymIdx int32 // TODO: replace RefIdx
 }
 
 // A FuncInfo contains extra fields for STEXT symbols.
@@ -409,6 +413,8 @@
 	GCLocals     *LSym
 	GCRegs       *LSym
 	StackObjects *LSym
+
+	FuncInfoSym *LSym
 }
 
 type InlMark struct {
@@ -460,7 +466,7 @@
 )
 
 // Attribute is a set of symbol attributes.
-type Attribute uint16
+type Attribute uint32
 
 const (
 	AttrDuplicateOK Attribute = 1 << iota
@@ -501,6 +507,10 @@
 	// keep unwinding beyond this frame.
 	AttrTopFrame
 
+	// Indexed indicates this symbol has been assigned with an index (when using the
+	// new object file format).
+	AttrIndexed
+
 	// attrABIBase is the value at which the ABI is encoded in
 	// Attribute. This must be last; all bits after this are
 	// assumed to be an ABI value.
@@ -524,6 +534,7 @@
 func (a Attribute) Static() bool        { return a&AttrStatic != 0 }
 func (a Attribute) WasInlined() bool    { return a&AttrWasInlined != 0 }
 func (a Attribute) TopFrame() bool      { return a&AttrTopFrame != 0 }
+func (a Attribute) Indexed() bool       { return a&AttrIndexed != 0 }
 
 func (a *Attribute) Set(flag Attribute, value bool) {
 	if value {
@@ -558,6 +569,7 @@
 	{bit: AttrStatic, s: "STATIC"},
 	{bit: AttrWasInlined, s: ""},
 	{bit: AttrTopFrame, s: "TOPFRAME"},
+	{bit: AttrIndexed, s: ""},
 }
 
 // TextAttrString formats a for printing in as part of a TEXT prog.
@@ -638,6 +650,7 @@
 	Flag_dynlink       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
@@ -671,6 +684,14 @@
 	// TODO(austin): Replace this with ABI wrappers once the ABIs
 	// actually diverge.
 	ABIAliases []*LSym
+
+	// pkgIdx maps package path to index. The index is used for
+	// symbol reference in the object file.
+	pkgIdx map[string]int32
+
+	defs       []*LSym // list of defined symbols in the current package
+	nonpkgdefs []*LSym // list of defined non-package symbols
+	nonpkgrefs []*LSym // list of referenced non-package symbols
 }
 
 func (ctxt *Link) Diag(format string, args ...interface{}) {
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index ab5627c..76fbc58 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -8,6 +8,7 @@
 
 import (
 	"bufio"
+	"cmd/internal/bio"
 	"cmd/internal/dwarf"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
@@ -80,7 +81,13 @@
 	}
 }
 
-func WriteObjFile(ctxt *Link, b *bufio.Writer, pkgpath string) {
+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
@@ -221,8 +228,7 @@
 	}
 }
 
-func (w *objWriter) writeSymDebug(s *LSym) {
-	ctxt := w.ctxt
+func (ctxt *Link) writeSymDebug(s *LSym) {
 	fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
 	if s.Type != 0 {
 		fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
@@ -302,7 +308,7 @@
 func (w *objWriter) writeSym(s *LSym) {
 	ctxt := w.ctxt
 	if ctxt.Debugasm > 0 {
-		w.writeSymDebug(s)
+		w.ctxt.writeSymDebug(s)
 	}
 
 	w.wr.WriteByte(symPrefix)
diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go
new file mode 100644
index 0000000..c51be02
--- /dev/null
+++ b/src/cmd/internal/obj/objfile2.go
@@ -0,0 +1,363 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Writing Go object files.
+
+package obj
+
+import (
+	"bytes"
+	"cmd/internal/bio"
+	"cmd/internal/goobj2"
+	"cmd/internal/objabi"
+	"fmt"
+	"strings"
+)
+
+// 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)
+	}
+
+	genFuncInfoSyms(ctxt)
+
+	w := writer{
+		Writer:  goobj2.NewWriter(b),
+		ctxt:    ctxt,
+		pkgpath: objabi.PathToPrefix(pkgpath),
+	}
+
+	start := b.Offset()
+	w.init()
+
+	// Header
+	// We just reserve the space. We'll fill in the offsets later.
+	h := goobj2.Header{Magic: goobj2.Magic}
+	h.Write(w.Writer)
+
+	// String table
+	w.StringTable()
+
+	// Package references
+	h.Offsets[goobj2.BlkPkgIdx] = w.Offset()
+	for _, pkg := range w.pkglist {
+		w.StringRef(pkg)
+	}
+
+	// DWARF file table
+	h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
+	for _, f := range ctxt.PosTable.DebugLinesFileTable() {
+		w.StringRef(f)
+	}
+
+	// Symbol definitions
+	h.Offsets[goobj2.BlkSymdef] = w.Offset()
+	for _, s := range ctxt.defs {
+		w.Sym(s)
+	}
+
+	// Non-pkg symbol definitions
+	h.Offsets[goobj2.BlkNonpkgdef] = w.Offset()
+	for _, s := range ctxt.nonpkgdefs {
+		w.Sym(s)
+	}
+
+	// Non-pkg symbol references
+	h.Offsets[goobj2.BlkNonpkgref] = w.Offset()
+	for _, s := range ctxt.nonpkgrefs {
+		w.Sym(s)
+	}
+
+	// Reloc indexes
+	h.Offsets[goobj2.BlkRelocIdx] = w.Offset()
+	nreloc := uint32(0)
+	lists := [][]*LSym{ctxt.defs, ctxt.nonpkgdefs}
+	for _, list := range lists {
+		for _, s := range list {
+			w.Uint32(nreloc)
+			nreloc += uint32(len(s.R))
+		}
+	}
+	w.Uint32(nreloc)
+
+	// Symbol Info indexes
+	h.Offsets[goobj2.BlkAuxIdx] = w.Offset()
+	naux := uint32(0)
+	for _, list := range lists {
+		for _, s := range list {
+			w.Uint32(naux)
+			if s.Gotype != nil {
+				naux++
+			}
+			if s.Func != nil {
+				// FuncInfo is an aux symbol, each Funcdata is an aux symbol
+				naux += 1 + uint32(len(s.Func.Pcln.Funcdata))
+			}
+		}
+	}
+	w.Uint32(naux)
+
+	// Data indexes
+	h.Offsets[goobj2.BlkDataIdx] = w.Offset()
+	dataOff := uint32(0)
+	for _, list := range lists {
+		for _, s := range list {
+			w.Uint32(dataOff)
+			dataOff += uint32(len(s.P))
+		}
+	}
+	w.Uint32(dataOff)
+
+	// Relocs
+	h.Offsets[goobj2.BlkReloc] = w.Offset()
+	for _, list := range lists {
+		for _, s := range list {
+			for i := range s.R {
+				w.Reloc(&s.R[i])
+			}
+		}
+	}
+
+	// Aux symbol info
+	h.Offsets[goobj2.BlkAux] = w.Offset()
+	for _, list := range lists {
+		for _, s := range list {
+			w.Aux(s)
+		}
+	}
+
+	// Data
+	h.Offsets[goobj2.BlkData] = w.Offset()
+	for _, list := range lists {
+		for _, s := range list {
+			w.Bytes(s.P)
+		}
+	}
+
+	// Pcdata
+	h.Offsets[goobj2.BlkPcdata] = w.Offset()
+	for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms
+		if s.Func != nil {
+			pc := &s.Func.Pcln
+			w.Bytes(pc.Pcsp.P)
+			w.Bytes(pc.Pcfile.P)
+			w.Bytes(pc.Pcline.P)
+			w.Bytes(pc.Pcinline.P)
+			for i := range pc.Pcdata {
+				w.Bytes(pc.Pcdata[i].P)
+			}
+		}
+	}
+
+	// Fix up block offsets in the header
+	end := start + int64(w.Offset())
+	b.MustSeek(start, 0)
+	h.Write(w.Writer)
+	b.MustSeek(end, 0)
+}
+
+type writer struct {
+	*goobj2.Writer
+	ctxt    *Link
+	pkgpath string   // the package import path (escaped), "" if unknown
+	pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
+}
+
+// prepare package index list
+func (w *writer) init() {
+	w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
+	w.pkglist[0] = "" // dummy invalid package for index 0
+	for pkg, i := range w.ctxt.pkgIdx {
+		w.pkglist[i] = pkg
+	}
+
+	// Also make sure imported packages appear in the list (even if no symbol is referenced).
+	for _, pkg := range w.ctxt.Imports {
+		if _, ok := w.ctxt.pkgIdx[pkg]; !ok {
+			w.pkglist = append(w.pkglist, pkg)
+		}
+	}
+}
+
+func (w *writer) StringTable() {
+	w.AddString("")
+	for _, pkg := range w.ctxt.Imports {
+		w.AddString(pkg)
+	}
+	for _, pkg := range w.pkglist {
+		w.AddString(pkg)
+	}
+	w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
+		if w.pkgpath != "" {
+			s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
+		}
+		w.AddString(s.Name)
+	})
+	w.ctxt.traverseSyms(traverseDefs, func(s *LSym) {
+		if s.Type != objabi.STEXT {
+			return
+		}
+		pc := &s.Func.Pcln
+		for _, f := range pc.File {
+			w.AddString(f)
+		}
+		for _, call := range pc.InlTree.nodes {
+			f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
+			w.AddString(f)
+		}
+	})
+	for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
+		w.AddString(f)
+	}
+}
+
+func (w *writer) Sym(s *LSym) {
+	abi := uint16(s.ABI())
+	if s.Static() {
+		abi = goobj2.SymABIstatic
+	}
+	flag := uint8(0)
+	if s.DuplicateOK() {
+		flag |= goobj2.SymFlagDupok
+	}
+	if s.Local() {
+		flag |= goobj2.SymFlagLocal
+	}
+	if s.MakeTypelink() {
+		flag |= goobj2.SymFlagTypelink
+	}
+	o := goobj2.Sym{
+		Name: s.Name,
+		ABI:  abi,
+		Type: uint8(s.Type),
+		Flag: flag,
+		Siz:  uint32(s.Size),
+	}
+	o.Write(w.Writer)
+}
+
+func makeSymRef(s *LSym) goobj2.SymRef {
+	if s == nil {
+		return goobj2.SymRef{}
+	}
+	if s.PkgIdx == 0 || !s.Indexed() {
+		fmt.Printf("unindexed symbol reference: %v\n", s)
+		panic("unindexed symbol reference")
+	}
+	return goobj2.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
+}
+
+func (w *writer) Reloc(r *Reloc) {
+	o := goobj2.Reloc{
+		Off:  r.Off,
+		Siz:  r.Siz,
+		Type: uint8(r.Type),
+		Add:  r.Add,
+		Sym:  makeSymRef(r.Sym),
+	}
+	o.Write(w.Writer)
+}
+
+func (w *writer) Aux(s *LSym) {
+	if s.Gotype != nil {
+		o := goobj2.Aux{
+			Type: goobj2.AuxGotype,
+			Sym:  makeSymRef(s.Gotype),
+		}
+		o.Write(w.Writer)
+	}
+	if s.Func != nil {
+		o := goobj2.Aux{
+			Type: goobj2.AuxFuncInfo,
+			Sym:  makeSymRef(s.Func.FuncInfoSym),
+		}
+		o.Write(w.Writer)
+
+		for _, d := range s.Func.Pcln.Funcdata {
+			o := goobj2.Aux{
+				Type: goobj2.AuxFuncdata,
+				Sym:  makeSymRef(d),
+			}
+			o.Write(w.Writer)
+		}
+	}
+}
+
+// generate symbols for FuncInfo.
+func genFuncInfoSyms(ctxt *Link) {
+	infosyms := make([]*LSym, 0, len(ctxt.Text))
+	var pcdataoff uint32
+	var b bytes.Buffer
+	symidx := int32(len(ctxt.defs))
+	for _, s := range ctxt.Text {
+		if s.Func == nil {
+			continue
+		}
+		nosplit := uint8(0)
+		if s.NoSplit() {
+			nosplit = 1
+		}
+		flags := uint8(0)
+		if s.Leaf() {
+			flags |= goobj2.FuncFlagLeaf
+		}
+		if s.CFunc() {
+			flags |= goobj2.FuncFlagCFunc
+		}
+		if s.ReflectMethod() {
+			flags |= goobj2.FuncFlagReflectMethod
+		}
+		if ctxt.Flag_shared { // This is really silly
+			flags |= goobj2.FuncFlagShared
+		}
+		if s.TopFrame() {
+			flags |= goobj2.FuncFlagTopFrame
+		}
+		o := goobj2.FuncInfo{
+			NoSplit: nosplit,
+			Flags:   flags,
+			Args:    uint32(s.Func.Args),
+			Locals:  uint32(s.Func.Locals),
+		}
+		pc := &s.Func.Pcln
+		o.Pcsp = pcdataoff
+		pcdataoff += uint32(len(pc.Pcsp.P))
+		o.Pcfile = pcdataoff
+		pcdataoff += uint32(len(pc.Pcfile.P))
+		o.Pcline = pcdataoff
+		pcdataoff += uint32(len(pc.Pcline.P))
+		o.Pcinline = pcdataoff
+		pcdataoff += uint32(len(pc.Pcinline.P))
+		o.Pcdata = make([]uint32, len(pc.Pcdata))
+		for i, pcd := range pc.Pcdata {
+			o.Pcdata[i] = pcdataoff
+			pcdataoff += uint32(len(pcd.P))
+		}
+		o.PcdataEnd = pcdataoff
+		o.Funcdataoff = make([]uint32, len(pc.Funcdataoff))
+		for i, x := range pc.Funcdataoff {
+			o.Funcdataoff[i] = uint32(x)
+		}
+		o.File = make([]goobj2.SymRef, len(pc.File))
+		for i, f := range pc.File {
+			fsym := ctxt.Lookup(f)
+			o.File[i] = makeSymRef(fsym)
+		}
+
+		o.Write(&b)
+		isym := &LSym{
+			Type:   objabi.SDATA, // for now, I don't think it matters
+			PkgIdx: goobj2.PkgIdxSelf,
+			SymIdx: symidx,
+			P:      append([]byte(nil), b.Bytes()...),
+		}
+		isym.Set(AttrIndexed, true)
+		symidx++
+		infosyms = append(infosyms, isym)
+		s.Func.FuncInfoSym = isym
+		b.Reset()
+	}
+	ctxt.defs = append(ctxt.defs, infosyms...)
+}
diff --git a/src/cmd/internal/obj/sizeof_test.go b/src/cmd/internal/obj/sizeof_test.go
index 05da9cc..ac65143b 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{}, 56, 104},
+		//{LSym{}, 56, 104}, // TODO: re-enable
 		{Prog{}, 132, 200},
 	}
 
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go
index 15a501c..e72ec3e 100644
--- a/src/cmd/internal/obj/sym.go
+++ b/src/cmd/internal/obj/sym.go
@@ -32,6 +32,7 @@
 package obj
 
 import (
+	"cmd/internal/goobj2"
 	"cmd/internal/objabi"
 	"fmt"
 	"log"
@@ -78,6 +79,13 @@
 // LookupABI looks up a symbol with the given ABI.
 // If it does not exist, it creates it.
 func (ctxt *Link) LookupABI(name string, abi ABI) *LSym {
+	return ctxt.LookupABIInit(name, abi, nil)
+}
+
+// LookupABI looks up a symbol with the given ABI.
+// If it does not exist, it creates it and
+// passes it to init for one-time initialization.
+func (ctxt *Link) LookupABIInit(name string, abi ABI, init func(s *LSym)) *LSym {
 	var hash map[string]*LSym
 	switch abi {
 	case ABI0:
@@ -94,6 +102,9 @@
 		s = &LSym{Name: name}
 		s.SetABI(abi)
 		hash[name] = s
+		if init != nil {
+			init(s)
+		}
 	}
 	ctxt.hashmu.Unlock()
 	return s
@@ -147,3 +158,117 @@
 		s.Set(AttrLocal, true)
 	})
 }
+
+// Assign index to symbols.
+// 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
+	}
+
+	ctxt.pkgIdx = make(map[string]int32)
+	ctxt.defs = []*LSym{}
+	ctxt.nonpkgdefs = []*LSym{}
+
+	var idx, nonpkgidx int32 = 0, 0
+	ctxt.traverseSyms(traverseDefs, func(s *LSym) {
+		if asm || s.Pkg == "_" || s.DuplicateOK() {
+			s.PkgIdx = goobj2.PkgIdxNone
+			s.SymIdx = nonpkgidx
+			if nonpkgidx != int32(len(ctxt.nonpkgdefs)) {
+				panic("bad index")
+			}
+			ctxt.nonpkgdefs = append(ctxt.nonpkgdefs, s)
+			nonpkgidx++
+		} else {
+			s.PkgIdx = goobj2.PkgIdxSelf
+			s.SymIdx = idx
+			if idx != int32(len(ctxt.defs)) {
+				panic("bad index")
+			}
+			ctxt.defs = append(ctxt.defs, s)
+			idx++
+		}
+		s.Set(AttrIndexed, true)
+	})
+
+	ipkg := int32(1) // 0 is invalid index
+	nonpkgdef := nonpkgidx
+	ctxt.traverseSyms(traverseRefs|traverseAux, func(rs *LSym) {
+		if rs.PkgIdx != goobj2.PkgIdxInvalid {
+			return
+		}
+		pkg := rs.Pkg
+		if pkg == "" || pkg == "\"\"" || pkg == "_" || !rs.Indexed() {
+			rs.PkgIdx = goobj2.PkgIdxNone
+			rs.SymIdx = nonpkgidx
+			rs.Set(AttrIndexed, true)
+			if nonpkgidx != nonpkgdef+int32(len(ctxt.nonpkgrefs)) {
+				panic("bad index")
+			}
+			ctxt.nonpkgrefs = append(ctxt.nonpkgrefs, rs)
+			nonpkgidx++
+			return
+		}
+		if k, ok := ctxt.pkgIdx[pkg]; ok {
+			rs.PkgIdx = k
+			return
+		}
+		rs.PkgIdx = ipkg
+		ctxt.pkgIdx[pkg] = ipkg
+		ipkg++
+	})
+}
+
+type traverseFlag uint32
+
+const (
+	traverseDefs traverseFlag = 1 << iota
+	traverseRefs
+	traverseAux
+
+	traverseAll = traverseDefs | traverseRefs | traverseAux
+)
+
+// Traverse symbols based on flag, call fn for each symbol.
+func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) {
+	lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases}
+	for _, list := range lists {
+		for _, s := range list {
+			if flag&traverseDefs != 0 {
+				fn(s)
+			}
+			if flag&traverseRefs != 0 {
+				for _, r := range s.R {
+					if r.Sym != nil {
+						fn(r.Sym)
+					}
+				}
+			}
+			if flag&traverseAux != 0 {
+				if s.Gotype != nil {
+					fn(s.Gotype)
+				}
+				if s.Type == objabi.STEXT {
+					pc := &s.Func.Pcln
+					for _, d := range pc.Funcdata {
+						if d != nil {
+							fn(d)
+						}
+					}
+					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)
+						}
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index fe16788..dbc7a59 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1085,13 +1085,13 @@
 	}
 
 	ptrsize := int64(p.ctxt.Arch.PtrSize)
-	nptr := decodetypePtrdata(p.ctxt.Arch, typ) / ptrsize
+	nptr := decodetypePtrdata(p.ctxt.Arch, typ.P) / ptrsize
 
 	if debugGCProg {
 		fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr)
 	}
 
-	if decodetypeUsegcprog(p.ctxt.Arch, typ) == 0 {
+	if decodetypeUsegcprog(p.ctxt.Arch, typ.P) == 0 {
 		// Copy pointers from mask into program.
 		mask := decodetypeGcmask(p.ctxt, typ)
 		for i := int64(0); i < nptr; i++ {
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index c880c0d..d0896fc 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -50,6 +50,11 @@
 		ctxt.Logf("%5.2f deadcode\n", Cputime())
 	}
 
+	if *flagNewobj {
+		deadcode2(ctxt)
+		return
+	}
+
 	d := &deadcodepass{
 		ctxt:        ctxt,
 		ifaceMethod: make(map[methodsig]bool),
@@ -60,8 +65,8 @@
 	d.init()
 	d.flood()
 
-	callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal)
-	methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal)
+	callSym := ctxt.Lookup("reflect.Value.Call", sym.SymVerABIInternal)
+	methSym := ctxt.Lookup("reflect.Value.Method", sym.SymVerABIInternal)
 	reflectSeen := false
 
 	if ctxt.DynlinkingGo() {
@@ -118,22 +123,70 @@
 		}
 	}
 
-	for _, lib := range ctxt.Library {
-		lib.Textp = lib.Textp[:0]
-	}
+	addToTextp(ctxt)
+}
 
+func addToTextp(ctxt *Link) {
 	// Remove dead text but keep file information (z symbols).
-	textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
+	textp := []*sym.Symbol{}
 	for _, s := range ctxt.Textp {
 		if s.Attr.Reachable() {
-			if s.Unit != nil {
-				s.Unit.Lib.Textp = append(s.Unit.Lib.Textp, s)
-				s.Unit.Textp = append(s.Unit.Textp, s)
-			}
 			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)
+	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
@@ -239,7 +292,7 @@
 
 				// 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)
+				exports := d.ctxt.Lookup("go.plugin.exports", 0)
 				if exports != nil {
 					for i := range exports.R {
 						d.mark(exports.R[i].Sym, nil)
@@ -254,9 +307,9 @@
 
 	for _, name := range names {
 		// Mark symbol as an data/ABI0 symbol.
-		d.mark(d.ctxt.Syms.ROLookup(name, 0), nil)
+		d.mark(d.ctxt.Lookup(name, 0), nil)
 		// Also mark any Go functions (internal ABI).
-		d.mark(d.ctxt.Syms.ROLookup(name, sym.SymVerABIInternal), nil)
+		d.mark(d.ctxt.Lookup(name, sym.SymVerABIInternal), nil)
 	}
 }
 
@@ -278,7 +331,7 @@
 				// later will give a better error than deadcode.
 				continue
 			}
-			if decodetypeKind(d.ctxt.Arch, s)&kindMask == kindInterface {
+			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)
diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go
new file mode 100644
index 0000000..354d158
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode2.go
@@ -0,0 +1,145 @@
+// 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/objabi"
+	"cmd/internal/sys"
+	"cmd/link/internal/objfile"
+	"cmd/link/internal/sym"
+	"fmt"
+	"strings"
+)
+
+var _ = fmt.Print
+
+// TODO:
+// - Live method tracking:
+//   Prune methods that are not directly called and cannot
+//   be potentially called by interface or reflect call.
+//   For now, all the methods from reachable type are alive.
+// - Shared object support:
+//   It basically marks everything. We could consider using
+//   a different mechanism to represent it.
+// - Field tracking support:
+//   It needs to record from where the symbol is referenced.
+
+type workQueue []objfile.Sym
+
+func (q *workQueue) push(i objfile.Sym) { *q = append(*q, i) }
+func (q *workQueue) pop() objfile.Sym   { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
+func (q *workQueue) empty() bool        { return len(*q) == 0 }
+
+type deadcodePass2 struct {
+	ctxt   *Link
+	loader *objfile.Loader
+	wq     workQueue
+}
+
+func (d *deadcodePass2) init() {
+	d.loader.InitReachable()
+
+	var names []string
+
+	// 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.
+			exportsIdx := d.loader.Lookup("go.plugin.exports", 0)
+			if exportsIdx != 0 {
+				relocs := d.loader.Relocs(exportsIdx)
+				for i := 0; i < relocs.Count; i++ {
+					d.mark(relocs.At(i).Sym)
+				}
+			}
+		}
+	}
+	for _, s := range dynexp {
+		d.mark(d.loader.Lookup(s.Name, int(s.Version)))
+	}
+
+	for _, name := range names {
+		// Mark symbol as an data/ABI0 symbol.
+		d.mark(d.loader.Lookup(name, 0))
+		// Also mark any Go functions (internal ABI).
+		d.mark(d.loader.Lookup(name, sym.SymVerABIInternal))
+	}
+}
+
+func (d *deadcodePass2) flood() {
+	for !d.wq.empty() {
+		symIdx := d.wq.pop()
+		relocs := d.loader.Relocs(symIdx)
+		for i := 0; i < relocs.Count; i++ {
+			r := relocs.At(i)
+			if r.Type == objabi.R_WEAKADDROFF {
+				continue
+			}
+			if r.Type == objabi.R_METHODOFF {
+				// TODO: we should do something about it
+				// For now, all the methods are considered live
+			}
+			d.mark(r.Sym)
+		}
+		naux := d.loader.NAux(symIdx)
+		for i := 0; i < naux; i++ {
+			d.mark(d.loader.AuxSym(symIdx, i))
+		}
+	}
+}
+
+func (d *deadcodePass2) mark(symIdx objfile.Sym) {
+	if symIdx != 0 && !d.loader.Reachable.Has(symIdx) {
+		d.wq.push(symIdx)
+		d.loader.Reachable.Set(symIdx)
+	}
+}
+
+func deadcode2(ctxt *Link) {
+	loader := ctxt.loader
+	d := deadcodePass2{ctxt: ctxt, loader: loader}
+	d.init()
+	d.flood()
+
+	n := loader.NSym()
+	if ctxt.BuildMode != BuildModeShared {
+		// Keep a itablink if the symbol it points at is being kept.
+		// (When BuildModeShared, always keep itablinks.)
+		for i := 1; i < n; i++ {
+			s := objfile.Sym(i)
+			if strings.HasPrefix(loader.RawSymName(s), "go.itablink.") {
+				relocs := loader.Relocs(s)
+				if relocs.Count > 0 && loader.Reachable.Has(relocs.At(0).Sym) {
+					loader.Reachable.Set(s)
+				}
+			}
+		}
+	}
+
+	// Set reachable attr for now.
+	for i := 1; i < n; i++ {
+		if loader.Reachable.Has(objfile.Sym(i)) {
+			s := loader.Syms[i]
+			if s != nil && s.Name != "" {
+				s.Attr.Set(sym.AttrReachable, true)
+			}
+		}
+	}
+}
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 3afb389..3271c85 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -65,28 +65,28 @@
 func uncommonSize() int                  { return 4 + 2 + 2 + 4 + 4 }      // runtime.uncommontype
 
 // Type.commonType.kind
-func decodetypeKind(arch *sys.Arch, s *sym.Symbol) uint8 {
-	return s.P[2*arch.PtrSize+7] & objabi.KindMask //  0x13 / 0x1f
+func decodetypeKind(arch *sys.Arch, p []byte) uint8 {
+	return p[2*arch.PtrSize+7] & objabi.KindMask //  0x13 / 0x1f
 }
 
 // Type.commonType.kind
-func decodetypeUsegcprog(arch *sys.Arch, s *sym.Symbol) uint8 {
-	return s.P[2*arch.PtrSize+7] & objabi.KindGCProg //  0x13 / 0x1f
+func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 {
+	return p[2*arch.PtrSize+7] & objabi.KindGCProg //  0x13 / 0x1f
 }
 
 // Type.commonType.size
-func decodetypeSize(arch *sys.Arch, s *sym.Symbol) int64 {
-	return int64(decodeInuxi(arch, s.P, arch.PtrSize)) // 0x8 / 0x10
+func decodetypeSize(arch *sys.Arch, p []byte) int64 {
+	return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10
 }
 
 // Type.commonType.ptrdata
-func decodetypePtrdata(arch *sys.Arch, s *sym.Symbol) int64 {
-	return int64(decodeInuxi(arch, s.P[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
+func decodetypePtrdata(arch *sys.Arch, p []byte) int64 {
+	return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
 }
 
 // Type.commonType.tflag
-func decodetypeHasUncommon(arch *sys.Arch, s *sym.Symbol) bool {
-	return s.P[2*arch.PtrSize+4]&tflagUncommon != 0
+func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool {
+	return p[2*arch.PtrSize+4]&tflagUncommon != 0
 }
 
 // Find the elf.Section of a given shared library that contains a given address.
@@ -138,7 +138,7 @@
 func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte {
 	if s.Type == sym.SDYNIMPORT {
 		addr := decodetypeGcprogShlib(ctxt, s)
-		ptrdata := decodetypePtrdata(ctxt.Arch, s)
+		ptrdata := decodetypePtrdata(ctxt.Arch, s.P)
 		sect := findShlibSection(ctxt, s.File, addr)
 		if sect != nil {
 			r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize))
@@ -181,17 +181,17 @@
 }
 
 // Type.FuncType.dotdotdot
-func decodetypeFuncDotdotdot(arch *sys.Arch, s *sym.Symbol) bool {
-	return uint16(decodeInuxi(arch, s.P[commonsize(arch)+2:], 2))&(1<<15) != 0
+func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool {
+	return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0
 }
 
 // Type.FuncType.inCount
-func decodetypeFuncInCount(arch *sys.Arch, s *sym.Symbol) int {
-	return int(decodeInuxi(arch, s.P[commonsize(arch):], 2))
+func decodetypeFuncInCount(arch *sys.Arch, p []byte) int {
+	return int(decodeInuxi(arch, p[commonsize(arch):], 2))
 }
 
-func decodetypeFuncOutCount(arch *sys.Arch, s *sym.Symbol) int {
-	return int(uint16(decodeInuxi(arch, s.P[commonsize(arch)+2:], 2)) & (1<<15 - 1))
+func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int {
+	return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1))
 }
 
 func decodetypeFuncInType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
@@ -199,14 +199,14 @@
 	if arch.PtrSize == 8 {
 		uadd += 4
 	}
-	if decodetypeHasUncommon(arch, s) {
+	if decodetypeHasUncommon(arch, s.P) {
 		uadd += uncommonSize()
 	}
 	return decodeRelocSym(s, int32(uadd+i*arch.PtrSize))
 }
 
 func decodetypeFuncOutType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
-	return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s))
+	return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s.P))
 }
 
 // Type.StructType.fields.Slice::length
@@ -216,7 +216,7 @@
 
 func decodetypeStructFieldArrayOff(arch *sys.Arch, s *sym.Symbol, i int) int {
 	off := commonsize(arch) + 4*arch.PtrSize
-	if decodetypeHasUncommon(arch, s) {
+	if decodetypeHasUncommon(arch, s.P) {
 		off += uncommonSize()
 	}
 	off += i * structfieldSize(arch)
@@ -264,8 +264,8 @@
 }
 
 // InterfaceType.methods.length
-func decodetypeIfaceMethodCount(arch *sys.Arch, s *sym.Symbol) int64 {
-	return int64(decodeInuxi(arch, s.P[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 {
+	return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
 }
 
 // methodsig is a fully qualified typed method signature, like
@@ -299,7 +299,7 @@
 		mtypSym := decodeRelocSym(s, int32(off+4))
 
 		buf.WriteRune('(')
-		inCount := decodetypeFuncInCount(arch, mtypSym)
+		inCount := decodetypeFuncInCount(arch, mtypSym.P)
 		for i := 0; i < inCount; i++ {
 			if i > 0 {
 				buf.WriteString(", ")
@@ -307,7 +307,7 @@
 			buf.WriteString(decodetypeFuncInType(arch, mtypSym, i).Name)
 		}
 		buf.WriteString(") (")
-		outCount := decodetypeFuncOutCount(arch, mtypSym)
+		outCount := decodetypeFuncOutCount(arch, mtypSym.P)
 		for i := 0; i < outCount; i++ {
 			if i > 0 {
 				buf.WriteString(", ")
@@ -324,7 +324,7 @@
 }
 
 func decodeIfaceMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
-	if decodetypeKind(arch, s)&kindMask != kindInterface {
+	if decodetypeKind(arch, s.P)&kindMask != kindInterface {
 		panic(fmt.Sprintf("symbol %q is not an interface", s.Name))
 	}
 	r := decodeReloc(s, int32(commonsize(arch)+arch.PtrSize))
@@ -335,17 +335,17 @@
 		panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", s.Name))
 	}
 	off := int(r.Add) // array of reflect.imethod values
-	numMethods := int(decodetypeIfaceMethodCount(arch, s))
+	numMethods := int(decodetypeIfaceMethodCount(arch, s.P))
 	sizeofIMethod := 4 + 4
 	return decodeMethodSig(arch, s, off, sizeofIMethod, numMethods)
 }
 
 func decodetypeMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
-	if !decodetypeHasUncommon(arch, s) {
+	if !decodetypeHasUncommon(arch, s.P) {
 		panic(fmt.Sprintf("no methods on %q", s.Name))
 	}
 	off := commonsize(arch) // reflect.rtype
-	switch decodetypeKind(arch, s) & kindMask {
+	switch decodetypeKind(arch, s.P) & kindMask {
 	case kindStruct: // reflect.structType
 		off += 4 * arch.PtrSize
 	case kindPtr: // reflect.ptrType
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index e426a6b..ebbfbb8 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -422,8 +422,8 @@
 
 func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
 	name := gotype.Name[5:] // could also decode from Type.string
-	kind := decodetypeKind(ctxt.Arch, gotype)
-	bytesize := decodetypeSize(ctxt.Arch, gotype)
+	kind := decodetypeKind(ctxt.Arch, gotype.P)
+	bytesize := decodetypeSize(ctxt.Arch, gotype.P)
 
 	var die, typedefdie *dwarf.DWDie
 	switch kind {
@@ -488,17 +488,17 @@
 		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 		typedefdie = dotypedef(ctxt, &dwtypes, name, die)
-		nfields := decodetypeFuncInCount(ctxt.Arch, gotype)
+		nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P)
 		for i := 0; i < nfields; i++ {
 			s := decodetypeFuncInType(ctxt.Arch, gotype, i)
 			fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
 			newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
 		}
 
-		if decodetypeFuncDotdotdot(ctxt.Arch, gotype) {
+		if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) {
 			newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
 		}
-		nfields = decodetypeFuncOutCount(ctxt.Arch, gotype)
+		nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P)
 		for i := 0; i < nfields; i++ {
 			s := decodetypeFuncOutType(ctxt.Arch, gotype, i)
 			fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
@@ -508,7 +508,7 @@
 	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))
+		nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P))
 		var s *sym.Symbol
 		if nfields == 0 {
 			s = lookupOrDiag(ctxt, "type.runtime.eface")
@@ -733,7 +733,7 @@
 		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), decodetypeSize(ctxt.Arch, valtype)
+		keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P)
 		keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype))
 
 		// compute size info like hashmap.c does.
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index 80d7ac3..13fbbed 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -111,7 +111,12 @@
 		}
 		p1 += p0
 
-		loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
+		if *flagNewobj {
+			// loadcgo creates sym.Symbol. Delay this until all the symbols are added.
+			ctxt.cgodata = append(ctxt.cgodata, [3]string{filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1]})
+		} else {
+			loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
+		}
 	}
 }
 
@@ -164,7 +169,7 @@
 			if i := strings.Index(remote, "#"); i >= 0 {
 				remote, q = remote[:i], remote[i+1:]
 			}
-			s := ctxt.Syms.Lookup(local, 0)
+			s := ctxt.LookupOrCreate(local, 0)
 			if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SHOSTOBJ {
 				s.SetDynimplib(lib)
 				s.SetExtname(remote)
@@ -183,7 +188,7 @@
 			}
 			local := f[1]
 
-			s := ctxt.Syms.Lookup(local, 0)
+			s := ctxt.LookupOrCreate(local, 0)
 			s.Type = sym.SHOSTOBJ
 			s.Size = 0
 			continue
@@ -204,7 +209,7 @@
 			// functions. Link.loadlib will resolve any
 			// ABI aliases we find here (since we may not
 			// yet know it's an alias).
-			s := ctxt.Syms.Lookup(local, 0)
+			s := ctxt.LookupOrCreate(local, 0)
 
 			switch ctxt.BuildMode {
 			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 182e5b0..d030340 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -375,15 +375,8 @@
 }
 
 func (ctxt *Link) loadlib() {
-	switch ctxt.BuildMode {
-	case BuildModeCShared, BuildModePlugin:
-		s := ctxt.Syms.Lookup("runtime.islibrary", 0)
-		s.Attr |= sym.AttrDuplicateOK
-		s.AddUint8(1)
-	case BuildModeCArchive:
-		s := ctxt.Syms.Lookup("runtime.isarchive", 0)
-		s.Attr |= sym.AttrDuplicateOK
-		s.AddUint8(1)
+	if *flagNewobj {
+		ctxt.loader = objfile.NewLoader()
 	}
 
 	loadinternal(ctxt, "runtime")
@@ -408,6 +401,52 @@
 		}
 	}
 
+	if *flagNewobj {
+		// Add references of externally defined symbols.
+		objfile.LoadRefs(ctxt.loader, ctxt.Arch, ctxt.Syms)
+
+		// Load cgo directives.
+		for _, p := range ctxt.cgodata {
+			loadcgo(ctxt, p[0], p[1], p[2])
+		}
+	}
+
+	iscgo = ctxt.Lookup("x_cgo_init", 0) != nil
+
+	// Record whether we can use plugins.
+	ctxt.canUsePlugins = (ctxt.Lookup("plugin.Open", sym.SymVerABIInternal) != nil)
+
+	// We now have enough information to determine the link mode.
+	determineLinkMode(ctxt)
+
+	// Now that we know the link mode, trim the dynexp list.
+	x := sym.AttrCgoExportDynamic
+
+	if ctxt.LinkMode == LinkExternal {
+		x = sym.AttrCgoExportStatic
+	}
+	w := 0
+	for i := range dynexp {
+		if dynexp[i].Attr&x != 0 {
+			dynexp[w] = dynexp[i]
+			w++
+		}
+	}
+	dynexp = dynexp[:w]
+
+	// Resolve ABI aliases in the list of cgo-exported functions.
+	// This is necessary because we load the ABI0 symbol for all
+	// cgo exports.
+	for i, s := range dynexp {
+		if s.Type != sym.SABIALIAS {
+			continue
+		}
+		t := resolveABIAlias(s)
+		t.Attr |= s.Attr
+		t.SetExtname(s.Extname())
+		dynexp[i] = t
+	}
+
 	for _, lib := range ctxt.Library {
 		if lib.Shlib != "" {
 			if ctxt.Debugvlog > 1 {
@@ -417,25 +456,6 @@
 		}
 	}
 
-	iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
-
-	// We now have enough information to determine the link mode.
-	determineLinkMode(ctxt)
-
-	// Recalculate pe parameters now that we have ctxt.LinkMode set.
-	if ctxt.HeadType == objabi.Hwindows {
-		Peinit(ctxt)
-	}
-
-	if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
-		*FlagTextAddr = 0
-	}
-
-	if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
-		toc := ctxt.Syms.Lookup(".TOC.", 0)
-		toc.Type = sym.SDYNIMPORT
-	}
-
 	if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && (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
@@ -453,7 +473,8 @@
 		}
 	}
 
-	if ctxt.LinkMode == LinkInternal {
+	// In internal link mode, read the host object files.
+	if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
 		// Drop all the cgo_import_static declarations.
 		// Turns out we won't be needing them.
 		for _, s := range ctxt.Syms.Allsym {
@@ -469,6 +490,104 @@
 				}
 			}
 		}
+
+		hostobjs(ctxt)
+
+		// 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
+				}
+			}
+		}
+		if any {
+			if *flagLibGCC == "" {
+				*flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
+			}
+			if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
+				// On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
+				// In this case we fail to load libgcc.a and can encounter link
+				// errors - see if we can find libcompiler_rt.a instead.
+				*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
+			}
+			if *flagLibGCC != "none" {
+				hostArchive(ctxt, *flagLibGCC)
+			}
+			if ctxt.HeadType == objabi.Hwindows {
+				if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
+					hostArchive(ctxt, p)
+				}
+				if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
+					hostArchive(ctxt, p)
+				}
+				// TODO: maybe do something similar to peimporteddlls to collect all lib names
+				// and try link them all to final exe just like libmingwex.a and libmingw32.a:
+				/*
+					for:
+					#cgo windows LDFLAGS: -lmsvcrt -lm
+					import:
+					libmsvcrt.a libm.a
+				*/
+			}
+		}
+	} else if ctxt.LinkMode == LinkExternal {
+		hostlinksetup(ctxt)
+	}
+
+	// We've loaded all the code now.
+	ctxt.Loaded = true
+
+	importcycles()
+}
+
+// Set up flags and special symbols depending on the platform build mode.
+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)
+	case BuildModeCArchive:
+		s := ctxt.Syms.Lookup("runtime.isarchive", 0)
+		s.Type = sym.SNOPTRDATA
+		s.Attr |= sym.AttrDuplicateOK
+		s.AddUint8(1)
+	}
+
+	// Recalculate pe parameters now that we have ctxt.LinkMode set.
+	if ctxt.HeadType == objabi.Hwindows {
+		Peinit(ctxt)
+	}
+
+	if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
+		*FlagTextAddr = 0
+	}
+
+	// If there are no dynamic libraries needed, gcc disables dynamic linking.
+	// Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
+	// assumes that a dynamic binary always refers to at least one dynamic library.
+	// Rather than be a source of test cases for glibc, disable dynamic linking
+	// the same way that gcc would.
+	//
+	// Exception: on OS X, programs such as Shark only work with dynamic
+	// binaries, so leave it enabled on OS X (Mach-O) binaries.
+	// Also leave it enabled on Solaris which doesn't support
+	// statically linked binaries.
+	if ctxt.BuildMode == BuildModeExe {
+		if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris {
+			*FlagD = true
+		}
+	}
+
+	if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
+		toc := ctxt.Syms.Lookup(".TOC.", 0)
+		toc.Type = sym.SDYNIMPORT
 	}
 
 	// The Android Q linker started to complain about underalignment of the our TLS
@@ -531,93 +650,6 @@
 	moduledata.Attr |= sym.AttrReachable
 	ctxt.Moduledata = moduledata
 
-	// Now that we know the link mode, trim the dynexp list.
-	x := sym.AttrCgoExportDynamic
-
-	if ctxt.LinkMode == LinkExternal {
-		x = sym.AttrCgoExportStatic
-	}
-	w := 0
-	for i := range dynexp {
-		if dynexp[i].Attr&x != 0 {
-			dynexp[w] = dynexp[i]
-			w++
-		}
-	}
-	dynexp = dynexp[:w]
-
-	// In internal link mode, read the host object files.
-	if ctxt.LinkMode == LinkInternal {
-		hostobjs(ctxt)
-
-		// 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
-				}
-			}
-		}
-		if any {
-			if *flagLibGCC == "" {
-				*flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
-			}
-			if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
-				// On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
-				// In this case we fail to load libgcc.a and can encounter link
-				// errors - see if we can find libcompiler_rt.a instead.
-				*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
-			}
-			if *flagLibGCC != "none" {
-				hostArchive(ctxt, *flagLibGCC)
-			}
-			if ctxt.HeadType == objabi.Hwindows {
-				if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
-					hostArchive(ctxt, p)
-				}
-				if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
-					hostArchive(ctxt, p)
-				}
-				// TODO: maybe do something similar to peimporteddlls to collect all lib names
-				// and try link them all to final exe just like libmingwex.a and libmingw32.a:
-				/*
-					for:
-					#cgo windows LDFLAGS: -lmsvcrt -lm
-					import:
-					libmsvcrt.a libm.a
-				*/
-			}
-		}
-	} else {
-		hostlinksetup(ctxt)
-	}
-
-	// We've loaded all the code now.
-	ctxt.Loaded = true
-
-	// Record whether we can use plugins.
-	ctxt.canUsePlugins = (ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil)
-
-	// If there are no dynamic libraries needed, gcc disables dynamic linking.
-	// Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
-	// assumes that a dynamic binary always refers to at least one dynamic library.
-	// Rather than be a source of test cases for glibc, disable dynamic linking
-	// the same way that gcc would.
-	//
-	// Exception: on OS X, programs such as Shark only work with dynamic
-	// binaries, so leave it enabled on OS X (Mach-O) binaries.
-	// Also leave it enabled on Solaris which doesn't support
-	// statically linked binaries.
-	if ctxt.BuildMode == BuildModeExe {
-		if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris {
-			*FlagD = true
-		}
-	}
-
 	// If package versioning is required, generate a hash of the
 	// packages used in the link.
 	if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
@@ -635,59 +667,6 @@
 			got.Attr |= sym.AttrReachable
 		}
 	}
-
-	importcycles()
-
-	// put 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)
-	for _, doInternal := range [2]bool{true, false} {
-		for _, lib := range ctxt.Library {
-			if isRuntimeDepPkg(lib.Pkg) != doInternal {
-				continue
-			}
-			ctxt.Textp = append(ctxt.Textp, lib.Textp...)
-			for _, s := range lib.DupTextSyms {
-				if !s.Attr.OnList() {
-					ctxt.Textp = append(ctxt.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)
-				}
-			}
-		}
-	}
-
-	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
-	}
-
-	// Resolve ABI aliases in the list of cgo-exported functions.
-	// This is necessary because we load the ABI0 symbol for all
-	// cgo exports.
-	for i, s := range dynexp {
-		if s.Type != sym.SABIALIAS {
-			continue
-		}
-		t := resolveABIAlias(s)
-		t.Attr |= s.Attr
-		t.SetExtname(s.Extname())
-		dynexp[i] = t
-	}
 }
 
 // mangleTypeSym shortens the names of symbols that represent Go types
@@ -1773,7 +1752,12 @@
 	default:
 		log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
 	}
-	c := objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
+	var c int
+	if *flagNewobj {
+		objfile.LoadNew(ctxt.loader, 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
 	addImports(ctxt, lib, pn)
 	return nil
@@ -1928,7 +1912,7 @@
 			ver = sym.SymVerABIInternal
 		}
 
-		lsym := ctxt.Syms.Lookup(elfsym.Name, ver)
+		lsym := ctxt.LookupOrCreate(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
@@ -2545,3 +2529,30 @@
 	mark[lib] = visited
 	*order = append(*order, lib)
 }
+
+func (ctxt *Link) loadlibfull() {
+	// Load full symbol contents, resolve indexed references.
+	objfile.LoadReloc(ctxt.loader)
+	objfile.LoadFull(ctxt.loader)
+
+	// For now, add all symbols to ctxt.Syms.
+	for _, s := range ctxt.loader.Syms {
+		if s != nil && s.Name != "" {
+			ctxt.Syms.Add(s)
+		}
+	}
+
+	// Drop the reference.
+	ctxt.loader = nil
+
+	addToTextp(ctxt)
+}
+
+func (ctxt *Link) dumpsyms() {
+	for _, s := range ctxt.Syms.Allsym {
+		fmt.Printf("%s %s %p\n", s, s.Type, s)
+		for i := range s.R {
+			fmt.Println("\t", s.R[i].Type, s.R[i].Sym)
+		}
+	}
+}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 53092d2..dfb686f 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -35,6 +35,7 @@
 	"cmd/internal/obj"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
+	"cmd/link/internal/objfile"
 	"cmd/link/internal/sym"
 	"debug/elf"
 	"fmt"
@@ -96,6 +97,9 @@
 	runtimeCU *sym.CompilationUnit   // One of the runtime CUs, the last one seen.
 
 	relocbuf []byte // temporary buffer for applying relocations
+
+	loader  *objfile.Loader
+	cgodata [][3]string // cgo directives to load, three strings are args for loadcgo
 }
 
 type unresolvedSymKey struct {
@@ -172,3 +176,32 @@
 	}
 	l.ImportStrings = nil
 }
+
+// convenient helper during the transition period.
+func (ctxt *Link) Lookup(name string, ver int) *sym.Symbol {
+	if *flagNewobj {
+		i := ctxt.loader.Lookup(name, ver)
+		if i == 0 {
+			return nil
+		}
+		return ctxt.loader.Syms[i]
+	} else {
+		return ctxt.Syms.ROLookup(name, ver)
+	}
+}
+
+// convenient helper during the transition period.
+func (ctxt *Link) LookupOrCreate(name string, ver int) *sym.Symbol {
+	if *flagNewobj {
+		i := ctxt.loader.Lookup(name, ver)
+		if i != 0 {
+			return ctxt.loader.Syms[i]
+		}
+		ctxt.loader.AddExtSym(name, ver)
+		s := ctxt.Syms.Newsym(name, ver)
+		ctxt.loader.Syms = append(ctxt.loader.Syms, s)
+		return s
+	} else {
+		return ctxt.Syms.Lookup(name, ver)
+	}
+}
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index 67e5ef9..e667afe 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -86,6 +86,7 @@
 	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`")
@@ -207,8 +208,13 @@
 	}
 	ctxt.loadlib()
 
-	ctxt.dostrdata()
 	deadcode(ctxt)
+	if *flagNewobj {
+		ctxt.loadlibfull() // XXX do it here for now
+	}
+	ctxt.linksetup()
+	ctxt.dostrdata()
+
 	dwarfGenerateDebugInfo(ctxt)
 	if objabi.Fieldtrack_enabled != 0 {
 		fieldtrack(ctxt)
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index d686a8a..98305c8 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -693,7 +693,7 @@
 	// creating the moduledata from scratch and it does not have a
 	// compiler-provided size, so read it from the type data.
 	moduledatatype := ctxt.Syms.ROLookup("type.runtime.moduledata", 0)
-	moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype)
+	moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype.P)
 	moduledata.Grow(moduledata.Size)
 
 	lastmoduledatap := ctxt.Syms.Lookup("runtime.lastmoduledatap", 0)
diff --git a/src/cmd/link/internal/objfile/objfile2.go b/src/cmd/link/internal/objfile/objfile2.go
new file mode 100644
index 0000000..ad3ea85
--- /dev/null
+++ b/src/cmd/link/internal/objfile/objfile2.go
@@ -0,0 +1,740 @@
+// 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 objfile
+
+import (
+	"bytes"
+	"cmd/internal/bio"
+	"cmd/internal/dwarf"
+	"cmd/internal/goobj2"
+	"cmd/internal/obj"
+	"cmd/internal/objabi"
+	"cmd/internal/sys"
+	"cmd/link/internal/sym"
+	"fmt"
+	"log"
+	"os"
+	"sort"
+	"strconv"
+	"strings"
+)
+
+var _ = fmt.Print
+
+// Sym encapsulates a global symbol index, used to identify a specific
+// Go symbol. The 0-valued Sym is corresponds to an invalid symbol.
+type Sym int
+
+// Relocs encapsulates the set of relocations on a given symbol; an
+// instance of this type is returned by the Loader Relocs() method.
+type Relocs struct {
+	Count int // number of relocs
+
+	li int      // local index of symbol whose relocs we're examining
+	r  *oReader // object reader for containing package
+	l  *Loader  // loader
+}
+
+// Reloc contains the payload for a specific relocation.
+// TODO: replace this with sym.Reloc, once we change the
+// relocation target from "*sym.Symbol" to "loader.Sym" in sym.Reloc.
+type Reloc struct {
+	Off  int32            // offset to rewrite
+	Size uint8            // number of bytes to rewrite: 0, 1, 2, or 4
+	Type objabi.RelocType // the relocation type
+	Add  int64            // addend
+	Sym  Sym              // global index of symbol the reloc addresses
+}
+
+// oReader is a wrapper type of obj.Reader, along with some
+// extra information.
+// TODO: rename to objReader once the old one is gone?
+type oReader struct {
+	*goobj2.Reader
+	unit      *sym.CompilationUnit
+	version   int // version of static symbol
+	pkgprefix string
+}
+
+type objIdx struct {
+	r *oReader
+	i Sym // start index
+}
+
+type nameVer struct {
+	name string
+	v    int
+}
+
+type bitmap []uint32
+
+// set the i-th bit.
+func (bm bitmap) Set(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 {
+	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)
+}
+
+// A Loader loads new object files and resolves indexed symbol references.
+//
+// TODO: describe local-global index mapping.
+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
+
+	symsByName map[nameVer]Sym // map symbol name to index
+
+	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.
+
+	Reachable bitmap // bitmap of reachable symbols, indexed by global index
+}
+
+func NewLoader() *Loader {
+	return &Loader{
+		start:      make(map[*oReader]Sym),
+		objs:       []objIdx{{nil, 0}},
+		symsByName: make(map[nameVer]Sym),
+		objByPkg:   make(map[string]*oReader),
+		Syms:       []*sym.Symbol{nil},
+	}
+}
+
+// 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 {
+		panic("already added")
+	}
+	pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
+	if _, ok := l.objByPkg[pkg]; !ok {
+		l.objByPkg[pkg] = r
+	}
+	n := r.NSym() + r.NNonpkgdef()
+	i := l.max + 1
+	l.start[r] = i
+	l.objs = append(l.objs, objIdx{r, i})
+	l.max += Sym(n)
+	return i
+}
+
+// Add a symbol with a given index, return if it is added.
+func (l *Loader) AddSym(name string, ver int, i Sym, dupok bool) bool {
+	if l.extStart != 0 {
+		panic("AddSym called after AddExtSym is called")
+	}
+	nv := nameVer{name, ver}
+	if _, ok := l.symsByName[nv]; ok {
+		if dupok || true { // TODO: "true" isn't quite right. need to implement "overwrite" logic.
+			return false
+		}
+		panic("duplicated definition of symbol " + name)
+	}
+	l.symsByName[nv] = i
+	return true
+}
+
+// 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 {
+	nv := nameVer{name, ver}
+	if _, ok := l.symsByName[nv]; ok {
+		return 0
+	}
+	i := l.max + 1
+	l.symsByName[nv] = i
+	l.max++
+	if l.extStart == 0 {
+		l.extStart = i
+	}
+	return i
+}
+
+// Convert a local index to a global index.
+func (l *Loader) ToGlobal(r *oReader, i int) Sym {
+	return l.StartIndex(r) + Sym(i)
+}
+
+// Convert a global index to a local index.
+func (l *Loader) ToLocal(i Sym) (*oReader, int) {
+	if l.extStart != 0 && i >= l.extStart {
+		return nil, int(i - l.extStart)
+	}
+	// 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
+	})
+	return l.objs[k-1].r, int(i - l.objs[k-1].i)
+}
+
+// Resolve a local symbol reference. Return global index.
+func (l *Loader) Resolve(r *oReader, s goobj2.SymRef) Sym {
+	var rr *oReader
+	switch p := s.PkgIdx; p {
+	case goobj2.PkgIdxInvalid:
+		if s.SymIdx != 0 {
+			panic("bad sym ref")
+		}
+		return 0
+	case goobj2.PkgIdxNone:
+		// 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)
+		nv := nameVer{name, v}
+		return l.symsByName[nv]
+	case goobj2.PkgIdxBuiltin:
+		panic("PkgIdxBuiltin not used")
+	case goobj2.PkgIdxSelf:
+		rr = r
+	default:
+		pkg := r.Pkg(int(p))
+		var ok bool
+		rr, ok = l.objByPkg[pkg]
+		if !ok {
+			log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
+		}
+	}
+	return l.ToGlobal(rr, int(s.SymIdx))
+}
+
+// Look up a symbol by name, return global index, or 0 if not found.
+// This is more like Syms.ROLookup than Lookup -- it doesn't create
+// new symbol.
+func (l *Loader) Lookup(name string, ver int) Sym {
+	nv := nameVer{name, ver}
+	return l.symsByName[nv]
+}
+
+// Number of total symbols.
+func (l *Loader) NSym() int {
+	return int(l.max + 1)
+}
+
+// Returns the raw (unpatched) name of the i-th symbol.
+func (l *Loader) RawSymName(i Sym) string {
+	r, li := l.ToLocal(i)
+	if r == nil {
+		return ""
+	}
+	osym := goobj2.Sym{}
+	osym.Read(r.Reader, r.SymOff(li))
+	return osym.Name
+}
+
+// Returns the (patched) name of the i-th symbol.
+func (l *Loader) SymName(i Sym) string {
+	r, li := l.ToLocal(i)
+	if r == nil {
+		return ""
+	}
+	osym := goobj2.Sym{}
+	osym.Read(r.Reader, r.SymOff(li))
+	return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+}
+
+// Returns the type of the i-th symbol.
+func (l *Loader) SymType(i Sym) sym.SymKind {
+	r, li := l.ToLocal(i)
+	if r == nil {
+		return 0
+	}
+	osym := goobj2.Sym{}
+	osym.Read(r.Reader, r.SymOff(li))
+	return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+}
+
+// Returns the number of aux symbols given a global index.
+func (l *Loader) NAux(i Sym) int {
+	r, li := l.ToLocal(i)
+	if r == nil {
+		return 0
+	}
+	return r.NAux(li)
+}
+
+// Returns the referred symbol of the j-th aux symbol of the i-th
+// symbol.
+func (l *Loader) AuxSym(i Sym, j int) Sym {
+	r, li := l.ToLocal(i)
+	a := goobj2.Aux{}
+	a.Read(r.Reader, r.AuxOff(li, j))
+	return l.Resolve(r, a.Sym)
+}
+
+// Initialize Reachable bitmap for running deadcode pass.
+func (l *Loader) InitReachable() {
+	l.Reachable = makeBitmap(l.NSym())
+}
+
+// At method returns the j-th reloc for a global symbol.
+func (relocs *Relocs) At(j int) Reloc {
+	rel := goobj2.Reloc{}
+	rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
+	target := relocs.l.Resolve(relocs.r, rel.Sym)
+	return Reloc{
+		Off:  rel.Off,
+		Size: rel.Siz,
+		Type: objabi.RelocType(rel.Type),
+		Add:  rel.Add,
+		Sym:  target,
+	}
+}
+
+// Relocs returns a Relocs object for the given global sym.
+func (l *Loader) Relocs(i Sym) Relocs {
+	r, li := l.ToLocal(i)
+	if r == nil {
+		return Relocs{}
+	}
+	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 {
+	return Relocs{
+		Count: r.NReloc(li),
+		li:    li,
+		r:     r,
+		l:     l,
+	}
+}
+
+// Preload a package: add autolibs, add symbols to the symbol table.
+// Does not read symbol data yet.
+func LoadNew(l *Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
+	roObject, readonly, err := f.Slice(uint64(length))
+	if err != nil {
+		log.Fatal("cannot read object file:", err)
+	}
+	r := goobj2.NewReaderFromBytes(roObject, readonly)
+	if r == nil {
+		panic("cannot read object file")
+	}
+	localSymVersion := syms.IncVersion()
+	pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+	or := &oReader{r, unit, localSymVersion, pkgprefix}
+
+	// Autolib
+	npkg := r.NPkg()
+	lib.ImportStrings = append(lib.ImportStrings, make([]string, npkg-1)...)[:len(lib.ImportStrings)]
+	for i := 1; i < npkg; i++ {
+		lib.ImportStrings = append(lib.ImportStrings, r.Pkg(i))
+	}
+
+	// DWARF file table
+	nfile := r.NDwarfFile()
+	unit.DWARFFileTable = make([]string, nfile)
+	for i := range unit.DWARFFileTable {
+		unit.DWARFFileTable[i] = r.DwarfFile(i)
+	}
+
+	istart := l.AddObj(lib.Pkg, or)
+
+	ndef := r.NSym()
+	nnonpkgdef := r.NNonpkgdef()
+
+	// XXX add all symbols for now
+	l.Syms = append(l.Syms, make([]*sym.Symbol, ndef+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.Flag&goobj2.SymFlagDupok != 0
+		if l.AddSym(name, v, istart+Sym(i), dupok) {
+			s := syms.Newsym(name, v)
+			preprocess(arch, s) // TODO: put this at a better place
+			l.Syms[istart+Sym(i)] = s
+		}
+	}
+
+	// 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 LoadRefs(l *Loader, arch *sys.Arch, syms *sym.Symbols) {
+	for _, o := range l.objs[1:] {
+		loadObjRefs(l, o.r, arch, syms)
+	}
+}
+
+func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) {
+	lib := r.unit.Lib
+	pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+	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, "\"\".", pkgprefix, -1)
+		v := abiToVer(osym.ABI, r.version)
+		if ii := l.AddExtSym(name, v); ii != 0 {
+			s := syms.Newsym(name, v)
+			preprocess(arch, s) // TODO: put this at a better place
+			if ii != Sym(len(l.Syms)) {
+				panic("AddExtSym returned bad index")
+			}
+			l.Syms = append(l.Syms, s)
+		}
+	}
+}
+
+func abiToVer(abi uint16, localSymVersion int) int {
+	var v int
+	if abi == goobj2.SymABIstatic {
+		// Static
+		v = 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: %d", abi)
+	}
+	return v
+}
+
+func preprocess(arch *sys.Arch, s *sym.Symbol) {
+	if s.Name != "" && 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(arch, uint32(x))
+		case "$f64.", "$i64.":
+			s.AddUint64(arch, x)
+		default:
+			log.Panicf("unrecognized $-symbol: %s", s.Name)
+		}
+		s.Attr.Set(sym.AttrReachable, false)
+	}
+}
+
+// Load relocations for building the dependency graph in deadcode pass.
+// For now, we load symbol types, relocations, gotype, and the contents
+// of type symbols, which are needed in deadcode.
+func LoadReloc(l *Loader) {
+	for _, o := range l.objs[1:] {
+		loadObjReloc(l, o.r)
+	}
+}
+
+func loadObjReloc(l *Loader, r *oReader) {
+	lib := r.unit.Lib
+	pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+	istart := l.StartIndex(r)
+
+	resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
+		i := l.Resolve(r, s)
+		return l.Syms[i]
+	}
+
+	for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+		s := l.Syms[istart+Sym(i)]
+		if s == nil || s.Name == "" {
+			continue
+		}
+
+		osym := goobj2.Sym{}
+		osym.Read(r.Reader, r.SymOff(i))
+		name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1)
+		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 s.Type != 0 && s.Type != sym.SXREF {
+			fmt.Println("symbol already processed:", lib, i, s)
+			panic("symbol already processed")
+		}
+
+		t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+		if t == sym.SXREF {
+			log.Fatalf("bad sxref")
+		}
+		if t == 0 {
+			log.Fatalf("missing type for %s in %s", s.Name, lib)
+		}
+		if !s.Attr.Reachable() && (t < sym.SDWARFSECT || t > sym.SDWARFLINES) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) {
+			// No need to load unreachable symbols.
+			// XXX DWARF symbols may be used but are not marked reachable.
+			// XXX type symbol's content may be needed in DWARF code, but they are not marked.
+			continue
+		}
+		if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
+			t = s.Type
+		}
+		s.Type = t
+		s.Unit = r.unit
+
+		// Relocs
+		relocs := l.relocs(r, i)
+		s.R = make([]sym.Reloc, relocs.Count)
+		for j := range s.R {
+			r := relocs.At(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],
+			}
+		}
+
+		// Aux symbol
+		naux := r.NAux(i)
+		for j := 0; j < naux; j++ {
+			a := goobj2.Aux{}
+			a.Read(r.Reader, r.AuxOff(i, j))
+			switch a.Type {
+			case goobj2.AuxGotype:
+				typ := resolveSymRef(a.Sym)
+				if typ != nil {
+					s.Gotype = typ
+				}
+			case goobj2.AuxFuncdata:
+				pc := s.FuncInfo
+				if pc == nil {
+					pc = &sym.FuncInfo{Funcdata: make([]*sym.Symbol, 0, 4)}
+					s.FuncInfo = pc
+				}
+				pc.Funcdata = append(pc.Funcdata, resolveSymRef(a.Sym))
+			}
+		}
+
+		if s.Type == sym.STEXT {
+			dupok := osym.Flag&goobj2.SymFlagDupok != 0
+			if !dupok {
+				if s.Attr.OnList() {
+					log.Fatalf("symbol %s listed multiple times", s.Name)
+				}
+				s.Attr |= sym.AttrOnList
+				lib.Textp = append(lib.Textp, s)
+			} else {
+				// there may ba a dup in another package
+				// put into a temp list and add to text later
+				lib.DupTextSyms = append(lib.DupTextSyms, s)
+			}
+		}
+	}
+}
+
+// Load full contents.
+// TODO: For now, some contents are already load in LoadReloc. Maybe
+// we should combine LoadReloc back into this, once we rewrite deadcode
+// pass to use index directly.
+func LoadFull(l *Loader) {
+	for _, o := range l.objs[1:] {
+		loadObjFull(l, o.r)
+	}
+}
+
+func loadObjFull(l *Loader, r *oReader) {
+	lib := r.unit.Lib
+	pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+	istart := l.StartIndex(r)
+
+	resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
+		i := l.Resolve(r, s)
+		return l.Syms[i]
+	}
+
+	pcdataBase := r.PcdataBase()
+	for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+		s := l.Syms[istart+Sym(i)]
+		if s == nil || s.Name == "" {
+			continue
+		}
+		if !s.Attr.Reachable() && (s.Type < sym.SDWARFSECT || s.Type > sym.SDWARFLINES) && !(s.Type == sym.SRODATA && strings.HasPrefix(s.Name, "type.")) {
+			// No need to load unreachable symbols.
+			// XXX DWARF symbols may be used but are not marked reachable.
+			// XXX type symbol's content may be needed in DWARF code, but they are not marked.
+			continue
+		}
+
+		osym := goobj2.Sym{}
+		osym.Read(r.Reader, r.SymOff(i))
+		name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1)
+		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")
+		}
+
+		dupok := osym.Flag&goobj2.SymFlagDupok != 0
+		local := osym.Flag&goobj2.SymFlagLocal != 0
+		makeTypelink := osym.Flag&goobj2.SymFlagTypelink != 0
+		size := osym.Siz
+
+		// Symbol data
+		s.P = r.Data(i)
+		s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
+
+		// Aux symbol info
+		isym := -1
+		naux := r.NAux(i)
+		for j := 0; j < naux; j++ {
+			a := goobj2.Aux{}
+			a.Read(r.Reader, r.AuxOff(i, j))
+			switch a.Type {
+			case goobj2.AuxGotype, goobj2.AuxFuncdata:
+				// already loaded
+			case goobj2.AuxFuncInfo:
+				if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
+					panic("funcinfo symbol not defined in current package")
+				}
+				isym = int(a.Sym.SymIdx)
+			default:
+				panic("unknown aux type")
+			}
+		}
+
+		s.File = pkgprefix[:len(pkgprefix)-1]
+		if dupok {
+			s.Attr |= sym.AttrDuplicateOK
+		}
+		if s.Size < int64(size) {
+			s.Size = int64(size)
+		}
+		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
+		}
+
+		// FuncInfo
+		if isym == -1 {
+			continue
+		}
+		b := r.Data(isym)
+		info := goobj2.FuncInfo{}
+		info.Read(b)
+
+		if info.NoSplit != 0 {
+			s.Attr |= sym.AttrNoSplit
+		}
+		if info.Flags&goobj2.FuncFlagReflectMethod != 0 {
+			s.Attr |= sym.AttrReflectMethod
+		}
+		if info.Flags&goobj2.FuncFlagShared != 0 {
+			s.Attr |= sym.AttrShared
+		}
+		if info.Flags&goobj2.FuncFlagTopFrame != 0 {
+			s.Attr |= sym.AttrTopFrame
+		}
+
+		info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
+		pc := s.FuncInfo
+		if pc == nil {
+			pc = &sym.FuncInfo{}
+			s.FuncInfo = pc
+		}
+		pc.Args = int32(info.Args)
+		pc.Locals = int32(info.Locals)
+		pc.Pcdata = make([]sym.Pcdata, len(info.Pcdata)-1) // -1 as we appended one above
+		pc.Funcdataoff = make([]int64, len(info.Funcdataoff))
+		pc.File = make([]*sym.Symbol, len(info.File))
+		pc.Pcsp.P = r.BytesAt(pcdataBase+info.Pcsp, int(info.Pcfile-info.Pcsp))
+		pc.Pcfile.P = r.BytesAt(pcdataBase+info.Pcfile, int(info.Pcline-info.Pcfile))
+		pc.Pcline.P = r.BytesAt(pcdataBase+info.Pcline, int(info.Pcinline-info.Pcline))
+		pc.Pcinline.P = r.BytesAt(pcdataBase+info.Pcinline, int(info.Pcdata[0]-info.Pcinline))
+		for k := range pc.Pcdata {
+			pc.Pcdata[k].P = r.BytesAt(pcdataBase+info.Pcdata[k], int(info.Pcdata[k+1]-info.Pcdata[k]))
+		}
+		for k := range pc.Funcdataoff {
+			pc.Funcdataoff[k] = int64(info.Funcdataoff[k])
+		}
+		for k := range pc.File {
+			pc.File[k] = resolveSymRef(info.File[k])
+		}
+	}
+}
+
+func patchDWARFName(s *sym.Symbol, r *oReader) {
+	// 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.pkgprefix)
+	patched := bytes.Replace(s.P[:e], emptyPkg, pkgprefix, -1)
+
+	s.P = append(patched, s.P[e:]...)
+	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)
+		}
+	}
+}
diff --git a/src/cmd/link/internal/sym/symbols.go b/src/cmd/link/internal/sym/symbols.go
index f0fcf23..e772496 100644
--- a/src/cmd/link/internal/sym/symbols.go
+++ b/src/cmd/link/internal/sym/symbols.go
@@ -86,6 +86,17 @@
 	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))