Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ libc = "0.2.186"
[build-dependencies]
pkg-config = "0.3.33"
bindgen = { version = "0.72", optional = true }
cc = "1.2"
Comment thread
diwic marked this conversation as resolved.

[features]
use-bindgen = ["bindgen"]
extra_traits = ["libc/extra_traits"]

[[bin]]
name = "regenerate_bindings"
Expand Down
51 changes: 51 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ extern crate pkg_config;
extern crate bindgen;

fn main() {
Comment thread
diwic marked this conversation as resolved.
probe_time64();

match pkg_config::Config::new().statik(false).probe("alsa") {
Err(pkg_config::Error::Failure { command, output }) => panic!(
"Pkg-config failed - usually this is because alsa development headers are not installed.\n\n\
Expand All @@ -19,6 +21,55 @@ fn main() {
};
}

// 32-bit glibc's struct timespec is 8 or 16 bytes depending on the time64
// transition; libc::timespec assumes 8, so ALSA can write past it and
// segfault on 32-bit targets with time64.
fn probe_time64() {
println!("cargo:rustc-check-cfg=cfg(alsa_sys_time64)");

let target_os = std::env::var("CARGO_CFG_TARGET_OS");
let target_env = std::env::var("CARGO_CFG_TARGET_ENV");
let target_pointer_width = std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH");

// Only glibc on 32-bit Linux has this ambiguity. musl defaulted to a
// 64-bit time_t from the start, and 64-bit targets already match
// libc::timespec natively.
if target_os.is_ok_and(|v| v != "linux")
|| target_env.is_ok_and(|v| v != "gnu")
|| target_pointer_width.is_ok_and(|v| v != "32")
{
return;
}

let probe_path = std::path::Path::new(&std::env::var("OUT_DIR").unwrap()).join("time64_probe.c");
if let Err(e) = std::fs::write(
&probe_path,
"#include <time.h>\n\
Comment thread
diwic marked this conversation as resolved.
#if defined(__TIMESIZE) && __TIMESIZE == 64\n\
alsa_sys_time64=yes\n\
#else\n\
alsa_sys_time64=no\n\
#endif\n",
) {
println!("cargo:warning=alsa-sys: could not write time64 probe ({e}), assuming legacy 32-bit time_t");
return;
}

// On failure, default to 32-bit time_t. A false negative is a no-op, a
// false positive causes segfaults.
let expanded = match cc::Build::new().file(&probe_path).try_expand() {
Ok(bytes) => bytes,
Err(e) => {
println!("cargo:warning=alsa-sys: could not probe glibc time_t width ({e}), assuming legacy 32-bit time_t");
return;
}
};

if String::from_utf8_lossy(&expanded).contains("alsa_sys_time64=yes") {
println!("cargo:rustc-cfg=alsa_sys_time64");
}
}

#[cfg(feature = "use-bindgen")]
fn generate_bindings(alsa_library: &pkg_config::Library) {
use std::env;
Expand Down
33 changes: 32 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,38 @@
#![allow(non_upper_case_globals)]
#![no_std]

use libc::{FILE, pid_t, pollfd, timespec, timeval};
use libc::{FILE, pid_t, pollfd};

#[cfg(not(alsa_sys_time64))]
pub use libc::{timespec, timeval};

// Mirrors glibc's 32-bit time64 struct timespec; see probe_time64 in build.rs.
#[cfg(alsa_sys_time64)]
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
#[cfg_attr(feature = "extra_traits", derive(PartialEq, Eq, Hash))]
pub struct timespec {
pub tv_sec: i64,
#[cfg(target_endian = "little")]
Comment thread
diwic marked this conversation as resolved.
pub tv_nsec: i32,
#[cfg(target_endian = "little")]
__pad: i32,
#[cfg(target_endian = "big")]
__pad: i32,
#[cfg(target_endian = "big")]
pub tv_nsec: i32,
}

// Mirrors glibc's 32-bit time64 struct timeval; see probe_time64 in build.rs.
#[cfg(alsa_sys_time64)]
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
#[cfg_attr(feature = "extra_traits", derive(PartialEq, Eq, Hash))]
pub struct timeval {
pub tv_sec: i64,
pub tv_usec: i32,
__pad: i32,
}

#[cfg(feature = "use-bindgen")]
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
Expand Down