runtime: make static/dynamic startup detection work with musl on ppc64le

The glibc loader explicitly sets the first doubleword on the stack (R1)
to $0 to indicate it was dynamically loaded.

An ELFv2 ABI compliant loader will set R3/R4 to argc/argv when starting
the process, and R13 to TLS. musl is not compliant. Instead it passes
argc/argv like the kernel, but R3/R4 are in an undefined state and R13
is valid.

With the knowledge above, the startup code can be modified to
dynamically handle all three cases when linked internally.

Fixes #51787

Change-Id: I5de33862c161900d9161817388bbc13a65fdc69c
Reviewed-on: https://go-review.googlesource.com/c/go/+/394654
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Paul Murphy <murp@ibm.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Trust: Paul Murphy <murp@ibm.com>
Trust: Lynn Boger <laboger@linux.vnet.ibm.com>
This commit is contained in:
Paul E. Murphy 2022-03-22 11:52:02 -05:00 committed by Paul Murphy
parent 817d6ea2b3
commit 946167906e

View file

@ -147,25 +147,35 @@ TEXT _main<>(SB),NOSPLIT,$-8
// In a statically linked binary, the stack contains argc,
// argv as argc string pointers followed by a NULL, envv as a
// sequence of string pointers followed by a NULL, and auxv.
// There is no TLS base pointer.
// The TLS pointer should be initialized to 0.
//
// In a dynamically linked binary, r3 contains argc, r4
// contains argv, r5 contains envp, r6 contains auxv, and r13
// In an ELFv2 compliant dynamically linked binary, R3 contains argc,
// R4 contains argv, R5 contains envp, R6 contains auxv, and R13
// contains the TLS pointer.
//
// Figure out which case this is by looking at r4: if it's 0,
// we're statically linked; otherwise we're dynamically
// linked.
CMP R0, R4
BNE dlink
// When loading via glibc, the first doubleword on the stack points
// to NULL a value. (that is *(uintptr)(R1) == 0). This is used to
// differentiate static vs dynamicly linked binaries.
//
// If loading with the musl loader, it doesn't follow the ELFv2 ABI. It
// passes argc/argv similar to the linux kernel, R13 (TLS) is
// initialized, and R3/R4 are undefined.
MOVD (R1), R12
CMP R0, R12
BEQ tls_and_argcv_in_reg
// Statically linked
// Arguments are passed via the stack (musl loader or a static binary)
MOVD 0(R1), R3 // argc
ADD $8, R1, R4 // argv
// Did the TLS pointer get set? If so, don't change it (e.g musl).
CMP R0, R13
BNE tls_and_argcv_in_reg
MOVD $runtime·m0+m_tls(SB), R13 // TLS
ADD $0x7000, R13
dlink:
tls_and_argcv_in_reg:
BR main(SB)
TEXT main(SB),NOSPLIT,$-8