Hacker News new | ask | show | jobs
by marssaxman 3981 days ago
The reason this isn't usually done is that executable size was significant relative to storage capacity up until the early 2000s or so, and people tried to economize by deduping common parts of their executables via shared libraries / DLLs. This worked well enough to catch on, but came with an extremely high cost in added complexity, and over the years a whole layer of additional infrastructure was created in order to manage it. The industry progressed, storage capacity grew dramatically, and executable sizes stopped mattering, but the use of shared libraries / DLLs continued out of inertia. As time passed, people started asking - why are we doing all this? And some of them invented a reason, which was the idea that one could swap out pieces of existing executables after installation, and thereby fix security problems in an application without needing to involve the application's developer in the process. This works about as well as you'd expect if you had spent years trying to fit all the rough edges of various third-party libraries together with varying degrees of success, but the idea caught on as a popular post-hoc justification for the huge layer of complexity we're all continuing to maintain long after its original justification became obsolete.

As is no doubt obvious from my tone, I'm not buying it and am very happy to see signs of a pendulum-swing back toward static linking and monolithic executables.

3 comments

It's hard to be sure why exactly shared libraries and dynamic linking appeared. Your explanation about reducing file size for smaller HDD and RAM footprint is probably one of the reasons, but I don't believe it's the only one - I don't remember many shared libraries from MSDOS days (where with 2MB of RAM and 40MB of HDD, storage was really scarce). In fact - I don't remember any libraries! To run Doom you just borrowed the floppies from a friend and it would 100% work. The same for Warcraft 1.

I believe it's more similar to Database Normalization from RDBMS world than to anything else. And the most important objective of Database Normalization is considered "Freeing the database of modification anomalies".

My own experience with shared libraries is pretty positive. I have fixed OpenSSL vulnerabilities many times by just updating the OpenSSL library and restarting all services. Compared to my own experience with docker where after waiting for a few weeks I had to change my base images (as nobody was updating them) updating just a single shared library and having the vulnerability fixed is way easier!

This, of course, is true if those who maintain the software you use do care about backwards compatibility (which tends to be true for the "boring" stuff and false for the stuff considered "cool" - looking at you nodejs library developers who break my build at least once a month).

There are other important downsides to static linking. Namely, critical security updates to shared components. It's better to have to update your tls library once per system, than to update every app that came with it. And that's the best case scenario where the developer notices and the app is actually repackaged.
Yes, that's the argument I was referring to, and as I said in the comment you're replying to, I don't believe that the cost is worth the benefit.
Well, you wouldn't want every executable to copy some unknown version of OpenSSL, and you'd get into all kinds of problems if you had several different versions of glibc around.

But for most libraries it may really be overkill.

I see OpenSSL and libc as being effectively the "system version" as you would know it in Mac OS or Windows. It's OK to link dynamically against the operating system platform, but generally a program knows what version of the OS it is built for and expecting to be compatible with. Users know that upgrading the OS is generally worth doing but may break things.

What we lack in the unix world is a coherent division between what ought to be a very small, stable, well-understood set of fundamental system libraries suitable for dynamic linking and the vast array of utilities a developer might choose in order to get an app built without having to reinvent everything from scratch.

Upgrading libraries out from under a built, tested executable is not something we should be doing lightly, because there is no possible way to know in advance whether the apps depending on the library have succeeded in programming to the interface rather than the implementation.