Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC: Retrieve the actual alignment of max_align_t #554

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 85 additions & 7 deletions secp256k1-sys/build.rs
Expand Up @@ -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::<usize>()
.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::<u64>()];
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"));
Expand All @@ -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")
Expand All @@ -69,3 +143,7 @@ fn main() {
}
}

fn main() {
gen_max_align();
build_secp256k1();
}
5 changes: 5 additions & 0 deletions secp256k1-sys/depend/max_align.c
@@ -0,0 +1,5 @@
#include <stddef.h>

// 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);
12 changes: 3 additions & 9 deletions secp256k1-sys/src/types.rs
Expand Up @@ -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::<AlignedType>()]);
}

#[cfg(all(feature = "alloc", not(rust_secp_no_symbol_renaming)))]
Expand Down