diff --git a/crates/build.rs b/crates/build.rs index 345c04f..7026905 100644 --- a/crates/build.rs +++ b/crates/build.rs @@ -4,6 +4,8 @@ use std::path::Path; use std::process::Command; const RAPIDSNARK_DOWNLOAD_SCRIPT: &str = include_str!("./download_rapidsnark.sh"); +const RAPIDSNARK_GIT: &str = "https://github.com/iden3/rapidsnark.git"; +const RAPIDSNARK_TAG: &str = "v0.0.8"; fn main() { let target = env::var("TARGET").unwrap(); @@ -13,6 +15,16 @@ fn main() { // See: https://github.com/zkmopro/chkstk_stub chkstk_stub::build(); + // On glibc-Linux with static linking the prebuilt iden3 archives don't work: + // they are non-PIC (cannot be linked into a downstream cdylib such as the + // node's C bindings) and are built against a newer glibc (undefined + // __isoc23_strtoll/ull on older hosts). Build rapidsnark from source with + // -fPIC against the host toolchain instead, which resolves both problems. + if build_from_source_applies() { + build_rapidsnark_from_source(&out_dir); + return; + } + // Try to list contents of the target directory let rapidsnark_path = Path::new(&out_dir).join(Path::new("rapidsnark")); // If the rapidsnark repo is not downloaded, download it @@ -71,29 +83,86 @@ 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() +fn build_from_source_applies() -> bool { + 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"); +} + +/// Build rapidsnark's static archives from source with position-independent code. +/// +/// Requires `git`, `cmake`, `make`, `nasm`, a C++ compiler, and the GMP/libsodium +/// development headers to be available on the build host. GMP is linked from the +/// system as a (PIC) shared library. +fn build_rapidsnark_from_source(out_dir: &str) { + let src = Path::new(out_dir).join("rapidsnark-src"); + if !src.join("CMakeLists.txt").exists() { + // Clean any partial checkout so the clone can succeed. + let _ = fs::remove_dir_all(&src); + run(Command::new("git").args([ + "clone", + "--depth", + "1", + "--branch", + RAPIDSNARK_TAG, + RAPIDSNARK_GIT, + src.to_str().unwrap(), + ])); } + // Only the field-element codegen (ffiasm) and JSON header are needed to build + // the prover/verifier static libraries. + run(Command::new("git").current_dir(&src).args([ + "submodule", + "update", + "--init", + "--depth", + "1", + "depends/ffiasm", + "depends/json", + ])); + + let build = src.join("build_pic"); + fs::create_dir_all(&build).expect("Failed to create rapidsnark build dir"); + run(Command::new("cmake").current_dir(&build).args([ + "..", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", + "-DUSE_ASM=ON", + "-DUSE_OPENMP=OFF", + "-DUSE_LOGGER=ON", + ])); + + let jobs = std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(4) + .to_string(); + run(Command::new("make").current_dir(&build).args([ + "-j", + &jobs, + "rapidsnarkStatic", + "fr", + "fq", + ])); + + let lib_dir = build.join("src"); + println!("cargo:rustc-link-search=native={}", lib_dir.display()); + println!("cargo:rustc-link-lib=static=rapidsnark"); + println!("cargo:rustc-link-lib=static=fr"); + println!("cargo:rustc-link-lib=static=fq"); + // GMP is provided by the system as a PIC shared library. + println!("cargo:rustc-link-lib=dylib=gmp"); + println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-lib=pthread"); +} + +fn run(cmd: &mut Command) { + eprintln!("rapidsnark build: running {cmd:?}"); + let status = cmd + .status() + .unwrap_or_else(|e| panic!("Failed to spawn {cmd:?}: {e}")); + assert!(status.success(), "Command failed ({status}): {cmd:?}"); } fn is_static_rapidsnark() -> bool { diff --git a/crates/isoc23_compat.c b/crates/isoc23_compat.c deleted file mode 100644 index a7f32e0..0000000 --- a/crates/isoc23_compat.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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); -}