From 4e83e2d542d8868c4ddb33a4921871bd035ffade Mon Sep 17 00:00:00 2001 From: dybucc <149513579+dybucc@users.noreply.github.com> Date: Tue, 14 Apr 2026 18:47:34 +0200 Subject: [PATCH] fix: fix differing bitwidth routines in win x86 gnu Until rust-lang/libc#5032, whenever `libc` was running under Windows x86 with GNU, the default bit-width of `time_t` values was 32-bits. This doesn't quite align with the defaults in MSVC and `mingw`, but that's been addressed in that PR. This PR is a quick patch for stable, as the changes in rust-lang/libc#5032 are not backwards-compatible. It separates the routines that under Windows use `time_t` values and changes their link-time symbols to the 32-bit variants exposed in both MSVC and `mingw`, for the affected target platform/environment. It also adds a test, akin to the one in rust-lang/libc#5032, that should ensure the correct bit-widths are in use for both the types and the linked routine symbols. --- libc-test/tests/windows_time.rs | 26 +++++++++++++++++++++++ src/windows/mod.rs | 37 ++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 libc-test/tests/windows_time.rs diff --git a/libc-test/tests/windows_time.rs b/libc-test/tests/windows_time.rs new file mode 100644 index 0000000000000..f5c2554b0d6a1 --- /dev/null +++ b/libc-test/tests/windows_time.rs @@ -0,0 +1,26 @@ +//! Ensures Windows `time`-related routines align with `libc`'s interface. By +//! default, both MSVC and GNU (under `mingw`) expose 64-bit symbols, but in +//! stable we need to cope with a 32-bit `time-t`, so the routines should link +//! to their 32-bit variants. + +#![cfg(windows)] + +/// Ensures a 64-bit write is performed on values that should always be 64 bits, +/// and that a corresponding 32-bit write is performed on values that should be +/// 32 bits. This basically makes sure `time_t`'s bit-width aligns with those of +/// functions that expect it as a parameter. +#[test] +fn test_bitwidth_store() { + #[cfg(all(target_arch = "x86", target_env = "gnu"))] + assert_eq!(size_of::(), 4); + #[cfg(not(all(target_arch = "x86", target_env = "gnu")))] + assert_eq!(size_of::(), 8); + + let mut time_values: [libc::time_t; 2] = [123, 456]; + let ptr = time_values.as_mut_ptr(); + + unsafe { libc::time(ptr) }; + + assert_ne!(time_values[0], 123); + assert_eq!(time_values[1], 456); +} diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 2cfd1977d2799..b844d9f414418 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -389,11 +389,36 @@ extern "C" { pub fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t; pub fn raise(signum: c_int) -> c_int; + // By default, the following link to 64-bit variants where the expected + // `time_t` is also 64-bits. + // + // Under Windows x86 with GNU, `time_t` is still 32-bit wide on stable, so + // these routines have to link with their 32-bit variants. + // + // FIXME(#5062): once that PR gets merged, add `not(windows_gnu_time64)` to + // the below predicate, within the `all()` clause. + cfg_if! { + if #[cfg(all(target_arch = "x86", target_env = "gnu"))] { + #[link_name = "_ctime32"] + pub fn ctime(sourceTime: *const time_t) -> *mut c_char; + #[link_name = "_difftime32"] + pub fn difftime(timeEnd: time_t, timeStart: time_t) -> c_double; + #[link_name = "_gmtime32_s"] + pub fn gmtime_s(destTime: *mut tm, srcTime: *const time_t) -> c_int; + #[link_name = "_localtime32_s"] + pub fn localtime_s(tmDest: *mut tm, sourceTime: *const time_t) -> crate::errno_t; + #[link_name = "_time32"] + pub fn time(destTime: *mut time_t) -> time_t; + } else { + pub fn ctime(sourceTime: *const time_t) -> *mut c_char; + pub fn difftime(timeEnd: time_t, timeStart: time_t) -> c_double; + pub fn gmtime_s(destTime: *mut tm, srcTime: *const time_t) -> c_int; + pub fn localtime_s(tmDest: *mut tm, sourceTime: *const time_t) -> crate::errno_t; + pub fn time(destTime: *mut time_t) -> time_t; + } + } + pub fn clock() -> clock_t; - pub fn ctime(sourceTime: *const time_t) -> *mut c_char; - pub fn difftime(timeEnd: time_t, timeStart: time_t) -> c_double; - #[link_name = "_gmtime64_s"] - pub fn gmtime_s(destTime: *mut tm, srcTime: *const time_t) -> c_int; #[link_name = "_get_daylight"] pub fn get_daylight(hours: *mut c_int) -> errno_t; #[link_name = "_get_dstbias"] @@ -407,10 +432,6 @@ extern "C" { size_in_bytes: size_t, index: c_int, ) -> errno_t; - #[link_name = "_localtime64_s"] - pub fn localtime_s(tmDest: *mut tm, sourceTime: *const time_t) -> crate::errno_t; - #[link_name = "_time64"] - pub fn time(destTime: *mut time_t) -> time_t; #[link_name = "_tzset"] pub fn tzset(); #[link_name = "_chmod"]