From 80f5e0b661ea3edc5ec893e4fda1dd576086cede Mon Sep 17 00:00:00 2001 From: David Rusu Date: Sun, 7 Jun 2026 03:28:47 -0400 Subject: [PATCH] fix: link glibc C23 strtoul/strtoll compat shim for static linking The prebuilt iden3 rapidsnark archives are compiled on a glibc >= 2.38 host (Ubuntu 24.04 / GCC 13), whose headers redirect strtoll/strtoull to the C23 interceptor symbols __isoc23_strtoll / __isoc23_strtoull. When the static archives (static-rapidsnark feature) are linked on a host with an older glibc (e.g. glibc 2.35), those symbols are undefined and linking fails: rust-lld: error: undefined symbol: __isoc23_strtoull Compile thin forwarders to the classic strtoll/strtoull symbols and link them via whole-archive so the static path resolves regardless of the host glibc version. Gated to the static feature on glibc-Linux; the shared library and all other targets are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/build.rs | 23 +++++++++++++++++++++++ crates/isoc23_compat.c | 24 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 crates/isoc23_compat.c diff --git a/crates/build.rs b/crates/build.rs index 39819cc..345c04f 100644 --- a/crates/build.rs +++ b/crates/build.rs @@ -71,6 +71,29 @@ fn main() { // Android bundles pthread into libc let thread_lib = if is_android_target() { "c" } else { "pthread" }; println!("cargo:rustc-link-lib={thread_lib}"); + + // glibc >= 2.38 redirects strtoll/strtoull to the C23 interceptor symbols + // __isoc23_strtoll / __isoc23_strtoull. The prebuilt iden3 rapidsnark archives + // were compiled against such a glibc, so when they are linked statically on a + // host with an older glibc (e.g. glibc 2.35) the verifier objects reference + // __isoc23_* symbols that do not exist there and linking fails. Compile thin + // forwarders to the classic strtoll/strtoull and link them so the static path + // resolves regardless of the host glibc version. Only relevant for the static + // archive on glibc-Linux; the shared library and other targets are unaffected. + println!("cargo:rerun-if-changed=isoc23_compat.c"); + if is_static_rapidsnark() + && env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") + && env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu") + { + cc::Build::new() + .file("isoc23_compat.c") + .cargo_metadata(false) + .compile("isoc23_compat"); + println!("cargo:rustc-link-search=native={out_dir}"); + // whole-archive so the forwarders are included unconditionally, regardless + // of this archive's position relative to librapidsnark.a on the link line. + println!("cargo:rustc-link-lib=static:+whole-archive=isoc23_compat"); + } } fn is_static_rapidsnark() -> bool { diff --git a/crates/isoc23_compat.c b/crates/isoc23_compat.c new file mode 100644 index 0000000..a7f32e0 --- /dev/null +++ b/crates/isoc23_compat.c @@ -0,0 +1,24 @@ +/* + * glibc >= 2.38 redirects strtoll/strtoull to the C23 interceptor symbols + * __isoc23_strtoll / __isoc23_strtoull at compile time. The prebuilt iden3 + * rapidsnark archives were compiled against such a glibc, so their verifier + * objects reference those symbols. When the static archives are linked on a + * host with an older glibc (e.g. Ubuntu 22.04 / glibc 2.35) the symbols are + * undefined and linking fails. + * + * Provide thin forwarders to the classic strtoll/strtoull symbols. We bind to + * the legacy symbol names explicitly via asm labels and deliberately avoid + * including , so this translation unit is itself immune to the C23 + * header redirect (otherwise the forwarder would recurse into itself when this + * file is compiled on a new-glibc build host). + */ +extern long long __legacy_strtoll(const char *, char **, int) __asm__("strtoll"); +extern unsigned long long __legacy_strtoull(const char *, char **, int) __asm__("strtoull"); + +long long __isoc23_strtoll(const char *nptr, char **endptr, int base) { + return __legacy_strtoll(nptr, endptr, base); +} + +unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { + return __legacy_strtoull(nptr, endptr, base); +}