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 <joel@sing.id.au>
Run-TryBot: Joel Sing <joel@sing.id.au>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Joel Sing 2020-05-19 18:56:01 +10:00
parent 974def803e
commit 393f2bb067
11 changed files with 469 additions and 21 deletions

View File

@ -0,0 +1,31 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build riscv64
// +build !gccgo
#include "textflag.h"
TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
// Rewind stack pointer so anything that happens on the stack
// will clobber the test pattern created by the caller
ADD $(1024*8), X2
// Ask signaller to setgid
MOV $1, X5
FENCE
MOVW X5, ·Baton(SB)
FENCE
// Wait for setgid completion
loop:
FENCE
MOVW ·Baton(SB), X5
OR X6, X6, X6 // hint that we're in a spin loop
BNE ZERO, X5, loop
FENCE
// Restore stack
ADD $(-1024*8), X2
RET

View File

@ -1549,7 +1549,7 @@ var cgoEnabled = map[string]bool{
"linux/mipsle": true, "linux/mipsle": true,
"linux/mips64": true, "linux/mips64": true,
"linux/mips64le": true, "linux/mips64le": true,
"linux/riscv64": false, // Issue 36641 "linux/riscv64": true,
"linux/s390x": true, "linux/s390x": true,
"linux/sparc64": true, "linux/sparc64": true,
"android/386": true, "android/386": true,

View File

@ -953,7 +953,7 @@ func (t *tester) internalLink() bool {
// Internally linking cgo is incomplete on some architectures. // Internally linking cgo is incomplete on some architectures.
// https://golang.org/issue/10373 // https://golang.org/issue/10373
// https://golang.org/issue/14449 // https://golang.org/issue/14449
if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" { if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" {
return false return false
} }
if goos == "aix" { if goos == "aix" {
@ -1117,8 +1117,8 @@ func (t *tester) cgoTest(dt *distTest) error {
"android-arm", "android-arm64", "android-arm", "android-arm64",
"dragonfly-amd64", "dragonfly-amd64",
"freebsd-386", "freebsd-amd64", "freebsd-arm", "freebsd-386", "freebsd-amd64", "freebsd-arm",
"linux-386", "linux-amd64", "linux-arm", "linux-ppc64le", "linux-s390x", "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x",
"netbsd-386", "netbsd-amd64", "linux-arm64": "netbsd-386", "netbsd-amd64":
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external")

View File

@ -1,5 +1,6 @@
# Plugins are only supported on linux,cgo and darwin,cgo. # Plugins are only supported on linux,cgo (!riscv64) and darwin,cgo.
[!linux] [!darwin] skip [!linux] [!darwin] skip
[linux] [riscv64] skip
[!cgo] skip [!cgo] skip
go build -n testdep go build -n testdep

View File

@ -24,7 +24,7 @@ func canInternalLink() bool {
} }
case "linux": case "linux":
switch runtime.GOARCH { switch runtime.GOARCH {
case "arm64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le": case "arm64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le", "riscv64":
return false return false
} }
case "openbsd": case "openbsd":

View File

@ -9,7 +9,6 @@
// func rt0_go() // func rt0_go()
TEXT runtime·rt0_go(SB),NOSPLIT,$0 TEXT runtime·rt0_go(SB),NOSPLIT,$0
// X2 = stack; A0 = argc; A1 = argv // X2 = stack; A0 = argc; A1 = argv
ADD $-24, X2 ADD $-24, X2
MOV A0, 8(X2) // argc MOV A0, 8(X2) // argc
MOV A1, 16(X2) // argv MOV A1, 16(X2) // argv
@ -313,10 +312,62 @@ TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8
CALL runtime·badctxt(SB) CALL runtime·badctxt(SB)
RET RET
// Save state of caller into g->sched. Smashes X31.
TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0
MOV X1, (g_sched+gobuf_pc)(g)
MOV X2, (g_sched+gobuf_sp)(g)
MOV ZERO, (g_sched+gobuf_lr)(g)
MOV ZERO, (g_sched+gobuf_ret)(g)
// Assert ctxt is zero. See func save.
MOV (g_sched+gobuf_ctxt)(g), X31
BEQ ZERO, X31, 2(PC)
CALL runtime·badctxt(SB)
RET
// func asmcgocall(fn, arg unsafe.Pointer) int32 // func asmcgocall(fn, arg unsafe.Pointer) int32
// Call fn(arg) on the scheduler stack,
// aligned appropriately for the gcc ABI.
// See cgocall.go for more details.
TEXT ·asmcgocall(SB),NOSPLIT,$0-20 TEXT ·asmcgocall(SB),NOSPLIT,$0-20
// TODO(jsing): Add support for cgo - issue #36641. MOV fn+0(FP), X5
WORD $0 // crash MOV arg+8(FP), X10
MOV X2, X8 // save original stack pointer
MOV g, X9
// Figure out if we need to switch to m->g0 stack.
// We get called to create new OS threads too, and those
// come in on the m->g0 stack already.
MOV g_m(g), X6
MOV m_g0(X6), X7
BEQ X7, g, g0
CALL gosave<>(SB)
MOV X7, g
CALL runtime·save_g(SB)
MOV (g_sched+gobuf_sp)(g), X2
// Now on a scheduling stack (a pthread-created stack).
g0:
// Save room for two of our pointers.
ADD $-16, X2
MOV X9, 0(X2) // save old g on stack
MOV (g_stack+stack_hi)(X9), X9
SUB X8, X9, X8
MOV X8, 8(X2) // save depth in old g stack (can't just save SP, as stack might be copied during a callback)
JALR RA, (X5)
// Restore g, stack pointer. X10 is return value.
MOV 0(X2), g
CALL runtime·save_g(SB)
MOV (g_stack+stack_hi)(g), X5
MOV 8(X2), X6
SUB X6, X5, X6
MOV X6, X2
MOVW X10, ret+16(FP)
RET
// func asminit() // func asminit()
TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
@ -444,6 +495,21 @@ CALLFN(·call268435456, 268435456)
CALLFN(·call536870912, 536870912) CALLFN(·call536870912, 536870912)
CALLFN(·call1073741824, 1073741824) CALLFN(·call1073741824, 1073741824)
// Called from cgo wrappers, this function returns g->m->curg.stack.hi.
// Must obey the gcc calling convention.
TEXT _cgo_topofstack(SB),NOSPLIT,$8
// g (X27) and REG_TMP (X31) might be clobbered by load_g.
// X27 is callee-save in the gcc calling convention, so save it.
MOV g, savedX27-8(SP)
CALL runtime·load_g(SB)
MOV g_m(g), X5
MOV m_curg(X5), X5
MOV (g_stack+stack_hi)(X5), X10 // return value in X10
MOV savedX27-8(SP), g
RET
// func goexit(neverCallThisFunction) // func goexit(neverCallThisFunction)
// The top-most function running on a goroutine // The top-most function running on a goroutine
// returns to goexit+PCQuantum. // returns to goexit+PCQuantum.
@ -453,11 +519,111 @@ TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0
// traceback from goexit1 must hit code range of goexit // traceback from goexit1 must hit code range of goexit
MOV ZERO, ZERO // NOP MOV ZERO, ZERO // NOP
// cgocallback(fn, frame unsafe.Pointer, ctxt uintptr) // func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details. // See cgocall.go for more details.
TEXT ·cgocallback(SB),NOSPLIT,$0-24 TEXT ·cgocallback(SB),NOSPLIT,$24-24
// TODO(jsing): Add support for cgo - issue #36641. NO_LOCAL_POINTERS
WORD $0 // crash
// Load m and g from thread-local storage.
MOVBU runtime·iscgo(SB), X5
BEQ ZERO, X5, nocgo
CALL runtime·load_g(SB)
nocgo:
// If g is nil, Go did not create the current thread.
// Call needm to obtain one for temporary use.
// In this case, we're running on the thread stack, so there's
// lots of space, but the linker doesn't know. Hide the call from
// the linker analysis by using an indirect call.
BEQ ZERO, g, needm
MOV g_m(g), X5
MOV X5, savedm-8(SP)
JMP havem
needm:
MOV g, savedm-8(SP) // g is zero, so is m.
MOV $runtime·needm(SB), X6
JALR RA, X6
// Set m->sched.sp = SP, so that if a panic happens
// during the function we are about to execute, it will
// have a valid SP to run on the g0 stack.
// The next few lines (after the havem label)
// will save this SP onto the stack and then write
// the same SP back to m->sched.sp. That seems redundant,
// but if an unrecovered panic happens, unwindm will
// restore the g->sched.sp from the stack location
// and then systemstack will try to use it. If we don't set it here,
// that restored SP will be uninitialized (typically 0) and
// will not be usable.
MOV g_m(g), X5
MOV m_g0(X5), X6
MOV X2, (g_sched+gobuf_sp)(X6)
havem:
// Now there's a valid m, and we're running on its m->g0.
// Save current m->g0->sched.sp on stack and then set it to SP.
// Save current sp in m->g0->sched.sp in preparation for
// switch back to m->curg stack.
// NOTE: unwindm knows that the saved g->sched.sp is at 8(X2) aka savedsp-24(SP).
MOV m_g0(X5), X6
MOV (g_sched+gobuf_sp)(X6), X7
MOV X7, savedsp-24(SP) // must match frame size
MOV X2, (g_sched+gobuf_sp)(X6)
// Switch to m->curg stack and call runtime.cgocallbackg.
// Because we are taking over the execution of m->curg
// but *not* resuming what had been running, we need to
// save that information (m->curg->sched) so we can restore it.
// We can restore m->curg->sched.sp easily, because calling
// runtime.cgocallbackg leaves SP unchanged upon return.
// To save m->curg->sched.pc, we push it onto the curg stack and
// open a frame the same size as cgocallback's g0 frame.
// Once we switch to the curg stack, the pushed PC will appear
// to be the return PC of cgocallback, so that the traceback
// will seamlessly trace back into the earlier calls.
MOV m_curg(X5), g
CALL runtime·save_g(SB)
MOV (g_sched+gobuf_sp)(g), X6 // prepare stack as X6
MOV (g_sched+gobuf_pc)(g), X7
MOV X7, -(24+8)(X6) // "saved LR"; must match frame size
// Gather our arguments into registers.
MOV fn+0(FP), X7
MOV frame+8(FP), X8
MOV ctxt+16(FP), X9
MOV $-(24+8)(X6), X2 // switch stack; must match frame size
MOV X7, 8(X2)
MOV X8, 16(X2)
MOV X9, 24(X2)
CALL runtime·cgocallbackg(SB)
// Restore g->sched (== m->curg->sched) from saved values.
MOV 0(X2), X7
MOV X7, (g_sched+gobuf_pc)(g)
MOV $(24+8)(X2), X6 // must match frame size
MOV X6, (g_sched+gobuf_sp)(g)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
// (Unlike m->curg, the g0 goroutine never uses sched.pc,
// so we do not have to restore it.)
MOV g_m(g), X5
MOV m_g0(X5), g
CALL runtime·save_g(SB)
MOV (g_sched+gobuf_sp)(g), X2
MOV savedsp-24(SP), X6 // must match frame size
MOV X6, (g_sched+gobuf_sp)(g)
// If the m on entry was nil, we called needm above to borrow an m
// for the duration of the call. Since the call is over, return it with dropm.
MOV savedm-8(SP), X5
BNE ZERO, X5, droppedm
MOV $runtime·dropm(SB), X6
JALR RA, X6
droppedm:
// Done!
RET
TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0
EBREAK EBREAK

View File

@ -0,0 +1,84 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build riscv64
#include "textflag.h"
// Called by C code generated by cmd/cgo.
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
// Saves C callee-saved registers and calls cgocallback with three arguments.
// fn is the PC of a func(a unsafe.Pointer) function.
TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
/*
* Push arguments for fn (X10, X11, X13), along with all callee-save
* registers. Note that at procedure entry the first argument is at
* 8(X2).
*/
ADD $(-8*31), X2
MOV X10, (8*1)(X2) // fn unsafe.Pointer
MOV X11, (8*2)(X2) // a unsafe.Pointer
MOV X13, (8*3)(X2) // ctxt uintptr
MOV X8, (8*4)(X2)
MOV X9, (8*5)(X2)
MOV X18, (8*6)(X2)
MOV X19, (8*7)(X2)
MOV X20, (8*8)(X2)
MOV X21, (8*9)(X2)
MOV X22, (8*10)(X2)
MOV X23, (8*11)(X2)
MOV X24, (8*12)(X2)
MOV X25, (8*13)(X2)
MOV X26, (8*14)(X2)
MOV g, (8*15)(X2)
MOV X3, (8*16)(X2)
MOV X4, (8*17)(X2)
MOV X1, (8*18)(X2)
MOVD F8, (8*19)(X2)
MOVD F9, (8*20)(X2)
MOVD F18, (8*21)(X2)
MOVD F19, (8*22)(X2)
MOVD F20, (8*23)(X2)
MOVD F21, (8*24)(X2)
MOVD F22, (8*25)(X2)
MOVD F23, (8*26)(X2)
MOVD F24, (8*27)(X2)
MOVD F25, (8*28)(X2)
MOVD F26, (8*29)(X2)
MOVD F27, (8*30)(X2)
// Initialize Go ABI environment
CALL runtime·load_g(SB)
CALL runtime·cgocallback(SB)
MOV (8*4)(X2), X8
MOV (8*5)(X2), X9
MOV (8*6)(X2), X18
MOV (8*7)(X2), X19
MOV (8*8)(X2), X20
MOV (8*9)(X2), X21
MOV (8*10)(X2), X22
MOV (8*11)(X2), X23
MOV (8*12)(X2), X24
MOV (8*13)(X2), X25
MOV (8*14)(X2), X26
MOV (8*15)(X2), g
MOV (8*16)(X2), X3
MOV (8*17)(X2), X4
MOV (8*18)(X2), X1
MOVD (8*19)(X2), F8
MOVD (8*20)(X2), F9
MOVD (8*21)(X2), F18
MOVD (8*22)(X2), F19
MOVD (8*23)(X2), F20
MOVD (8*24)(X2), F21
MOVD (8*25)(X2), F22
MOVD (8*26)(X2), F23
MOVD (8*27)(X2), F24
MOVD (8*28)(X2), F25
MOVD (8*29)(X2), F26
MOVD (8*30)(X2), F27
ADD $(8*31), X2
RET

View File

@ -0,0 +1,74 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include "libcgo.h"
#include "libcgo_unix.h"
static void *threadentry(void*);
void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
static void (*setg_gcc)(void*);
void
_cgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
sigset_t ign, oset;
pthread_t p;
size_t size;
int err;
sigfillset(&ign);
pthread_sigmask(SIG_SETMASK, &ign, &oset);
// Not sure why the memset is necessary here,
// but without it, we get a bogus stack size
// out of pthread_attr_getstacksize. C'est la Linux.
memset(&attr, 0, sizeof attr);
pthread_attr_init(&attr);
size = 0;
pthread_attr_getstacksize(&attr, &size);
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts->g->stackhi = size;
err = _cgo_try_pthread_create(&p, &attr, threadentry, ts);
pthread_sigmask(SIG_SETMASK, &oset, nil);
if (err != 0) {
fatalf("pthread_create failed: %s", strerror(err));
}
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
static void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
crosscall1(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{
pthread_attr_t attr;
size_t size;
setg_gcc = setg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stacklo = (uintptr)&attr - size + 4096;
pthread_attr_destroy(&attr);
if (x_cgo_inittls) {
x_cgo_inittls(tlsg, tlsbase);
}
}

View File

@ -0,0 +1,80 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
* void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g)
*
* Calling into the gc tool chain, where all registers are caller save.
* Called from standard RISCV ELF psABI, where x8-x9, x18-x27, f8-f9 and
* f18-f27 are callee-save, so they must be saved explicitly, along with
* x1 (LR).
*/
.globl crosscall1
crosscall1:
sd x1, -200(sp)
addi sp, sp, -200
sd x8, 8(sp)
sd x9, 16(sp)
sd x18, 24(sp)
sd x19, 32(sp)
sd x20, 40(sp)
sd x21, 48(sp)
sd x22, 56(sp)
sd x23, 64(sp)
sd x24, 72(sp)
sd x25, 80(sp)
sd x26, 88(sp)
sd x27, 96(sp)
fsd f8, 104(sp)
fsd f9, 112(sp)
fsd f18, 120(sp)
fsd f19, 128(sp)
fsd f20, 136(sp)
fsd f21, 144(sp)
fsd f22, 152(sp)
fsd f23, 160(sp)
fsd f24, 168(sp)
fsd f25, 176(sp)
fsd f26, 184(sp)
fsd f27, 192(sp)
// a0 = *fn, a1 = *setg_gcc, a2 = *g
mv s1, a0
mv s0, a1
mv a0, a2
jalr ra, s0 // call setg_gcc (clobbers x30 aka g)
jalr ra, s1 // call fn
ld x1, 0(sp)
ld x8, 8(sp)
ld x9, 16(sp)
ld x18, 24(sp)
ld x19, 32(sp)
ld x20, 40(sp)
ld x21, 48(sp)
ld x22, 56(sp)
ld x23, 64(sp)
ld x24, 72(sp)
ld x25, 80(sp)
ld x26, 88(sp)
ld x27, 96(sp)
fld f8, 104(sp)
fld f9, 112(sp)
fld f18, 120(sp)
fld f19, 128(sp)
fld f20, 136(sp)
fld f21, 144(sp)
fld f22, 152(sp)
fld f23, 160(sp)
fld f24, 168(sp)
fld f25, 176(sp)
fld f26, 184(sp)
fld f27, 192(sp)
addi sp, sp, 200
jr ra
#ifdef __ELF__
.section .note.GNU-stack,"",%progbits
#endif

View File

@ -309,7 +309,7 @@ func unwindm(restore *bool) {
switch GOARCH { switch GOARCH {
default: default:
throw("unwindm not implemented") throw("unwindm not implemented")
case "386", "amd64", "arm", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "mips", "mipsle": case "386", "amd64", "arm", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "mips", "mipsle", "riscv64":
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + sys.MinFrameSize)) sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + sys.MinFrameSize))
case "arm64": case "arm64":
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16)) sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16))

View File

@ -9,10 +9,22 @@
// If !iscgo, this is a no-op. // If !iscgo, this is a no-op.
// //
// NOTE: mcall() assumes this clobbers only R23 (REGTMP). // NOTE: mcall() assumes this clobbers only X31 (REG_TMP).
// FIXME: cgo
TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0
MOVB runtime·iscgo(SB), X31
BEQ X0, X31, nocgo
MOV runtime·tls_g(SB), X31
ADD X4, X31 // add offset to thread pointer (X4)
MOV g, (X31)
nocgo:
RET RET
TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0
MOV runtime·tls_g(SB), X31
ADD X4, X31 // add offset to thread pointer (X4)
MOV (X31), g
RET RET
GLOBL runtime·tls_g(SB), TLSBSS, $8