diff options
| author | Michael Pratt <mpratt@google.com> | 2026-03-31 16:34:59 -0400 |
|---|---|---|
| committer | Michael Pratt <mpratt@google.com> | 2026-04-02 09:58:36 -0700 |
| commit | 78d5260426899e934a4d680910b1484953e78087 (patch) | |
| tree | 720363c8910cf1a4cf3b6e8318b2cc4e7c463742 | |
| parent | ef90a565b50af191c4f20b62770b084d6978a88d (diff) | |
| download | go-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.s | 9 | ||||
| -rw-r--r-- | src/runtime/asm_s390x.s | 23 | ||||
| -rw-r--r-- | src/runtime/libinit.go | 34 | ||||
| -rw-r--r-- | src/runtime/proc.go | 21 | ||||
| -rw-r--r-- | src/runtime/stubs_loong64.go | 5 | ||||
| -rw-r--r-- | src/runtime/stubs_s390x.go | 5 |
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 |
