Skip to content

Commit 15eaa87

Browse files
committed
cmd/link: add support for external linking on linux/riscv64
Fixes #36739 Change-Id: Id7573b343786360c72524f9f27d2a8f08d379cf3 Reviewed-on: https://go-review.googlesource.com/c/go/+/243517 Trust: Joel Sing <[email protected]> Reviewed-by: Cherry Zhang <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Run-TryBot: Cherry Zhang <[email protected]> TryBot-Result: Go Bot <[email protected]>
1 parent 515e6a9 commit 15eaa87

File tree

8 files changed

+169
-10
lines changed

8 files changed

+169
-10
lines changed

src/cmd/dist/test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ func (t *tester) extLink() bool {
921921
"darwin-amd64", "darwin-arm64",
922922
"dragonfly-amd64",
923923
"freebsd-386", "freebsd-amd64", "freebsd-arm",
924-
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-s390x",
924+
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-riscv64", "linux-s390x",
925925
"netbsd-386", "netbsd-amd64",
926926
"openbsd-386", "openbsd-amd64",
927927
"windows-386", "windows-amd64":

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
202202
// Internally linking cgo is incomplete on some architectures.
203203
// https://golang.org/issue/14449
204204
// https://golang.org/issue/21961
205-
if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64) {
205+
if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
206206
return true, objabi.GOARCH + " does not support internal cgo"
207207
}
208208
if iscgo && objabi.GOOS == "android" {
@@ -285,8 +285,6 @@ func determineLinkMode(ctxt *Link) {
285285
}
286286
case LinkExternal:
287287
switch {
288-
case objabi.GOARCH == "riscv64":
289-
Exitf("external linking not supported for %s/riscv64", objabi.GOOS)
290288
case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
291289
Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
292290
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,9 @@ func Elfinit(ctxt *Link) {
506506
if ctxt.Arch.Family == sys.MIPS64 {
507507
ehdr.flags = 0x20000004 /* MIPS 3 CPIC */
508508
}
509+
if ctxt.Arch.Family == sys.RISCV64 {
510+
ehdr.flags = 0x4 /* RISCV Float ABI Double */
511+
}
509512
elf64 = true
510513

511514
ehdr.phoff = ELF64HDRSIZE /* Must be ELF64HDRSIZE: first PHdr must follow ELF header */

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,16 @@ type Arch struct {
247247
Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool
248248
ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1.
249249
Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
250-
Gentext func(*Link, *loader.Loader)
250+
Gentext func(*Link, *loader.Loader) // Generate text before addressing has been performed.
251251
Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
252252
MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1.
253253
PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
254254
Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
255255

256+
// Generate additional symbols for the native symbol table just prior to
257+
// code generation.
258+
GenSymsLate func(*Link, *loader.Loader)
259+
256260
// TLSIEtoLE converts a TLS Initial Executable relocation to
257261
// a TLS Local Executable relocation.
258262
//

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

+8
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,14 @@ func Main(arch *sys.Arch, theArch Arch) {
341341
}(f, s)
342342
}
343343
wg.Wait()
344+
345+
// Generate additional symbols for the native symbol table just prior
346+
// to code generation.
347+
bench.Start("GenSymsLate")
348+
if thearch.GenSymsLate != nil {
349+
thearch.GenSymsLate(ctxt, ctxt.loader)
350+
}
351+
344352
bench.Start("Asmb2")
345353
asmb2(ctxt)
346354

src/cmd/link/internal/loader/loader.go

+5
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,11 @@ func (l *Loader) SortSub(s Sym) Sym {
17911791
return sl[0].s
17921792
}
17931793

1794+
// SortSyms sorts a list of symbols by their value.
1795+
func (l *Loader) SortSyms(ss []Sym) {
1796+
sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) })
1797+
}
1798+
17941799
// Insure that reachable bitmap and its siblings have enough size.
17951800
func (l *Loader) growAttrBitmaps(reqLen int) {
17961801
if reqLen > l.attrReachable.Len() {

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

+143-5
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,138 @@ import (
1111
"cmd/link/internal/ld"
1212
"cmd/link/internal/loader"
1313
"cmd/link/internal/sym"
14+
"debug/elf"
1415
"fmt"
1516
"log"
17+
"sort"
1618
)
1719

20+
// fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils.
21+
const fakeLabelName = ".L0 "
22+
1823
func gentext(ctxt *ld.Link, ldr *loader.Loader) {
1924
}
2025

26+
func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) {
27+
if ctxt.LinkMode != ld.LinkExternal {
28+
return
29+
}
30+
31+
// Generate a local text symbol for each relocation target, as the
32+
// R_RISCV_PCREL_LO12_* relocations generated by elfreloc1 need it.
33+
if ctxt.Textp == nil {
34+
log.Fatal("genSymsLate called before Textp has been assigned")
35+
}
36+
var hi20Syms []loader.Sym
37+
for _, s := range ctxt.Textp {
38+
relocs := ldr.Relocs(s)
39+
for ri := 0; ri < relocs.Count(); ri++ {
40+
r := relocs.At(ri)
41+
if r.Type() != objabi.R_RISCV_PCREL_ITYPE && r.Type() != objabi.R_RISCV_PCREL_STYPE {
42+
continue
43+
}
44+
if r.Off() == 0 && ldr.SymType(s) == sym.STEXT {
45+
// Use the symbol for the function instead of creating
46+
// an overlapping symbol.
47+
continue
48+
}
49+
50+
// TODO(jsing): Consider generating ELF symbols without needing
51+
// loader symbols, in order to reduce memory consumption. This
52+
// would require changes to genelfsym so that it called
53+
// putelfsym and putelfsyment as appropriate.
54+
sb := ldr.MakeSymbolBuilder(fakeLabelName)
55+
sb.SetType(sym.STEXT)
56+
sb.SetValue(ldr.SymValue(s) + int64(r.Off()))
57+
sb.SetLocal(true)
58+
sb.SetReachable(true)
59+
sb.SetVisibilityHidden(true)
60+
sb.SetSect(ldr.SymSect(s))
61+
if outer := ldr.OuterSym(s); outer != 0 {
62+
ldr.AddInteriorSym(outer, sb.Sym())
63+
}
64+
hi20Syms = append(hi20Syms, sb.Sym())
65+
}
66+
}
67+
ctxt.Textp = append(ctxt.Textp, hi20Syms...)
68+
ldr.SortSyms(ctxt.Textp)
69+
}
70+
71+
func findHI20Symbol(ctxt *ld.Link, ldr *loader.Loader, val int64) loader.Sym {
72+
idx := sort.Search(len(ctxt.Textp), func(i int) bool { return ldr.SymValue(ctxt.Textp[i]) >= val })
73+
if idx >= len(ctxt.Textp) {
74+
return 0
75+
}
76+
if s := ctxt.Textp[idx]; ldr.SymValue(s) == val && ldr.SymType(s) == sym.STEXT {
77+
return s
78+
}
79+
return 0
80+
}
81+
2182
func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
22-
log.Fatalf("elfreloc1")
23-
return false
83+
elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
84+
switch r.Type {
85+
case objabi.R_ADDR, objabi.R_DWARFSECREF:
86+
out.Write64(uint64(sectoff))
87+
switch r.Size {
88+
case 4:
89+
out.Write64(uint64(elf.R_RISCV_32) | uint64(elfsym)<<32)
90+
case 8:
91+
out.Write64(uint64(elf.R_RISCV_64) | uint64(elfsym)<<32)
92+
default:
93+
ld.Errorf(nil, "unknown size %d for %v relocation", r.Size, r.Type)
94+
return false
95+
}
96+
out.Write64(uint64(r.Xadd))
97+
98+
case objabi.R_CALLRISCV:
99+
// Call relocations are currently handled via R_RISCV_PCREL_ITYPE.
100+
// TODO(jsing): Consider generating elf.R_RISCV_CALL instead of a
101+
// HI20/LO12_I pair.
102+
103+
case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
104+
// Find the text symbol for the AUIPC instruction targeted
105+
// by this relocation.
106+
relocs := ldr.Relocs(s)
107+
offset := int64(relocs.At(ri).Off())
108+
hi20Sym := findHI20Symbol(ctxt, ldr, ldr.SymValue(s)+offset)
109+
if hi20Sym == 0 {
110+
ld.Errorf(nil, "failed to find text symbol for HI20 relocation at %d (%x)", sectoff, ldr.SymValue(s)+offset)
111+
return false
112+
}
113+
hi20ElfSym := ld.ElfSymForReloc(ctxt, hi20Sym)
114+
115+
// Emit two relocations - a R_RISCV_PCREL_HI20 relocation and a
116+
// corresponding R_RISCV_PCREL_LO12_I or R_RISCV_PCREL_LO12_S relocation.
117+
// Note that the LO12 relocation must point to a target that has a valid
118+
// HI20 PC-relative relocation text symbol, which in turn points to the
119+
// given symbol. For further details see the ELF specification for RISC-V:
120+
//
121+
// https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#pc-relative-symbol-addresses
122+
//
123+
var hiRel, loRel elf.R_RISCV
124+
switch r.Type {
125+
case objabi.R_RISCV_PCREL_ITYPE:
126+
hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_I
127+
case objabi.R_RISCV_PCREL_STYPE:
128+
hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S
129+
}
130+
out.Write64(uint64(sectoff))
131+
out.Write64(uint64(hiRel) | uint64(elfsym)<<32)
132+
out.Write64(uint64(r.Xadd))
133+
out.Write64(uint64(sectoff + 4))
134+
out.Write64(uint64(loRel) | uint64(hi20ElfSym)<<32)
135+
out.Write64(uint64(0))
136+
137+
default:
138+
return false
139+
}
140+
141+
return true
24142
}
25143

26144
func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
27-
log.Fatalf("elfsetuplt")
145+
log.Fatalf("elfsetupplt")
28146
}
29147

30148
func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
@@ -33,8 +151,20 @@ func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtRe
33151
}
34152

35153
func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
36-
rs := r.Sym()
37-
rs = ldr.ResolveABIAlias(rs)
154+
if target.IsExternal() {
155+
switch r.Type() {
156+
case objabi.R_CALLRISCV:
157+
return val, 0, true
158+
159+
case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
160+
return val, 2, true
161+
}
162+
163+
return val, 0, false
164+
}
165+
166+
rs := ldr.ResolveABIAlias(r.Sym())
167+
38168
switch r.Type() {
39169
case objabi.R_CALLRISCV:
40170
// Nothing to do.
@@ -89,3 +219,11 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant
89219
log.Fatalf("archrelocvariant")
90220
return -1
91221
}
222+
223+
func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
224+
switch r.Type() {
225+
case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
226+
return ld.ExtrelocViaOuterSym(ldr, r, s), true
227+
}
228+
return loader.ExtReloc{}, false
229+
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ func Init() (*sys.Arch, ld.Arch) {
2323
Archinit: archinit,
2424
Archreloc: archreloc,
2525
Archrelocvariant: archrelocvariant,
26+
Extreloc: extreloc,
2627
Elfreloc1: elfreloc1,
28+
ElfrelocSize: 24,
2729
Elfsetupplt: elfsetupplt,
2830
Gentext: gentext,
31+
GenSymsLate: genSymsLate,
2932
Machoreloc1: machoreloc1,
3033

3134
Linuxdynld: "/lib/ld.so.1",

0 commit comments

Comments
 (0)