https://github.com/windsunil created
https://github.com/llvm/llvm-project/pull/219828
While upgrading clang/llvm to 23.1.0 in openembedded-core I hit this
link failure on 32-bit x86 musl:
ld: undefined reference to `__stack_chk_fail_local'
I dug into it. musl's libc has __stack_chk_fail but not
__stack_chk_fail_local. GCC emits calls to the _local variant in
PIC/PIE code on some targets - I checked with GCC 16: 32-bit x86 does
it, x86_64 does not. So on those targets, any GCC-built object linked
with clang on musl fails once stack protection is used.
Distros already solve this on the GCC side: Alpine and OpenEmbedded
ship a tiny libssp_nonshared.a containing just that symbol, and patch
GCC to add -lssp_nonshared when stack protection is on. Both carry
the same downstream patch for clang too (OpenEmbedded since 2016),
and the GCC side is being fixed in parallel (gcc PR driver/127138,
v2 on gcc-patches:
https://gcc.gnu.org/pipermail/gcc-patches/2026-August/729351.html)
This patch makes the clang driver do what those patched GCCs do: on
musl, when stack protection is enabled, add -lssp_nonshared - but
only if the library is actually present in the toolchain library
paths. Toolchains that do not ship it see no change.
Tested on i686 musl: a GCC 16 -fPIC -fstack-protector-strong object
fails to link with current clang and links fine with this change.
Added a driver test; the rest of clang/test/Driver is unaffected.
>From 998c7d2e5e621f0160511b303e8285903de8545e Mon Sep 17 00:00:00 2001
From: Sunil Dora <[email protected]>
Date: Sun, 30 Aug 2026 20:51:10 +0530
Subject: [PATCH] [clang][Driver] Link libssp_nonshared.a on musl with stack
protector
musl does not provide __stack_chk_fail_local, but GCC emits calls to
it in PIC/PIE code on some targets (32-bit x86, PowerPC), so linking
GCC-built objects with clang on musl fails:
ld: undefined reference to `__stack_chk_fail_local'
musl distributions (Alpine, OpenEmbedded) ship the symbol in a small
libssp_nonshared.a and patch GCC to pass -lssp_nonshared when stack
protection is enabled. Do the same here, but only when the library is
present in the toolchain library paths, so toolchains without it are
not affected.
Co-authored-by: Khem Raj <[email protected]>
Signed-off-by: Sunil Dora <[email protected]>
---
clang/docs/ReleaseNotes.md | 4 +++
clang/lib/Driver/ToolChains/Gnu.cpp | 18 ++++++++++++
.../musl_ssp_tree/usr/lib/libssp_nonshared.a | 0
clang/test/Driver/linux-musl-ssp.c | 28 +++++++++++++++++++
4 files changed, 50 insertions(+)
create mode 100644
clang/test/Driver/Inputs/musl_ssp_tree/usr/lib/libssp_nonshared.a
create mode 100644 clang/test/Driver/linux-musl-ssp.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index bdbabf2cd98d0..47fb498cd5e3f 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -205,6 +205,10 @@ features cannot lower the translation-unit ABI level;
- Clang tools now resolve tool names without a path in compilation databases
through `PATH`.
+- On musl targets, the driver now links ``libssp_nonshared.a`` when stack
+ protection is enabled and the library is present in the toolchain library
+ paths, matching what musl distributions configure GCC to do.
+
- Clang now allows GNU computed `goto` extension in `constexpr` functions,
matching the relaxed
`constexpr` function body rules introduced in C++23.
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp
b/clang/lib/Driver/ToolChains/Gnu.cpp
index 04f3b8d2200d6..5285f55897732 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -535,6 +535,24 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C,
const JobAction &JA,
if (!Args.hasArg(options::OPT_nolibc))
CmdArgs.push_back("-lc");
+ // musl does not provide __stack_chk_fail_local, but GCC emits calls
+ // to it in PIC/PIE code on some targets (32-bit x86, PowerPC). musl
+ // distributions ship the symbol in libssp_nonshared.a and make GCC
+ // link it when stack protection is on; match that if the library
+ // exists.
+ if (ToolChain.getTriple().isMusl()) {
+ bool WantsSSP = ToolChain.GetDefaultStackProtectorLevel(
+ /*KernelOrKext=*/false) != LangOptions::SSPOff;
+ if (Arg *A = Args.getLastArg(options::OPT_fno_stack_protector,
+ options::OPT_fstack_protector,
+ options::OPT_fstack_protector_all,
+ options::OPT_fstack_protector_strong))
+ WantsSSP = !A->getOption().matches(options::OPT_fno_stack_protector);
+ if (WantsSSP &&
+ ToolChain.GetFilePath("libssp_nonshared.a") !=
"libssp_nonshared.a")
+ CmdArgs.push_back("-lssp_nonshared");
+ }
+
// Add IAMCU specific libs, if needed.
if (IsIAMCU)
CmdArgs.push_back("-lgloss");
diff --git a/clang/test/Driver/Inputs/musl_ssp_tree/usr/lib/libssp_nonshared.a
b/clang/test/Driver/Inputs/musl_ssp_tree/usr/lib/libssp_nonshared.a
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang/test/Driver/linux-musl-ssp.c
b/clang/test/Driver/linux-musl-ssp.c
new file mode 100644
index 0000000000000..4e6bc9cc427ce
--- /dev/null
+++ b/clang/test/Driver/linux-musl-ssp.c
@@ -0,0 +1,28 @@
+// Check that on musl the driver links libssp_nonshared.a when stack
+// protection is enabled and the sysroot provides the library.
+
+// RUN: %clang -### --target=i686-unknown-linux-musl \
+// RUN: --sysroot=%S/Inputs/musl_ssp_tree -fstack-protector-strong %s 2>&1 \
+// RUN: | FileCheck --check-prefix=SSP %s
+// SSP: "-lc" "-lssp_nonshared"
+
+// Not with stack protection disabled (last flag wins).
+// RUN: %clang -### --target=i686-unknown-linux-musl \
+// RUN: --sysroot=%S/Inputs/musl_ssp_tree -fstack-protector-strong \
+// RUN: -fno-stack-protector %s 2>&1 | FileCheck --check-prefix=NOSSP %s
+// NOSSP-NOT: "-lssp_nonshared"
+
+// Not without any stack protector flag.
+// RUN: %clang -### --target=i686-unknown-linux-musl \
+// RUN: --sysroot=%S/Inputs/musl_ssp_tree %s 2>&1 \
+// RUN: | FileCheck --check-prefix=NOSSP %s
+
+// Not on glibc: libc_nonshared.a is linked via the libc.so linker script.
+// RUN: %clang -### --target=i686-unknown-linux-gnu \
+// RUN: --sysroot=%S/Inputs/musl_ssp_tree -fstack-protector-strong %s 2>&1 \
+// RUN: | FileCheck --check-prefix=NOSSP %s
+
+// Not when the sysroot does not provide the library.
+// RUN: %clang -### --target=i686-unknown-linux-musl \
+// RUN: --sysroot=%S/Inputs/basic_linux_tree -fstack-protector-strong %s \
+// RUN: 2>&1 | FileCheck --check-prefix=NOSSP %s
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits