Remix.run Logo
dgrunwald 9 hours ago

$ORIGIN was only supported when the loader was looking for other dependencies. The loader itself (field PT_INTERP) is loaded by the kernel. So prior to this change, every program must hardcode the absolute path to ld.so. With support for an $ORIGIN-relative loader, each program could use its own copy of ld.so.

charcircuit 8 hours ago | parent [-]

Each program having its own loader is an anti pattern. Such a requirement is overkill. You can have a single loader that supports everything on the system.

roblabla 8 hours ago | parent | next [-]

Not with NixOS. ld.so is tied to a version of glibc, in ways that can be subtly incompatible. And nixos can have multiple glibc version installed on a single machine.

Besides, it allows for upgrades/downgrades to be done in a way that's much less error-prone.

mort96 7 hours ago | parent | next [-]

"ld.so is tied to a version of glibc" is such a horrible GNUism.

myrmidon 6 hours ago | parent [-]

Isn't that kinda to be expected if you want to provide dynamic loading functionality (dlopen)?

Is the windows situation really all that different/better (with GetProcAddress in kernel32.dll)?

roblabla 6 hours ago | parent | next [-]

In theory, ld.so could provide a stable interface to its dynamic loading capabilities independently of glibc. Then glibc would not have to be updated in concert with ld.so. There's no inherent reason that glibc should be the library shipping a dynamic linker - we could easily be in a world where libld was developed independently from libc.

On Windows, things are less modular, so it's less of an issue. That said, there's also weird shenanigans when it comes to the CRT (which can be statically linked) vs ntdll (which provides the actual linker implementation), that can make certain niche features of the linker misbehave (delay loading in particular is weird).

pas 5 hours ago | parent [-]

still, eventually there's a need to support on one platform different ld.so/glibc pairs (even if they are API/ABI compatible)

it seems nixos could set up a wrapper that invokes the right ld.so based on the executable. though at this point they could probably edit the ELF binaries and patch the fixed path to ld.so when nix is installing the program.

yeah, it seems strange that this needs kernel support. but more eBFP extension points are usually welcome, so sure, why not?

roblabla 4 hours ago | parent [-]

> though at this point they could probably edit the ELF binaries and patch the fixed path to ld.so when nix is installing the program.

That's exactly what they're doing right now - though instead of being "when installing the program", it's "when compiling the program". The problem with hardcoding the path is that it pins the "nix store" (where nix installs all of its programs) to a hardcoded location. If you want to move it, you have to rebuild all your packages - which is suboptimal.

In theory, nix could have a system in place to just patch all binaries when moving the nix store, but that would be incompatible with content-addressed derivation and otherwise break some other nice properties of the nix store.

setheron an hour ago | parent [-]

(author) You did a great job articulating the points!

jcgl 6 hours ago | parent | prev | next [-]

Not my area, but isn't it really only because glibc doesn't maintain stable interfaces across versions? If it did, you absolutely could use the same ld.so with different glibc versions. But it doesn't, so here we are.

geocar 6 hours ago | parent [-]

The C standard isn’t stable across versions.

extern int errno;

mort96 6 hours ago | parent | prev [-]

The Windows situation is way different: every process is supposed to link against kernel32.dll, that's the public interface to the kernel. In Linux, glibc is just one of many C stdlib implementations, you can have many versions of glibc on the same system, etc.

myrmidon 5 hours ago | parent [-]

Sure, but if you want dynamic loading from your c stdlib (which is defensible IMO), and you want the behavior/implementation to match with the loader, then you need some kind of coupling somewhere no?

You could have a very slim libdlopen that is used by both loader and libc, but I don't really see how that's any improvement/much different.

charcircuit 8 hours ago | parent | prev [-]

>in ways that can be subtly incompatible

There is much more value to be had in making glibc properly backward compatible so you can have a single one that can be used with everything than trying to make it so that you can swap everything around, creating extra complexity and compatibility risks.

dspillett 7 hours ago | parent | next [-]

> […] in making glibc properly backward compatible […]

You could only do that going forward though, and would be stuck, at least for a time, with the historic versions that still need extra handling. In an ideal world that wouldn't be needed, but in an ideal world you'd not need multiple versions of glibc at all so we aren't there.

Support in the kernel means that it will work even if applied to old packages that for some other reason need a held-back version of glibc. As mentioned elsewhere it also gives the feature to #! directives in scripts too.

roblabla 7 hours ago | parent | prev [-]

How is that more valuable? It comes with two big pitfalls:

- It would still not allow downgrades to work properly

- It would cause glibc/ld.so to have a harder time adding new features, as they now need to worry about incompatible versions being used together

Meanwhile, having different ld.so has many good use-cases, like simplifying development of ld.so itself, allowing them to swapped during updates in ways that are safer, etc...

And the eBPF binfmt support is a rather simple, generic mechanism that is likely to have many other use-cases beyond ld.so. So it's overall a pretty good resolution to the issue?

charcircuit 30 minutes ago | parent [-]

>It would still not allow downgrades to work properly

If you really need this, then there are options like swapping the linker with an older version or making a hardcoded linker that now points to an older one.

>It would cause glibc/ld.so to have a harder time adding new features, as they now need to worry about incompatible versions being used together

Every language runtime has to care about not breaking compatibility with apps that have been released already. This is not a new or unique problem.

HexDecOctBin 8 hours ago | parent | prev [-]

Unfortunately, Glibc and loader are linked together intrinsically in the Linux ecosystem. So, if you want to be able to launch a program reliably, shipping your own loader and libc might actually be the only way.

VorpalWay 7 hours ago | parent [-]

Glibc is backward compatible though, they even have symbol versioning to provide multiple versions of the same symbol. So as long as you have the same or a newer version of glibc (than what was built against) you should be good to go. And I don't remember hearing about breakages for this.

Other libraries on the system is far more hit and miss, but glibc is quite compatible.

The other way around is harder though, you can't take a program built against a newer glibc and run it on an older version. So you generally need to spin up a container with some LTS distro and build your binary in it if you want it to be maximally compatible. (However, zig apparently is able to deal with this by shipping a mapping between glibc versions and symbol versions and doing the linking themselves. You can even use zig to link rust code using cargo-zigbuild and get that benefit.)

But if you want to be maximally portable: static linking against musl. Though beware that many things are slower in musl, such as the allocator.

HexDecOctBin 4 hours ago | parent | next [-]

Glibc is the opposite of backwards compatible. This thread I was a part of explains some issues faced in the past: https://news.ycombinator.com/item?id=47029789

VorpalWay 3 hours ago | parent [-]

Reading the linked bug report about executable stacks they fixed it? So they did the right thing. I'm not saying there will never be bugs (no non-trivial software is bug free), but as long as those are handled correctly that seems reasonable to me.

Joker_vD 6 hours ago | parent | prev [-]

No, glibc isn't backwards compatible; I've had instances when the loader would refuse to load the executable because the installed glibc is too new for it.

bonzini 6 hours ago | parent [-]

That's not supposed to happen. I would like to have more info.