Skip to content

Commit 393f2bb

Browse files
committed
cmd/dist,cmd/go,runtime: add support for cgo on linux/riscv64
Fixes #36641 Change-Id: I51868d83ce341d78d33b221d184c5a5110c60d14 Reviewed-on: https://go-review.googlesource.com/c/go/+/263598 Trust: Joel Sing <[email protected]> Run-TryBot: Joel Sing <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent 974def8 commit 393f2bb

File tree

11 files changed

+469
-21
lines changed

11 files changed

+469
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build riscv64
6+
// +build !gccgo
7+
8+
#include "textflag.h"
9+
10+
TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
11+
// Rewind stack pointer so anything that happens on the stack
12+
// will clobber the test pattern created by the caller
13+
ADD $(1024*8), X2
14+
15+
// Ask signaller to setgid
16+
MOV $1, X5
17+
FENCE
18+
MOVW X5, ·Baton(SB)
19+
FENCE
20+
21+
// Wait for setgid completion
22+
loop:
23+
FENCE
24+
MOVW ·Baton(SB), X5
25+
OR X6, X6, X6 // hint that we're in a spin loop
26+
BNE ZERO, X5, loop
27+
FENCE
28+
29+
// Restore stack
30+
ADD $(-1024*8), X2
31+
RET

src/cmd/dist/build.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1549,7 +1549,7 @@ var cgoEnabled = map[string]bool{
15491549
"linux/mipsle": true,
15501550
"linux/mips64": true,
15511551
"linux/mips64le": true,
1552-
"linux/riscv64": false, // Issue 36641
1552+
"linux/riscv64": true,
15531553
"linux/s390x": true,
15541554
"linux/sparc64": true,
15551555
"android/386": true,

src/cmd/dist/test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ func (t *tester) internalLink() bool {
953953
// Internally linking cgo is incomplete on some architectures.
954954
// https://golang.org/issue/10373
955955
// https://golang.org/issue/14449
956-
if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" {
956+
if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" {
957957
return false
958958
}
959959
if goos == "aix" {
@@ -1117,8 +1117,8 @@ func (t *tester) cgoTest(dt *distTest) error {
11171117
"android-arm", "android-arm64",
11181118
"dragonfly-amd64",
11191119
"freebsd-386", "freebsd-amd64", "freebsd-arm",
1120-
"linux-386", "linux-amd64", "linux-arm", "linux-ppc64le", "linux-s390x",
1121-
"netbsd-386", "netbsd-amd64", "linux-arm64":
1120+
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x",
1121+
"netbsd-386", "netbsd-amd64":
11221122

11231123
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
11241124
cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external")

src/cmd/go/testdata/script/build_plugin_non_main.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
# Plugins are only supported on linux,cgo and darwin,cgo.
1+
# Plugins are only supported on linux,cgo (!riscv64) and darwin,cgo.
22
[!linux] [!darwin] skip
3+
[linux] [riscv64] skip
34
[!cgo] skip
45

56
go build -n testdep

src/cmd/nm/nm_cgo_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func canInternalLink() bool {
2424
}
2525
case "linux":
2626
switch runtime.GOARCH {
27-
case "arm64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le":
27+
case "arm64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le", "riscv64":
2828
return false
2929
}
3030
case "openbsd":

src/runtime/asm_riscv64.s

+178-12
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
// func rt0_go()
1010
TEXT runtime·rt0_go(SB),NOSPLIT,$0
1111
// X2 = stack; A0 = argc; A1 = argv
12-
1312
ADD $-24, X2
14-
MOV A0, 8(X2) // argc
15-
MOV A1, 16(X2) // argv
13+
MOV A0, 8(X2) // argc
14+
MOV A1, 16(X2) // argv
1615

1716
// create istack out of the given (operating system) stack.
1817
// _cgo_init may update stackguard.
@@ -28,10 +27,10 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
2827
MOV _cgo_init(SB), T0
2928
BEQ T0, ZERO, nocgo
3029

31-
MOV ZERO, A3 // arg 3: not used
32-
MOV ZERO, A2 // arg 2: not used
30+
MOV ZERO, A3 // arg 3: not used
31+
MOV ZERO, A2 // arg 2: not used
3332
MOV $setg_gcc<>(SB), A1 // arg 1: setg
34-
MOV g, A0 // arg 0: G
33+
MOV g, A0 // arg 0: G
3534
JALR RA, T0
3635

3736
nocgo:
@@ -313,10 +312,62 @@ TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8
313312
CALL runtime·badctxt(SB)
314313
RET
315314

315+
// Save state of caller into g->sched. Smashes X31.
316+
TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0
317+
MOV X1, (g_sched+gobuf_pc)(g)
318+
MOV X2, (g_sched+gobuf_sp)(g)
319+
MOV ZERO, (g_sched+gobuf_lr)(g)
320+
MOV ZERO, (g_sched+gobuf_ret)(g)
321+
// Assert ctxt is zero. See func save.
322+
MOV (g_sched+gobuf_ctxt)(g), X31
323+
BEQ ZERO, X31, 2(PC)
324+
CALL runtime·badctxt(SB)
325+
RET
326+
316327
// func asmcgocall(fn, arg unsafe.Pointer) int32
328+
// Call fn(arg) on the scheduler stack,
329+
// aligned appropriately for the gcc ABI.
330+
// See cgocall.go for more details.
317331
TEXT ·asmcgocall(SB),NOSPLIT,$0-20
318-
// TODO(jsing): Add support for cgo - issue #36641.
319-
WORD $0 // crash
332+
MOV fn+0(FP), X5
333+
MOV arg+8(FP), X10
334+
335+
MOV X2, X8 // save original stack pointer
336+
MOV g, X9
337+
338+
// Figure out if we need to switch to m->g0 stack.
339+
// We get called to create new OS threads too, and those
340+
// come in on the m->g0 stack already.
341+
MOV g_m(g), X6
342+
MOV m_g0(X6), X7
343+
BEQ X7, g, g0
344+
345+
CALL gosave<>(SB)
346+
MOV X7, g
347+
CALL runtime·save_g(SB)
348+
MOV (g_sched+gobuf_sp)(g), X2
349+
350+
// Now on a scheduling stack (a pthread-created stack).
351+
g0:
352+
// Save room for two of our pointers.
353+
ADD $-16, X2
354+
MOV X9, 0(X2) // save old g on stack
355+
MOV (g_stack+stack_hi)(X9), X9
356+
SUB X8, X9, X8
357+
MOV X8, 8(X2) // save depth in old g stack (can't just save SP, as stack might be copied during a callback)
358+
359+
JALR RA, (X5)
360+
361+
// Restore g, stack pointer. X10 is return value.
362+
MOV 0(X2), g
363+
CALL runtime·save_g(SB)
364+
MOV (g_stack+stack_hi)(g), X5
365+
MOV 8(X2), X6
366+
SUB X6, X5, X6
367+
MOV X6, X2
368+
369+
MOVW X10, ret+16(FP)
370+
RET
320371

321372
// func asminit()
322373
TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
@@ -444,6 +495,21 @@ CALLFN(·call268435456, 268435456)
444495
CALLFN(·call536870912, 536870912)
445496
CALLFN(·call1073741824, 1073741824)
446497

498+
// Called from cgo wrappers, this function returns g->m->curg.stack.hi.
499+
// Must obey the gcc calling convention.
500+
TEXT _cgo_topofstack(SB),NOSPLIT,$8
501+
// g (X27) and REG_TMP (X31) might be clobbered by load_g.
502+
// X27 is callee-save in the gcc calling convention, so save it.
503+
MOV g, savedX27-8(SP)
504+
505+
CALL runtime·load_g(SB)
506+
MOV g_m(g), X5
507+
MOV m_curg(X5), X5
508+
MOV (g_stack+stack_hi)(X5), X10 // return value in X10
509+
510+
MOV savedX27-8(SP), g
511+
RET
512+
447513
// func goexit(neverCallThisFunction)
448514
// The top-most function running on a goroutine
449515
// returns to goexit+PCQuantum.
@@ -453,11 +519,111 @@ TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0
453519
// traceback from goexit1 must hit code range of goexit
454520
MOV ZERO, ZERO // NOP
455521

456-
// cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
522+
// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
457523
// See cgocall.go for more details.
458-
TEXT ·cgocallback(SB),NOSPLIT,$0-24
459-
// TODO(jsing): Add support for cgo - issue #36641.
460-
WORD $0 // crash
524+
TEXT ·cgocallback(SB),NOSPLIT,$24-24
525+
NO_LOCAL_POINTERS
526+
527+
// Load m and g from thread-local storage.
528+
MOVBU runtime·iscgo(SB), X5
529+
BEQ ZERO, X5, nocgo
530+
CALL runtime·load_g(SB)
531+
nocgo:
532+
533+
// If g is nil, Go did not create the current thread.
534+
// Call needm to obtain one for temporary use.
535+
// In this case, we're running on the thread stack, so there's
536+
// lots of space, but the linker doesn't know. Hide the call from
537+
// the linker analysis by using an indirect call.
538+
BEQ ZERO, g, needm
539+
540+
MOV g_m(g), X5
541+
MOV X5, savedm-8(SP)
542+
JMP havem
543+
544+
needm:
545+
MOV g, savedm-8(SP) // g is zero, so is m.
546+
MOV $runtime·needm(SB), X6
547+
JALR RA, X6
548+
549+
// Set m->sched.sp = SP, so that if a panic happens
550+
// during the function we are about to execute, it will
551+
// have a valid SP to run on the g0 stack.
552+
// The next few lines (after the havem label)
553+
// will save this SP onto the stack and then write
554+
// the same SP back to m->sched.sp. That seems redundant,
555+
// but if an unrecovered panic happens, unwindm will
556+
// restore the g->sched.sp from the stack location
557+
// and then systemstack will try to use it. If we don't set it here,
558+
// that restored SP will be uninitialized (typically 0) and
559+
// will not be usable.
560+
MOV g_m(g), X5
561+
MOV m_g0(X5), X6
562+
MOV X2, (g_sched+gobuf_sp)(X6)
563+
564+
havem:
565+
// Now there's a valid m, and we're running on its m->g0.
566+
// Save current m->g0->sched.sp on stack and then set it to SP.
567+
// Save current sp in m->g0->sched.sp in preparation for
568+
// switch back to m->curg stack.
569+
// NOTE: unwindm knows that the saved g->sched.sp is at 8(X2) aka savedsp-24(SP).
570+
MOV m_g0(X5), X6
571+
MOV (g_sched+gobuf_sp)(X6), X7
572+
MOV X7, savedsp-24(SP) // must match frame size
573+
MOV X2, (g_sched+gobuf_sp)(X6)
574+
575+
// Switch to m->curg stack and call runtime.cgocallbackg.
576+
// Because we are taking over the execution of m->curg
577+
// but *not* resuming what had been running, we need to
578+
// save that information (m->curg->sched) so we can restore it.
579+
// We can restore m->curg->sched.sp easily, because calling
580+
// runtime.cgocallbackg leaves SP unchanged upon return.
581+
// To save m->curg->sched.pc, we push it onto the curg stack and
582+
// open a frame the same size as cgocallback's g0 frame.
583+
// Once we switch to the curg stack, the pushed PC will appear
584+
// to be the return PC of cgocallback, so that the traceback
585+
// will seamlessly trace back into the earlier calls.
586+
MOV m_curg(X5), g
587+
CALL runtime·save_g(SB)
588+
MOV (g_sched+gobuf_sp)(g), X6 // prepare stack as X6
589+
MOV (g_sched+gobuf_pc)(g), X7
590+
MOV X7, -(24+8)(X6) // "saved LR"; must match frame size
591+
// Gather our arguments into registers.
592+
MOV fn+0(FP), X7
593+
MOV frame+8(FP), X8
594+
MOV ctxt+16(FP), X9
595+
MOV $-(24+8)(X6), X2 // switch stack; must match frame size
596+
MOV X7, 8(X2)
597+
MOV X8, 16(X2)
598+
MOV X9, 24(X2)
599+
CALL runtime·cgocallbackg(SB)
600+
601+
// Restore g->sched (== m->curg->sched) from saved values.
602+
MOV 0(X2), X7
603+
MOV X7, (g_sched+gobuf_pc)(g)
604+
MOV $(24+8)(X2), X6 // must match frame size
605+
MOV X6, (g_sched+gobuf_sp)(g)
606+
607+
// Switch back to m->g0's stack and restore m->g0->sched.sp.
608+
// (Unlike m->curg, the g0 goroutine never uses sched.pc,
609+
// so we do not have to restore it.)
610+
MOV g_m(g), X5
611+
MOV m_g0(X5), g
612+
CALL runtime·save_g(SB)
613+
MOV (g_sched+gobuf_sp)(g), X2
614+
MOV savedsp-24(SP), X6 // must match frame size
615+
MOV X6, (g_sched+gobuf_sp)(g)
616+
617+
// If the m on entry was nil, we called needm above to borrow an m
618+
// for the duration of the call. Since the call is over, return it with dropm.
619+
MOV savedm-8(SP), X5
620+
BNE ZERO, X5, droppedm
621+
MOV $runtime·dropm(SB), X6
622+
JALR RA, X6
623+
droppedm:
624+
625+
// Done!
626+
RET
461627

462628
TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0
463629
EBREAK

src/runtime/cgo/asm_riscv64.s

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build riscv64
6+
7+
#include "textflag.h"
8+
9+
// Called by C code generated by cmd/cgo.
10+
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
11+
// Saves C callee-saved registers and calls cgocallback with three arguments.
12+
// fn is the PC of a func(a unsafe.Pointer) function.
13+
TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
14+
/*
15+
* Push arguments for fn (X10, X11, X13), along with all callee-save
16+
* registers. Note that at procedure entry the first argument is at
17+
* 8(X2).
18+
*/
19+
ADD $(-8*31), X2
20+
MOV X10, (8*1)(X2) // fn unsafe.Pointer
21+
MOV X11, (8*2)(X2) // a unsafe.Pointer
22+
MOV X13, (8*3)(X2) // ctxt uintptr
23+
MOV X8, (8*4)(X2)
24+
MOV X9, (8*5)(X2)
25+
MOV X18, (8*6)(X2)
26+
MOV X19, (8*7)(X2)
27+
MOV X20, (8*8)(X2)
28+
MOV X21, (8*9)(X2)
29+
MOV X22, (8*10)(X2)
30+
MOV X23, (8*11)(X2)
31+
MOV X24, (8*12)(X2)
32+
MOV X25, (8*13)(X2)
33+
MOV X26, (8*14)(X2)
34+
MOV g, (8*15)(X2)
35+
MOV X3, (8*16)(X2)
36+
MOV X4, (8*17)(X2)
37+
MOV X1, (8*18)(X2)
38+
MOVD F8, (8*19)(X2)
39+
MOVD F9, (8*20)(X2)
40+
MOVD F18, (8*21)(X2)
41+
MOVD F19, (8*22)(X2)
42+
MOVD F20, (8*23)(X2)
43+
MOVD F21, (8*24)(X2)
44+
MOVD F22, (8*25)(X2)
45+
MOVD F23, (8*26)(X2)
46+
MOVD F24, (8*27)(X2)
47+
MOVD F25, (8*28)(X2)
48+
MOVD F26, (8*29)(X2)
49+
MOVD F27, (8*30)(X2)
50+
51+
// Initialize Go ABI environment
52+
CALL runtime·load_g(SB)
53+
CALL runtime·cgocallback(SB)
54+
55+
MOV (8*4)(X2), X8
56+
MOV (8*5)(X2), X9
57+
MOV (8*6)(X2), X18
58+
MOV (8*7)(X2), X19
59+
MOV (8*8)(X2), X20
60+
MOV (8*9)(X2), X21
61+
MOV (8*10)(X2), X22
62+
MOV (8*11)(X2), X23
63+
MOV (8*12)(X2), X24
64+
MOV (8*13)(X2), X25
65+
MOV (8*14)(X2), X26
66+
MOV (8*15)(X2), g
67+
MOV (8*16)(X2), X3
68+
MOV (8*17)(X2), X4
69+
MOV (8*18)(X2), X1
70+
MOVD (8*19)(X2), F8
71+
MOVD (8*20)(X2), F9
72+
MOVD (8*21)(X2), F18
73+
MOVD (8*22)(X2), F19
74+
MOVD (8*23)(X2), F20
75+
MOVD (8*24)(X2), F21
76+
MOVD (8*25)(X2), F22
77+
MOVD (8*26)(X2), F23
78+
MOVD (8*27)(X2), F24
79+
MOVD (8*28)(X2), F25
80+
MOVD (8*29)(X2), F26
81+
MOVD (8*30)(X2), F27
82+
ADD $(8*31), X2
83+
84+
RET

0 commit comments

Comments
 (0)