Skip to content

Commit aeb27b2

Browse files
committed
fix: fix differing bitwidth routines in win x86 gnu
Until [rust-lang#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#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. [rust-lang#5032]: rust-lang#5032
1 parent f3417ae commit aeb27b2

File tree

3 files changed

+88
-8
lines changed

3 files changed

+88
-8
lines changed

.github/workflows/windows.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Windows test
2+
3+
on: workflow_dispatch
4+
5+
defaults:
6+
run:
7+
shell: bash
8+
9+
jobs:
10+
test:
11+
name: Test
12+
runs-on: windows-2025
13+
strategy:
14+
matrix:
15+
include:
16+
- target: x86_64-pc-windows-msvc
17+
- target: i686-pc-windows-gnu
18+
env:
19+
TARGET: ${{ matrix.target }}
20+
steps:
21+
- uses: actions/checkout@v6
22+
- name: Set up Rust toolchain
23+
run: |
24+
if [ "$TARGET" = "i686-pc-windows-gnu" ]; then
25+
rustup target add i686-pc-windows-gnu
26+
fi
27+
rustup default nightly-$TARGET
28+
rustc -Vv
29+
- name: Test
30+
run: |
31+
cd ./libc-test
32+
cargo t --target ${{ matrix.target }}

libc-test/tests/windows_time.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//! Ensures Windows `time`-related routines align with `libc`'s interface. By
2+
//! default, both MSVC and GNU (under `mingw`) expose 64-bit symbols. `libc`
3+
//! also does that, but there's been slight inconsistencies in the past.
4+
5+
#![cfg(windows)]
6+
7+
/// Ensures a 64-bit write is performed on values that should always be 64 bits.
8+
/// This may fail if
9+
/// (1) the routine links with its 32-bit variant. This only happens if
10+
/// `_USE_32BIT_TIME_T` is defined. In theory, this should not be
11+
/// possible when working with Rust's `libc`.
12+
/// (2) Or `time_t` is 32-bits, and a 64-bit write overwrites both array items.
13+
/// This should neither be possible unless the above macro is defined.
14+
/// Support for non-64-bit values in `libc` should thus be non-existent.
15+
#[test]
16+
fn test_64_bit_store() {
17+
#[cfg(all(target_arch = "x86", target_env = "gnu"))]
18+
assert_eq!(size_of::<libc::time_t>(), 4);
19+
#[cfg(not(all(target_arch = "x86", target_env = "gnu")))]
20+
assert_eq!(size_of::<libc::time_t>(), 8);
21+
22+
let mut time_values: [libc::time_t; 2] = [123, 456];
23+
let ptr = time_values.as_mut_ptr();
24+
25+
unsafe { libc::time(ptr) };
26+
27+
assert!(time_values[0] != 123);
28+
assert_eq!(time_values[1], 456);
29+
}

src/windows/mod.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,6 @@ extern "C" {
390390
pub fn raise(signum: c_int) -> c_int;
391391

392392
pub fn clock() -> clock_t;
393-
pub fn ctime(sourceTime: *const time_t) -> *mut c_char;
394-
pub fn difftime(timeEnd: time_t, timeStart: time_t) -> c_double;
395-
#[link_name = "_gmtime64_s"]
396-
pub fn gmtime_s(destTime: *mut tm, srcTime: *const time_t) -> c_int;
397393
#[link_name = "_get_daylight"]
398394
pub fn get_daylight(hours: *mut c_int) -> errno_t;
399395
#[link_name = "_get_dstbias"]
@@ -407,10 +403,6 @@ extern "C" {
407403
size_in_bytes: size_t,
408404
index: c_int,
409405
) -> errno_t;
410-
#[link_name = "_localtime64_s"]
411-
pub fn localtime_s(tmDest: *mut tm, sourceTime: *const time_t) -> crate::errno_t;
412-
#[link_name = "_time64"]
413-
pub fn time(destTime: *mut time_t) -> time_t;
414406
#[link_name = "_tzset"]
415407
pub fn tzset();
416408
#[link_name = "_chmod"]
@@ -549,6 +541,33 @@ extern "C" {
549541
) -> crate::errno_t;
550542
}
551543

544+
// By default, the following link to 64-bit variants where the expected `time_t`
545+
// is also 64-bits.
546+
#[cfg(not(all(target_arch = "x86", target_env = "gnu")))]
547+
extern "C" {
548+
pub fn ctime(sourceTime: *const time_t) -> *mut c_char;
549+
pub fn difftime(timeEnd: time_t, timeStart: time_t) -> c_double;
550+
pub fn gmtime_s(destTime: *mut tm, srcTime: *const time_t) -> c_int;
551+
pub fn localtime_s(tmDest: *mut tm, sourceTime: *const time_t) -> crate::errno_t;
552+
pub fn time(destTime: *mut time_t) -> time_t;
553+
}
554+
555+
// Under Windows x86 with GNU, `time_t` is still 32-bit wide on stable, so the
556+
// above routines have to link with their 32-bit variants.
557+
#[cfg(all(target_arch = "x86", target_env = "gnu"))]
558+
extern "C" {
559+
#[link_name = "_ctime32"]
560+
pub fn ctime(sourceTime: *const time_t) -> *mut c_char;
561+
#[link_name = "_difftime32"]
562+
pub fn difftime(timeEnd: time_t, timeStart: time_t) -> c_double;
563+
#[link_name = "_gmtime32_s"]
564+
pub fn gmtime_s(destTime: *mut tm, srcTime: *const time_t) -> c_int;
565+
#[link_name = "_localtime32_s"]
566+
pub fn localtime_s(tmDest: *mut tm, sourceTime: *const time_t) -> crate::errno_t;
567+
#[link_name = "_time32"]
568+
pub fn time(destTime: *mut time_t) -> time_t;
569+
}
570+
552571
extern "system" {
553572
pub fn listen(s: SOCKET, backlog: c_int) -> c_int;
554573
pub fn accept(s: SOCKET, addr: *mut crate::sockaddr, addrlen: *mut c_int) -> SOCKET;

0 commit comments

Comments
 (0)