From 946167906ed8646c433c257b074a10e01f0a7dab Mon Sep 17 00:00:00 2001 From: "Paul E. Murphy" Date: Tue, 22 Mar 2022 11:52:02 -0500 Subject: [PATCH] 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 Run-TryBot: Paul Murphy TryBot-Result: Gopher Robot Trust: Paul Murphy Trust: Lynn Boger --- src/runtime/rt0_linux_ppc64le.s | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/runtime/rt0_linux_ppc64le.s b/src/runtime/rt0_linux_ppc64le.s index 4f7c6e6c99f60..66f7e7b22a411 100644 --- a/src/runtime/rt0_linux_ppc64le.s +++ b/src/runtime/rt0_linux_ppc64le.s @@ -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 - - // Statically linked + // 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 + + // 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