From 22154611cb35408febdb91b513090258f1e8e6a6 Mon Sep 17 00:00:00 2001 From: Manuel Linares Date: Fri, 29 May 2026 01:34:39 -0300 Subject: [PATCH] linux: fix musl linking and warn on dynamic glibc cross-compilation ### `c3c ... -linux-libc=musl` - warn user if they are in a glibc system compiling with `-linux-libc=musl` that they should use the builtin linker - warn user if they are dynamically linking in a glibc system. - warn user if they are statically linking against glibc, reminding them to use musl instead. --- scripts/tools/test_distros.sh | 4 ++-- src/build/builder.c | 31 +++++++++++++++++++++++++++++++ src/compiler/linker.c | 22 +++++++++++++--------- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/scripts/tools/test_distros.sh b/scripts/tools/test_distros.sh index a63000190c..8f44cc7c49 100755 --- a/scripts/tools/test_distros.sh +++ b/scripts/tools/test_distros.sh @@ -34,10 +34,10 @@ declare -A DISTRO_IMAGES=( [debian]="debian:bookworm-slim|apt-get update -qq && apt-get install -y -qq gcc bash" [fedora]="fedora:latest|dnf install -y -q gcc bash" [alpine]="alpine:3.23|apk add --no-cache gcc musl-dev bash" - [arch]="archlinux:latest|pacman -Sy --noconfirm gcc bash" + [arch]="archlinux:latest|pacman -Sy --noconfirm gcc bash libx11 libxrandr libxinerama libxi libxxf86vm libxcursor" [opensuse]="opensuse/tumbleweed:latest|zypper install -y gcc glibc-devel bash" [rocky]="rockylinux:9|dnf install -y -q gcc bash" - [void]="ghcr.io/void-linux/void-linux:latest-full-x86_64|xbps-install -Suy xbps && xbps-install -Sy gcc bash" + [void]="ghcr.io/void-linux/void-linux:latest-full-x86_64|xbps-install -Suy xbps && xbps-install -Sy gcc bash libX11-devel libXrandr-devel libXinerama-devel libXi-devel libXxf86vm-devel libXcursor-devel" ) # Resolve which distros to test diff --git a/src/build/builder.c b/src/build/builder.c index 9ec12b0939..92d6857068 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -629,6 +629,37 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * } #endif if (target->linuxpaths.libc == LINUX_LIBC_NOT_SET) target->linuxpaths.libc = default_libc; + + bool is_static = false; + for (size_t i = 0; i < options->linker_arg_count; i++) + { + const char *arg = options->linker_args[i]; + if (strcmp(arg, "-static") == 0 || strcmp(arg, "--static") == 0 || strcmp(arg, "static") == 0) + { + is_static = true; + break; + } + } + + if (target->linuxpaths.libc == LINUX_LIBC_MUSL && default_libc == LINUX_LIBC_GNU) + { + if (is_static) + { + if (target->linker_type != LINKER_TYPE_BUILTIN) + { + WARNING("Static linking against musl on a glibc system should use the built-in linker to avoid linking host libraries. Consider adding `--linker=builtin`."); + } + } + else + { + WARNING("Dynamically linking against musl on a glibc system produces non-portable binaries. Consider using `-z -static --linker=builtin`."); + } + } + else if (target->linuxpaths.libc == LINUX_LIBC_GNU && is_static) + { + WARNING("Statically linking against glibc produces binaries that still require glibc shared libraries at runtime for NSS/dns lookups. Consider targeting musl instead using `-linux-libc=musl --linker=builtin`."); + } + target->benchmarking = options->benchmarking; target->testing = options->testing; target->docgen = options->command == COMMAND_DOCGEN; diff --git a/src/compiler/linker.c b/src/compiler/linker.c index cd5dc3ae84..7fb275ac0d 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -586,11 +586,12 @@ static void linker_setup_linux(const char ***args_ref, Linker linker_type, bool } add_plain_arg("--eh-frame-hdr"); if (!link_libc()) return; - const char *crt_begin_dir = find_linux_crt_begin(); + bool is_musl = compiler.build.linuxpaths.libc == LINUX_LIBC_MUSL; + const char *crt_begin_dir = is_musl ? NULL : find_linux_crt_begin(); const char *crt_dir = find_linux_crt(); if (is_exe && strip_unused()) add_plain_arg("--gc-sections"); - if (!crt_begin_dir || !crt_dir) + if (!crt_dir || (!is_musl && !crt_begin_dir)) { error_exit("Failed to find the C runtime at link time."); } @@ -607,19 +608,19 @@ static void linker_setup_linux(const char ***args_ref, Linker linker_type, bool { add_concat_file_arg(crt_dir, "crti.o"); if (!is_dylib) add_concat_file_arg(crt_dir, "Scrt1.o"); - add_concat_file_arg(crt_begin_dir, "crtbeginS.o"); - add_concat_file_arg(crt_begin_dir, "crtendS.o"); + if (!is_musl) add_concat_file_arg(crt_begin_dir, "crtbeginS.o"); + if (!is_musl) add_concat_file_arg(crt_begin_dir, "crtendS.o"); } else { add_concat_file_arg(crt_dir, "crti.o"); if (!is_dylib) add_concat_file_arg(crt_dir, "crt1.o"); - add_concat_file_arg(crt_begin_dir, "crtbegin.o"); - add_concat_file_arg(crt_begin_dir, "crtend.o"); + if (!is_musl) add_concat_file_arg(crt_begin_dir, "crtbegin.o"); + if (!is_musl) add_concat_file_arg(crt_begin_dir, "crtend.o"); } add_concat_file_arg(crt_dir, "crtn.o"); add_concat_quote_arg("-L", crt_dir); - add_concat_quote_arg("-L", crt_begin_dir); + if (!is_musl) add_concat_quote_arg("-L", crt_begin_dir); if (compiler.platform.arch == ARCH_TYPE_RISCV64 || compiler.platform.arch == ARCH_TYPE_RISCV32) { @@ -637,8 +638,11 @@ static void linker_setup_linux(const char ***args_ref, Linker linker_type, bool if (compiler.linking.link_math) linking_add_link(&compiler.linking, "m"); linking_add_link(&compiler.linking, "pthread"); - linking_add_link(&compiler.linking, "gcc"); - linking_add_link(&compiler.linking, "gcc_s"); + if (!is_musl) + { + linking_add_link(&compiler.linking, "gcc"); + linking_add_link(&compiler.linking, "gcc_s"); + } linking_add_link(&compiler.linking, "c"); add_plain_arg("-L/usr/lib/"); add_plain_arg("-L/lib/");