{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreidasx2xhtaffcds2nnsppzyu7pk6u2g4gv5qhjrzefe7rn6w3hacy",
    "uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mlcdreshnnz2"
  },
  "path": "/t/pre-rfc-target-glibc-proposal-to-add-native-support-for-glibc-versions-for-the-gnu-targets/24225#post_15",
  "publishedAt": "2026-05-07T21:46:25.000Z",
  "site": "https://internals.rust-lang.org",
  "tags": [
    "explicitly choosing a symbol version at compile time"
  ],
  "textContent": "zackw:\n\n> Another very important reason for _this_ proposal is that I expect your AI-assisted MVP implementation probably hits all the cases where \"just call the old symbol\" works by accident, and none of the ones where it'll link OK but then when you _run_ the program it'll fail to load, crash, corrupt memory, or worse\n\nThing is, for better or worse, Rust is _already_ in this situation.\n\nIn C, function signatures and struct definitions come from the headers present at compile time, while symbol versions come from the `.so` present at link time. These are expected to come from the same glibc version, so glibc can simultaneously update the headers to change a function signature and update the `.so` to point to a new version.\n\nBut in Rust, nearly everyone – including `std` – accesses glibc through the `libc` crate. And the `libc` trace ignores headers and hardcodes its own declarations, which are always the same regardless of the glibc version. Yet linking still works like usual, so you might end up linking against an older or newer symbol version depending on the version of the `.so` in your sysroot.\n\nTherefore, for all symbols where glibc has made an incompatible change, `libc` must already be broken on either old sysroots or new sysroots!\n\nThere is one exception. For the `cfsetspeed` family of functions, glibc made a backwards-incompatible change in 2025, and the breakage was reported. `libc` addressed this, not by varying its declaration based on glibc version, but by explicitly choosing a symbol version at compile time (this is a mechanism that C generally doesn't use), so that it always links against the old symbol version regardless of the `.so` version.\n\nIt would actually be an improvement over the status quo if `libc` used explicit symbol versions for all glibc symbols. If glibc makes a backwards-incompatible change, `libc` is not going to pick up the new declaration by default, so logically speaking it shouldn't pick up the new symbol version by default either.\n\nAnd if `libc` does that, then it's relatively straightforward to switch to `raw-dylib` and make `target-glibc` work.\n\nFurthermore, `target-glibc` would provide a way for `libc` to know at compile time which versions are available. Right now it doesn't know, and so it's stuck offering only the old `cfsetspeed` interface, even if someone wanted to step up to add declarations for the new one. I suppose `libc` could instead infer the glibc version from the sysroot somehow, but an explicit `target-glibc` would be nicer.\n\nWhy do we care – why not just use the old interface forever? Two reasons. First, the change was made for a reason: in this case, supporting arbitrary speeds on serial ports. We don't want to be stuck not having access to newer functionality. Second, glibc only includes compatibility symbols on architectures that shipped the corresponding old glibc versions. If glibc adds a new architecture today, it will not include the old `cfsetspeed` interface, so when `libc` adds support for that architecture it will be forced to use the new interface.\n\nThere is no free lunch. For every incompatible change from glibc, `libc` really ought to do that reverse engineering you mentioned and declare old and new symbols with the correct versions. Otherwise, `libc` will at best get stuck on an old symbol version (with the previously mentioned consequences), or at worst get the versions mixed up and just be broken. The need to manually address ABI changes is a fundamental consequence of the choice to hardcode FFI declarations – which I personally believe is a bad approach. But that choice was made long ago, and is not changing anytime soon. Given that, if `libc` switches to explicitly tracking glibc symbol versions, at worst the situation will be equivalent to today, and at best it might be considerably better. In particular, ideally the hypothetical abilist-parsing tool should detect cases where a symbol has a version change after being initially introduced on a given architecture, and automatically flag those symbols for review.\n\nThe good news is that there aren't _that_ many incompatible changes. Otherwise we would be seeing much more breakage in practice. But there are some, and we definitely get some symbols wrong today. As one example I found while researching this post, glibc prior to 2.33 had no `mknodat` symbol and instead made `mknodat` an inline function pointing to a `__xmknodat` symbol. However, `libc` always links directly to `mknodat`, so AFAICT, any program using `mknodat` will fail to link if the sysroot has an old glibc version. This would have been caught by the standard library build, which does use an old sysroot, except that the standard library never uses `mknodat`.",
  "title": "[Pre-RFC] target-glibc: Proposal to add native support for GLIBC versions for the -gnu targets"
}