This is a big forwards-compatibility risk. Suppose glibc adds a new symbol, and then a GPU driver adds a dependency on that symbol. The user wants to run an old executable with the updated GPU driver (maybe the old GPU driver doesn’t support their GPU). Normally, this would work fine: the user has to use a new copy of glibc, which will be compatible with both the new GPU driver and the old executable. But with your approach, the GPU driver is forced to use the glibc reimplementation which has been statically linked into the executable. Which, since the executable is old, can’t possibly implement the new symbol.
The same issue would occur if glibc adds a new version of an existing symbol and then the GPU driver is recompiled. (Or, for that matter, if a GPU driver adds a dependency on a symbol which glibc has always supported but which isn’t in the subset that you reimplemented, though in theory that could be solved if you reimplemented 100% of the symbols.)
If you can figure out your own ELF loader, you can figure out how to build a partially static executable that doesn't need this. You can mix static and dynamic linking. Build tooling around that is just shit.
> GPU: Vulkan and OpenGL drivers are supplied by the host as shared objects, usually built against glibc, and a fully static musl binary cannot normally dlopen() them.
Why? Have people managed to break the ancient concept of shared libraries, and this is a fix for that?
Because glibc and GNU set a terrible precedent. On GNU/Linux systems the shared binary interpreter / loader, GCC compiler, the C library and the system C/C++ ABI all depend into each other. You cannot change any of them independently. All shared libraries depend on the specific glibc version to load them into memory to be able to use that specific glibc version as their C library and make calls like dlopen.
Shared libraries have always been broken in Linux. Unfortunately many things like GPU drivers, graphics libraries and NSS need shared libraries to dynamically load certain runtimes (because you don't want to load all possible GPU drivers in existence to your RAM). So an ecosystem has been developed on top of terrible ABI and architecture GNU/glibc provided.
Yeah, it's mind boggling how everything hard depends on glibc, even critical graphics systems.
I've become obsessed with getting rid of it, especially after I realized that contributing to GNU itself was a dead end. Freestanding Linux programming turned out to be much more fun anyway.
All libraries out there should adopt the SQLite design: programmers provide it with all the necessary functions. Instead of libraries hard depending on glibc, we get to inject the libc-ish subset it needs. Then we can use whatever we want under the hood. I'm working on porting SQLite to freestanding Linux system calls so it can run with zero dependencies. Wish I could say the same for software like mesa, I'd need a lot of help for this one...
Somehow most of my portability issues seem to be caused by glibc, its symbol versioning and close ties to the dynamic loader. Minor versions aren't compatible, no two Linux distros ship the same version and you can't just provide your own without also patching in your own dynamic loader.
At least as far as the defaults on Linux go I consider C the root of all evil.
AFAIK glibc is backwards compatible as long as a) your program is running on a newer version (i.e. you're not trying to dynamically link against an older version) and b) you're not using hidden/undocumented symbols.
In which case as long as you're using the documented public API and compile your program with the oldest version of glibc you want to support (some Ubuntu from 4-5 years ago should cover pretty much every current desktop) you should be fine. And with something like Docker this is trivial to do.
Sure it is annoying that you cannot use your current distro (especially if you use some rolling distro) to make binaries for everyone, but it takes very little effort to work around that. The only issue i can think of is if you absolutely want to compile using the latest version of your compiler and you cannot build the compiler from source to work in the Docker (or whatever) contain to work against the older glibc.
While I don't disagree with some of the pain you describe, you conveniently gloss over the fact that gnu developed a system that worked, and then made it free to everyone to consult and use.
BSD also did it. They did it better. Maybe more modern but AOSP also did it but at a different level of binary: instead of ELF, using compiled Java bytecode archives.
The root cause is alternative libc implementations. Are BSD syscalls considered stable? I remember Go moving to use libc on OpenBSD. Solaris also has the libc as the stable interface. Linux kernel is an outlier here guaranteeing stable syscalls but you wanting to use another libc is not Glibc's problem.
In what sense do binary interpreter / loader, GCC compiler, C library and system C/C++ ABI dependent on each other? I have certainly mixed different versions of all these components without problems so far.
When you compile GCC you need to provide a full glibc installation as your target. It is also a dependency of libstdc++.
C++ global/static variable initialization depends on the specific version of glibc (they don't usually break compat, but they can and they did in the past) which also provides ld-linux.so that loads those global variable placeholders in the correct manner such that glibc and libstdc++ can initialize them correctly.
This is just one example. Thread local variables and behavior of things like pthreads with signal, fork etc all depend on glibc.
I can't comment on the C++, I can imagine there plenty of issues, but for C I don't see this. You need some libc if you compile with gcc, but this generally does not introduce a hard version dependency on the specific version (there may be a minimum requirement if you compile against a new version that a symbol with a different ABI).
I don’t imagine that you’re unaware of any of this, but: ld.so and libc.so are heavily interdependent in deliberately undocumented ways with Glibc and outright the same file with shared Musl. And while you might usually get away with using any old GCC with the right architecture and ABI (especially for C; cf the musl-gcc hack), technically it needs to be built to target a specific libc version (particularly via symbol versioning; I’ve long wanted to gather a set of patches to build an old Glibc and subsequently a cross-compiler using a new GCC so I could avoid PyPA’s manylinux monster or its moral equivalent for compatible dynamic binaries in simple cases). The C compiler of course is tied to the C ABI, and this wouldn’t be really worth mentioning except for the time where the GCC devs accidentally the whole SysV i386 ABI and pretended that the stack was always 16-byte aligned, why do you ask, except on RHEL. The C++ parts I can’t really comment on.
I am not really sure. For ld.so and libc.so I may believe this. The C ABI is very stable, and if you use a new symbol from a newer glibc, you certainly depend on it, but this can also be avoided. In any case, I do not see what is fundamentally misdesigned here. I can't quite image how it could work differently. If you upgrade something so that the e ABI changed you natually need to update other components. Static linking certainly seems a very poor replacement for this.
The interpreter/loader is glibc and a key part of bootstrapping an executable built against glibc is loading libc itself before continuing on to load the program. Versioning is a problem when distributing binaries linked against a newer glibc to distros that ship an older one. The C compiler doesn't really care as much.
Until you define a thread local variable (C11) or use atomics (also C11) or define a global with an initial value. Then it happily generates code that depends on "whatever my target glibc + ld-linux.so needs".
You'd expect that but, no. That's why you cannot load glibc-linked binaries in a Musl distro. Edit: that's why the hacks like the original post is needed, as well.
The ABI is strongly dependent on explicit libc implementation in current Linux systems. There is no libc independent ABI on Linux.
Sorry, can you be more specific. I do not understand what the problem is. If Musl does not implement support for the ABI, this would be a musl problem?
There is no libc independent ABI. ABI doesn't purely mean just calling conventions.
When you compile libc, you also get a binary loader ld-linux.so with it. They are not two independent components of a system.
Basically all .so files compiled with glibc require the ld-linux.so that's also generated by that glibc (or a later version, if they didn't break the binary compatibility).
There are a lot of stuff that's executed by ld-linux.so and glibc that are not explicitly documented but they are absolutely necessary for your program to start and correctly initialize things like global variables or signal handling or loading other dynamic libraries. Some of that functionality sits in ld-linux.so and some of that in glibc. They have circular dependencies to each other. glibc expects ld-linux.so to put things in certain order but ld-linux.so also must load glibc first to have access to certain APIs. They are not part of System V ABI. They are not documented.
Musl maybe can implement this but it is simply reverse engineering what glibc did and then playing a game of cat and mouse. There is no independent ABI standard.
> all shared libraries depend on the specific glibc version to load them
Not really, though. glibc uses symbol versions that are forward but not backward compatible. If you got an error that said "this program was built for a newer version of <distro>" would you say the same thing?
Note this is the same (if not worse) on MacOS, and on windows you used to distribute the CRT with your application just to deal with the same problem.
Yes glibc has some backwards compat but you cannot load a binary compiled with a newer version of glibc using an older ld-linux.so. That's because the interdependency. Nor you can load binaries that depend on different libc.so files with glibc systems
I cannot comment on macOS, I have never used it. However this is not a problem with Windows. You can ship a newer CRT or you can install it as a system component using Microsoft's MSI. The dependency is one way on Windows. CRT purely depends on Win32. Moreover the loader is completely independent and DLLs are loaded into their own unique scoped namespace unlike Linux that loads them in global symbol namespace. That's why you can mix and match DLLs compiled for different CRT versions.
It is not just compatibility. You cannot load them into the memory with your system dynamic loader. You need to also ship ld-linux.so with the new version of glibc you have, if you were to distribute your program independently.
On Windows you don't need to ship a new binary loader. I can just ship Windows 10 UCRT DLL (which is the new libc of Windows) to Vista and my binaries will work. The binary loader isn't interlinked with the libc.
In what way are they "broken" when Linux runs fine on millions of boxes? Sure, it might be a pain for proprietary software, but if your app is open source it's not that hard to build it on whichever distro you want. If your app is popular enough the distro maintainer will build it for you
Well, now it's possible! Furthermore, SoLo binaries can run, without modification, on glibc-based distros, alpine, and soon on android/bionic (not committed yet).
On the one hand, this is technically true, but on the other, what serious issues do you know that will cause problems in practice? I run tests on 1,000 of the most popular Debian packages.
> Do you belive the machine is taking offence? It can not.
Obviously, this is an offense to me.
> Telling me, a coder you never met or interacted with before, that I would introduce more bugs than The Product(tm) is pretty rude and prejudiced.
Don't exaggerate. I didn't say that you personally introduce more bugs (as you rightly pointed out, I don't know you and don't know how often you introduce bugs), I said that the average developer introduces more bugs than an SOTA LLM.
I don’t think I’ve said it out loud more than a couple times in my life. But in general I think I spell out / pronounce the “dot” in file extensions unless it’s completely obvious from context.
Technically, you're right, it's a dynamic loader. Technically, it's pure dynamic loading.
If we look at the issue at its core, we're still a statically linked program in a hostile environment, forced to dynamically load device drivers from the system.
It's similar to Golang; on MacOS, it has to use libSystem, even though otherwise, these are the statically linked Go binaries we're used to and love.
Let me add a little more detail: if I use vdso with gettimeofday in a statically linked program on Linux, am I still a statically linked program, or not? :)
It is a testament to the complete failure of the GNU/Linux userland that something like this seems at all attractive to spend time on (or, it seems, LLM tokens).
Actually, scratch that, because Windows and macOS have historically struggled with ABI compatibility as well (macOS less so, due to not caring about backward compatibility in the first place).
How did we get to the point where people feel they need to go to the length of embedding an ELF loader in their binary (!!) rather than just linking with glibc?
> How did we get to the point where people feel they need to go to the length of embedding an ELF loader in their binary (!!) rather than just linking with glibc?
Most Linux distros have been built around the ability to compile their software together in a large repository, from source, so this was rarely ever an issue. Proprietary distribution or executing binaries from the internet like on Windows just wasn't really a common issue.
The problem arises when you start combining distros (glibc and MUSL for instance) or if you try to do the Windows model of sharing software. Historically, projects just compiled different versions for different distros.
When doing static compilation, just targetting an old version of glibc (which is generally forward compatible) also works.
You can hack your way into using software like this (or rather, have an LLM hack its way in) but I don't think any real distro actually cares. This issue exists in a quite small space where people are trying to use proprietary software built for glibc in MUSL environments for whatever reason, and the usual compatibility tricks don't work.
It's a niche use case for most Linux distros. It's not a "complete failure" of the GNU/Linux userland, it's the result of a couple of proprietary components not having MUSL builds available, or MUSL-based distributions not including libraries people want.
I'd like glibc to change so that these hacks aren't necessary for these use cases anymore, but it's not really a problem in practice for the vast majority of Linux use cases.
I'm mostly taken aback all the solutions devised to go around the issue, especially the container-based ones. I really disliked it when I grabbed the flatpak version of Blender only to find out that it can't have HIP support. (they might have fixed it by now but you get the point)
Linux loves to leave papercuts unfixed or undocumented for decades. The solution is to build against an older version of glibc but no one tells you that or how to do it.
Luckily Zig makes this quite easy to do. In Mach[0] we are able to just `zig build -Dtarget=x86_64-linux-gnu.2.28` to build GUI apps against an ~8 year old glibc version for maximum compatibility.
This is possible because Zig allows for targeting most glibc versions out of the box with its cross-compilation support.
1) Why should I limit myself to the available APIs?
2) Not just glibc. For example, if I build against the latest libstdc++, it will automatically support the more recent glibc. And pinning the old libstdc++ -well, that's just not a good idea.
Complete failure is strong words when lots and lots of Linux boxes are running just fine.
I think the disconnect is mostly people that can't decide if they want a stable distro or a rolling release distro. Most everyone uses a stable distro because it's stable, but then the want some up-to-date software that isn't ore-built for their (crusty old) stable distro and they get annoyed. My solution was to finally give in and embrace a rolling release distro (I use arch, btw). If there isn't a package for something I want, it's not hard to build something myself because all my build tools, kernel, and libs are up to date.
Other reasons to want static linking is to distribute proprietary software with no source code available. Linux certainly does not cater to that scenario and I suppose some might call that a complete failure ¯ \ _ ( ツ ) _ / ¯
Glibc has a terrible history of binary incompatibility. If that's so hard to believe, try running binaries built on one distribution on other distributions. Linux has two stable ABIs: the kernel ABI for static programs, and, ironically, WINE.
I haven’t heard of this and I don’t think you’re right. Glibc, for all its faults, as a general rule does backward compatibility well. The problem is if you compile against a newer glibc (common in CI by default) and try to run on a distro with an older (common in the wild). If your CI uses an older glibc you should be fine AFAIK.
Not sure what you’re trying to show with that bug report but it’s not a case of cross distro glibc issues. If I read correctly it’s a vanilla behavioral change that exposed preexisting UB in flash.
Not sure how the comments about alpine or bionic relate either to my claim that cross distro glibc is fine.
It depends on how we define the ABI. I see it as a set of client-visible invariants that they rely on. In my world, glibc changed the client's visible invariants, breaking the client. The client works on one glibc-based host, but not on another. What is this if not "a case of cross-distro glibc issues?"
Overall, both of our points of view on compatibility were discussed well in that thread; we probably shouldn't repeat ourselves. :)
How are we supposed to take this stuff seriously if the author (sic) isn't even willing to write the readme? Claude exists! If I want some slop I can push the button myself.
Disclaimer: This is a user’s perspective rather than a programmer’s perspective.
valid point. I am usually okay with LLM generated code since even if it might not be architecturally sound It is usually well commented and has tests and documentation for helping another agent/human debug any issues.
But, just the painful experience of debugging any dlopen related crashes and/or intermittent bugs; and the sheer amount of tokens burnt by an LLM chasing tangents when shown a stack trace; I wouldn’t touch this at least as a packager/consumer of certain apps for personal usage on older distros.
So far, AnyLinux-Appimages seem to be a mature solution with great support from the developers, in case anyone lands here for packaging applications to run on older distros.
Modern models, when properly managed with a human in the loop, write higher-quality code than humans and introduce significantly fewer bugs.
Therefore, it's quite the opposite - you should expect fewer "dlopen-related crashes and/or intermittent bugs."
README.md was written by me, and I, of course, used claude/codex for it. In general, I do everything through claude/codex, the reasons are described in https://github.com/pg83/solo/blob/main/CONTRIBUTING.md . And no, it's not low effort, and no, I don't see the point in wasting time de-claude-ifying the text just to avoid it looking like I didn't spend enough time on it.
You make it clear why you write all your code through a llm. But a README is not code. Presumably you would like people to read it. A machine authored readme reflects poorly on a project.
In any case, it's open source. If you don't like something, even if the project seems generally useful, go ahead and fix it. The PR came in. I'm an engineer and I can write good code, but that doesn't mean I can write good README.mds!
The author of the README is me, the machine just wrote it.
I am not a native speaker of English, my written English is simply terrible, no one wants to read the text that I wrote exactly :))
For context, I read Claude output every single day, I know what it is reliable with and what it is not. Or at least I have a feel for how much I can trust it.
It may not be clear to a non-english first language person, but when I read Claudes documentation, my brain immediately picks up claude-speak. Therefore, I expect the code to be generally correct, maybe, depending on how specific I was during my prompting. In no way shape or form do I really trust it, at least until i dig into the code and validate my mental model. And query the review for edge cases etc...
By writing your documentation with Claude, my brain immediately associates the quality of the project with the quality of unreviewed Claude output.
To a degree this is unfair, as it is like judging the quality of someone's work based on their accent.
But Claudes accent, has a high correlation with Claude, so unlike with people, where an accent has no bearing on technical ability, Claude being Claude does.
I would much rather read a typo ridden sentence than claudism, even just as a forward, explaining what you did vs the ai, and telling the users how much we should trust it.
Also, If you really insist on using AI to write, have another model rewrite docs/comments into regular English (opus 4.6 for example is much better than 4.7, 4.8, or 5.
It is open source so you can do whatever you like, but people (especially native English speakers) will discount your work, because Claude, especially opus 5 writes very very badly.
The people who say they would rather see a typo infested mess of a readme are serious. Or even just write in your native language and then have Claude translate.
Both of those are better indications of proof of effort than a Claude readme.
> By writing your documentation with Claude, my brain immediately associates the quality of the project with the quality of unreviewed Claude output.
I'm sorry, but I agree with the author: if a certain writing style makes you associate the work with low-quality, then that's your problem. The author shouldn't have to rewrite the readme just to avoid triggering your automatic unfounded associations. If you look at the substance of the work, including the test cases, then this is clearly not easy work that can be vibe coded in a single pass.
It's just like emdash. Everybody digs on how it's a signifier of LLM text, but I've used emdash for years because it's gramatically correct. I shouldn't have to stop using emdash just to avoid kneejerk reactions.
I believe usually when someone complains about text written by a language model they are hoping to read human-written text instead of human-laundered LLM output.
If you want to load the OpenGL/Vulkan vendor driver then unfortunately you don’t have much of a choice: those are linked against glibc, and I believe generally also against libwayland so screw off if you want a different protocol library (I might be wrong about the latter part). If you instead want to load plugins or whatnot into your statically linked executable, then personally I’d argue that you shouldn’t be emulating Linux dynamic linking semantics at all, because the whole late-bound global namespace thing is silly and wrong. (Solaris, which is where Glibc took this model from, moved away from it[1] as much as compatibility allowed, and so did Darwin[2], whereas Windows never made the mistake to begin with, but Glibc persisted and Musl copied it.)
Mapping parts of files into executable memory, and then executing them, had better be bulletproof! Exploiting this seems like a direct path to RCE, and it's likely that this sort of library is used by privileged code.
Purely academically, this is a very cool piece of code! Just hoping that it gets a thorough vetting before used by privileged/security-critical software :)
Well, ld.so already does this, and it's no big deal. The Python interpreter also does this when executing a .py script (code is code, whether it's machine-readable or human-readable).
In any case, we take testing very seriously—every glibc shim we've written is covered with tests, and we run our loader against 1000 of the most popular Debian packages. The project has 100% code coverage. Perhaps, if I have the time, I'll also do some fuzzing on this thing.
The same issue would occur if glibc adds a new version of an existing symbol and then the GPU driver is recompiled. (Or, for that matter, if a GPU driver adds a dependency on a symbol which glibc has always supported but which isn’t in the subset that you reimplemented, though in theory that could be solved if you reimplemented 100% of the symbols.)
> GPU: Vulkan and OpenGL drivers are supplied by the host as shared objects, usually built against glibc, and a fully static musl binary cannot normally dlopen() them.
Why? Have people managed to break the ancient concept of shared libraries, and this is a fix for that?
Shared libraries have always been broken in Linux. Unfortunately many things like GPU drivers, graphics libraries and NSS need shared libraries to dynamically load certain runtimes (because you don't want to load all possible GPU drivers in existence to your RAM). So an ecosystem has been developed on top of terrible ABI and architecture GNU/glibc provided.
I've become obsessed with getting rid of it, especially after I realized that contributing to GNU itself was a dead end. Freestanding Linux programming turned out to be much more fun anyway.
All libraries out there should adopt the SQLite design: programmers provide it with all the necessary functions. Instead of libraries hard depending on glibc, we get to inject the libc-ish subset it needs. Then we can use whatever we want under the hood. I'm working on porting SQLite to freestanding Linux system calls so it can run with zero dependencies. Wish I could say the same for software like mesa, I'd need a lot of help for this one...
That's currently the real core of the problem.
The loader (and libdl) need to be decoupled from the glibc itself under Linux.
Without that, any attempt to ship static binaries (or any binary with a different Libc) will be a source of perpetual pain.
nss plugins and its associated pain (sssd and avahi) are an other examples of that.
C++ abi should not be included in this. It is independent from the other pieces and historically a source of incompatibility on its own.
Saying "C/C++ abi" as if they are the same is looney tunes, the former is very simple and stable and the latter is very complex.
Somehow most of my portability issues seem to be caused by glibc, its symbol versioning and close ties to the dynamic loader. Minor versions aren't compatible, no two Linux distros ship the same version and you can't just provide your own without also patching in your own dynamic loader.
At least as far as the defaults on Linux go I consider C the root of all evil.
In which case as long as you're using the documented public API and compile your program with the oldest version of glibc you want to support (some Ubuntu from 4-5 years ago should cover pretty much every current desktop) you should be fine. And with something like Docker this is trivial to do.
Sure it is annoying that you cannot use your current distro (especially if you use some rolling distro) to make binaries for everyone, but it takes very little effort to work around that. The only issue i can think of is if you absolutely want to compile using the latest version of your compiler and you cannot build the compiler from source to work in the Docker (or whatever) contain to work against the older glibc.
How libstdc++ initializes global variables absolutely depends on glibc and ld-linux.so. That is part of C++ ABI.
C++ global/static variable initialization depends on the specific version of glibc (they don't usually break compat, but they can and they did in the past) which also provides ld-linux.so that loads those global variable placeholders in the correct manner such that glibc and libstdc++ can initialize them correctly.
This is just one example. Thread local variables and behavior of things like pthreads with signal, fork etc all depend on glibc.
Until you define a thread local variable (C11) or use atomics (also C11) or define a global with an initial value. Then it happily generates code that depends on "whatever my target glibc + ld-linux.so needs".
The ABI is strongly dependent on explicit libc implementation in current Linux systems. There is no libc independent ABI on Linux.
When you compile libc, you also get a binary loader ld-linux.so with it. They are not two independent components of a system.
Basically all .so files compiled with glibc require the ld-linux.so that's also generated by that glibc (or a later version, if they didn't break the binary compatibility).
There are a lot of stuff that's executed by ld-linux.so and glibc that are not explicitly documented but they are absolutely necessary for your program to start and correctly initialize things like global variables or signal handling or loading other dynamic libraries. Some of that functionality sits in ld-linux.so and some of that in glibc. They have circular dependencies to each other. glibc expects ld-linux.so to put things in certain order but ld-linux.so also must load glibc first to have access to certain APIs. They are not part of System V ABI. They are not documented.
Musl maybe can implement this but it is simply reverse engineering what glibc did and then playing a game of cat and mouse. There is no independent ABI standard.
Not really, though. glibc uses symbol versions that are forward but not backward compatible. If you got an error that said "this program was built for a newer version of <distro>" would you say the same thing?
Note this is the same (if not worse) on MacOS, and on windows you used to distribute the CRT with your application just to deal with the same problem.
Yes glibc has some backwards compat but you cannot load a binary compiled with a newer version of glibc using an older ld-linux.so. That's because the interdependency. Nor you can load binaries that depend on different libc.so files with glibc systems
I cannot comment on macOS, I have never used it. However this is not a problem with Windows. You can ship a newer CRT or you can install it as a system component using Microsoft's MSI. The dependency is one way on Windows. CRT purely depends on Win32. Moreover the loader is completely independent and DLLs are loaded into their own unique scoped namespace unlike Linux that loads them in global symbol namespace. That's why you can mix and match DLLs compiled for different CRT versions.
On Windows you don't need to ship a new binary loader. I can just ship Windows 10 UCRT DLL (which is the new libc of Windows) to Vista and my binaries will work. The binary loader isn't interlinked with the libc.
What you can't do is build something statically with musl and then reliably dlopen shared libraries built with glibc.
https://github.com/pg83/solo/commits/main/README.md
https://github.com/pg83/solo/commits/main/
Rude.
It only gets better from there - https://github.com/pg83/solo/blob/main/CONTRIBUTING.md!
Telling me, a coder you never met or interacted with before, that I would introduce more bugs than The Product(tm) is pretty rude and prejudiced.
> The project author believes that, with capable human direction, modern LLMs write code faster than people and introduce fewer bugs.
Obviously, this is an offense to me.
> Telling me, a coder you never met or interacted with before, that I would introduce more bugs than The Product(tm) is pretty rude and prejudiced.
Don't exaggerate. I didn't say that you personally introduce more bugs (as you rightly pointed out, I don't know you and don't know how often you introduce bugs), I said that the average developer introduces more bugs than an SOTA LLM.
But why? You didn't write it. You said so yourself, you wrote the initial text but it was all reworded by claude.
I could choose to believe in a social fiction and you can't stop me.
If we look at the issue at its core, we're still a statically linked program in a hostile environment, forced to dynamically load device drivers from the system.
It's similar to Golang; on MacOS, it has to use libSystem, even though otherwise, these are the statically linked Go binaries we're used to and love.
Let me add a little more detail: if I use vdso with gettimeofday in a statically linked program on Linux, am I still a statically linked program, or not? :)
Actually, scratch that, because Windows and macOS have historically struggled with ABI compatibility as well (macOS less so, due to not caring about backward compatibility in the first place).
How did we get to the point where people feel they need to go to the length of embedding an ELF loader in their binary (!!) rather than just linking with glibc?
Most Linux distros have been built around the ability to compile their software together in a large repository, from source, so this was rarely ever an issue. Proprietary distribution or executing binaries from the internet like on Windows just wasn't really a common issue.
The problem arises when you start combining distros (glibc and MUSL for instance) or if you try to do the Windows model of sharing software. Historically, projects just compiled different versions for different distros.
When doing static compilation, just targetting an old version of glibc (which is generally forward compatible) also works.
You can hack your way into using software like this (or rather, have an LLM hack its way in) but I don't think any real distro actually cares. This issue exists in a quite small space where people are trying to use proprietary software built for glibc in MUSL environments for whatever reason, and the usual compatibility tricks don't work.
It's a niche use case for most Linux distros. It's not a "complete failure" of the GNU/Linux userland, it's the result of a couple of proprietary components not having MUSL builds available, or MUSL-based distributions not including libraries people want.
I'd like glibc to change so that these hacks aren't necessary for these use cases anymore, but it's not really a problem in practice for the vast majority of Linux use cases.
This is possible because Zig allows for targeting most glibc versions out of the box with its cross-compilation support.
[0] https://machengine.org
1) Why should I limit myself to the available APIs?
2) Not just glibc. For example, if I build against the latest libstdc++, it will automatically support the more recent glibc. And pinning the old libstdc++ -well, that's just not a good idea.
I think the disconnect is mostly people that can't decide if they want a stable distro or a rolling release distro. Most everyone uses a stable distro because it's stable, but then the want some up-to-date software that isn't ore-built for their (crusty old) stable distro and they get annoyed. My solution was to finally give in and embrace a rolling release distro (I use arch, btw). If there isn't a package for something I want, it's not hard to build something myself because all my build tools, kernel, and libs are up to date.
Other reasons to want static linking is to distribute proprietary software with no source code available. Linux certainly does not cater to that scenario and I suppose some might call that a complete failure ¯ \ _ ( ツ ) _ / ¯
There are also much less well-known "little things" that regularly pop up here and there.
> If your CI uses an older glibc you should be fine AFAIK.
In any case, my binaries work not only under glibc, but also under Alpine, and (work in progress) under android/bionic.
Not sure how the comments about alpine or bionic relate either to my claim that cross distro glibc is fine.
Overall, both of our points of view on compatibility were discussed well in that thread; we probably shouldn't repeat ourselves. :)
- Linus Torvalds
That bug report was a good read.
https://docs.appimage.org/reference/best-practices.html
I hear you about WINE though.
valid point. I am usually okay with LLM generated code since even if it might not be architecturally sound It is usually well commented and has tests and documentation for helping another agent/human debug any issues.
But, just the painful experience of debugging any dlopen related crashes and/or intermittent bugs; and the sheer amount of tokens burnt by an LLM chasing tangents when shown a stack trace; I wouldn’t touch this at least as a packager/consumer of certain apps for personal usage on older distros. So far, AnyLinux-Appimages seem to be a mature solution with great support from the developers, in case anyone lands here for packaging applications to run on older distros.
I don't think anybody believes this, and interjecting it into every thread is not really convincing anyone.
It may not be clear to a non-english first language person, but when I read Claudes documentation, my brain immediately picks up claude-speak. Therefore, I expect the code to be generally correct, maybe, depending on how specific I was during my prompting. In no way shape or form do I really trust it, at least until i dig into the code and validate my mental model. And query the review for edge cases etc...
By writing your documentation with Claude, my brain immediately associates the quality of the project with the quality of unreviewed Claude output.
To a degree this is unfair, as it is like judging the quality of someone's work based on their accent.
But Claudes accent, has a high correlation with Claude, so unlike with people, where an accent has no bearing on technical ability, Claude being Claude does.
I would much rather read a typo ridden sentence than claudism, even just as a forward, explaining what you did vs the ai, and telling the users how much we should trust it.
Also, If you really insist on using AI to write, have another model rewrite docs/comments into regular English (opus 4.6 for example is much better than 4.7, 4.8, or 5.
It is open source so you can do whatever you like, but people (especially native English speakers) will discount your work, because Claude, especially opus 5 writes very very badly.
The people who say they would rather see a typo infested mess of a readme are serious. Or even just write in your native language and then have Claude translate.
Both of those are better indications of proof of effort than a Claude readme.
That's exactly what I did.
I'm sorry, but I agree with the author: if a certain writing style makes you associate the work with low-quality, then that's your problem. The author shouldn't have to rewrite the readme just to avoid triggering your automatic unfounded associations. If you look at the substance of the work, including the test cases, then this is clearly not easy work that can be vibe coded in a single pass.
It's just like emdash. Everybody digs on how it's a signifier of LLM text, but I've used emdash for years because it's gramatically correct. I shouldn't have to stop using emdash just to avoid kneejerk reactions.
Yacks
[1] https://www.linker-aliens.org/blogs/rie/entry/direct_binding...
[2] https://web.archive.org/web/20011004090044/http://developer....
Purely academically, this is a very cool piece of code! Just hoping that it gets a thorough vetting before used by privileged/security-critical software :)
In any case, we take testing very seriously—every glibc shim we've written is covered with tests, and we run our loader against 1000 of the most popular Debian packages. The project has 100% code coverage. Perhaps, if I have the time, I'll also do some fuzzing on this thing.