You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
glibc 2.42 changed the definitions of baud rate constants (B50, B75, ..., B9600, B57600, ...) #4692
In glibc 2.41 and before, baud rate constants like B50, B75, ..., B9600, B57600, B115200, ... were enum-like values. B50 was 1, B75 was 2, etc.
In glibc 2.42 they have been redefined. B50 is now just 50, and B75 is now just 75. Additionally, the tcset{i,o,}speed and tcget{i,o,}speed functions are adjusted to deal with this internally (the kernel struct going to the ioctl still needs the legacy values, or a custom baud rate with BOTHER).
The old baud rate constants are still available as __B50, __B75, ...
On Linux, the <termios.h> interface now supports arbitrary baud rates; speed_t is redefined to simply be the baud rate specified as an unsigned int, which matches the kernel interface.
So.. is there a clean way to deal with this in the libc crate? The only portable thing for an application to do now is to manually do the TC{G,S}ETS2 ioctl and completely ignore the baud rate constants.
Pb with glibc version detection, it can be done at runtime only, ie gnu_get_libc_version() (or directly __libc_version symbol), not too great neither since it s a string.
Maybe explicitly linking to an old version of the cf{g,s}et{i,o,}speed functions that support the old baud rate constants will work for now? In the future, when glibc 2.42 is widespread enough, the constants can be updated and the functions can be switched to the new ones.
I guess the only question is: which version should we use now? :]
objdump -T libc.so.6 | grep cfsetispeed
000000000010eea0 g DF .text 000000000000002a GLIBC_2.42 cfsetispeed
000000000016d0f0 g DF .text 0000000000000054 (GLIBC_2.2.5) cfsetispeed
That would be a very poor solution, and will make users do what they have done in C code, poorly, for a long time now: invoke ioctl() directly, usually incorrectly.
See my notes in the other linked thread.
The legacy version number varies depending on architecture; also, on MIPS and SPARC the tcsetattr() and tcgetattr() symbols are also versioned, as the format of struct termios has changed.
There are also the new cfget/setbaud() interfaces. If I'm not entirely mistaken, you might be able to do a weak undefined symbol to pick them up if available.
Finally, here is a trick to probe at initialization time: create a dummy struct termios, and call something like cfsetospeed(&dummy_termios, 0x3fffffff) – if it succeeds, you are using the new glibc (in fact, any constant greater than 0x100f should fail on the old system.) This will not cause any system calls.
You do know that the legacy baud rates constants, as well as struct termios format, are architecture-dependent, right?!
That would be a very poor solution, and will make users do what they have done in C code, poorly, for a long time now: invoke ioctl() directly, usually incorrectly.
In Rust, people mostly don't use the libc crate directly. This is mostly a problem for library authors. Those already have to deal with the mess of serial ports, and they can not rely on glibc 2.42 being available. Not yet anyway.
The legacy version number varies depending on architecture; also, on MIPS and SPARC the tcsetattr() and tcgetattr() symbols are also versioned, as the format of struct termios has changed.
Thanks. And yeah. I did just figure out that the version symbols are architecture specific when trying to figure out the authoritative source for the right number (x86_64, aarch64).
Finally, here is a trick to probe at initialization time: create an empty struct termios, and call something like cfsetospeed(&dummy_termios, 0x40000000) – of of succeeds, you are using the new glibc.
Sadly, this will not be usable to change the actual value of the baud rate constants. This can only be done at build time because they are constants.
You do know that the legacy baud rates constants, as well as struct termios format, are architecture-dependent, right?!
I'm sure this is well intended, but I would appreciate it if you phrased a bit less aggressively. There's nothing new about that, it has already been the case before glibc 2.42.
Anyway, when trying to use the old symbols from pre-2.42, I already run into the issue that the auto generated tests will fail because they will still use the latest version. So now I think the best thing to do is just to do a build time check which glibc is being used, base the value of the baud rate constants on that, and fail to link at runtime if glibc 2.42 is not available.
That is consistent with glibc policy (you have to build and link against the headers and the .so file from the oldest version of glibc you want to run against.)
Apologies if I'm a bit snippy about this; I have just seen a lot of ABI abuse recently.
I'm trying to understand the exact situation here given it is evolving, but is there a specific symbol version that we would be able to link and continue using the old (octal) constants? If so, I think that would be the best solution here.
I also think it would be good if the libc crate uses versioned symbols correctly for glibc, but that will be a major overhaul.
I'm trying to understand the exact situation here given it is evolving, but is there a specific symbol version that we would be able to link and continue using the old (octal) constants? If so, I think that would be the best solution here.
There are specific versions, but it's architecture specific which version that is. Personally I'm in no mood to figure out the correct versions for each architecture and move the functions into the architecture specific submodules. And adjust the tests for each architecture.
If this is the desired solution, I understand, but I don't have the time to do it.
However, if we do, the behavior of the libc crate will not match the documentation of glibc. Since the libc crate does not come with it's own documentation, I also think this also somewhat confusing.
Lastly, if we stick with the pre 2.42 versions, then it is not possible to set custom baud rates on serial ports through libc. The only possibility is to bypass libc and do raw ioctls. People will likley mix raw ioctls with using libc APIS (I did), and that will lead to undefined behaviour.
I also think it would be good if the libc crate uses versioned symbols correctly for glibc, but that will be a major overhaul.
Doing this case-by-case can lead to undefined behaviour: libc is upgraded, a rust program compiles with a new glibc, it uses new versions of symbols that have not explicitly been pinned to a version. However, constants and struct layouts in the libc crate have not been updated to match the ABI change. (This is exactly what happened to serial2, fixed here.)
There are specific versions, but it's architecture specific which version that is. Personally I'm in no mood to figure out the correct versions for each architecture and move the functions into the architecture specific submodules. And adjust the tests for each architecture.
No need to move anything around, it would look something like adding this to each of the relevant functions, like we do for similar cases:
With output collected from rg 'cf(get|set)[io]speed' -g '**/*.abilist' in glibc sources. A bit tedious, but of course it's not your responsibility if you're not up for it.
However, if we do, the behavior of the libc crate will not match the documentation of glibc. Since the libc crate does not come with it's own documentation, I also think this also somewhat confusing.
Agreed that this isn't ideal, but I don't think we can do much better without proper symbol versioning. If we pin a version, adding a note in the docs to indicate that is reasonable.
Lastly, if we stick with the pre 2.42 versions, then it is not possible to set custom baud rates on serial ports through libc. The only possibility is to bypass libc and do raw ioctls. People will likley mix raw ioctls with using libc APIS (I did), and that will lead to undefined behaviour.
Doing this case-by-case can lead to undefined behaviour: libc is upgraded, a rust program compiles with a new glibc, it uses new versions of symbols that have not explicitly been pinned to a version. However, constants and struct layouts in the libc crate have not been updated to match the ABI change. (This is exactly what happened to serial2, fixed here.)
To be clear: the concern is that case-by-case isn't sufficient compared to versioning all symbols, correct? As opposed to case-by-case versioning introducing UB that isn't possible otherwise. I opened #4700 to continue this discussion separate from baud rates.
The problem comes when passing glibc structs to the kernel or passing kernel structs to glibc. And you're forced to do in glibc before 2.42 because it didn't support custom baud rates. (Okay, not forced, you should be translating between them properly yourself, but they used to be ABI compatible, I think.)
I mean, it has always been messy dealing with serial ports. glibc 2.42 improved it a lot, but now we end up with even more chaos in the libc crate :]
To be clear: the concern is that case-by-case isn't sufficient compared to versioning all symbols, correct? As opposed to case-by-case versioning introducing UB that isn't possible otherwise. I opened #4700 to continue this discussion separate from baud rates.
Yeah. And I would be worried that cherry-picking some symbols can easily lead to a set of linked functions in libc that don't agree on struct definitions or constants.
But, on the other hand, right now we also end up with functions that don't agree with the struct definitions and constants defined in libc.
On September 18, 2025 12:25:30 PM PDT, Trevor Gross ***@***.***> wrote:
> Lastly, if we stick with the pre 2.42 versions, then it is not possible to set custom baud rates on serial ports through libc. The only possibility is to bypass libc and do raw ioctls. People will likley mix raw ioctls with using libc APIS (I did), and that will lead to undefined behaviour.
Where is the impedance mismatch here? The kernel seems to use enum-style baud variables https://github.com/torvalds/linux/blob/cbf658dd09419f1ef9de11b9604e950bdd5c170b/include/uapi/asm-generic/termbits-common.h#L30-L45, so I would expect the 2.42+ definitions to be more problematic if mixing and matching there.
The impedance mismatch is that the kernel has an escape from the enumeration constant system (BOTHER), which glibc 2.42 uses.
As mentioned on the other issue: I'm not sure that pinning only tc[gs]et[io]speed is enough: for some platforms the layout of the termios struct changed, so all functions taking a termios should be pinned. Especially tc[gs]etattr.
Activity
devnexen commentedon Sep 15, 2025
Pb with glibc version detection, it can be done at runtime only, ie gnu_get_libc_version() (or directly __libc_version symbol), not too great neither since it s a string.
hpax commentedon Sep 16, 2025
See also discussion at: nix-rust/nix#2672
de-vri-es commentedon Sep 16, 2025
Maybe explicitly linking to an old version of the
cf{g,s}et{i,o,}speedfunctions that support the old baud rate constants will work for now? In the future, when glibc 2.42 is widespread enough, the constants can be updated and the functions can be switched to the new ones.I guess the only question is: which version should we use now? :]
I guess the GLIBC_2.2.25 version.
hpax commentedon Sep 16, 2025
That would be a very poor solution, and will make users do what they have done in C code, poorly, for a long time now: invoke ioctl() directly, usually incorrectly.
See my notes in the other linked thread.
The legacy version number varies depending on architecture; also, on MIPS and SPARC the tcsetattr() and tcgetattr() symbols are also versioned, as the format of struct termios has changed.
There are also the new cfget/setbaud() interfaces. If I'm not entirely mistaken, you might be able to do a weak undefined symbol to pick them up if available.
Finally, here is a trick to probe at initialization time: create a dummy struct termios, and call something like cfsetospeed(&dummy_termios, 0x3fffffff) – if it succeeds, you are using the new glibc (in fact, any constant greater than 0x100f should fail on the old system.) This will not cause any system calls.
You do know that the legacy baud rates constants, as well as struct termios format, are architecture-dependent, right?!
de-vri-es commentedon Sep 16, 2025
In Rust, people mostly don't use the
libccrate directly. This is mostly a problem for library authors. Those already have to deal with the mess of serial ports, and they can not rely on glibc 2.42 being available. Not yet anyway.Thanks. And yeah. I did just figure out that the version symbols are architecture specific when trying to figure out the authoritative source for the right number (x86_64, aarch64).
Sadly, this will not be usable to change the actual value of the baud rate constants. This can only be done at build time because they are constants.
I'm sure this is well intended, but I would appreciate it if you phrased a bit less aggressively. There's nothing new about that, it has already been the case before glibc 2.42.
Anyway, when trying to use the old symbols from pre-2.42, I already run into the issue that the auto generated tests will fail because they will still use the latest version. So now I think the best thing to do is just to do a build time check which glibc is being used, base the value of the baud rate constants on that, and fail to link at runtime if glibc 2.42 is not available.
de-vri-es commentedon Sep 16, 2025
I also think it would be good if the libc crate uses versioned symbols correctly for glibc, but that will be a major overhaul.
hpax commentedon Sep 16, 2025
That is consistent with glibc policy (you have to build and link against the headers and the .so file from the oldest version of glibc you want to run against.)
Apologies if I'm a bit snippy about this; I have just seen a lot of ABI abuse recently.
de-vri-es commentedon Sep 16, 2025
No problem, thanks for the useful insights and information :)
hpax commentedon Sep 16, 2025
As far as I can tell, it looks like the immediate consumer is the "unix" crate... which presumably should then get pulled by someone else...
tgross35 commentedon Sep 18, 2025
I'm trying to understand the exact situation here given it is evolving, but is there a specific symbol version that we would be able to link and continue using the old (octal) constants? If so, I think that would be the best solution here.
We don't have a general mechanism for link-time symbol versioning as we haven't really needed it, but we can specify a version on a case-by-case basis. Currently there isn't anywhere this is used for glibc, but it is for some other platforms https://github.com/search?q=repo%3Arust-lang%2Flibc%20%2Flink_name.*%40%2F&type=code.
de-vri-es commentedon Sep 18, 2025
There are specific versions, but it's architecture specific which version that is. Personally I'm in no mood to figure out the correct versions for each architecture and move the functions into the architecture specific submodules. And adjust the tests for each architecture.
If this is the desired solution, I understand, but I don't have the time to do it.
However, if we do, the behavior of the libc crate will not match the documentation of glibc. Since the libc crate does not come with it's own documentation, I also think this also somewhat confusing.
Lastly, if we stick with the pre 2.42 versions, then it is not possible to set custom baud rates on serial ports through libc. The only possibility is to bypass libc and do raw ioctls. People will likley mix raw ioctls with using libc APIS (I did), and that will lead to undefined behaviour.
Doing this case-by-case can lead to undefined behaviour: libc is upgraded, a rust program compiles with a new glibc, it uses new versions of symbols that have not explicitly been pinned to a version. However, constants and struct layouts in the libc crate have not been updated to match the ABI change. (This is exactly what happened to
serial2, fixed here.)tgross35 commentedon Sep 18, 2025
No need to move anything around, it would look something like adding this to each of the relevant functions, like we do for similar cases:
With output collected from
rg 'cf(get|set)[io]speed' -g '**/*.abilist'in glibc sources. A bit tedious, but of course it's not your responsibility if you're not up for it.Agreed that this isn't ideal, but I don't think we can do much better without proper symbol versioning. If we pin a version, adding a note in the docs to indicate that is reasonable.
Where is the impedance mismatch here? The kernel seems to use enum-style baud variables https://github.com/torvalds/linux/blob/cbf658dd09419f1ef9de11b9604e950bdd5c170b/include/uapi/asm-generic/termbits-common.h#L30-L45, so I would expect the 2.42+ definitions to be more problematic if mixing and matching there.
To be clear: the concern is that case-by-case isn't sufficient compared to versioning all symbols, correct? As opposed to case-by-case versioning introducing UB that isn't possible otherwise. I opened #4700 to continue this discussion separate from baud rates.
de-vri-es commentedon Sep 18, 2025
The problem comes when passing glibc structs to the kernel or passing kernel structs to glibc. And you're forced to do in glibc before 2.42 because it didn't support custom baud rates. (Okay, not forced, you should be translating between them properly yourself, but they used to be ABI compatible, I think.)
I mean, it has always been messy dealing with serial ports. glibc 2.42 improved it a lot, but now we end up with even more chaos in the libc crate :]
Yeah. And I would be worried that cherry-picking some symbols can easily lead to a set of linked functions in libc that don't agree on struct definitions or constants.
But, on the other hand, right now we also end up with functions that don't agree with the struct definitions and constants defined in libc.
hpax commentedon Oct 3, 2025
tgross35 commentedon Jan 15, 2026
This issue should have been resolved as of the 0.2.179 since the symbols now pin the old glibc version, so I'm going to close this.
There is one followup issue if anyone has any ideas, seems like things still aren't working properly specifically on ppc64le #4921.
de-vri-es commentedon Jan 16, 2026
As mentioned on the other issue: I'm not sure that pinning only
tc[gs]et[io]speedis enough: for some platforms the layout of thetermiosstruct changed, so all functions taking atermiosshould be pinned. Especiallytc[gs]etattr.tgross35 commentedon Jul 9, 2026
I believe that with 6abfe75 those should have been taken care of. Feel free to correct me again if there are others :)