cmd/link: apply relocations later

Move the phase of applying relocations later, after the sections
and segments are written to the mmap'd output region. Then apply
relocations directly in the output region, instead of the input.
So the input slices we read in don't need to be modified.

This is in preparation for mmap'ing input files read-only.

Change-Id: If9c80657b4469da36aec5a9ab6acf664f5af8fa0
Reviewed-on: https://go-review.googlesource.com/c/go/+/170739
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 7f4fe71..5d31de9 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -765,7 +765,7 @@
 			out.WriteStringPad("", int(s.Value-addr), pad)
 			addr = s.Value
 		}
-		out.Write(s.P)
+		out.WriteSym(s)
 		addr += int64(len(s.P))
 		if addr < s.Value+s.Size {
 			out.WriteStringPad("", int(s.Value+s.Size-addr), pad)
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index aac3788..e0725a1 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -240,7 +240,6 @@
 	ctxt.dodata()
 	order := ctxt.address()
 	dwarfcompress(ctxt)
-	ctxt.reloc()
 	filesize := ctxt.layout(order)
 
 	// Write out the output file.
@@ -257,9 +256,15 @@
 		outputMmapped = err == nil
 	}
 	if outputMmapped {
+		// Asmb will redirect symbols to the output file mmap, and relocations
+		// will be applied directly there.
 		thearch.Asmb(ctxt)
+		ctxt.reloc()
 		ctxt.Out.Munmap()
 	} else {
+		// If we don't mmap, we need to apply relocations before
+		// writing out.
+		ctxt.reloc()
 		thearch.Asmb(ctxt)
 	}
 	thearch.Asmb2(ctxt)
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
index f1b5d74..3efd43d 100644
--- a/src/cmd/link/internal/ld/outbuf.go
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -7,6 +7,7 @@
 import (
 	"bufio"
 	"cmd/internal/sys"
+	"cmd/link/internal/sym"
 	"encoding/binary"
 	"log"
 	"os"
@@ -148,6 +149,20 @@
 	}
 }
 
+// WriteSym writes the content of a Symbol, then changes the Symbol's content
+// to point to the output buffer that we just wrote, so we can apply further
+// edit to the symbol content.
+// If the output file is not Mmap'd, just writes the content.
+func (out *OutBuf) WriteSym(s *sym.Symbol) {
+	if out.buf != nil {
+		start := out.off
+		out.Write(s.P)
+		s.P = out.buf[start:out.off]
+	} else {
+		out.Write(s.P)
+	}
+}
+
 func (out *OutBuf) Flush() {
 	var err error
 	if out.buf != nil {