Skip to content

Conversation

@de-vri-es
Copy link
Contributor

@de-vri-es de-vri-es commented Sep 16, 2025

In glibc 2.42, baud rate constants changed. Their value is now simply the baud rate as integer. Additionally, the tcset{i,o,}speed function accepts any arbitrary baud rate, and the tcget{i,o,}speed can return any arbitrary baud rate.

On MIPS and SPARC, the termios struct from glibc now also has the c_ispeed and c_ospeed fields. This is not a backwards compatible change, and I'm not sure how this should be addressed. Adding public or private fields will break code that create these structs manually. But we need to make sure the size is correct for glibc 2.42.

Making the existence of these fields depend on the glibc version is a nightmare for docs.rs and portable code. But adding these fields on mips and sparc without glibc 2.42 means the fields will not be initialized by libc calls, so anyone using MaybeUninit::assume_init() could be triggering undefined behaviour.

Also note: there is no explicit linking against glibc 2.42 symbols. I was thinking we could force having GLIBC_2.42 as undefined symbol, but if you call any of the libc functions that interpret or return the baud rates, you will already be linking against versioned symbols implicitly and the binary will fail to load at runtime on systems with an older glibc. I think this is probably good enough.

Should fix #4692.

Sources

Baud rate constants: https://github.com/bminor/glibc/blob/13d67746cbe1273afaf6b9de9d6065ab76ee7697/bits/termios-baud.h#L24-L70
termios struct: https://github.com/bminor/glibc/blob/13d67746cbe1273afaf6b9de9d6065ab76ee7697/sysdeps/unix/sysv/linux/bits/termios-struct.h#L32-L42

In glibc 2.42, baud rate constants changed. Their value is now simply
the baud rate as integer. Additionally, the `tcset{i,o,}speed` function
accepts any arbitrary baud rate, and the `tcget{i,o,}speed` can return
any arbitrary baud rate.

On MIPS and SPARC, the termios struct from glibc now also has the
`c_ispeed` and `c_ospeed` fields.
@de-vri-es de-vri-es force-pushed the fix-glibc-2.4-baud-rates branch from 4c92494 to 2b49289 Compare September 16, 2025 12:40
@hpax
Copy link

hpax commented Sep 16, 2025

By the way, 2.42 isn't 2.4.2 :)
Someone else said 2.42 was "a patch release"; no, it is the 43rd major release of glibc 2.

@hpax
Copy link

hpax commented Sep 16, 2025

How come this is present in the "new" definition?

    cfg_if! {
        if #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))] {

Comment on lines +1087 to +1094
cfg_if! {
if #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))] {
pub const B2500000: crate::speed_t = 2500000;
pub const B3000000: crate::speed_t = 3000000;
pub const B3500000: crate::speed_t = 3500000;
pub const B4000000: crate::speed_t = 4000000;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hpax: You mean this? This is just a pragmatic choice to avoid having constants in the libc crate whose presence depends on the glibc version at build time. They're not available pre-2.42. Starting from 2.42, the baud rate constants are basically pointless (which is nice), so I don't think anyone is hurt by not having them.

Copy link
Contributor

@asomers asomers left a comment

Choose a reason for hiding this comment

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

This PR runs contrary to existing libc practice. The existing practice is to provide bindings that work with the oldest supported version of each OS. That's why libc doesn't run bindgen at build time. Instead, it relies on pregenerated bindings. For glibc, the bindings must be compatible with glibc 2.17, because that's Rust's minimum requirement, and Rust depends on libc.
Furthermore, attempting to detect the installed glibc version at build time makes cross-compiling much harder.
So I think that instead of attempting to detect the glibc version at runtime, we should only use the old baud rate symbols. And we should set the ELF symbol version (i.e. Rust's #[link_name] attribute) for any glibc function that takes those symbols. That way Rust programs using libc will continue to work with any glibc version.

@de-vri-es
Copy link
Contributor Author

That way Rust programs using libc will continue to work with any glibc version.

This isn't true now though. If you compile a program with new glibc it will fail to link at runtime with an older glibc. That's exactly what will happen with this PR too.

That said, I know this isn't ideal. But using the old link names is extremely annoying, as they are architecture dependent. Using the old link names was also my first idea.

@asomers
Copy link
Contributor

asomers commented Sep 16, 2025

This isn't true now though. If you compile a program with new glibc it will fail to link at runtime with an older glibc. That's exactly what will happen with this PR too.

Which are the offending symbols? They probably need to be fixed, too.

@de-vri-es
Copy link
Contributor Author

de-vri-es commented Sep 16, 2025

Any symbol really. The linker will automatically use the latest version of any symbol you use. And if that one isn't present in the old glibc being used at runtime, the dynamic linker will fail to load the binary.

For example: when I compile a program on Arch Linux, I can not run it on Ubuntu, because the glibc of ubuntu is too old. The other way around works fine.

@de-vri-es
Copy link
Contributor Author

And we should set the ELF symbol version (i.e. Rust's #[link_name] attribute) for any glibc function that takes those symbols. That way Rust programs using libc will continue to work with any glibc version.

Note that I did try this, and the tests will fail because the symbol used by the generated C code is not affected.

@asomers
Copy link
Contributor

asomers commented Sep 16, 2025

Note that I did try this, and the tests will fail because the symbol used by the generated C code is not affected.

Are you talking about libc's CI? That can be made to work. FreeBSD's C library has many versioned symbols, and libc still used the FreeBSD 11-compat versions by default until very recently (PR #2406) . Look for example at the fstatat function. libc's CI tested that one both ways, both the 11-compat version and the 12+ version, until PR #2406 landed.

Copy link
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.

As @asomers mentioned, there is no guarantee that the host glibc matches the version that is getting linked, so this isn't going to be robust. I don't think we have a good way to detect that version, so our best bet (for now) is going to have to be based on link_name.

@de-vri-es
Copy link
Contributor Author

de-vri-es commented Sep 18, 2025

As @asomers mentioned, there is no guarantee that the host glibc matches the version that is getting linked, so this isn't going to be robust. I don't think we have a good way to detect that version, so our best bet (for now) is going to have to be based on link_name.

This isn't totally true. If your program calls cfgetispeed (for example), it will end up linking cfgetispeed@GLIBC_2.42. This is just what the linker does already.

If you try to run the program with an older glibc it will fail to resolve the symbol at runtime.

This already happens now with all symbols from glibc.

So I think technically the solution in the PR is fine, and mimics how glibc is supposed to be used.

/edit: Ah, fair, the host glibc used to compile build.rs doesn't have to match the target glibc. Hmmmm... okay, that's a very important problem with this approach.

@tgross35
Copy link
Contributor

I see you got it with your edit - but yeah, doing something like e.g. building on Arch and deploying on Debian means you'll hit the same problems, just a bit more sneaky. I'm going to ask around and see if anyone can think of a good way for us to do more proper symbol versioning (more to talk about at #4700).

In the meantime, is there any downside of specifying a specific older symbol version? Aside from, of course, not having the latest API.

@tgross35
Copy link
Contributor

On MIPS and SPARC, the termios struct from glibc now also has the c_ispeed and c_ospeed fields. This is not a backwards compatible change, and I'm not sure how this should be addressed. Adding public or private fields will break code that create these structs manually. But we need to make sure the size is correct for glibc 2.42.

This is indeed a perpetual problem. For 1.0 we'll be making everything non_exhaustive which will make this smoother #4080. Unfortunately there isn't really any good solution until then.

@de-vri-es
Copy link
Contributor Author

de-vri-es commented Sep 18, 2025

I see you got it with your edit - but yeah, doing something like e.g. building on Arch and deploying on Debian means you'll hit the same problems, just a bit more sneaky. I'm going to ask around and see if anyone can think of a good way for us to do more proper symbol versioning (more to talk about at #4700).

Yeah, my point was that this already happens now, so I didn't think it is a new problem. But when cross compiling for a totally different architecture it is certainly a new problem. (Or if otherwise using a different glibc for build.rs and the output binaries. Note sure if this is possible when host == target, but I suppose it might be.)

In the meantime, is there any downside of specifying a specific older symbol version? Aside from, of course, not having the latest API.

It should be fine to link the older versions. That's what binaries linked against older glibc versions do anyway. As long as we get all the right ones :]

@de-vri-es
Copy link
Contributor Author

Closing this as not the desired solution.

@oech3
Copy link

oech3 commented Nov 28, 2025

Still no acceptible fix?

@tgross35
Copy link
Contributor

tgross35 commented Dec 2, 2025

Setting #[link_name = "..."] attributes on relevant functions to pin them to a specific version is the thing we can do here (help welcome if you have some time)

@oech3
Copy link

oech3 commented Dec 2, 2025 via email

@oech3
Copy link

oech3 commented Dec 2, 2025

Why do you want to avoid gnu_get_libc_version to get version at runtime? I want to use new API.

@tgross35
Copy link
Contributor

tgross35 commented Dec 2, 2025

The C function? Adding a build dependency on cc would not be nice. The correct solution here would probably be a rustc flag to specify a minimum supported version, which has had a few RFCs floating around.

(I'm happy to discuss better version support further but please open a new issue, discussion in closed PRs gets lost)

@oech3
Copy link

oech3 commented Dec 4, 2025

I'm not understanding why is this PR probrematic honesty. uutils's reository is distributing stty binary built with old glibc which works on Arch (linked with old cfgetospeed). stty linked with newer glibc does not need to support old glibc (by glibc's policy).

Doesn't cross or container links binary with old glibc if container have old glibc?

@tgross35
Copy link
Contributor

tgross35 commented Dec 4, 2025

The problem is what is mentioned at #4697 (review). The build script is checking the host glibc version: if the program will run on the same machine then that's fine, but if you're distributing a binary or cross compiling then this becomes meaningless.

@oech3
Copy link

oech3 commented Dec 4, 2025

The build script is checking the host glibc

So we only need solution for this with this PR. Right? (We can already distribute binary linked with old glibc to Arch).

@tgross35
Copy link
Contributor

tgross35 commented Dec 4, 2025

To be unambiguous: anything that attempts to retrieve this information from a libc.so or otherwise inspecting the environment is a nack. There just isn't any way to do this in a way that isn't fragile in some way, and we already have the link_name solution that just needs to be added.

It is unfortunate that this prevents us from upgrading to newer API, but this is just one of the limitations we hit by needing to support a range of versions without knowing what is being targeted. I am hopeful that we eventually get a --min-version or similar flag to give the users the option to raise the floor, but spare effort is better spent working on such a feature than approximating it in libc.

(see rust-lang/rfcs#3857, rust-lang/rfcs#3750, #t-lang > cfg(version(..)) as a version comparison predicate if you're interested in more on that)

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

glibc 2.42 changed the definitions of baud rate constants (B50, B75, ..., B9600, B57600, ...)

6 participants