Hacker News new | ask | show | jobs
by sylware 1164 days ago
ELF is way too complex and not really adapted anymore.

We should start to deprecate DT_NEEDED and make dlopen/dlsym/dlclose (maybe, dlvsym) hard symbols in the loader.

And game devs should stop using main() as some genius glibc dev did add a new libc_start_main version in 2.34. Namely, any game executable linked with a glibc from 2.34 will refuse to load on system with a previous glibc.

Actually, game binaries should be pure ELF64 binaries (not using main()) which "libdl" (dlopen/dlsym/dlclose) everything they need from the system. And of course, as much as possible should be statically linked (I think this is what unity is doing, but unreal/godot have a big issue: the static libstdc++ which, as of late, does not libdl anything from the system).

2 comments

Use rpath and ship your dependencies. Even if you dlopen every shared library explicitly you have the same problem.

The glibc version problem is not new, you have never been able to rely (safely) that a version of glibc exists that works for your compiled program. This is an example of why containers exist, but unfortunately the problem isn't ELF - it's glibc!

And for that matter, glibc also ships the loader. This is the part that's mildly insane.

Yep, the issue is mostly the glibc but ELF does not help. Additionaly, the static libstdc++ is also an issue as it seems not "libdl-ing" any of its system dependencies (did not check if c++ gcc devs fixed that already).

That does not mean ELF is not overkill nowdays. In the case of dynamic linking, deprecating DT_NEEDED to rely on hardcoded (with probably specific relocations) and simplified dlopen/dlsym/dlclose in the ELF interpreter (that would deprecate tls_get_addr() as it would become redondant with dlsym) seems to be a sane cleanup: explicitely split dynamic linking from static linking.

Nowadays mitigation:game devs should go pure ELF64 (no main()) for their binaries and fully libdl-ized. The hard part is to fork a gcc static libstdc++ to libdl-ize its system dependencies (dunno if it was done).

I think what you're advocating for is getting rid of automatic dynamic loading, which is certainly a take.

> go pure ELF64 (no main())

What does "pure" ELF64 even mean? No dependency on libc?

ELF is just an object file format. It doesn't imply anything about how the loader behaves or what features an ELF loader must have, or how that object relates to other ELF objects.

Yep, services from the libc would be libdl-ed too. You would have to decide: either you use the machine code in a shared object, or you would statically link that machine code, but the idea is to make those explicit and different, not that current mess.

It means using the sysv x86_64 ABI entry point (which is basically a main()...).

The file format alone is useless, you need the ELF and ABI specs to know how to use properly the information defined by this very file format.

There is a huge amount of tooling relying on DT_NEEDED for dependency detection. I am not so sure about general purpose Linux, but in the embedded Linux world this would be a disaster. The Yocto system for example would no longer be able to determine the runtime dependencies of generated binaries.

For the static library part, this is such a beaten down argument I just will not argue. I hope you enjoy re-installing your OS every time there is an security update on a library like openssl.

You missed the point: using "shared objects" would have to be explicit with "dlopen/dlsym/dlclose'.

Mixing static linking with dynamic linking was not a good idea in the first place, and I mean it.

ELF should be "fixed" about this, but to be sincere and honest, I think a lot could be removed from ELF on modern systems.

Maybe it is not worth to fix ELF, but to go something like NGELF which would be excrutiatingly simpler and cleaner than ELF, namely real and disruptive innovation.

> You missed the point: using "shared objects" would have to be explicit with "dlopen/dlsym/dlclose'.

dlopen and friends are function calls that you cannot evaluate build time. Actually not even at runtime as they are by nature dynamic and conditionally dlopen is a thing. Any shared object dependency tracking would be impossible or a new standard would be required.

Also dlopen is a POSIX standard. ELFs are used in many other places non POSIX.

> Mixing static linking with dynamic linking was not a good idea in the first place, and I mean it.

Why was it not a good idea? This happens all the time, especially the code that is at the very first executable address of the elf until some libc prepares things is arguably statically linked.

> [...] but to go something like NGELF [...]

Sounds interesting. Could you paste a link? I could not find it in google.