From c9fd4542d7f23897f8986f8ab678b4106f090d54 Mon Sep 17 00:00:00 2001 From: JohnE Date: Fri, 13 Mar 2026 19:26:20 -0700 Subject: [PATCH] FIX: build iOS fixes, parse the CMake-generated options.h to extract all #define directives --- crates/ccc-crypto-wolfssl/build.rs | 86 +++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/crates/ccc-crypto-wolfssl/build.rs b/crates/ccc-crypto-wolfssl/build.rs index 3548153..7ec7b2f 100644 --- a/crates/ccc-crypto-wolfssl/build.rs +++ b/crates/ccc-crypto-wolfssl/build.rs @@ -171,6 +171,32 @@ fn build_wolfssl_cmake(source_dir: &Path, _out_dir: &Path) -> (PathBuf, PathBuf) (include_dir, lib_dir) } +/// Parse the CMake-generated `options.h` to extract all `#define` directives. +/// Returns a list of `-DNAME` clang args. +fn parse_options_defines(include_dir: &Path) -> Vec { + let options_h = include_dir.join("wolfssl/options.h"); + if !options_h.exists() { + return Vec::new(); + } + + let content = std::fs::read_to_string(&options_h).expect("failed to read options.h"); + let mut defines = Vec::new(); + for line in content.lines() { + let line = line.trim(); + if !line.starts_with("#define ") { + continue; + } + let rest = &line["#define ".len()..]; + let name = rest.split_whitespace().next().unwrap_or(""); + // Skip the header guard macro. + if name.is_empty() || name == "WOLFSSL_OPTIONS_H" { + continue; + } + defines.push(format!("-D{}", name)); + } + defines +} + /// Run bindgen over the wolfCrypt headers we actually use. fn generate_bindings(include_dir: &Path, out_dir: &Path) { let header = include_dir.join("wolfssl/wolfcrypt/aes.h"); @@ -187,7 +213,17 @@ fn generate_bindings(include_dir: &Path, out_dir: &Path) { return; } - let bindings = bindgen::Builder::default() + // Parse the CMake-generated options.h so that bindgen's clang sees the + // exact same preprocessor flags as the compiled wolfSSL library. This is + // essential for correct struct layout — e.g. GCM_TABLE_4BIT adds a 512-byte + // M0 table to the Gcm struct that changes the size of Aes. + let options_defines = parse_options_defines(include_dir); + + // For cross-compilation, tell bindgen's clang the target triple so that + // platform-specific types (pointer size, alignment, builtins) are correct. + let target = env::var("TARGET").unwrap_or_default(); + + let mut builder = bindgen::Builder::default() // Include all wolfCrypt headers we need. .header(include_dir.join("wolfssl/wolfcrypt/aes.h").to_str().unwrap()) .header(include_dir.join("wolfssl/wolfcrypt/chacha20_poly1305.h").to_str().unwrap()) @@ -201,31 +237,33 @@ fn generate_bindings(include_dir: &Path, out_dir: &Path) { .header(include_dir.join("wolfssl/wolfcrypt/curve25519.h").to_str().unwrap()) .header(include_dir.join("wolfssl/wolfcrypt/curve448.h").to_str().unwrap()) .header(include_dir.join("wolfssl/wolfcrypt/random.h").to_str().unwrap()) - .clang_arg(format!("-I{}", include_dir.display())) - // Pass all required preprocessor defines explicitly so that bindgen's - // libclang sees the same feature flags as the C build, regardless of - // whether wolfSSL's options.h is correctly resolved by clang. - // - // AES-GCM (wc_AesGcmSetKey etc. are behind #ifdef HAVE_AESGCM) - .clang_arg("-DHAVE_AESGCM") - // ChaCha20 / Poly1305 / XChaCha20 - .clang_arg("-DHAVE_CHACHA") - .clang_arg("-DHAVE_POLY1305") + .clang_arg(format!("-I{}", include_dir.display())); + + // Pass all defines from the generated options.h. This includes critical + // struct-affecting flags like GCM_TABLE_4BIT, HAVE___UINT128_T, + // WOLFSSL_USE_ALIGN, etc. that change the layout of Gcm, Aes, and other types. + for def in &options_defines { + builder = builder.clang_arg(def); + } + + // Pass additional defines that are added via .cflag() in the CMake build + // (not cmake options, so they don't appear in options.h). + builder = builder .clang_arg("-DHAVE_XCHACHA") - // BLAKE2 is not a cmake option — enable via clang preprocessor flags - // so that the headers expose the BLAKE2b/BLAKE2s function prototypes. .clang_arg("-DHAVE_BLAKE2") - .clang_arg("-DHAVE_BLAKE2B") - // Required so sha512.h exposes SHA384 typedef + functions. - .clang_arg("-DWOLFSSL_SHA384") - .clang_arg("-DWOLFSSL_SHA512") - // SHA3 functions (wc_InitSha3_256, etc.) - .clang_arg("-DWOLFSSL_SHA3") - // HKDF via TLS-1.3 primitives (wc_Tls13_HKDF_Extract/Expand_Label) - .clang_arg("-DHAVE_HKDF") - // Curve25519 / Curve448 need HAVE_CURVE25519 / HAVE_CURVE448 - .clang_arg("-DHAVE_CURVE25519") - .clang_arg("-DHAVE_CURVE448") + .clang_arg("-DHAVE_BLAKE2B"); + + // Set the cross-compilation target if it differs from the host. + if !target.is_empty() { + // Map Rust target triples to clang-compatible triples. + let clang_target = match target.as_str() { + "aarch64-apple-ios-sim" => "arm64-apple-ios-simulator".to_string(), + other => other.to_string(), + }; + builder = builder.clang_arg(format!("--target={}", clang_target)); + } + + let bindings = builder // Only generate bindings for the types/functions we use. .allowlist_function("wc_Aes.*") .allowlist_function("wc_AesGcm.*")