Skip to content

Commit 59a702a

Browse files
committed
[dev.link] cmd/link: emit Mach-O relocations in mmap
Following CL 240399 and CL 240400, do the same for Mach-O. Linking cmd/compile with external linking, name old time/op new time/op delta Asmb2_GC 32.7ms ± 2% 13.5ms ± 6% -58.56% (p=0.008 n=5+5) name old alloc/op new alloc/op delta Asmb2_GC 16.5MB ± 0% 6.4MB ± 0% -61.15% (p=0.008 n=5+5) Change-Id: I0fd7019d8713d1940e5fbbce4ee8eebd926451a1 Reviewed-on: https://go-review.googlesource.com/c/go/+/241178 Reviewed-by: Jeremy Faller <jeremy@golang.org> Reviewed-by: Than McIntosh <thanm@google.com>
1 parent 041d885 commit 59a702a

File tree

7 files changed

+87
-61
lines changed

7 files changed

+87
-61
lines changed

src/cmd/link/internal/amd64/obj.go

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func Init() (*sys.Arch, ld.Arch) {
6565
Elfsetupplt: elfsetupplt,
6666
Gentext: gentext,
6767
Machoreloc1: machoreloc1,
68+
MachorelocSize: 8,
6869
PEreloc1: pereloc1,
6970
TLSIEtoLE: tlsIEtoLE,
7071

src/cmd/link/internal/arm64/asm.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,6 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
453453
default:
454454
case objabi.R_ARM64_GOTPCREL,
455455
objabi.R_ADDRARM64:
456-
nExtReloc = 2 // need two ELF relocations. see elfreloc1
457456

458457
// set up addend for eventual relocation via outer symbol.
459458
rs, off := ld.FoldSubSymbolOffset(ldr, rs)
@@ -464,6 +463,11 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
464463
}
465464
rr.Xsym = rs
466465

466+
nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
467+
if target.IsDarwin() && rt == objabi.R_ADDRARM64 && rr.Xadd != 0 {
468+
nExtReloc = 4 // need another two relocations for non-zero addend
469+
}
470+
467471
// Note: ld64 currently has a bug that any non-zero addend for BR26 relocation
468472
// will make the linking fail because it thinks the code is not PIC even though
469473
// the BR26 relocation should be fully resolved at link time.

src/cmd/link/internal/arm64/obj.go

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func Init() (*sys.Arch, ld.Arch) {
5555
Elfsetupplt: elfsetupplt,
5656
Gentext: gentext,
5757
Machoreloc1: machoreloc1,
58+
MachorelocSize: 8,
5859

5960
Androiddynld: "/system/bin/linker64",
6061
Linuxdynld: "/lib/ld-linux-aarch64.so.1",

src/cmd/link/internal/ld/asmb.go

+54
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ package ld
66

77
import (
88
"cmd/internal/objabi"
9+
"cmd/link/internal/loader"
10+
"cmd/link/internal/sym"
911
"fmt"
12+
"runtime"
1013
"sync"
1114
)
1215

@@ -163,3 +166,54 @@ func asmbPlan9(ctxt *Link) {
163166
ctxt.Out.SeekSet(0)
164167
writePlan9Header(ctxt.Out, thearch.Plan9Magic, Entryvalue(ctxt), thearch.Plan9_64Bit)
165168
}
169+
170+
// sizeExtRelocs precomputes the size needed for the reloc records,
171+
// sets the size and offset for relocation records in each section,
172+
// and mmap the output buffer with the proper size.
173+
func sizeExtRelocs(ctxt *Link, relsize uint32) {
174+
if relsize == 0 {
175+
panic("sizeExtRelocs: relocation size not set")
176+
}
177+
var sz int64
178+
for _, seg := range Segments {
179+
for _, sect := range seg.Sections {
180+
sect.Reloff = uint64(ctxt.Out.Offset() + sz)
181+
sect.Rellen = uint64(relsize * sect.Relcount)
182+
sz += int64(sect.Rellen)
183+
}
184+
}
185+
filesz := ctxt.Out.Offset() + sz
186+
ctxt.Out.Mmap(uint64(filesz))
187+
}
188+
189+
// relocSectFn wraps the function writing relocations of a section
190+
// for parallel execution. Returns the wrapped function and a wait
191+
// group for which the caller should wait.
192+
func relocSectFn(ctxt *Link, relocSect func(*Link, *OutBuf, *sym.Section, []loader.Sym)) (func(*Link, *sym.Section, []loader.Sym), *sync.WaitGroup) {
193+
var fn func(ctxt *Link, sect *sym.Section, syms []loader.Sym)
194+
var wg sync.WaitGroup
195+
var sem chan int
196+
if ctxt.Out.isMmapped() {
197+
// Write sections in parallel.
198+
sem = make(chan int, 2*runtime.GOMAXPROCS(0))
199+
fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
200+
wg.Add(1)
201+
sem <- 1
202+
out, err := ctxt.Out.View(sect.Reloff)
203+
if err != nil {
204+
panic(err)
205+
}
206+
go func() {
207+
relocSect(ctxt, out, sect, syms)
208+
wg.Done()
209+
<-sem
210+
}()
211+
}
212+
} else {
213+
// We cannot Mmap. Write sequentially.
214+
fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
215+
relocSect(ctxt, ctxt.Out, sect, syms)
216+
}
217+
}
218+
return fn, &wg
219+
}

src/cmd/link/internal/ld/elf.go

+3-44
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ import (
1313
"encoding/binary"
1414
"encoding/hex"
1515
"path/filepath"
16-
"runtime"
1716
"sort"
1817
"strings"
19-
"sync"
2018
)
2119

2220
/*
@@ -1405,48 +1403,9 @@ func elfEmitReloc(ctxt *Link) {
14051403
ctxt.Out.Write8(0)
14061404
}
14071405

1408-
// Precompute the size needed for the reloc records if we can
1409-
// Mmap the output buffer with the proper size.
1410-
if thearch.ElfrelocSize == 0 {
1411-
panic("elfEmitReloc: ELF relocation size not set")
1412-
}
1413-
var sz int64
1414-
for _, seg := range Segments {
1415-
for _, sect := range seg.Sections {
1416-
sect.Reloff = uint64(ctxt.Out.Offset() + sz)
1417-
sect.Rellen = uint64(thearch.ElfrelocSize * sect.Relcount)
1418-
sz += int64(sect.Rellen)
1419-
}
1420-
}
1421-
filesz := ctxt.Out.Offset() + sz
1422-
ctxt.Out.Mmap(uint64(filesz))
1423-
1424-
// Now emits the records.
1425-
var relocSect func(ctxt *Link, sect *sym.Section, syms []loader.Sym)
1426-
var wg sync.WaitGroup
1427-
var sem chan int
1428-
if ctxt.Out.isMmapped() {
1429-
// Write sections in parallel.
1430-
sem = make(chan int, 2*runtime.GOMAXPROCS(0))
1431-
relocSect = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
1432-
wg.Add(1)
1433-
sem <- 1
1434-
out, err := ctxt.Out.View(sect.Reloff)
1435-
if err != nil {
1436-
panic(err)
1437-
}
1438-
go func() {
1439-
elfrelocsect(ctxt, out, sect, syms)
1440-
wg.Done()
1441-
<-sem
1442-
}()
1443-
}
1444-
} else {
1445-
// We cannot Mmap. Write sequentially.
1446-
relocSect = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
1447-
elfrelocsect(ctxt, ctxt.Out, sect, syms)
1448-
}
1449-
}
1406+
sizeExtRelocs(ctxt, thearch.ElfrelocSize)
1407+
relocSect, wg := relocSectFn(ctxt, elfrelocsect)
1408+
14501409
for _, sect := range Segtext.Sections {
14511410
if sect.Name == ".text" {
14521411
relocSect(ctxt, sect, ctxt.Textp)

src/cmd/link/internal/ld/lib.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,14 @@ type Arch struct {
237237
Asmb func(*Link, *loader.Loader)
238238
Asmb2 func(*Link, *loader.Loader)
239239

240-
Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
241-
ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1.
242-
Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
243-
Gentext func(*Link, *loader.Loader)
244-
Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
245-
PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
246-
Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
240+
Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
241+
ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1.
242+
Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
243+
Gentext func(*Link, *loader.Loader)
244+
Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
245+
MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1.
246+
PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
247+
Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool
247248

248249
// TLSIEtoLE converts a TLS Initial Executable relocation to
249250
// a TLS Local Executable relocation.

src/cmd/link/internal/ld/macho.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -1020,13 +1020,13 @@ func doMachoLink(ctxt *Link) int64 {
10201020
return Rnd(int64(size), int64(*FlagRound))
10211021
}
10221022

1023-
func machorelocsect(ctxt *Link, ldr *loader.Loader, sect *sym.Section, syms []loader.Sym) {
1023+
func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) {
10241024
// If main section has no bits, nothing to relocate.
10251025
if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
10261026
return
10271027
}
1028+
ldr := ctxt.loader
10281029

1029-
sect.Reloff = uint64(ctxt.Out.Offset())
10301030
for i, s := range syms {
10311031
if !ldr.AttrReachable(s) {
10321032
continue
@@ -1055,27 +1055,32 @@ func machorelocsect(ctxt *Link, ldr *loader.Loader, sect *sym.Section, syms []lo
10551055
if !ldr.AttrReachable(r.Xsym) {
10561056
ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(r.Xsym))
10571057
}
1058-
if !thearch.Machoreloc1(ctxt.Arch, ctxt.Out, ldr, s, r, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) {
1058+
if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, r, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) {
10591059
ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym()))
10601060
}
10611061
}
10621062
}
10631063

1064-
sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
1064+
// sanity check
1065+
if uint64(out.Offset()) != sect.Reloff+sect.Rellen {
1066+
panic("machorelocsect: size mismatch")
1067+
}
10651068
}
10661069

10671070
func machoEmitReloc(ctxt *Link) {
10681071
for ctxt.Out.Offset()&7 != 0 {
10691072
ctxt.Out.Write8(0)
10701073
}
10711074

1072-
ldr := ctxt.loader
1073-
machorelocsect(ctxt, ldr, Segtext.Sections[0], ctxt.Textp)
1075+
sizeExtRelocs(ctxt, thearch.MachorelocSize)
1076+
relocSect, wg := relocSectFn(ctxt, machorelocsect)
1077+
1078+
relocSect(ctxt, Segtext.Sections[0], ctxt.Textp)
10741079
for _, sect := range Segtext.Sections[1:] {
1075-
machorelocsect(ctxt, ldr, sect, ctxt.datap)
1080+
relocSect(ctxt, sect, ctxt.datap)
10761081
}
10771082
for _, sect := range Segdata.Sections {
1078-
machorelocsect(ctxt, ldr, sect, ctxt.datap)
1083+
relocSect(ctxt, sect, ctxt.datap)
10791084
}
10801085
for i := 0; i < len(Segdwarf.Sections); i++ {
10811086
sect := Segdwarf.Sections[i]
@@ -1084,8 +1089,9 @@ func machoEmitReloc(ctxt *Link) {
10841089
ctxt.loader.SymSect(si.secSym()) != sect {
10851090
panic("inconsistency between dwarfp and Segdwarf")
10861091
}
1087-
machorelocsect(ctxt, ldr, sect, si.syms)
1092+
relocSect(ctxt, sect, si.syms)
10881093
}
1094+
wg.Wait()
10891095
}
10901096

10911097
// hostobjMachoPlatform returns the first platform load command found

0 commit comments

Comments
 (0)