aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2026-03-31 16:34:59 -0400
committerMichael Pratt <mpratt@google.com>2026-04-02 09:58:36 -0700
commit78d5260426899e934a4d680910b1484953e78087 (patch)
tree720363c8910cf1a4cf3b6e8318b2cc4e7c463742
parentef90a565b50af191c4f20b62770b084d6978a88d (diff)
downloadgo-78d5260426899e934a4d680910b1484953e78087.tar.xz
runtime: use asmcgocall_no_g in libInit
libInit runs before rt0_go, which is where TLS setup occurs. Thus the contents of the TLS may not be defined, so the g lookup in asmcgocall is not safe. Concretely, android-386 c-shared builds crash without this change because asmcgocall reads an invalid non-zero g from the TLS. Move libInit to a file limited to GOARCH that support c-archive or c-shared so that only those require asmcgocall_no_g. In addition, loong64 and s390x need asmcgocall_no_g implementations. Fixes #78480. Cq-Include-Trybots: luci.golang.try:gotip-linux-loong64,gotip-linux-s390x Change-Id: I175e6d020339af89c9b576535d79c1e76a6a6964 Reviewed-on: https://go-review.googlesource.com/c/go/+/761541 Reviewed-by: Quim Muntal <quimmuntal@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
-rw-r--r--src/runtime/asm_loong64.s9
-rw-r--r--src/runtime/asm_s390x.s23
-rw-r--r--src/runtime/libinit.go34
-rw-r--r--src/runtime/proc.go21
-rw-r--r--src/runtime/stubs_loong64.go5
-rw-r--r--src/runtime/stubs_s390x.go5
6 files changed, 76 insertions, 21 deletions
diff --git a/src/runtime/asm_loong64.s b/src/runtime/asm_loong64.s
index b444bc9305..46215f3bd2 100644
--- a/src/runtime/asm_loong64.s
+++ b/src/runtime/asm_loong64.s
@@ -568,6 +568,15 @@ nosave:
MOVW R4, ret+16(FP)
RET
+// func asmcgocall_no_g(fn, arg unsafe.Pointer)
+// Call fn(arg) aligned appropriately for the gcc ABI.
+// Called on a system stack, and there may be no g yet.
+TEXT ·asmcgocall_no_g(SB),NOSPLIT,$0-16
+ MOVV fn+0(FP), R25
+ MOVV arg+8(FP), R4
+ JAL (R25)
+ RET
+
// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details.
TEXT ·cgocallback(SB),NOSPLIT,$24-24
diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s
index f0669af551..cfd21e7d0d 100644
--- a/src/runtime/asm_s390x.s
+++ b/src/runtime/asm_s390x.s
@@ -593,6 +593,29 @@ nosave:
MOVW R2, ret+16(FP)
RET
+// func asmcgocall_no_g(fn, arg unsafe.Pointer)
+// Call fn(arg) aligned appropriately for the gcc ABI.
+// Called on a system stack, and there may be no g yet.
+TEXT ·asmcgocall_no_g(SB),NOSPLIT,$0-16
+ MOVD fn+0(FP), R3
+ MOVD arg+8(FP), R4
+
+ MOVD R15, R2 // Save original stack pointer.
+
+ // Save room for the stack pointer, plus 160 bytes of callee
+ // save area that lives on the caller stack.
+ SUB $168, R15
+ MOVD $~7, R6
+ AND R6, R15
+
+ MOVD R2, 160(R15) // Save original stack pointer.
+ MOVD $0, 0(R15) // clear back chain pointer
+ MOVD R4, R2 // arg in R2
+ BL R3
+ XOR R0, R0
+ MOVD 160(R15), R15 // Restore stack pointer.
+ RET
+
// cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details.
TEXT ·cgocallback(SB),NOSPLIT,$24-24
diff --git a/src/runtime/libinit.go b/src/runtime/libinit.go
new file mode 100644
index 0000000000..7e298a2eca
--- /dev/null
+++ b/src/runtime/libinit.go
@@ -0,0 +1,34 @@
+// Copyright 2026 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 386 || amd64 || arm || arm64 || loong64 || ppc64 || ppc64le || riscv64 || s390x
+
+package runtime
+
+import (
+ "internal/abi"
+ "unsafe"
+)
+
+// libInit is common startup code for most architectures when
+// using -buildmode=c-archive or -buildmode=c-shared.
+//
+// May run with m.p==nil, so write barriers are not allowed.
+//
+//go:nowritebarrierrec
+//go:nosplit
+func libInit() {
+ // Synchronous initialization.
+ libpreinit()
+
+ // Asynchronous initialization.
+ // Prefer creating a thread via cgo if it is available.
+ if _cgo_sys_thread_create != nil {
+ // No g because the TLS is not set up until later in rt0_go.
+ asmcgocall_no_g(_cgo_sys_thread_create, unsafe.Pointer(abi.FuncPCABIInternal(rt0_lib_go)))
+ } else {
+ const stackSize = 0x800000 // 8192KB
+ newosproc0(stackSize, unsafe.Pointer(abi.FuncPCABIInternal(rt0_lib_go)))
+ }
+}
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 7e177c72cd..56971c7dbe 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -8153,24 +8153,3 @@ func doInit1(t *initTask) {
t.state = 2 // initialization done
}
}
-
-// libInit is common startup code for most architectures when
-// using -buildmode=c-archive or -buildmode=c-shared.
-//
-// May run with m.p==nil, so write barriers are not allowed.
-//
-//go:nowritebarrierrec
-//go:nosplit
-func libInit() {
- // Synchronous initialization.
- libpreinit()
-
- // Asynchronous initialization.
- // Prefer creating a thread via cgo if it is available.
- if _cgo_sys_thread_create != nil {
- asmcgocall(_cgo_sys_thread_create, unsafe.Pointer(abi.FuncPCABIInternal(rt0_lib_go)))
- } else {
- const stackSize = 0x800000 // 8192KB
- newosproc0(stackSize, unsafe.Pointer(abi.FuncPCABIInternal(rt0_lib_go)))
- }
-}
diff --git a/src/runtime/stubs_loong64.go b/src/runtime/stubs_loong64.go
index 88d5985db0..dfcfff2d16 100644
--- a/src/runtime/stubs_loong64.go
+++ b/src/runtime/stubs_loong64.go
@@ -6,10 +6,15 @@
package runtime
+import "unsafe"
+
// Called from assembly only; declared for go vet.
func load_g()
func save_g()
+//go:noescape
+func asmcgocall_no_g(fn, arg unsafe.Pointer)
+
// Used by reflectcall and the reflect package.
//
// Spills/loads arguments in registers to/from an internal/abi.RegArgs
diff --git a/src/runtime/stubs_s390x.go b/src/runtime/stubs_s390x.go
index 144e3cdf91..c872799a56 100644
--- a/src/runtime/stubs_s390x.go
+++ b/src/runtime/stubs_s390x.go
@@ -4,10 +4,15 @@
package runtime
+import "unsafe"
+
// Called from assembly only; declared for go vet.
func load_g()
func save_g()
+//go:noescape
+func asmcgocall_no_g(fn, arg unsafe.Pointer)
+
// Used by reflectcall and the reflect package.
//
// Spills/loads arguments in registers to/from an internal/abi.RegArgs