I’ve been bitten for the n-th time now by a library mismatch between a build and deployment environment. The build environment had libruby.so.2.0
and the deployment environment had libruby.a
. One ruby was built with RVM, the other was built with ruby-build
. The reason I ran into a problem was because zookeeper was compiled in a build environment that had the shared library but the deployment environment only had the static library.
In all the years I’ve been writing application code I have never once wished that the binaries I was using where linked against shared objects. What is the reason the dichotomy persists to this day on modern operating systems?
1
Static
With a statically linked application, everything that you need to run the application is part of the application itself. It depends on nothing else.
If there is a fatal error in a dynamically linked library, it doesn’t ‘care’.
If the dynamically linked version of a library it uses isn’t there (someone accidentally removed /lib
on a unix system?) it doesn’t care.
It requires no additional installs of libraries to run on any binary compatible platform. You download the app and you can run it.
There is an entire directory of statically linked applications on every unix system in /sbin
that are guaranteed to always work even if significant parts of the system are otherwise broken or missing. reboot
? halt
? ifconfig
? mount
? fsck
? ping
? Those are likely all in /sbin
so that even if the libraries are missing or corrupt (or not mounted because they’re on a network filesystem…) you can still run those commands to get the system working (and mount that networked file system with the libraries you are looking for).
True story: One sysadmin many years ago, back when an 80 megabyte hard disk was big was looking for some space. Standard compiles included debugging/symbol information and that took a few kilobytes in each binary. So, he found every executable on the system and ran strip on it. Did you know that shared libraries are ‘executables’ on many systems? Removing the symbol information from them prevents anything from dynamically linking to them… oops. The only things working where those in /sbin. Fortunately, there was enough there that he was able to mount the other system of the same type and copy the shared libraries back over to the messed up system…
Dynamic
You want to update a library? No problem. Just put the library in the proper spot and all is well. Yea, that’s a bit simplistic, but its the idea.
$ ldd /bin/date linux-vdso.so.1 => (0x00007fff6ffff000) librt.so.1 => /lib64/librt.so.1 (0x00007f54ba710000) libc.so.6 => /lib64/libc.so.6 (0x00007f54ba384000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f54ba167000) /lib64/ld-linux-x86-64.so.2 (0x00007f54ba919000)
Those are all libraries that are already loaded somewhere that you don’t have to have in your application. There can be some savings in disk space size because you don’t need to put a full copy of libc or any other library as part of your application.
System patching becomes easier. Instead of needing to push out a patch for everything in /bin because something changed (you need to do this for /sbin), you just push out a new shared library object and update the symbolic link chain in /lib.
Drop in replacements become possible. Just provide the same API and symbols, drop in the new library and update the symbolic link chain and all should be good.
On a system that runs multiple processes (almost all of them these days… but it was a significant distinction back in old times) static libraries meant that each application running had a full copy of its libraries taking up memory resources. Shared libraries means that they can all just point to the same spot in already loaded memory.
Statically linking an application can be considered a derivative work of the libraries. This becomes an issue with GPL software. There are differing opinions on if dynamically linking a GPL licensed library creates a derivative work.
Dynamic linking allows for plugins. The application loads the plugins and dynamically calls the functions available to it.
Multiple languages can use the same calling conventions and call a dynamic library. Look at Java for an example there – dynamic linking means that Clojure, Scala, Groovy, etc… with dynamic linking they can all use the same shared libraries. Alternatively look at all the different languages that can invoke a .DLL on windows.
Related reading:
- SO Static linking vs dynamic linking
- Dynamic linking considered harmful
- MSDN Advantages of Dynamic Linking
- P.SE How does the GPL static vs. dynamic linking rule apply to interpreted languages? SO Can I dynamically call a LGPL/GPL software in my closed-source application?
1
To me, the answer is very simple: to save resources like space. If all N applications on a system need library X, and all of them statically link this library into their executables, then there is (N – 1) (size of X) bytes you are wasting.
The reason why statically linking has survived at all is precisely because there is no system that can guarantee that all applications find installed the libraries they need, in the version that they need them, at the same time (see dependency hell).
Many solutions have been tried: keeping versioned libraries, smart package managers, even virtual machines are a way of putting a layer of indirection between your application and the system “as it is now”. Each of these has led to its own hell: DLL hell, RPM hell and JAR hell.
The only bullet-proof way of deploying an executable to another machine is that it contains everything it needs to run. That is, by wasting resources.
2
There are advantages to each, as is the case for pretty much everything in software development.
Say I am developing a Windows application, that involves some sort of cryptographic feature. Because those functions are complicated, and rolling your own is almost always a bad idea, I instead use the libraries provided by the Windows platform.
Pretend, after awhile, a nasty bug is revealed in the Windows libraries, that causes your pets to be murdered if you visit the wrong web page.
If you dynamically linked your application to the libraries, an update can be made to the platform, and your software can be fixed of its vulnerability without you doing anything. Windows update can solve your problems.
If you statically linked your application to the libraries, you would have to recompile an updated version of your software, and distribute it to your users, and feel the guilt of all the puppies murdered while you got around to doing so.
The same principle applies for other platforms and libraries.
Additionally, dynamic linking affords the ability to have stuff like plugins. You can also update shared libraries while an application is using them, and use the new libraries once the application is restarted.
Those benefits are why we have dynamic linking.
3