runtime: set up TLS without cgo on darwin/arm64

Currently, on darwin/arm64 we set up TLS using cgo. TLS is not
set for pure Go programs. As we use libc for syscalls on darwin,
we need to save the G register before the libc call. Otherwise it
is not signal-safe, as a signal may land during the execution of
a libc function, where the G register may be clobbered.

This CL initializes TLS in Go, by calling the pthread functions
directly without cgo. This makes it possible to save the G
register to TLS in pure Go programs (done in a later CL).

Inspired by Elias's CL 209197. Write the logic in Go instead of
assembly.

Updates #38485, #35853.

Change-Id: I257ba2a411ad387b2f4d50d10129d37fec7a226e
Reviewed-on: https://go-review.googlesource.com/c/go/+/265118
Trust: Cherry Zhang <cherryyz@google.com>
Trust: Elias Naur <mail@eliasnaur.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Cherry Zhang 2020-10-24 13:14:36 -04:00
parent cf6cfba4d5
commit 72dec90bfd
5 changed files with 93 additions and 36 deletions

View File

@ -15,6 +15,19 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
MOVW R0, 8(RSP) // argc
MOVD R1, 16(RSP) // argv
#ifdef TLS_darwin
// Initialize TLS.
MOVD ZR, g // clear g, make sure it's not junk.
SUB $32, RSP
MRS_TPIDR_R0
AND $~7, R0
MOVD R0, 16(RSP) // arg2: TLS base
MOVD $runtime·tls_g(SB), R2
MOVD R2, 8(RSP) // arg1: &tlsg
BL ·tlsinit(SB)
ADD $32, RSP
#endif
// create istack out of the given (operating system) stack.
// _cgo_init may update stackguard.
MOVD $runtime·g0(SB), g
@ -29,9 +42,9 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
MOVD _cgo_init(SB), R12
CBZ R12, nocgo
#ifdef GOOS_android
MRS_TPIDR_R0 // load TLS base pointer
MOVD R0, R3 // arg 3: TLS base pointer
#ifdef TLSG_IS_VARIABLE
MOVD $runtime·tls_g(SB), R2 // arg 2: &tls_g
#else
MOVD $0, R2 // arg 2: not used when using platform's TLS

View File

@ -20,37 +20,6 @@
#include <CoreFoundation/CFString.h>
#endif
#define magic (0xc476c475c47957UL)
// inittls allocates a thread-local storage slot for g.
//
// It finds the first available slot using pthread_key_create and uses
// it as the offset value for runtime.tlsg.
static void
inittls(void **tlsg, void **tlsbase)
{
pthread_key_t k;
int i, err;
err = pthread_key_create(&k, nil);
if(err != 0) {
fprintf(stderr, "runtime/cgo: pthread_key_create failed: %d\n", err);
abort();
}
//fprintf(stderr, "runtime/cgo: k = %d, tlsbase = %p\n", (int)k, tlsbase); // debug
pthread_setspecific(k, (void*)magic);
// The first key should be at 257.
for (i=0; i<PTHREAD_KEYS_MAX; i++) {
if (*(tlsbase+i) == (void*)magic) {
*tlsg = (void*)(i*sizeof(void *));
pthread_setspecific(k, 0);
return;
}
}
fprintf(stderr, "runtime/cgo: could not find pthread key.\n");
abort();
}
static void *threadentry(void*);
static void (*setg_gcc)(void*);
@ -156,7 +125,7 @@ init_working_dir()
#endif // TARGET_OS_IPHONE
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
x_cgo_init(G *g, void (*setg)(void*))
{
pthread_attr_t attr;
size_t size;
@ -168,9 +137,6 @@ x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
g->stacklo = (uintptr)&attr - size + 4096;
pthread_attr_destroy(&attr);
// yes, tlsbase from mrs might not be correctly aligned.
inittls(tlsg, (void**)((uintptr)tlsbase & ~7));
#if TARGET_OS_IPHONE
darwin_arm_init_mach_exception_handler();
darwin_arm_init_thread_exception_port();

View File

@ -94,6 +94,8 @@ const (
_PTHREAD_CREATE_DETACHED = 0x2
_PTHREAD_KEYS_MAX = 512
_F_SETFD = 0x2
_F_GETFL = 0x3
_F_SETFL = 0x4
@ -233,3 +235,5 @@ type machTimebaseInfo struct {
numer uint32
denom uint32
}
type pthreadkey uint64

View File

@ -0,0 +1,62 @@
// 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.
package runtime
import (
"runtime/internal/sys"
"unsafe"
)
// libc function wrappers. Must run on system stack.
//go:nosplit
//go:cgo_unsafe_args
func g0_pthread_key_create(k *pthreadkey, destructor uintptr) int32 {
return asmcgocall(unsafe.Pointer(funcPC(pthread_key_create_trampoline)), unsafe.Pointer(&k))
}
func pthread_key_create_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func g0_pthread_setspecific(k pthreadkey, value uintptr) int32 {
return asmcgocall(unsafe.Pointer(funcPC(pthread_setspecific_trampoline)), unsafe.Pointer(&k))
}
func pthread_setspecific_trampoline()
//go:cgo_import_dynamic libc_pthread_key_create pthread_key_create "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic libc_pthread_setspecific pthread_setspecific "/usr/lib/libSystem.B.dylib"
// tlsinit allocates a thread-local storage slot for g.
//
// It finds the first available slot using pthread_key_create and uses
// it as the offset value for runtime.tlsg.
//
// This runs at startup on g0 stack, but before g is set, so it must
// not split stack (transitively). g is expected to be nil, so things
// (e.g. asmcgocall) will skip saving or reading g.
//
//go:nosplit
func tlsinit(tlsg *uintptr, tlsbase *[_PTHREAD_KEYS_MAX]uintptr) {
var k pthreadkey
err := g0_pthread_key_create(&k, 0)
if err != 0 {
abort()
}
const magic = 0xc476c475c47957
err = g0_pthread_setspecific(k, magic)
if err != 0 {
abort()
}
for i, x := range tlsbase {
if x == magic {
*tlsg = uintptr(i * sys.PtrSize)
g0_pthread_setspecific(k, 0)
return
}
}
abort()
}

View File

@ -497,6 +497,18 @@ TEXT runtime·pthread_kill_trampoline(SB),NOSPLIT,$0
BL libc_pthread_kill(SB)
RET
TEXT runtime·pthread_key_create_trampoline(SB),NOSPLIT,$0
MOVD 8(R0), R1 // arg 2 destructor
MOVD 0(R0), R0 // arg 1 *key
BL libc_pthread_key_create(SB)
RET
TEXT runtime·pthread_setspecific_trampoline(SB),NOSPLIT,$0
MOVD 8(R0), R1 // arg 2 value
MOVD 0(R0), R0 // arg 1 key
BL libc_pthread_setspecific(SB)
RET
// syscall calls a function in libc on behalf of the syscall package.
// syscall takes a pointer to a struct like:
// struct {