diff --git a/secp256k1-sys/build.rs b/secp256k1-sys/build.rs index 2d9f8441b..e08c77a99 100644 --- a/secp256k1-sys/build.rs +++ b/secp256k1-sys/build.rs @@ -25,14 +25,77 @@ extern crate cc; use std::env; -fn main() { - // Actual build +fn gen_max_align() { + configured_cc() + .file("depend/max_align.c") + .cargo_metadata(false) + .compile("max_align.o"); + let out_dir = std::path::PathBuf::from(std::env::var_os("OUT_DIR").expect("missing OUT_DIR")); + let target_endian = std::env::var("CARGO_CFG_TARGET_ENDIAN") + .expect("missing CARGO_CFG_TARGET_ENDIAN"); + let target_pointer_width_bytes = std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH") + .expect("missing CARGO_CFG_TARGET_POINTER_WIDTH") + .parse::() + .expect("malformed CARGO_CFG_TARGET_POINTER_WIDTH") + // CARGO_CFG_TARGET_POINTER_WIDTH is in bits, we want bytes + / 8; + let max_align_bin = out_dir.join("max_align.bin"); + // Note that this copies *whole* sections to a binary file. + // It's a bit brittle because some other symbol could theoretically end up there. + // Currently it only has one on my machine and we guard against unexpected changes by checking + // the size - it must match the target pointer width. + let objcopy = std::process::Command::new("objcopy") + .args(&["-O", "binary"]) + // cc inserts depend - WTF + .arg(out_dir.join("depend/max_align.o")) + .arg(&max_align_bin) + .spawn() + .expect("failed to run objcopy") + .wait() + .expect("failed to wait for objcopy"); + assert!(objcopy.success(), "objcopy failed"); + let mut max_align_bytes = std::fs::read(max_align_bin).expect("failed to read max_align.bin"); + // The `usize` of target and host may not match so we need to do conversion. + // Sensible alignments should be very small anyway but we don't want crappy `unsafe` code. + // Little endian happens to be a bit easier to process so we convert into that. + // If the type is smaller than `u64` we zero-pad it. + // If the type is larger than `u6` but the number fits into `u64` it'll have + // unused tail which is easy to cut-off. + // If the number is larger than `u64::MAX` then bytes beyond `u64` size will + // be non-zero. + // + // So as long as the max alignment fits into `u64` this can decode alignment + // for any architecture on any architecture. + assert_eq!(max_align_bytes.len(), target_pointer_width_bytes); + if target_endian != "little" { + max_align_bytes.reverse() + } + // copying like this auto-pads the number with zeroes + let mut buf = [0; std::mem::size_of::()]; + let to_copy = buf.len().min(max_align_bytes.len()); + // Overflow check + if max_align_bytes[to_copy..].iter().any(|b| *b != 0) { + panic!("max alignment overflowed u64"); + } + buf[..to_copy].copy_from_slice(&max_align_bytes[..to_copy]); + let max_align = u64::from_le_bytes(buf); + let src = format!(r#" +/// A type that is as aligned as the biggest alignment for fundamental types in C. +/// +/// Since C11 that means as aligned as `max_align_t` is. +/// The exact size/alignment is unspecified. +#[repr(align({}))] +#[derive(Default, Copy, Clone)] +pub struct AlignedType([u8; {}]);"#, max_align, max_align); + std::fs::write(out_dir.join("aligned_type.rs"), src.as_bytes()).expect("failed to write aligned_type.rs"); +} + +/// Returns CC builder configured with all defines but no C files. +fn configured_cc() -> cc::Build { + // While none of these currently affect max alignment we prefer to keep the "hygiene" so that + // new code will be correct. let mut base_config = cc::Build::new(); - base_config.include("depend/secp256k1/") - .include("depend/secp256k1/include") - .include("depend/secp256k1/src") - .flag_if_supported("-Wno-unused-function") // some ecmult stuff is defined but not used upstream - .define("SECP256K1_API", Some("")) + base_config.define("SECP256K1_API", Some("")) .define("ENABLE_MODULE_ECDH", Some("1")) .define("ENABLE_MODULE_SCHNORRSIG", Some("1")) .define("ENABLE_MODULE_EXTRAKEYS", Some("1")); @@ -48,6 +111,17 @@ fn main() { #[cfg(feature = "recovery")] base_config.define("ENABLE_MODULE_RECOVERY", Some("1")); + base_config +} + +fn build_secp256k1() { + let mut base_config = configured_cc(); + base_config.include("depend/secp256k1/") + .include("depend/secp256k1/include") + .include("depend/secp256k1/src") + .flag_if_supported("-Wno-unused-function"); // some ecmult stuff is defined but not used upstream + + // WASM headers and size/align defines. if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "wasm32" { base_config.include("wasm/wasm-sysroot") @@ -69,3 +143,7 @@ fn main() { } } +fn main() { + gen_max_align(); + build_secp256k1(); +} diff --git a/secp256k1-sys/depend/max_align.c b/secp256k1-sys/depend/max_align.c new file mode 100644 index 000000000..521171803 --- /dev/null +++ b/secp256k1-sys/depend/max_align.c @@ -0,0 +1,5 @@ +#include + +// Note that this symbol is NOT linked with the rest of the library. +// The name is sort of unique in case it accidentally gets linked. +const size_t rust_secp256k1_private_max_align = _Alignof(max_align_t); diff --git a/secp256k1-sys/src/types.rs b/secp256k1-sys/src/types.rs index e457ec41c..224f81e35 100644 --- a/secp256k1-sys/src/types.rs +++ b/secp256k1-sys/src/types.rs @@ -11,21 +11,15 @@ pub type c_char = i8; pub use core::ffi::c_void; -/// A type that is as aligned as the biggest alignment for fundamental types in C -/// since C11 that means as aligned as `max_align_t` is. -/// the exact size/alignment is unspecified. -// 16 matches is as big as the biggest alignment in any arch that rust currently supports https://github.com/rust-lang/rust/blob/2c31b45ae878b821975c4ebd94cc1e49f6073fd0/library/std/src/sys_common/alloc.rs -#[repr(align(16))] -#[derive(Default, Copy, Clone)] -pub struct AlignedType([u8; 16]); +include!(concat!(env!("OUT_DIR"), "/aligned_type.rs")); impl AlignedType { pub fn zeroed() -> Self { - AlignedType([0u8; 16]) + Self::ZERO } /// A static zeroed out AlignedType for use in static assignments of [AlignedType; _] - pub const ZERO: AlignedType = AlignedType([0u8; 16]); + pub const ZERO: AlignedType = AlignedType([0u8; core::mem::size_of::()]); } #[cfg(all(feature = "alloc", not(rust_secp_no_symbol_renaming)))]