[dev.link] cmd/link: batch allocations when converting external relocations

Change-Id: Iad81cb159e46f694a03d58892ca7dfde3ee3095a
Reviewed-on: https://go-review.googlesource.com/c/go/+/231219
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 60c38cc..e1d5c86 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -284,7 +284,8 @@
 	// the symbol that triggered the marking of symbol K as live.
 	Reachparent []Sym
 
-	relocBatch []sym.Reloc // for bulk allocation of relocations
+	relocBatch    []sym.Reloc    // for bulk allocation of relocations
+	relocExtBatch []sym.RelocExt // for bulk allocation of relocations
 
 	flags uint32
 
@@ -2037,9 +2038,16 @@
 	l.growSyms(l.NSym())
 	l.growSects(l.NSym())
 
+	if needReloc && len(l.extRelocs) != 0 {
+		// If needReloc is true, we are going to convert the loader's
+		// "internal" relocations to sym.Relocs. In this case, external
+		// relocations shouldn't be used.
+		panic("phase error")
+	}
+
 	nr := 0 // total number of sym.Reloc's we'll need
 	for _, o := range l.objs[1:] {
-		nr += loadObjSyms(l, syms, o.r)
+		nr += loadObjSyms(l, syms, o.r, needReloc)
 	}
 
 	// Make a first pass through the external symbols, making
@@ -2052,7 +2060,12 @@
 			continue
 		}
 		pp := l.getPayload(i)
-		nr += len(pp.relocs)
+		if needReloc {
+			nr += len(pp.relocs)
+		}
+		if int(i) < len(l.extRelocs) {
+			nr += len(l.extRelocs[i])
+		}
 		// create and install the sym.Symbol here so that l.Syms will
 		// be fully populated when we do relocation processing and
 		// outer/sub processing below. Note that once we do this,
@@ -2064,8 +2077,11 @@
 	}
 
 	// allocate a single large slab of relocations for all live symbols
-	if needReloc {
+	if nr != 0 {
 		l.relocBatch = make([]sym.Reloc, nr)
+		if len(l.extRelocs) != 0 {
+			l.relocExtBatch = make([]sym.RelocExt, nr)
+		}
 	}
 
 	// convert payload-based external symbols into sym.Symbol-based
@@ -2101,6 +2117,11 @@
 		loadObjFull(l, o.r, needReloc)
 	}
 
+	// Sanity check: we should have consumed all batched allocations.
+	if len(l.relocBatch) != 0 || len(l.relocExtBatch) != 0 {
+		panic("batch allocation mismatch")
+	}
+
 	// Note: resolution of ABI aliases is now also handled in
 	// loader.convertRelocations, so once the host object loaders move
 	// completely to loader.Sym, we can remove the code below.
@@ -2417,7 +2438,7 @@
 // loadObjSyms creates sym.Symbol objects for the live Syms in the
 // object corresponding to object reader "r". Return value is the
 // number of sym.Reloc entries required for all the new symbols.
-func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
+func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader, needReloc bool) int {
 	nr := 0
 	for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
 		gi := r.syms[i]
@@ -2447,7 +2468,12 @@
 		}
 
 		l.addNewSym(gi, name, ver, r.unit, t)
-		nr += r.NReloc(i)
+		if needReloc {
+			nr += r.NReloc(i)
+		}
+		if int(gi) < len(l.extRelocs) {
+			nr += len(l.extRelocs[gi])
+		}
 	}
 	return nr
 }
@@ -2764,13 +2790,18 @@
 	if len(dst.R) != 0 {
 		panic("bad")
 	}
-	dst.R = make([]sym.Reloc, len(extRelocs))
+
+	n := len(extRelocs)
+	batch := l.relocBatch
+	dst.R = batch[:n:n]
+	l.relocBatch = batch[n:]
 	relocs := l.Relocs(src)
 	for i := range dst.R {
 		er := &extRelocs[i]
 		sr := relocs.At2(er.Idx)
 		r := &dst.R[i]
-		r.InitExt()
+		r.RelocExt = &l.relocExtBatch[0]
+		l.relocExtBatch = l.relocExtBatch[1:]
 		r.Off = sr.Off()
 		r.Siz = sr.Siz()
 		r.Type = sr.Type()
diff --git a/src/cmd/link/internal/sym/reloc.go b/src/cmd/link/internal/sym/reloc.go
index 4809db8..f589447 100644
--- a/src/cmd/link/internal/sym/reloc.go
+++ b/src/cmd/link/internal/sym/reloc.go
@@ -28,20 +28,20 @@
 	Type      objabi.RelocType // the relocation type
 	Add       int64            // addend
 	Sym       *Symbol          // symbol the relocation addresses
-	*relocExt                  // extra fields (see below), may be nil, call InitExt before use
+	*RelocExt                  // extra fields (see below), may be nil, call InitExt before use
 }
 
 // relocExt contains extra fields in Reloc that are used only in
 // certain cases.
-type relocExt struct {
+type RelocExt struct {
 	Xadd    int64        // addend passed to external linker
 	Xsym    *Symbol      // symbol passed to external linker
 	Variant RelocVariant // variation on Type, currently used only on PPC64 and S390X
 }
 
 func (r *Reloc) InitExt() {
-	if r.relocExt == nil {
-		r.relocExt = new(relocExt)
+	if r.RelocExt == nil {
+		r.RelocExt = new(RelocExt)
 	}
 }