Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ fn main() {
}
}

let uclibc_use_time64 = env_flag("CARGO_CFG_LIBC_UNSTABLE_UCLIBC_TIME64");
if target_env == "uclibc" && uclibc_use_time64 {
set_cfg("linux_time_bits64");
}

let linux_time_bits64 = env::var("RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64").is_ok();
println!("cargo:rerun-if-env-changed=RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64");
if linux_time_bits64 {
Expand Down
8 changes: 3 additions & 5 deletions ci/docker/armv7-unknown-linux-uclibceabihf/Dockerfile
Comment thread
skrap marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ RUN sed -i -E 's/(archive|security)\.ubuntu\.com/old-releases.ubuntu.com/g' \
qemu-user \
xz-utils

RUN mkdir /toolchain

RUN curl --retry 5 -L https://toolchains.bootlin.com/downloads/releases/toolchains/armv7-eabihf/tarballs/armv7-eabihf--uclibc--bleeding-edge-2021.11-1.tar.bz2 | \
tar xjf - -C /toolchain --strip-components=1
RUN /toolchain/relocate-sdk.sh
ARG TEST_UCLIBC_TIIME64
COPY install-uclibc.sh /
RUN /install-uclibc.sh "$TEST_UCLIBC_TIIME64"

ENV PATH=$PATH:/rust/bin:/toolchain/bin \
STAGING_DIR=/toolchain/armv7-buildroot-linux-uclibceabihf/sysroot \
Expand Down
20 changes: 20 additions & 0 deletions ci/install-uclibc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/bash
#
# Installs the appropriate uclibc toolchain into /toolchain

set -eux

time64="$1"

if [ "${time64:-0}" != "0" ]; then
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this script to use a toolchain with or without 64-bit time_t depending on $1.

version='bleeding-edge-2025.08-1'
else
version='bleeding-edge-2024.05-1' # last version with 32-bit time_t
fi
Comment on lines +11 to +13
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this more or less mean that you're stuck on an old uclibc version if you want to use time APIs? There aren't the duplicate symbols and header config like glibc has?

If so, I think we could flip the default at some point in the near future. Technically breaking but we can get away with it on T3 targets, and musl and glibc aren't quite as stuck.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, no - let me explain. uClibc is intended for use on embedded devices where space (and thus code size) is at a premium. A developer would typically build their own uclibc with only the features required for their use case. uclibc facilitates this customization by having a configuration step (via a menuconfig, like the kernel menuconfig system, if you've ever used that) where the developer chooses the features they like, including whether to use a 32-bit or 64-bit time_t, and then builds their own uclibc for their project.

So we cannot say for sure within the libc crate which size of time_t will be present in the underlying uclibc library.

The bootlin folks have chosen to enable the 64-bit time_t feature on their build of uclibc as of the toolchain noted above. Having a reference toolchain is really useful for ease of CI and developer bootstrapping purposes, but the uclibc configuration choices that the bootlin toolchain makes aren't necessarily the same (nor binary compatible) with any other developer's use of uclibc.

This is why I expect everyone using uclibc with rust ends up either using -Zbuild-std or building their own cross toolchain, in either case targeting their project-specific uclibc headers, and (as of this change) making use of the --cfg=libc_unstable_uclibc_time64 rustflag if needed.


mkdir /toolchain

curl --retry 5 -L "https://toolchains.bootlin.com/downloads/releases/toolchains/armv7-eabihf/tarballs/armv7-eabihf--uclibc--${version}.tar.xz" | \
tar xjf - -C /toolchain --strip-components=1
Comment on lines +17 to +18
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change this of course but fyi, | at the end of the line allows wrapping. So the \ isn't needed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, TIL!


/toolchain/relocate-sdk.sh
5 changes: 5 additions & 0 deletions ci/run-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ run() {
fi
fi

if [ -n "${TEST_UCLIBC_TIIME64:-}" ]; then
build_args+=("--build-arg=TEST_UCLIBC_TIIME64=1")
export RUSTFLAGS="$RUSTFLAGS --cfg=libc_unstable_uclibc_time64"
fi

# use -f so we can use ci/ as build context
docker build "${build_args[@]}"

Expand Down
221 changes: 197 additions & 24 deletions libc-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3736,6 +3736,12 @@ fn test_linux(target: &str) {
cfg.cfg("musl_redir_time64", None);
}
}
let uclibc_use_time64 = env::var("CARGO_CFG_LIBC_UNSTABLE_UCLIBC_TIME64")
.map(|val| val != "0")
.unwrap_or(false);
if uclibc && uclibc_use_time64 {
cfg.cfg("linux_time_bits64", None);
}
cfg.define("_GNU_SOURCE", None)
// This macro re-defines fscanf,scanf,sscanf to link to the symbols that are
// deprecated since glibc >= 2.29. This allows Rust binaries to link against
Expand Down Expand Up @@ -3939,9 +3945,14 @@ fn test_linux(target: &str) {
"linux/wait.h",
"linux/wireless.h",
"sys/fanotify.h",
// <sys/auxv.h> is not present on uclibc
(!uclibc, "sys/auxv.h"),
"sys/auxv.h",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uclibc has this header, at least in the toolchain versions in the CI.

(gnu || musl, "linux/close_range.h"),
(uclibc, "linux/fanotify.h"),
(uclibc, "linux/auxvec.h"),
(uclibc, "linux/close_range.h"),
(uclibc, "linux/if_packet.h"),
(uclibc, "linux/elf-em.h"),
(uclibc, "sys/resource.h"),
);
}

Expand Down Expand Up @@ -4177,6 +4188,18 @@ fn test_linux(target: &str) {
// On 64 bits the size did not change, skip only for 32 bits.
"ptrace_syscall_info" if pointer_width == 32 => true,

"canxl_frame"
| "fanotify_event_info_header" // not in sys/fanotify.h in uclibc
| "fanotify_event_info_fid" // not in sys/fanotify.h in uclibc
| "tls12_crypto_info_sm4_gcm"
| "tls12_crypto_info_sm4_ccm"
| "tls12_crypto_info_aria_gcm_128"
| "tls12_crypto_info_aria_gcm_256"
if uclibc =>
{
true
}

_ => false,
}
});
Expand Down Expand Up @@ -4302,6 +4325,170 @@ fn test_linux(target: &str) {
}
}

if uclibc {
match name {
// The canonical uClibc toolchain, bootlin bleeding-edge-2024.02-1,
// uses linux 5.15, so several constants are not available.

// requires linux 5.16
"PR_SCHED_CORE_SCOPE_PROCESS_GROUP"
| "PR_SCHED_CORE_SCOPE_THREAD_GROUP"
| "PR_SCHED_CORE_SCOPE_THREAD"
| "NF_NETDEV_EGRESS"
| "SO_RESERVE_MEM" => return true,

// TLS_CIPHER_SM4_[GC]CM requires linux 5.16
"TLS_CIPHER_SM4_CCM_IV_SIZE"
| "TLS_CIPHER_SM4_CCM_KEY_SIZE"
| "TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE"
| "TLS_CIPHER_SM4_CCM_SALT_SIZE"
| "TLS_CIPHER_SM4_CCM_TAG_SIZE"
| "TLS_CIPHER_SM4_CCM"
| "TLS_CIPHER_SM4_GCM_IV_SIZE"
| "TLS_CIPHER_SM4_GCM_KEY_SIZE"
| "TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE"
| "TLS_CIPHER_SM4_GCM_SALT_SIZE"
| "TLS_CIPHER_SM4_GCM_TAG_SIZE"
| "TLS_CIPHER_SM4_GCM" => return true,

// requires linux 5.17
"PR_SET_VMA_ANON_NAME"
| "PR_SET_VMA"
| "RTNLGRP_MCTP_IFADDR" => return true,

// requires linux 5.18
"RTNLGRP_STATS"
| "RTNLGRP_TUNNEL"
| "TLS_TX_ZEROCOPY_RO"
| "MADV_DONTNEED_LOCKED"
| "NFQA_PRIORITY"
| "SO_TXREHASH" => return true,

// requires linux 5.19
"SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV"
| "NLM_F_BULK"
| "SO_RCVMARK"
| "TLS_INFO_ZC_RO_TX" => return true,

// CAN_* consts requiring linux 6.0
"CAN_BUS_OFF_THRESHOLD"
| "CAN_CTRLMODE_TDC_AUTO"
| "CAN_CTRLMODE_TDC_MANUAL"
| "CAN_ERR_CNT"
| "CAN_ERROR_PASSIVE_THRESHOLD"
| "CAN_ERROR_WARNING_THRESHOLD" => return true,

// requires linux 6.0
"IFF_NO_CARRIER"
| "TLS_INFO_RX_NO_PAD"
| "TLS_RX_EXPECT_NO_PAD" => return true,

// CAN_* consts requiring linux 6.1
"CAN_RAW_XL_FRAMES"
| "CANXL_HDR_SIZE"
| "CANXL_MAX_DLC_MASK"
| "CANXL_MAX_DLC"
| "CANXL_MAX_DLEN"
| "CANXL_MAX_MTU"
| "CANXL_MIN_DLC"
| "CANXL_MIN_DLEN"
| "CANXL_MIN_MTU"
| "CANXL_MTU"
| "CANXL_PRIO_BITS"
| "CANXL_PRIO_MASK"
| "CANXL_SEC"
| "CANXL_XLF" => return true,

// TLS_CIPHER_ARIA_GCM_* requires linux 6.1
"TLS_CIPHER_ARIA_GCM_128_IV_SIZE"
| "TLS_CIPHER_ARIA_GCM_128_KEY_SIZE"
| "TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE"
| "TLS_CIPHER_ARIA_GCM_128_SALT_SIZE"
| "TLS_CIPHER_ARIA_GCM_128_TAG_SIZE"
| "TLS_CIPHER_ARIA_GCM_128"
| "TLS_CIPHER_ARIA_GCM_256_IV_SIZE"
| "TLS_CIPHER_ARIA_GCM_256_KEY_SIZE"
| "TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE"
| "TLS_CIPHER_ARIA_GCM_256_SALT_SIZE"
| "TLS_CIPHER_ARIA_GCM_256_TAG_SIZE"
| "TLS_CIPHER_ARIA_GCM_256" => return true,

// requires linux 6.2
"ALG_SET_KEY_BY_KEY_SERIAL"
| "PACKET_FANOUT_FLAG_IGNORE_OUTGOING"
| "SOF_TIMESTAMPING_OPT_ID_TCP"
| "TUN_F_USO4"
| "TUN_F_USO6" => return true,

// FAN_* consts require kernel 6.3
"FAN_INFO"
| "FAN_RESPONSE_INFO_AUDIT_RULE"
| "FAN_RESPONSE_INFO_NONE" => return true,

// requires linux 6.3
"MFD_EXEC"
| "MFD_NOEXEC_SEAL"
| "PR_GET_MDWE"
| "PR_SET_MDWE" => return true,

// requires linux 6.4
"PACKET_VNET_HDR_SZ" => return true,

// requires linux 6.5
"SO_PASSPIDFD"
| "SO_PEERPIDFD" => return true,

// requires linux 6.6
"PR_MDWE_NO_INHERIT"
| "PR_MDWE_REFUSE_EXEC_GAIN" => return true,

// defined as a synonym for EM_ARC_COMPACT in gnu but not uclibc
"EM_ARC_A5" => return true,

/*
Here are a list of kernel UAPI constants which appear in linux/ headers,
but cannot be imported due to conflicts with the uclibc headers.
The conflicting linux/ header is noted.
*/
Comment on lines +4448 to +4452
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected flow for these when you're writing C if there are conflicts?

Copy link
Copy Markdown
Contributor Author

@skrap skrap Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My view on it is that these constants are passed through the underlying target_env and to the kernel, rather than invoking specific behavior within the target_env c std lib. For constants where this is true, it means that they're safe and appropriate to use regardless of whether the c std lib re-exports them or not. The case where they could lead to unintentional bad behavior is when the c std lib needed to change behavior based on the constant. I am not individually researching and testing each of these, so it's possible that there's a bug here, but I would like to consider those issues as outside the scope of this PR. (This is just moving around these constants and fixing the build.)

To answer your question, if I were writing a piece of C code and needed one of these constants, I would probably find a way to untangle the header mess or just copy the #define into my implementation. Ugly, yes, but this is the state of system headers in C in my experience.

There are also of other examples in libc-test/build.rs of this type of issue - e.g. see test_linux_like_apis() which appears to try to work around this issue for combinations of specific constants and headers which don't work.

// linux/signal.h
"BUS_MCEERR_AO"
| "BUS_MCEERR_AR"
// linux/termios.h
| "EXTPROC"
// linux/inotify.h
| "IN_MASK_CREATE"
// linux/in.h
| "IPPROTO_BEETPH"
| "IPPROTO_ETHERNET"
| "IPPROTO_MPLS"
| "IPPROTO_MPTCP"
// linux/in6.h
| "IPV6_HDRINCL"
| "IPV6_MULTICAST_ALL"
| "IPV6_PMTUDISC_INTERFACE"
| "IPV6_PMTUDISC_OMIT"
| "IPV6_ROUTER_ALERT_ISOLATE"
// linux/elf.h
| "NT_PRFPREG"
// linux/sem.h
| "SEM_STAT_ANY"
// linux/shm.h
| "SHM_EXEC"
// linux/signal.h
| "SI_DETHREAD"
| "TRAP_BRANCH"
| "TRAP_HWBKPT"
| "TRAP_UNK"
// linux/timerfd.h
| "TFD_TIMER_CANCEL_ON_SET"
// linux/udp.h
| "UDP_GRO"
| "UDP_SEGMENT" => return true,

_ => (),
}
}

match name {
// These constants are not available if gnu headers have been included
// and can therefore not be tested here
Expand Down Expand Up @@ -4365,25 +4552,6 @@ fn test_linux(target: &str) {
// Skip as this signal codes and trap reasons need newer headers
"TRAP_PERF" => true,

// kernel constants not available in uclibc 1.0.34
"EXTPROC"
| "IPPROTO_BEETPH"
| "IPPROTO_MPLS"
| "IPV6_HDRINCL"
| "IPV6_MULTICAST_ALL"
| "IPV6_PMTUDISC_INTERFACE"
| "IPV6_PMTUDISC_OMIT"
| "IPV6_ROUTER_ALERT_ISOLATE"
| "PACKET_MR_UNICAST"
| "RUSAGE_THREAD"
| "SHM_EXEC"
| "UDP_GRO"
| "UDP_SEGMENT"
if uclibc =>
{
true
}

// headers conflicts with linux/pidfd.h
"PIDFD_NONBLOCK" => true,
// Linux >= 6.9
Expand Down Expand Up @@ -4424,6 +4592,10 @@ fn test_linux(target: &str) {
// value changed
"NF_NETDEV_NUMHOOKS" if sparc64 => true,

// Canonical uclibc latest from toolchains.bootlin.com is based on kernel 5.15,
// so opt out of tests for constants which are different in later kernels.
"NF_NETDEV_NUMHOOKS" | "RLIM_NLIMITS" | "NFT_MSG_MAX" if uclibc => true,

// kernel 6.9 minimum
"RWF_NOAPPEND" => true,

Expand Down Expand Up @@ -4633,8 +4805,9 @@ fn test_linux(target: &str) {
// Needs glibc 2.33 or later.
"mallinfo2" => true,

// Not defined in uclibc as of 1.0.34
// Not defined in uclibc as of 1.0.45
"gettid" if uclibc => true,
"getauxval" if uclibc => true,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc doesn't define getauxval.


// There are two versions of basename(3) on Linux with glibc, see
//
Expand Down Expand Up @@ -4766,7 +4939,7 @@ fn test_linux(target: &str) {
// FIXME(linux): `max_phase_adj` requires >= 5.19 kernel headers
// the rsv field shrunk when those fields got added, so is omitted too
("ptp_clock_caps", "adjust_phase" | "max_phase_adj" | "rsv")
if loongarch64 || sparc64 =>
if loongarch64 || sparc64 || uclibc =>
{
true
}
Expand All @@ -4783,7 +4956,7 @@ fn test_linux(target: &str) {
("bcm_msg_head", "frames") => true,
// FAM
("af_alg_iv", "iv") => true,
("file_handle", "f_handle") if musl => true,
("file_handle", "f_handle") if musl || uclibc => true,
// FIXME(ctest): ctest does not translate the rust code which computes the padding size
("pthread_cond_t", "__padding") if l4re => true,
_ => false,
Expand Down
1 change: 1 addition & 0 deletions src/unix/linux_like/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,7 @@ s_no_extra_traits! {
}

pub const POSIX_SPAWN_USEVFORK: c_short = 64;
#[cfg(not(target_env = "uclibc"))]
pub const POSIX_SPAWN_SETSID: c_short = 128;

pub const F_SEAL_FUTURE_WRITE: c_int = 0x0010;
Expand Down
13 changes: 11 additions & 2 deletions src/unix/linux_like/linux/uclibc/arm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@ use crate::off64_t;
use crate::prelude::*;

pub type wchar_t = c_uint;
pub type time_t = c_long;

cfg_if! {
// Set cfg(libc_unstable_uclibc_time64) in rustflags if your uclibc has 64-bit time
if #[cfg(linux_time_bits64)] {
pub type time_t = c_longlong;
pub type suseconds_t = c_longlong;
} else {
pub type time_t = c_long;
pub type suseconds_t = c_long;
}
}
Comment on lines +6 to +15
Copy link
Copy Markdown
Contributor

@tgross35 tgross35 Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View changes since the review

Do any structs need to be updated here? I know on other targets the padding in the timespec struct sometimes needs to be adjusted. I assume there are no function link names to update if uclibc had a pretty clean cut 32->64 transition.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am relying on the libc-test infrastructure a bit here to catch errors, but I think everything should be OK. I am happy to be the point person for any issues raised for 64-bit time_t in uclibc, if bugs are reported.


pub type clock_t = c_long;
pub type fsblkcnt_t = c_ulong;
pub type fsfilcnt_t = c_ulong;
pub type ino_t = c_ulong;
pub type off_t = c_long;
pub type pthread_t = c_ulong;
pub type suseconds_t = c_long;

pub type nlink_t = c_uint;
pub type blksize_t = c_long;
Expand Down
Loading