Re: Discussion about why GNU/Linux system upgrades cause old programs to break
Arsen Arsenović <[email protected]> Mon, 10 Aug 2026 20:50:56 +0200
| Newsgroups | gmane.emacs.devel |
|---|---|
| Organization | BayLibre |
| Message-ID | <[email protected]> |
Eli Zaretskii <[email protected]> writes: >> The program didn't necessarily break in this example; the admin removed >> libraries that the program was linked against. > > The admin didn't remove any libraries, no. The admin clearly did: Emacs was built and linked against a library that was there, and now it's not there. Whether an upgrade did that, I do not know. I have no idea what goes on on Fencepost. > What happened was a system upgrade. I don't expect a system upgrade > to remove capabilities. Am I alone in this expectation? An upgrade can most certainly do that: if A version 1 requires B and C, and A version 2 requires merely B, the upgrade and and subsequent cleanup will remove C. This is only avoided if C was also marked as wanted (or "manually" installed, or added to @world, ... depending on package manager). In fact, this is almost certainly what happened. Many programs moved away from GTK3 (but Emacs is not among them, and certainly not Emacs 27). Thus, an upgrade could easily lose it. Incidentally, I went to compile Emacs 27.2 on modern glibc and found that there was, in fact, a real breakage: SIGSTKSZ on GNU/Linux is not a constant expression anymore (seemingly because some CPUs have started requiring varying sizes of stacks to save register pages), if _DYNAMIC_STACK_SIZE_SOURCE, which is enabled with _GNU_SOURCE. This happened in glibc 2.34; quoting NEWS: > * When _DYNAMIC_STACK_SIZE_SOURCE or _GNU_SOURCE are defined, > PTHREAD_STACK_MIN is no longer constant and is redefined to > sysconf(_SC_THREAD_STACK_MIN). This supports dynamic sized register > sets for modern architectural features like Arm SVE. > > * Add _SC_MINSIGSTKSZ and _SC_SIGSTKSZ. When _DYNAMIC_STACK_SIZE_SOURCE > or _GNU_SOURCE are defined, MINSIGSTKSZ and SIGSTKSZ are no longer > constant on Linux. MINSIGSTKSZ is redefined to sysconf(_SC_MINSIGSTKSZ) > and SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ). This supports > dynamic sized register sets for modern architectural features like > Arm SVE. The workaround in Emacs was added in f97e07ea807cc6d38774a3888a15091b20645ac6. Though, this wouldn't have broken an already-compiled Emacs. >> If I removed libraries that my copy of Emacs 32 was linked against, it'd >> also "break" in the same manner, even though the Emacs 32 I have >> installed was built ~18 hours ago. > > Please drop the sarcasm. This is a real problem, not a joke. There was no sarcasm there. That was a direct and apt analogy that explains that the specific error here has nothing to do with the age of the code, or indeed any factor related to the code itself. >> More broadly speaking, note that, IME as a distro maintainer, programs >> quite rarely break due to issues in gcc, glibc, etc. >> >> In the vast majority of cases, "breaks" are in things that previously >> only worked by coincidence. >> >> For instance, the example Eli gave, of programs "breaking" because GCC >> versions up to 14 failed to properly diagnose invalid C99, the correct >> interpretation is that the programs were only "working" (in air-quotes, >> because they cannot possibly be considered working, the same way as >> someone falling off a cliff is not truly flying) because GCC in >> particular did not enforce language rules. > > K&R syntax does not mean a program using it is broken, no. Not necessarily, no. A C99+ program using K&R-exclusive syntax is, however. Which is precisely what I said: "GCC [...] failed to properly diagnose invalid C99". >> In reality, the programs have been broken for decades, often in subtle >> ways, for instance by silently being compiled into wrong code. Indeed, >> such miscompilations were among the reasons the diagnostics were >> strengthened. >> >> Were we to keep this broken status quo, of having a compiler that fails >> to diagnose incorrect programs, because people, 20+ years in, were not >> correcting their habits of writing incorrect code? >> >> In my opinion, said habits are a reason to correct the status quo to >> start diagnosing incorrect code. >> >> I don't care to relitigate that discussion further, it went on long >> enough on the GCC mailing list when it initially happen. We've put in >> the effort of correcting existing programs, and >> https://gcc.gnu.org/gcc-14/porting_to.html was written to help everyone >> else deal with the "break". >> >> I can't recall any actual breaks (i.e. not something like what was >> described above) in the last five years, so I'd argue we don't break >> programs that quickly. All the breaks I can remember are simply >> instances of Hyrum's Law. > > The context here is the ability (or the lack thereof) to build a > program using old enough source code. That ability was never removed, as you know. But, indeed. Not every K&R C program is incorrect. Here's an example: ~$ gcc -x c -std=gnu89 - <<<'main(argc, argv) int argc; char **argv; { printf ("%d %s\n", argc, argv[0]); exit (0); }' <stdin>: In function ‘main’: <stdin>:1:43: warning: incompatible implicit declaration of built-in function ‘printf’ [-Wbuiltin-declaration-mismatch] <stdin>:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’ <stdin>:1:78: warning: incompatible implicit declaration of built-in function ‘exit’ [-Wbuiltin-declaration-mismatch] <stdin>:1:1: note: include ‘<stdlib.h>’ or provide a declaration of ‘exit’ ~$ ./a.out 1 ./a.out ~$ ./a.out 121 3132 12 4 ./a.out ~$ This program is correct because it properly declares parameters of main, and because the default return types and promotion rules happen to work out for exit and probably printf (I'm not as sure that's the case for printf, because printf is treated specially by GCC, so it may have placated it, but I'm still leaning towards it being correct even for printf). Here's an example where they don't happen to work out: ~$ gcc -x c -std=gnu89 - <<<'main(argc, argv) int argc; char **argv; { char *x = reallocarray(strdup (argv[0]), 100, 1); printf ("%d %s\n", argc, x); exit (0); }' <stdin>: In function ‘main’: <stdin>:1:66: warning: incompatible implicit declaration of built-in function ‘strdup’ [-Wbuiltin-declaration-mismatch] <stdin>:1:53: warning: initialization of ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion] <stdin>:1:94: warning: incompatible implicit declaration of built-in function ‘printf’ [-Wbuiltin-declaration-mismatch] <stdin>:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’ <stdin>:1:123: warning: incompatible implicit declaration of built-in function ‘exit’ [-Wbuiltin-declaration-mismatch] <stdin>:1:1: note: include ‘<stdlib.h>’ or provide a declaration of ‘exit’ ~$ ./a.out Segmentation fault (core dumped) ./a.out ~ 139 $ Indeed, the above is a reduction of a real-world case where this lack of compile-time error meant a runtime failure: https://developers.redhat.com/blog/2019/04/22/implicit-function-declarations-flexs-use-of-reallocarray This will work, accidentally, on IP32 systems, or in general on systems where sizeof (int) == sizeof (void *); > We are proud in providing the source code of each of our programs, > indefinitely, and consider that part of the software freedom of our > users. This is a point of policy, and an admirable and laudable one, that I wholeheartedly agree with, but... > But in reality, try to compile an old enough source tree, and you get > gazillion warnings and error messages, and in some cases the problem > will fail to build without a lot of non-trivial tweaking (which > requires significant expertise). ... this is a technical point, where Hyrum's Law manifests. (to those who have not heard of Hyrum's Law, its statement, in brief, is: "for a system with enough users [for which GNU qualifies, easily], every observable behavior will be depended on by somebody") There is no system in the world that manages to solve this, except by "simply" shipping era-appropriate copies of dependencies. Of course, one can do that, but one does not need to prevent improvements to current era software to do that, nor to default to lowering the quality of compile-time diagnostics. We never chose to do that for most GNU/Linux distros, to my awareness. We probably also couldn't, given how much of our work is done on volunteer basis. The other approach requires a lot of redundant work (maintaining many such "era-appropriate" sets of libraries), whereas in reality there's already insufficient volunteers, though some approximate this to an extent (e.g. RHEL and other LTS distros). This has us labeled as "unstable" and "prone to breakage", etc etc by many. In fact, the only difference between these the approaches which see and don't see this breakage is who plays the role of integrator. For most Linux (GNU and non-GNU) distros, the integrator is the distributor, whereas other systems do not have such integrators at all. The system vendor (be it Apple or Microsoft or Google or whoever) simply ships you a set of dependencies dated to, say, some year, so the developer ends up seeing breakages when they change which date they attach themselves to, because they're the ones acting as integrators for the vast majority of their dependencies, and their code, with the vendor-provided code. The users end up not seeing this at all because each program gets distributed with nearly all dependencies, with the ones distributed as part of the OS being installed multiple times for differences in version (those who have used MS-w are aware of "VC++ Redistributable XYZW" - or whatever the name was - which they have to install on occasion for various XYZW). (Obviously, there are details to this approach also; some vendors have "app stores" that act to an extent as integrators, and they hence do a combination of these approaches; Android is one such case, though Android still provides backwards-compatibility in a manner comparable to above, by shipping many "SDK versions" maintained by their armies of developers, but anyway..) This is also why many cling to static linking. I'd love it if the compiler could diagnose any and all misuse and thus prevent all future breakage, but that's unfortunately not always possible, especially not in C, so one has to trust the developer not to write wrong code. > One example of this is compiling a program that used K&R syntax. > E.g., Emacs switched to C90-style function definitions only in version > 24.1, which was released in 2012. All the older versions used K&R > syntax; they are now "broken" by your definitions, and cannot be > easily compiled. Well, yes, if Emacs versions up to 2012 were telling the compiler that they were C99 programs, but were not valid C99, they were certainly broken, and only worked due to a defect in GCC. Indeed, GCC should've been diagnosing those constructs back in 2012. Or 2000. Then, this situation would not have happened. In name of backwards compatibility, we've made many more incorrect programs to be "compatible" with. I've argued before, and stand by it now, that source-level backwards compatibility should not be the default for compiled languages. When a compiler is present, it should only be backwards-compatible if told to be explicitly. That's, obviously, not to say that we should break things willy-nilly, but the removal of certain K&R syntax from C99 was a considered move done long ago. That we weren't diagnosing for this change up until GCC 14 has done far more harm than good. > IOW, we cannot always keep our promise of Free Software that can > always be rebuilt and fixed using the sources we provide. That's what > this is about. Let's please be serious about what we promise. I agree. -- Arsen Arsenović
signature.asc
(application/pgp-signature, 418 B)
-----BEGIN PGP SIGNATURE----- iQECBAEWCgCqFiEE/uKz0RP8AKMWLWBhUsKUMB6ixJMFAmp6HZAbFIAAAAAABAAO bWFudTIsMi41KzEuMTIsMiwyXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25z Lm9wZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRGRUUyQjNEMTEzRkMwMEEzMTYyRDYw NjE1MkMyOTQzMDFFQTJDNDkzEBxhcnNlbkBhYXJzZW4ubWUACgkQUsKUMB6ixJPJ 4gEAgAr1ByQcswXLdu3hVJl1Wz22iGURJrMsMMU2zc8GanIBAPkvpIuYq5DA89c3 zb4FrOWHL8rdCuxGj5ub9nKJ4WkN =OkZJ -----END PGP SIGNATURE-----