Skip to content

uclibc: fix build + CI, add unstable time64 support for newer uclibc toolchains #5046

Open
skrap wants to merge 2 commits intorust-lang:mainfrom
skrap:main
Open

uclibc: fix build + CI, add unstable time64 support for newer uclibc toolchains #5046
skrap wants to merge 2 commits intorust-lang:mainfrom
skrap:main

Conversation

@skrap
Copy link
Copy Markdown
Contributor

@skrap skrap commented Apr 7, 2026

Description

Goals of this change:

  • Fix uclibc build
  • Fix uclibc CI
  • support uclibc configurations which use a 64-bit time_t configuration, which is enabled on the latest bootlin toolchains.

Testing instructions

For testing, I have done the following:

  • build a custom rust toolchain against these changes (cherry-picked onto the libc-0.2 branch)
  • verified libc-test passes against the last bootlin toolchain to use 32-bit time_t (bleeding-edge-2024.02-1)
  • verified libc-test passes against the most recent bootlin toolchain (bleeding-edge-2025.08-1)
    • This required setting RUSTFLAGS="--cfg=libc_unstable_uclibc_time64" as expected

Sources

I am not changing APIs so I don't think this applies.

Checklist

  • Relevant tests in libc-test/semver have been updated
  • No placeholder or unstable values like *LAST or *MAX are
    included (see #3131)
    • NOTE: I only included such values where semver required them.
  • Tested locally (cd libc-test && cargo test --target mytarget);
    especially relevant for platforms that may not be checked in CI

@rustbot label +stable-nominated

@rustbot rustbot added A-CI Area: CI-related items stable-nominated This PR should be considered for cherry-pick to libc's stable release branch labels Apr 7, 2026
Comment thread ci/install-uclibc.sh

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.

Comment thread libc-test/build.rs
"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.

Comment thread libc-test/build.rs
// 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.

pub nr: crate::__s32,
}

#[cfg_attr(
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.

Several definitions here were moved around in the linux/l4re shared restructuring. These are now defined elsewhere; remove them here.

pub const EM_MN10300: u16 = 89;
pub const EM_MN10200: u16 = 90;
pub const EM_PJ: u16 = 91;
#[cfg(not(target_env = "uclibc"))]
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.

Many of these guarded constants are actually defined in uclibc. I removed guards where appropriate.

pub const IP_ORIGDSTADDR: c_int = 20;
pub const IP_RECVORIGDSTADDR: c_int = IP_ORIGDSTADDR;
pub const IP_MINTTL: c_int = 21;
#[cfg(not(target_env = "uclibc"))]
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.

Similar removal of unnecessary guards.

@skrap
Copy link
Copy Markdown
Contributor Author

skrap commented Apr 7, 2026

CI seems to error on a rustc issue when building; I think this is unrelated. Awaiting a fix there, but feel free to review in the meantime!

Comment thread .github/workflows/ci.yaml Outdated
Comment thread ci/docker/armv7-unknown-linux-uclibceabihf/Dockerfile
Comment on lines -1199 to +1206
cfg_if! {
if #[cfg(not(target_env = "uclibc"))] {
pub const MFD_NOEXEC_SEAL: c_uint = 0x0008;
pub const MFD_EXEC: c_uint = 0x0010;
pub const MFD_HUGE_64KB: c_uint = 0x40000000;
pub const MFD_HUGE_512KB: c_uint = 0x4c000000;
pub const MFD_HUGE_1MB: c_uint = 0x50000000;
pub const MFD_HUGE_2MB: c_uint = 0x54000000;
pub const MFD_HUGE_8MB: c_uint = 0x5c000000;
pub const MFD_HUGE_16MB: c_uint = 0x60000000;
pub const MFD_HUGE_32MB: c_uint = 0x64000000;
pub const MFD_HUGE_256MB: c_uint = 0x70000000;
pub const MFD_HUGE_512MB: c_uint = 0x74000000;
pub const MFD_HUGE_1GB: c_uint = 0x78000000;
pub const MFD_HUGE_2GB: c_uint = 0x7c000000;
pub const MFD_HUGE_16GB: c_uint = 0x88000000;
pub const MFD_HUGE_MASK: c_uint = 63;
pub const MFD_HUGE_SHIFT: c_uint = 26;
}
}
pub const MFD_NOEXEC_SEAL: c_uint = 0x0008;
pub const MFD_EXEC: c_uint = 0x0010;
pub const MFD_HUGE_64KB: c_uint = 0x40000000;
pub const MFD_HUGE_512KB: c_uint = 0x4c000000;
pub const MFD_HUGE_1MB: c_uint = 0x50000000;
pub const MFD_HUGE_2MB: c_uint = 0x54000000;
pub const MFD_HUGE_8MB: c_uint = 0x5c000000;
pub const MFD_HUGE_16MB: c_uint = 0x60000000;
pub const MFD_HUGE_32MB: c_uint = 0x64000000;
pub const MFD_HUGE_256MB: c_uint = 0x70000000;
pub const MFD_HUGE_512MB: c_uint = 0x74000000;
pub const MFD_HUGE_1GB: c_uint = 0x78000000;
pub const MFD_HUGE_2GB: c_uint = 0x7c000000;
pub const MFD_HUGE_16GB: c_uint = 0x88000000;
pub const MFD_HUGE_MASK: c_uint = 63;
pub const MFD_HUGE_SHIFT: c_uint = 26;
Copy link
Copy Markdown
Contributor

@tgross35 tgross35 Apr 8, 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

@farao do the constants in this file that are changed here not exist on l4re? If so, they could just move to linux rather than linux_l4re_shared.

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.

yes that's true they do not exist on l4re so they should rather be moved to linux. I think I only put them in here to have all the MFD_* consts in one file.

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 about the constants like EM_OPENRISC or PTHREAD_MUTEX_STALLED above?

@skrap for the constants here, could you update them to not(target_os = "l4re") rather than removing the gate? I think that's easier than moving them all.

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.

Maybe also with a comment that this is done to keep similar constants in the same file

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.

Re EM_OPENRISC:

Some of these are a bit unexpected to have live in libc, to be honest. The EM_* constants are defined not by libc or by the kernel, but by the ELF format spec. They're fixed no matter what OS or std library we're running. So for this constant, I would prefer that libc present a unified interface to higher-level crates, rather than adopt the uclibc quirk of having EM_OR1K be a synonym for EM_OPENRISC.

Re PTHREAD_MUTEX_STALLED:
I'm not sure I understand the question here. This constant isn't conditional based on the target_env.

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.

could you update them to not(target_os = "l4re")

I think your request is reasonable, but I would like to ask that the requested l4re change go into a separate PR, as removing those constants for the l4re build could easily introduce breakage, and I don't know anything about that platform.

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.

To answer the question a few comments above: yes, all constants that were disabled for uclibc are not present on l4re. I would of course prefer if they stayed disabled for l4re in one way or the other

Comment thread ci/run-docker.sh Outdated
Comment thread libc-test/build.rs Outdated
Comment on lines +4379 to +4382
// constants not available in uclibc 1.0.45
// but defined outside the uclibc library,
// e.g. file format constants or kernel-defined
// constants.
Copy link
Copy Markdown
Contributor

@tgross35 tgross35 Apr 8, 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

Nit: can be rewrapped to 88 or 100 chars to match the rest (kind of wish rustfmt just did this)

Comment thread libc-test/build.rs Outdated
Comment on lines +4383 to +4390
"ALG_SET_KEY_BY_KEY_SERIAL"
| "AT_MINSIGSTKSZ"
| "BUS_MCEERR_AO"
| "BUS_MCEERR_AR"
| "CAN_BUS_OFF_THRESHOLD"
| "CAN_CTRLMODE_TDC_AUTO"
| "CAN_CTRLMODE_TDC_MANUAL"
| "CAN_ERR_CNT"
Copy link
Copy Markdown
Contributor

@tgross35 tgross35 Apr 8, 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

It seems like many of these should be fine if included via linux/... .h paths; is there a reason that isn't recommended or doesn't work on uclibc?

If so, it would be good to mark them deprecated on the target and remove in a release or two.

Copy link
Copy Markdown
Contributor Author

@skrap skrap Apr 8, 2026

Choose a reason for hiding this comment

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

I can do that.

I was unsure of the philosophy here with respect to kernel-defined constants. They would either be present in linux/*.h headers or not depending on the kernel version, regardless of whatever libc library/version was being used on the system. So what triggers a particular constant being promoted into the libc crate?

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 included the constants via linux UAPI headers as much as possible, but in some cases doing so conflicted with the definitions from uclibc. In this case, I left a comment.

In order to increase readability, I moved the uclibc-specific handling of exceptions to its own code branch and out of the catch-all branch, mirroring how musl does things.

@rustbot

This comment has been minimized.

@skrap
Copy link
Copy Markdown
Contributor Author

skrap commented Apr 8, 2026

I addressed the review feedback. Thanks for having a look!

I had to fence out POSIX_SPAWN_SETSID under target_env = uclibc. This is a GNU-only constant and is not implemented in uclibc. It was mistakenly included.

@skrap
Copy link
Copy Markdown
Contributor Author

skrap commented Apr 13, 2026

@tgross35 I am unsure of the desired workflow here - I see a "request re-review" button in the github UI. Is this on your plate already, or do I push that button now that I have addressed your feedback? (Not trying to rush, just trying to learn the intended workflow.)

Copy link
Copy Markdown
Contributor

@tgross35 tgross35 left a comment

Choose a reason for hiding this comment

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

Think the todo list here should be about the last one, also some questions.

@tgross35 I am unsure of the desired workflow here - I see a "request re-review" button in the github UI. Is this on your plate already, or do I push that button now that I have addressed your feedback? (Not trying to rush, just trying to learn the intended workflow.)

You can request a review. Usually I just go based on labels but requesting a review should also flip to S-waiting-on-review IIRC (as does @rustbot review).

The cycle is a bit slow for libc for nontrivial PRs, typically I go through everything every ~2 weeks.

View changes since this review

Comment thread ci/install-uclibc.sh
Comment on lines +17 to +18
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
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!

Comment on lines -1199 to +1206
cfg_if! {
if #[cfg(not(target_env = "uclibc"))] {
pub const MFD_NOEXEC_SEAL: c_uint = 0x0008;
pub const MFD_EXEC: c_uint = 0x0010;
pub const MFD_HUGE_64KB: c_uint = 0x40000000;
pub const MFD_HUGE_512KB: c_uint = 0x4c000000;
pub const MFD_HUGE_1MB: c_uint = 0x50000000;
pub const MFD_HUGE_2MB: c_uint = 0x54000000;
pub const MFD_HUGE_8MB: c_uint = 0x5c000000;
pub const MFD_HUGE_16MB: c_uint = 0x60000000;
pub const MFD_HUGE_32MB: c_uint = 0x64000000;
pub const MFD_HUGE_256MB: c_uint = 0x70000000;
pub const MFD_HUGE_512MB: c_uint = 0x74000000;
pub const MFD_HUGE_1GB: c_uint = 0x78000000;
pub const MFD_HUGE_2GB: c_uint = 0x7c000000;
pub const MFD_HUGE_16GB: c_uint = 0x88000000;
pub const MFD_HUGE_MASK: c_uint = 63;
pub const MFD_HUGE_SHIFT: c_uint = 26;
}
}
pub const MFD_NOEXEC_SEAL: c_uint = 0x0008;
pub const MFD_EXEC: c_uint = 0x0010;
pub const MFD_HUGE_64KB: c_uint = 0x40000000;
pub const MFD_HUGE_512KB: c_uint = 0x4c000000;
pub const MFD_HUGE_1MB: c_uint = 0x50000000;
pub const MFD_HUGE_2MB: c_uint = 0x54000000;
pub const MFD_HUGE_8MB: c_uint = 0x5c000000;
pub const MFD_HUGE_16MB: c_uint = 0x60000000;
pub const MFD_HUGE_32MB: c_uint = 0x64000000;
pub const MFD_HUGE_256MB: c_uint = 0x70000000;
pub const MFD_HUGE_512MB: c_uint = 0x74000000;
pub const MFD_HUGE_1GB: c_uint = 0x78000000;
pub const MFD_HUGE_2GB: c_uint = 0x7c000000;
pub const MFD_HUGE_16GB: c_uint = 0x88000000;
pub const MFD_HUGE_MASK: c_uint = 63;
pub const MFD_HUGE_SHIFT: c_uint = 26;
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 about the constants like EM_OPENRISC or PTHREAD_MUTEX_STALLED above?

@skrap for the constants here, could you update them to not(target_os = "l4re") rather than removing the gate? I think that's easier than moving them all.

Comment on lines -1199 to +1206
cfg_if! {
if #[cfg(not(target_env = "uclibc"))] {
pub const MFD_NOEXEC_SEAL: c_uint = 0x0008;
pub const MFD_EXEC: c_uint = 0x0010;
pub const MFD_HUGE_64KB: c_uint = 0x40000000;
pub const MFD_HUGE_512KB: c_uint = 0x4c000000;
pub const MFD_HUGE_1MB: c_uint = 0x50000000;
pub const MFD_HUGE_2MB: c_uint = 0x54000000;
pub const MFD_HUGE_8MB: c_uint = 0x5c000000;
pub const MFD_HUGE_16MB: c_uint = 0x60000000;
pub const MFD_HUGE_32MB: c_uint = 0x64000000;
pub const MFD_HUGE_256MB: c_uint = 0x70000000;
pub const MFD_HUGE_512MB: c_uint = 0x74000000;
pub const MFD_HUGE_1GB: c_uint = 0x78000000;
pub const MFD_HUGE_2GB: c_uint = 0x7c000000;
pub const MFD_HUGE_16GB: c_uint = 0x88000000;
pub const MFD_HUGE_MASK: c_uint = 63;
pub const MFD_HUGE_SHIFT: c_uint = 26;
}
}
pub const MFD_NOEXEC_SEAL: c_uint = 0x0008;
pub const MFD_EXEC: c_uint = 0x0010;
pub const MFD_HUGE_64KB: c_uint = 0x40000000;
pub const MFD_HUGE_512KB: c_uint = 0x4c000000;
pub const MFD_HUGE_1MB: c_uint = 0x50000000;
pub const MFD_HUGE_2MB: c_uint = 0x54000000;
pub const MFD_HUGE_8MB: c_uint = 0x5c000000;
pub const MFD_HUGE_16MB: c_uint = 0x60000000;
pub const MFD_HUGE_32MB: c_uint = 0x64000000;
pub const MFD_HUGE_256MB: c_uint = 0x70000000;
pub const MFD_HUGE_512MB: c_uint = 0x74000000;
pub const MFD_HUGE_1GB: c_uint = 0x78000000;
pub const MFD_HUGE_2GB: c_uint = 0x7c000000;
pub const MFD_HUGE_16GB: c_uint = 0x88000000;
pub const MFD_HUGE_MASK: c_uint = 63;
pub const MFD_HUGE_SHIFT: c_uint = 26;
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.

Maybe also with a comment that this is done to keep similar constants in the same file

Comment thread libc-test/build.rs
Comment on lines +4448 to +4452
/*
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.
*/
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.

Comment thread ci/install-uclibc.sh
Comment on lines +11 to +13
else
version='bleeding-edge-2024.05-1' # last version with 32-bit time_t
fi
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.

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 13, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

Comment on lines +6 to +15
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;
}
}
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.

Copy link
Copy Markdown
Contributor Author

@skrap skrap left a comment

Choose a reason for hiding this comment

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

@rustbot ready

Thanks for the review! I have replied inline to the issues raised.

View changes since this review

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 13, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CI Area: CI-related items S-waiting-on-review stable-nominated This PR should be considered for cherry-pick to libc's stable release branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants