Hacker News new | ask | show | jobs
by devit 439 days ago
That makes no sense: shipping all dependencies (e.g. shipping a container image) gives perfect binary compatibility on Linux, which is what flatpak/snap/appimage do.

It can also be achieved with static linking and by shipping all needed library and using a shell script loader that sets LD_LIBRARY_PATH.

Also glibc (contrary to the author's false claims) and properly designed libraries are backwards compatible, so in principle just adding the debs/rpms from an older Debian/Fedora that ships the needed libraries to the packaging repositories and running apt/dnf should work in theory, although unfortunately might not in practice due to the general incompetence of programmers and distribution maintainers.

Win32 is obviously not appropriate for GNU/Linux applications, and you also have the same dependency problem here, with the same solution (ship a whole Wine prefix, or maybe ship a bunch of DLLs).

4 comments

> shipping all dependencies (e.g. shipping a container image) gives perfect binary compatibility on Linux

That doesn’t work for GUI programs which use a hardware 3D GPU. Linux doesn’t have a universally available GPU API: some systems have GL, some have GLES, some have Vulkan, all 3 come in multiple versions of limited compatibility, and optional features many of them are vendor specific.

In contrast, it’s impossible to run modern Windows without working Direct3D 11.0 because dwm.exe desktop compositor requires it. If a software consumes Direct3D 11.0 and doesn’t require any optional features (for example, FP64 math support in shaders is an optional feature, but sticking to the required set of features is not very limiting in practice unless you need to support very old GPUs which don’t implement feature level 11.0), will run on any modern Windows. Surprisingly, it will also run on Linux systems which support Wine: without Vulkan-capable GPU will be slow but should still work due to Lavapipe, which is a Linux equivalent of microsoft’s WARP they use on Windows computers without hardware 3D GPU.

Note that this also underlines that the post's premise of Windows having a simple stable ABI - win32 sure is stable, but that's not what applications are coded against anymore.

Sure, you can run a 20 year old app, but that is not the same as a current app still working in 20 years, or even 5.

> that's not what applications are coded against anymore

Not sure I follow. Sure, most modern programs are not using old-school WinAPI with GDI, but the stuff they added later is also rather stable. For example, the Chromium-based browser I’m looking at uses Direct3D 11 for graphics. It implements a few abstraction layers on top (ANGLE, Skia) but these are parts of the browser not the OS.

I view all that modern stuff like Direct3D, Direct2D, DirectWrite, Media Foundation as simply newer parts of the WinAPI. Pretty sure Microsoft will continue to support them for long time. For example, they can’t even deprecate the 23 years old DirectX 9 because still widely used, e.g. current version of Microsoft’s own WPF GUI framework relies on Direct3D 9 for graphics.

I agree. On Linux (and Mac really), new APIs replace old ones and old binaries stop working.

On Windows, new layers are applied over the old. There is DirectX 9-12. New binaries may use 12 but the ones still using 9 are perfectly happy. Things like .NET work the same. You can have multiple apps installed relying on different .NET versions.

It's not necessarily the same code, though. But COM is nice for a stable ABI like that - so long as you consistently version your interfaces, the apps can just QueryInterface for the old one they need and know that it's there, even if it's just a thin wrapper around the new stuff.
You can still use OpenGL 1.0 and Xlib-like-it's-1999 on modern Linux distributions.
These are however the same on Linux - mesa may change, but what the app uses is OpenGL and GLX. A more modern app might use EGL instead of GLX, or have switched to Vulkan, but that doesn't break old code.

You can also run an old mesa from the time the app was built if it supports your newer hardware, but I'd rather consider that to be part of the platform the same way you'd consider the DirectX libraries to be part of windows.

> These are however the same on Linux .. that doesn't break old code

An example from another comment: https://news.ycombinator.com/item?id=43519949

Apologies, but "I heard that..." is not an example.
Win32 is quite extensive for an OS API. It covers the space from low-level stuff like syscalls and page allocation and all the way up to localization, simple media access and GUI. So everything from glibc, libsystemd, libpam to libalsa and egl on Linux side. And it is all stable.

Microsoft also provides quite good stability for DirectX and other extension APIs. You can still run old .Net apps without issues as long as they didn't pull a Hyrum's Law on you and depended on apparent behavior.

Sure, win32 contains GUI bits, but modern apps do not use those GUI bits.

OpenGL and Vulkan ABIs are also stable on Linux, provided by mesa. The post is pretty focused on the simplicity of win32 though, which is what I'm refuting as being as relevant today for new apps.

> As long as they didn't pull a Hyrum's Law on you

It is guaranteed that they "pull a Hyrum's Law", the question is just what apparent behavior they relied on.

> Sure, win32 contains GUI bits, but modern apps do not use those GUI bits.

Which is probably why so many "modern apps" look just like "modern" Web pages running on the desktop... i.e, why so many "modern apps" suck. They freaking should use those GUI bits.

> Note that this also underlines that the post's premise of Windows having a simple stable ABI - win32 sure is stable, but that's not what applications are coded against anymore.

It's true, but this touches on another point they made: what apps code to is other dynamically linked libraries. The kind that wine (or other host environments) can provide, without needing to mess with the kernel.

That's what apps are supposed to code to. When it comes to games and especially anti-cheat that's not always the case though and so Wine does have to handle direct system calls, which needs support from the kernel (at least to not be unusably slow).
Question, from an application developer's perspective: What is the implication in regards to cross-platform Vulkan applications? I.e., my 3D applications all use Vulkan, and they compile and just work on both Windows, and Ubuntu. Does this mean that on other or older distros, they might not work?
I don’t think the support depends on distros much, I think the main variable is hardware. If you have a desktop PC bought in the last ~5 years the support should be OK, for the hardware older than that the support is not guaranteed. GeForce GT 730 (launched in 2014) doesn’t support Vulkan, Intel only supports Vulkan on Windows starting from Skylake launched in 2015.

Then there’re quality issues. If you search internets for “Windows Vulkan issue” you’ll find many end users with crashing games, game developers with crashing game engines https://github.com/godotengine/godot/issues/100807 recommendations to update drivers or disable some Vulkan layers in registry, etc.

On Windows, Vulkan is simply not as reliable as D3D. The reasons include market share, D3D being a requirement to render the desktop, D3D runtime being a part of the OS supported by Microsoft (Vulkan relies solely on GPU vendors), and D3D being older (first version of VK spec released in 2016, D3D11 is from 2009).

Another thing, on Linux, the situation with Vulkan support is less than ideal for mobile and embedded systems. Some embedded ARM SoCs only support GLES 3.1 (which BTW is not too far from D3D 11.0 feature-wise) but not Vulkan.

Agree overall. Just want to point out that Vulkan works on Intel Haswell. I have a 2013 MacBook Air and a 2013 Mac Pro that both have Haswell. Linux kernel 6.14 actually includes an Haswell Vulkan update from Intel themselves.
> Vulkan works on Intel Haswell

Unless you are running Windows, in which case it doesn’t. Intel simply has not made a driver.

> Does this mean that on other or older distros, they might not work

Yep exactly. While Vulkan API is well defined and mostly stable, there is no guarantee in Linux implementation will also be stable. Moreover Khronos graphics APIs only deal with the stuff after you allocated a buffer and did all the handshakes with the OS and GPU drivers. On Linux none of those have API / ABI / runtime configuration stability guarantees. Basically it works until only one of the libraries in the chain breaks the compatibility.

This is BS. Vulkan buffers are allocated with Vulkan functions. Window system integration is also provided by window-system specific Vulkan extensions just like it was with WGL/GLX/EGL etc. These are all well defined and stable.
I'm not sure what they are taking about. And they might not know what they're taking about.

All Linuxes I'm familiar with run Mesa, which gives you OpenGL and Vulkan.

That depends how you build you program and what other dependencies you pull in. But as far as Vulkan is concerned your program should run on any distro that is as least as new as the one you build on (talking about ABI, runtime requirements depend on hardware but don't depend on the system you build on).
This is FUD. There isn't a single real desktop Linux distribution without OpenGL support. The basic OpenGL API hasn't changed ever, it's just been extended. It has even more backwards compatibility than Direct3D. Sure you can deliberately build a distro with only Vulkan or GLES (a mobile API) if you want to be an ass but the same goes for Windows. Same for X11 - Xlib works everywhere even any Wayland-only distribution that gives a single crap about running binary distributed software.

Now GUI toolkits are more of an issue. That's annoying for some programs, many others do their own thing anyway.

without vulkan-capable gpu you still get 3D acceleration via wined3d. Unless you meant, without any gpu at all :s
Good point. I forgot that in addition to DXVK they also have WineD3D.
> That makes no sense: shipping all dependencies (e.g. shipping a container image) gives perfect binary compatibility on Linux, which is what flatpak/snap/appimage do.

True, but sad. The way to achieve compatibility on Linux is to distribute applications in the form of what are essentially tarballs of entire Linux systems. This is the "fuck it" solution.

Of course I suppose it's not unusual for Windows stuff to be statically linked or to ship every DLL with the installer "just in case." This is also a "fuck it" solution.

> to distribute applications in the form of what are essentially tarballs of entire Linux systems.

No so bad when Linux ran from a floppy with 2Mb of RAM. Sadly every library just got bigger and bigger without any practical way to generate a lighter application specific version.

Also, 64-bit code and especially data are just larger, because every address is 8 bytes, and data has to be aligned on at least 4-byte boundary.

You can still have very tiny Linux with a relatively modern kernel on tiny m0 cores, and there's ELKS for 16-bit cores.

You could say the same about container images for server apps. (Packaging is hard.)
If Linux userspace had libraries with stable ABI, you could just tar or zip binaries and they would work. You wouldn't need to bundle system layer. This is how you deploy server apps on Windows Server systems. You just unpack and they work.

It is not a packaging problem. It is a system design problem. Linux ecosystem simply isn't nice for binary distribution except the kernel, mostly.

Linux feels a bit different since the complete system is not controlled by a single vendor. You have multiple distributions with their own kernel versions, libc versions, library dependencies, etc.

Mac OS has solved this but that is obviously a single vendor. FreeBSD has decent backwards compatibility (through the -compat packages), but that is also a single vendor.

> Linux feels a bit different since the complete system is not controlled by a single vendor. You have multiple distributions with their own kernel versions, libc versions, library dependencies, etc.

No, AFAICS that can't be it. The problem is that all those libraries (libc and others?) change all the time, and aren't backwards-compatible with earlier versions of themselves. If they were backwards-compatible, you could just make sure to have the newest one any of your applications needs, and everything would work.

-compat packages exist on fedora-like systems too, usually allowing it older versions to run. I can't say how far back, but RHEL usually has current version - 1 for -compat packages.
Yep, having a single supplier of the system layer usually ends up with better backwards compatibility for binary distribution.

That's also why I have the opinion that the world is worse off due to the fact that Linux "won" Unix wars.

Packaging is “hard” but mobile and app stores do it.

They do it by having standards in the OS, partial containerization, and above all: applications are not installed “on” the OS. They are self contained. They are also jailed and interact via APIs that grant them permissions or allow them to do things by proxy. This doesn’t just help with security but also with modularity. There is no such thing as an “installer” really.

The idea of an app being installed at a bunch of locations across a system is something that really must die. It’s a legacy holdover from old PC and/or special snowflake Unix server days when there were just not many machines in the world and every one had its loving admin. Things were also less complex back then. It was easy for an admin or PC owner to stroll around the filesystem and see everything. Now even my Mac laptop has thousands of processes and a gigantic filesystem larger than a huge UNIX server in the 90s.

I can't think of a single thing that would kill the bit last of joy I take in computing more. If I woke up in such a world, I'd immediately look to reimplement Linux in an app and proceed to totally ignore the host OS.
I agree. Though it helps that Apple (NeXT, really) got it right with their .app directory format, even outside the app store.
> Also glibc (contrary to the author's false claims) and properly designed libraries are backwards compatible, so in principle just adding the debs/rpms from an older Debian/Fedora that ships the needed libraries to the packaging repositories and running apt/dnf should work in theory, although unfortunately might not in practice due to the general incompetence of programmers and distribution maintainers.

Got it. So everything is properly designed but somehow there's a lot of general incompetence preventing it from working. I'm pretty sure the principle of engineering design is to make things work in the face of incompetence by others.

And while glibc is backward compatible & that generally does work, glibc is NOT forward compatible which is a huge problem - it means that you have to build on the oldest bistro you can find so that the built binaries actually work on arbitrary machines you try to run it on. Whereas on Mac & Windows it's pretty easy to build applications on my up-to-date system targeting older variants.

> So everything is properly designed but somehow there's a lot of general incompetence preventing it from working.

But it is working, actually:

* If you update your distro with binaries from apt, yum, zypper etc. - they work.

* If you download statically-linked binaries - they work.

* If you download Snaps/Flatpak, they work.

> it means that you have to build on the oldest bistro you can find so that the built binaries actually work on arbitrary machines you try to run it on.

Only if you want to distribute a dynamically-linked binary without its dependencies. And even then - you have to build with a toolchain for that distro, not with that distro itself.

> Only if you want to distribute a dynamically-linked binary

Even statically linked code tends to be dynamically linked against glibc. You’ve basically said “it works but only if you use the package manager in your OS”. In other words, it’s broken and hostile for commercial 3p binary distribution which explains the state of commercial 3p binary ecosystem on Linux (there’s more to it than just that, but being actively hostile to making it easy to distribute software to your platform is a compounding factor).

I really dislike snaps/flat pack as they’re distro specific and overkill if I’m statically linking and my only dynamic dependency is glibc.

Glibc is fantastically stable and backwards compatible in all the same ways , and I think you're overstating how backwards compatible windows is as well. Microsoft has the exact same dynamic library issues that Linux does via it's Microsoft Visual C++ distrubutables (as one example). Likewise, there's forwards compatibility issues on Windows as well (if you build a program in Windows 11 you'll have a hard time running that on windows XP/Vista for a huge number of reasons).

If you build a statically linked program with only glibc dynamically linked, and you do that on Linux from 2005,then that program should run exactly the same today on Linux. The same is true for Windows software.

Im pretty sure it’s safe to distribute Windows 11 built binaries to windows 7 and windows 10 if it’s a valid target set in Visual Studio. The c++ runtime is its own thing because of a combination of c++ BS (no stable runtime) and c++ isn’t an official part of Windows. It’s a developer tool they offer. But you can statically link the c++ runtime in which case you can build with the latest runtime on Windows 11 and distribute to an older Windows.

Linux is the only space where you have to literally do your build on an old snapshot of a distro with an old glibc so that you can distribute said software. If you’re in c++ land you’re in for a world of hurt because the version of the language is now constrained to whatever was available at the time that old distro from 5+ years ago snapshotted unless you build a newer compiler yourself from scratch. With Rust at least this is much easier since they build their toolchain on an old version of Linux and thus their binaries are similarly easily distributed and the latest Rust compiler is trivially easy to obtain on old Linux distros.

Source: I’m literally doing this today for my day job

You can also build a cross-compiler to target an older glibc, you are not limited to the distro-provided toolchain. This also allows to to use newer C++ features (with exceptions) as those mostly depend on the GCC version and not glibc version. Of course the supported range of glibc version varies with gcc version, just like visual studio doesn't support XP anymore - the difference is that if you are sufficiently motivated you can patch gcc.
Flatpaks aren't distro specific.

As for being overkill, surely you can see the advantage of having a single uniform distribution format from the end user's perspective? Which, sure, might be overkill for your case (although app isolation isn't just about dependencies), but the important thing is that it is a working solution that you can use, and users only need to know how to install and manage them.

You have to install the flat pack runtime to begin with so that’s one obstacle for distribution. And it also doesn’t really isolate as much as you’d like to believe - eg dealing with audio will still be a mess because there’s like 4 different major audio interfaces. And now I have to host a flat pack repo and get the user to add my repo if it’s proprietary software. It’s really nowhere near as smooth and simple as on Windows/Mac/Android/ios.
> You have to install the flat pack runtime to begin with so that’s one obstacle for distribution.

Only if you're using a distro that doesn't come with it preinstalled. But that doesn't make it distro-specific?

> And now I have to host a flat pack repo and get the user to add my repo if it’s proprietary software.

You don't have to do that, you can just give them a .flatpak file to install: https://docs.flatpak.org/en/latest/single-file-bundles.html

The reason to host a repo regardless is to enable easy auto-updates - and I don't think you can call this bit "smooth and simple" on Windows and Mac, what with most apps each doing their own thing for updates. Unless you use the app store, but then that's exactly the same as repos...

Windows toolchain (even gnu) just provides old libraries to link with. This should work the same on linux, and AFAIK zig does just that.
Windows toolchain provides the import libraries to link with, and these are basically just tables mapping function names to indices in the DLL export table. So long as you don't actually use the new functions, an app linked against a modern Windows SDK will run just fine on old Windows, unlike the situation with glibc.
The situation with glibc is the same, you only depend on functions you use.
Almost - with glibc your code uses functions like memcpy but you end up linking against symbols like memcpy@GLIBC_2.14 which is the version of memcpy added in glibc 2.14 and which won't be present in older versions. Which symbol version your calls use depends on the glibc version you build against - generally it's the most recent version of that particular function. For the Win32 this is rarely the case and instead you have to explicitly opt in to newer functions with fixed semantics.

Still, to reliably target older Windows versions you need to tell your toolchain what to target. The Windows SDK also lets you specify the Windows version you want to target via WINVER / _WIN32_WINNT macros which make it harder to accidentally use unsupported functions. Similarly, the compilers and linkers for Windows have options to specify the minimum Windows version recorded in the final binary and which libraries to link against (classic win32 dlls or ucrt). Unfortunately there is no such mechanism to specify target version for glibc/gcc and you have you either build against older glibc versions or rely on third-party headers. Both solutions are workable and allow you to create binaries with a wide range of glibc version compatibility but they are not as ideal as direct support in the toolchain would be.

Yeah maybe I should just be complaining that the Rust tool chain (or rather distros) should be including old versions of prebuilt glibc to link against?
> And while glibc is backward compatible & that generally does work, glibc is NOT forward compatible which is a huge problem - it means that you have to build on the oldest bistro you can find so that the built binaries actually work on arbitrary machines you try to run it on.

Isn’t this easily solved by building in a container? Something a lot of people do anyway - I do it all the time because it insulates the build from changes in the underlying build agents - if the CI team decides to upgrade the build agent OS to a new release next month or migrate them to a different distro, building in a container (mostly) isolates my build job from that change, doing it directly on the agents exposes it to them

Umm... that doesn't isolate you in any meaningful way because you're surrounding OS is still the container with a base image from Linux years ago?
glibc really doesn't want to be statically linked so if you go this route your option is to ship another libc. It does work but comes with its own problems— mostly revolving around nss.
And NSS defines how usernames are matched to uids, how DNS works, how localization works and so on. If you're changing libc you need to ship an entire distro as well since it will not use the system libraries of a glibc distro correctly.