cmd/link,runtime: fix c-shared dlopen on non-glibc systems (#13492) #40
+2,371
−153
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR fixes long-standing compatibility issues preventing Go c-shared
and c-archive libraries from working correctly on non-glibc systems,
particularly Alpine Linux (musl libc) and other alternative libc
implementations.
Problem
Go's c-shared/c-archive build modes make glibc-specific assumptions that
cause failures on other systems:
SIGSEGV on dlopen(): Go assumes DT_INIT_ARRAY functions receive
(argc, argv, envp) arguments, but this is glibc-specific behavior not
guaranteed by the ELF specification. On musl and other systems, these
pointers are null or garbage, causing immediate segfaults when Go
tries to dereference argv in runtime.sysargs().
TLS incompatibility: Go uses Initial Exec TLS model which requires
static TLS allocation. This prevents dlopen() from working on musl and
other dynamic loaders that don't reserve extra TLS space. The error
"initial-exec TLS resolves to dynamic definition" makes Go shared
libraries impossible to load dynamically.
Solution
This PR implements three key fixes:
argc/argv null safety: Add null checks in runtime initialization
to handle systems where DT_INIT_ARRAY functions don't receive arguments.
The code now gracefully handles null argv instead of crashing.
TLS General Dynamic support: Implement TLS GD model across all
architectures (x86, ARM64, ARM, PowerPC64, s390x, RISC-V, LoongArch64,
MIPS) with a new -tls flag for explicit control. The GD model uses
__tls_get_addr() for dynamic TLS resolution, enabling dlopen()
compatibility.
Standards compliance: All changes follow established standards:
Implementation
The implementation maintains full backward compatibility with existing
glibc systems while enabling support for other libc implementations. The
-tls flag allows users to control TLS model selection (auto/LE/IE/GD)
with sensible defaults that automatically select GD for c-shared/c-archive
builds on Unix platforms.
Testing
freebsd/amd64, openbsd/amd64
Impact
This enables Go shared libraries to work correctly on Alpine Linux and
other non-glibc systems, addressing a major compatibility gap that has
affected container deployments and embedded systems for years.
Fixes golang#13492
Fixes golang#54805
Co-authored-by: Alexander Musman [email protected]
🔄 This is a mirror of upstream PR golang#75048