1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
From cdc95607940b2acb66cb184dec08d4cc8a469042 Mon Sep 17 00:00:00 2001
From: Meng Zhuo <mengzhuo1203@gmail.com>
Date: Thu, 12 Sep 2024 20:15:56 +0800
Subject: [PATCH] all: implement plugin build mode for riscv64
Change-Id: I8d7bbeebbf4a46f2fd8d630b1edbaf79b8ffccc5
Reviewed-on: https://go-review.googlesource.com/c/go/+/420114
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Joel Sing <joel@sing.id.au>
TryBot-Bypass: Joel Sing <joel@sing.id.au>
---
src/cmd/dist/test.go | 2 +-
src/cmd/internal/obj/riscv/obj.go | 121 +++++++++++++++++++++++++++
src/cmd/link/internal/riscv64/asm.go | 21 ++++-
src/internal/platform/supported.go | 2 +-
src/runtime/asm_riscv64.s | 9 ++
5 files changed, 152 insertions(+), 3 deletions(-)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index b137c7db7990bd..6199dbbb93cdcc 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -1767,7 +1767,7 @@ func buildModeSupported(compiler, buildmode, goos, goarch string) bool {
case "plugin":
switch platform {
- case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le",
+ case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/riscv64", "linux/s390x", "linux/ppc64le",
"android/amd64", "android/386",
"darwin/amd64", "darwin/arm64",
"freebsd/amd64":
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index 3a4ab556f7df2b..c41d99c0c7a659 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -157,6 +157,127 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
p.From.Offset = 0
}
}
+
+ if ctxt.Flag_dynlink {
+ rewriteToUseGot(ctxt, p, newprog)
+ }
+}
+
+// Rewrite p, if necessary, to access global data via the global offset table.
+func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
+ if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
+ // ADUFFxxx $offset
+ // becomes
+ // MOV runtime.duffxxx@GOT, REG_TMP
+ // ADD $offset, REG_TMP
+ // CALL REG_TMP
+ var sym *obj.LSym
+ if p.As == obj.ADUFFCOPY {
+ sym = ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
+ } else {
+ sym = ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
+ }
+ offset := p.To.Offset
+ p.As = AMOV
+ p.From.Type = obj.TYPE_MEM
+ p.From.Name = obj.NAME_GOTREF
+ p.From.Sym = sym
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_TMP
+ p.To.Name = obj.NAME_NONE
+ p.To.Offset = 0
+ p.To.Sym = nil
+
+ p1 := obj.Appendp(p, newprog)
+ p1.As = AADD
+ p1.From.Type = obj.TYPE_CONST
+ p1.From.Offset = offset
+ p1.To.Type = obj.TYPE_REG
+ p1.To.Reg = REG_TMP
+
+ p2 := obj.Appendp(p1, newprog)
+ p2.As = obj.ACALL
+ p2.To.Type = obj.TYPE_REG
+ p2.To.Reg = REG_TMP
+ }
+
+ // We only care about global data: NAME_EXTERN means a global
+ // symbol in the Go sense and p.Sym.Local is true for a few internally
+ // defined symbols.
+ if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
+ // MOV $sym, Rx becomes MOV sym@GOT, Rx
+ // MOV $sym+<off>, Rx becomes MOV sym@GOT, Rx; ADD <off>, Rx
+ if p.As != AMOV {
+ ctxt.Diag("don't know how to handle TYPE_ADDR in %v with -dynlink", p)
+ }
+ if p.To.Type != obj.TYPE_REG {
+ ctxt.Diag("don't know how to handle LD instruction to non-register in %v with -dynlink", p)
+ }
+ p.From.Type = obj.TYPE_MEM
+ p.From.Name = obj.NAME_GOTREF
+ if p.From.Offset != 0 {
+ q := obj.Appendp(p, newprog)
+ q.As = AADD
+ q.From.Type = obj.TYPE_CONST
+ q.From.Offset = p.From.Offset
+ q.To = p.To
+ p.From.Offset = 0
+ }
+
+ }
+
+ if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
+ ctxt.Diag("don't know how to handle %v with -dynlink", p)
+ }
+
+ var source *obj.Addr
+ // MOVx sym, Ry becomes MOV sym@GOT, X31; MOVx (X31), Ry
+ // MOVx Ry, sym becomes MOV sym@GOT, X31; MOV Ry, (X31)
+ // An addition may be inserted between the two MOVs if there is an offset.
+ if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
+ if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
+ ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
+ }
+ source = &p.From
+ } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
+ source = &p.To
+ } else {
+ return
+ }
+ if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
+ return
+ }
+ if source.Sym.Type == objabi.STLSBSS {
+ return
+ }
+ if source.Type != obj.TYPE_MEM {
+ ctxt.Diag("don't know how to handle %v with -dynlink", p)
+ }
+ p1 := obj.Appendp(p, newprog)
+ p1.As = AMOV
+ p1.From.Type = obj.TYPE_MEM
+ p1.From.Sym = source.Sym
+ p1.From.Name = obj.NAME_GOTREF
+ p1.To.Type = obj.TYPE_REG
+ p1.To.Reg = REG_TMP
+
+ p2 := obj.Appendp(p1, newprog)
+ p2.As = p.As
+ p2.From = p.From
+ p2.To = p.To
+ if p.From.Name == obj.NAME_EXTERN {
+ p2.From.Reg = REG_TMP
+ p2.From.Name = obj.NAME_NONE
+ p2.From.Sym = nil
+ } else if p.To.Name == obj.NAME_EXTERN {
+ p2.To.Reg = REG_TMP
+ p2.To.Name = obj.NAME_NONE
+ p2.To.Sym = nil
+ } else {
+ return
+ }
+ obj.Nopout(p)
+
}
// addrToReg extracts the register from an Addr, handling special Addr.Names.
diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go
index 8e5d5be41ec0dd..527f09e17c7dba 100644
--- a/src/cmd/link/internal/riscv64/asm.go
+++ b/src/cmd/link/internal/riscv64/asm.go
@@ -20,7 +20,26 @@ import (
// fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils.
const fakeLabelName = ".L0 "
-func gentext(ctxt *ld.Link, ldr *loader.Loader) {}
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ // Emit the following function:
+ //
+ // go.link.addmoduledatainit:
+ // auipc a0, %pcrel_hi(local.moduledata)
+ // addi a0, %pcrel_lo(local.moduledata)
+ // j runtime.addmoduledata
+
+ sz := initfunc.AddSymRef(ctxt.Arch, ctxt.Moduledata, 0, objabi.R_RISCV_PCREL_ITYPE, 8)
+ initfunc.SetUint32(ctxt.Arch, sz-8, 0x00000517) // auipc a0, %pcrel_hi(local.moduledata)
+ initfunc.SetUint32(ctxt.Arch, sz-4, 0x00050513) // addi a0, %pcrel_lo(local.moduledata)
+
+ sz = initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_RISCV_JAL, 4)
+ initfunc.SetUint32(ctxt.Arch, sz-4, 0x0000006f) // j runtime.addmoduledata
+}
func findHI20Reloc(ldr *loader.Loader, s loader.Sym, val int64) *loader.Reloc {
outer := ldr.OuterSym(s)
diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go
index e864c37d6897d4..702a255e4cd928 100644
--- a/src/internal/platform/supported.go
+++ b/src/internal/platform/supported.go
@@ -208,7 +208,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
case "plugin":
switch platform {
- case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le",
+ case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/riscv64", "linux/s390x", "linux/ppc64le",
"android/amd64", "android/386",
"darwin/amd64", "darwin/arm64",
"freebsd/amd64":
diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s
index ef654a3a229e09..71b32304d7b0ae 100644
--- a/src/runtime/asm_riscv64.s
+++ b/src/runtime/asm_riscv64.s
@@ -541,6 +541,15 @@ TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0
// traceback from goexit1 must hit code range of goexit
MOV ZERO, ZERO // NOP
+
+// This is called from .init_array and follows the platform, not the Go ABI.
+TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
+ // Use X31 as it is a scratch register in both the Go ABI and psABI.
+ MOV runtime·lastmoduledatap(SB), X31
+ MOV X10, moduledata_next(X31)
+ MOV X10, runtime·lastmoduledatap(SB)
+ RET
+
// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details.
TEXT ·cgocallback(SB),NOSPLIT,$24-24
|