runtime/cgo: deduplicate x_cgo_init and crosscall1

Most platforms share the same implementation for x_cgo_init and
crosscall1. Solaris diverges too much and is left for a future CL.

Cq-Include-Trybots: luci.golang.try:gotip-freebsd-amd64,gotip-darwin-amd64_14,gotip-darwin-arm64_15,gotip-netbsd-arm64,gotip-openbsd-ppc64,gotip-solaris-amd64,gotip-linux-ppc64_power10
Change-Id: Ib2eeb6456caa5c055e1ac1907c2fdf63db58dafc
Reviewed-on: https://go-review.googlesource.com/c/go/+/708035
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
This commit is contained in:
qmuntal
2025-09-30 17:23:49 +02:00
committed by Quim Muntal
parent 65b9017c0e
commit 8a0bc29bed
15 changed files with 168 additions and 496 deletions

View File

@@ -1,30 +0,0 @@
// Copyright 2009 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 <string.h> /* for strerror */
#include "libcgo.h"
#include "libcgo_unix.h"
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
crosscall1(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}

View File

@@ -1,29 +0,0 @@
// Copyright 2009 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 <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*))
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
crosscall1(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}

View File

@@ -1,41 +1,16 @@
// Copyright 2009 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.
//go:build freebsd && (386 || arm || arm64 || riscv64)
#include <machine/sysarch.h>
#include <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
#ifdef ARM_TP_ADDRESS
// ARM_TP_ADDRESS is (ARM_VECTORS_HIGH + 0x1000) or 0xffff1000
// and is known to runtime.read_tls_fallback. Verify it with
// cpp.
#if ARM_TP_ADDRESS != 0xffff1000
#error Wrong ARM_TP_ADDRESS!
#endif
#endif
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*))
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
crosscall1(ts.fn, setg_gcc, ts.g);
return nil;
}
// Copyright 2009 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.
//go:build freebsd
#include <machine/sysarch.h>
#ifdef ARM_TP_ADDRESS
// ARM_TP_ADDRESS is (ARM_VECTORS_HIGH + 0x1000) or 0xffff1000
// and is known to runtime.read_tls_fallback. Verify it with
// cpp.
#if ARM_TP_ADDRESS != 0xffff1000
#error Wrong ARM_TP_ADDRESS!
#endif
#endif

View File

@@ -1,41 +0,0 @@
// Copyright 2009 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 <errno.h>
#include <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*))
{
uintptr *pbounds;
// Deal with memory sanitizer/clang interaction.
// See gcc_linux_amd64.c for details.
setg_gcc = setg;
pbounds = (uintptr*)malloc(2 * sizeof(uintptr));
if (pbounds == NULL) {
fatalf("malloc failed: %s", strerror(errno));
}
_cgo_set_stacklo(g, pbounds);
free(pbounds);
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
_cgo_tsan_acquire();
free(v);
_cgo_tsan_release();
crosscall1(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}

View File

@@ -9,7 +9,6 @@
#include <stdlib.h>
#include "libcgo.h"
#include "libcgo_unix.h"
#include <TargetConditionals.h>
@@ -18,27 +17,16 @@
#include <CoreFoundation/CFString.h>
#endif
static void (*setg_gcc)(void*);
#if TARGET_OS_IPHONE
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
static void
threadentry_platform(void)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
#if TARGET_OS_IPHONE
darwin_arm_init_thread_exception_port();
#endif
crosscall1(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
#if TARGET_OS_IPHONE
// init_working_dir sets the current working directory to the app root.
// By default ios/arm64 processes start in "/".
static void
@@ -101,16 +89,15 @@ init_working_dir()
#endif // TARGET_OS_IPHONE
void
x_cgo_init(G *g, void (*setg)(void*))
static void
init_platform()
{
//fprintf(stderr, "x_cgo_init = %p\n", &x_cgo_init); // aid debugging in presence of ASLR
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
#if TARGET_OS_IPHONE
darwin_arm_init_mach_exception_handler();
darwin_arm_init_thread_exception_port();
init_working_dir();
#endif
}
void (*x_cgo_init_platform)(void) = init_platform;
void (*x_cgo_threadentry_platform)(void) = threadentry_platform;

View File

@@ -83,30 +83,6 @@ _cgo_wait_runtime_init_done(void) {
return 0;
}
// _cgo_set_stacklo sets g->stacklo based on the stack size.
// This is common code called from x_cgo_init, which is itself
// called by rt0_go in the runtime package.
void _cgo_set_stacklo(G *g, uintptr *pbounds)
{
uintptr bounds[2];
// pbounds can be passed in by the caller; see gcc_linux_amd64.c.
if (pbounds == NULL) {
pbounds = &bounds[0];
}
x_cgo_getstackbound(pbounds);
g->stacklo = *pbounds;
// Sanity check the results now, rather than getting a
// morestack on g0 crash.
if (g->stacklo >= g->stackhi) {
fprintf(stderr, "runtime/cgo: bad stack bounds: lo=%p hi=%p\n", (void*)(g->stacklo), (void*)(g->stackhi));
abort();
}
}
// Store the g into a thread-specific value associated with the pthread key pthread_g.
// And pthread_key_destructor will dropm when the thread is exiting.
void x_cgo_bindm(void* g) {

View File

@@ -1,37 +0,0 @@
// Copyright 2009 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.
//go:build linux && (386 || arm || loong64 || mips || mipsle || mips64 || mips64le || riscv64)
#include <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
void (*x_cgo_inittls)(void **tlsg, void **tlsbase) __attribute__((common));
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
if (x_cgo_inittls) {
x_cgo_inittls(tlsg, tlsbase);
}
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
crosscall1(ts.fn, setg_gcc, ts.g);
return nil;
}

View File

@@ -1,63 +0,0 @@
// Copyright 2009 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 <errno.h>
#include <string.h> // strerror
#include <stdlib.h>
#include "libcgo.h"
#include "libcgo_unix.h"
static void (*setg_gcc)(void*);
// This will be set in gcc_android.c for android-specific customization.
void (*x_cgo_inittls)(void **tlsg, void **tlsbase) __attribute__((common));
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{
uintptr *pbounds;
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
misc/cgo/testsanitizers.
GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually use the memory we allocate. */
setg_gcc = setg;
pbounds = (uintptr*)malloc(2 * sizeof(uintptr));
if (pbounds == NULL) {
fatalf("malloc failed: %s", strerror(errno));
}
_cgo_set_stacklo(g, pbounds);
free(pbounds);
if (x_cgo_inittls) {
x_cgo_inittls(tlsg, tlsbase);
}
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
_cgo_tsan_acquire();
free(v);
_cgo_tsan_release();
crosscall1(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}

View File

@@ -1,58 +0,0 @@
// Copyright 2015 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 <errno.h>
#include <string.h>
#include <stdlib.h>
#include "libcgo.h"
#include "libcgo_unix.h"
void (*x_cgo_inittls)(void **tlsg, void **tlsbase) __attribute__((common));
static void (*setg_gcc)(void*);
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
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)
{
uintptr *pbounds;
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
misc/cgo/testsanitizers.
GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually use the memory we allocate. */
setg_gcc = setg;
pbounds = (uintptr*)malloc(2 * sizeof(uintptr));
if (pbounds == NULL) {
fatalf("malloc failed: %s", strerror(errno));
}
_cgo_set_stacklo(g, pbounds);
free(pbounds);
if (x_cgo_inittls) {
x_cgo_inittls(tlsg, tlsbase);
}
}

View File

@@ -1,34 +0,0 @@
// Copyright 2016 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 <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsbase)
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
}
extern void crosscall_s390x(void (*fn)(void), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
// Save g for this thread in C TLS
setg_gcc((void*)ts.g);
crosscall_s390x(ts.fn, (void*)ts.g);
return nil;
}

View File

@@ -1,44 +1,26 @@
// Copyright 2009 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.
//go:build netbsd && (386 || amd64 || arm || arm64)
#include <signal.h>
#include <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*))
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
stack_t ss;
ts = *(ThreadStart*)v;
free(v);
// On NetBSD, a new thread inherits the signal stack of the
// creating thread. That confuses minit, so we remove that
// signal stack here before calling the regular mstart. It's
// a bit baroque to remove a signal stack here only to add one
// in minit, but it's a simple change that keeps NetBSD
// working like other OS's. At this point all signals are
// blocked, so there is no race.
memset(&ss, 0, sizeof ss);
ss.ss_flags = SS_DISABLE;
sigaltstack(&ss, nil);
crosscall1(ts.fn, setg_gcc, ts.g);
return nil;
}
// Copyright 2009 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.
//go:build netbsd
#include <string.h>
#include <signal.h>
static void
threadentry_platform(void)
{
// On NetBSD, a new thread inherits the signal stack of the
// creating thread. That confuses minit, so we remove that
// signal stack here before calling the regular mstart. It's
// a bit baroque to remove a signal stack here only to add one
// in minit, but it's a simple change that keeps NetBSD
// working like other OS's. At this point all signals are
// blocked, so there is no race.
stack_t ss;
memset(&ss, 0, sizeof ss);
ss.ss_flags = SS_DISABLE;
sigaltstack(&ss, NULL);
}
void (*x_cgo_threadentry_platform)(void) = threadentry_platform;

View File

@@ -1,31 +0,0 @@
// Copyright 2009 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.
//go:build openbsd && (386 || arm || amd64 || arm64 || riscv64)
#include <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*))
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
}
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
free(v);
crosscall1(ts.fn, setg_gcc, ts.g);
return nil;
}

View File

@@ -1,38 +0,0 @@
// Copyright 2014 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.
//go:build ppc64 || ppc64le
#include <string.h>
#include "libcgo.h"
#include "libcgo_unix.h"
void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
static void (*setg_gcc)(void*);
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsbase)
{
setg_gcc = setg;
_cgo_set_stacklo(g, NULL);
}
extern void crosscall_ppc64(void (*fn)(void), void *g);
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
_cgo_tsan_acquire();
free(v);
_cgo_tsan_release();
// Save g for this thread in C TLS
setg_gcc((void*)ts.g);
crosscall_ppc64(ts.fn, (void*)ts.g);
return nil;
}

118
src/runtime/cgo/gcc_unix.c Normal file
View File

@@ -0,0 +1,118 @@
// Copyright 2009 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.
//go:build unix && !solaris
#include <signal.h>
#include <string.h>
#include <errno.h>
#include "libcgo.h"
#include "libcgo_unix.h"
// Platform-specific hooks.
void (*x_cgo_inittls)(void **tlsg, void **tlsbase) __attribute__((weak));
void (*x_cgo_init_platform)(void) __attribute__((weak));
void (*x_cgo_threadentry_platform)(void) __attribute__((weak));
static void (*setg_gcc)(void*);
// _cgo_set_stacklo sets g->stacklo based on the stack size.
// This is common code called from x_cgo_init, which is itself
// called by rt0_go in the runtime package.
static void
_cgo_set_stacklo(G *g)
{
uintptr bounds[2];
x_cgo_getstackbound(bounds);
g->stacklo = bounds[0];
// Sanity check the results now, rather than getting a
// morestack on g0 crash.
if (g->stacklo >= g->stackhi) {
fprintf(stderr, "runtime/cgo: bad stack bounds: lo=%p hi=%p\n", (void*)(g->stacklo), (void*)(g->stackhi));
abort();
}
}
static void
clang_init()
{
#if defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__))
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
cmd/cgo/internal/testsanitizers . */
uintptr *p;
p = (uintptr*)malloc(sizeof(uintptr));
if (p == NULL) {
fatalf("malloc failed: %s", strerror(errno));
}
/* GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually touch the memory we allocate. */
((volatile char *)p)[0] = 0;
free(p);
#endif
}
void
x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{
clang_init();
setg_gcc = setg;
_cgo_set_stacklo(g);
if (x_cgo_inittls) {
x_cgo_inittls(tlsg, tlsbase);
}
if (x_cgo_init_platform) {
x_cgo_init_platform();
}
}
// TODO: change crosscall_ppc64 and crosscall_s390x so that it matches crosscall1
// signature and behavior.
#if defined(__powerpc64__)
extern void crosscall_ppc64(void (*fn)(void), void *g);
#elif defined(__s390x__)
extern void crosscall_s390x(void (*fn)(void), void *g);
#else
extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g);
#endif
void*
threadentry(void *v)
{
ThreadStart ts;
ts = *(ThreadStart*)v;
_cgo_tsan_acquire();
free(v);
_cgo_tsan_release();
if (x_cgo_threadentry_platform != NULL) {
x_cgo_threadentry_platform();
}
#if defined(__powerpc64__)
// Save g for this thread in C TLS
setg_gcc((void*)ts.g);
crosscall_ppc64(ts.fn, (void*)ts.g);
#elif defined(__s390x__)
// Save g for this thread in C TLS
setg_gcc((void*)ts.g);
crosscall_s390x(ts.fn, (void*)ts.g);
#else
crosscall1(ts.fn, setg_gcc, ts.g);
#endif
return NULL;
}

View File

@@ -4,11 +4,6 @@
#include <pthread.h>
/*
* Initialize g->stacklo.
*/
extern void _cgo_set_stacklo(G *, uintptr *);
/*
* Call pthread_create, retrying on EAGAIN.
*/