Static Linking - where did it go?

Rod Evans — Monday December 06, 2004

Surfing with the Linker-Aliens

With Solaris 10 you can no longer build a static executable. It's not that ld(1) doesn't allow static linking, or using archives, it's just that libc.a, the archive version of libc.so.1, is no longer provided. This library provides the interfaces between user land and the kernel, and without this library it is rather hard to create any form of application.

We've been warning users against static linking for some time now, and linking against libc.a has been especially problematic. Every solaris release, or update (even some patches) has resulted in some application that was built against libc.a, failing. The problem is that libc is supposed to isolate an application from the user/kernel boundary, a boundary which can undergo changes from release to release.

If an application is built against libc.a, then any kernel interface it references is extracted from the archive and becomes a part of the application. Thus, this application can only run on a kernel that is in-sync with the kernel interfaces used. Should these interfaces change, the application is treading on shaky ground.

In addition, implementation details of libc, such as localization and the name service switch, require dynamic linking (they dlopen() other objects). As we antispated libc.a to be used in a static environment, any dynamic capabilities were #ifdef'ed out of the code. libc.a has always been a subset of libc.so.1.

One common failing we have discovered is that many folks built against libc.a but otherwise used dynamic objects to supply other interfaces. These applications, termed partially static, are particularly fragile. The dynamic objects these partially static applications invoke, commonly depend on interfaces contained in libc.so.1. However, at runtime these dynamic objects are bound to the portions of libc that have been statically linked with the executable, leaving any other references to be bound to the dynamic libc.so.1. This combination of inconsistent interfaces can lead to chaos and ruin.

Because of this potential for distroying any application binary interface guarantee, the 64-bit version of Solaris never delivered any 64-bit system archives libraries. We figured we nip that problem in the bud right away.

But why did we wait until Solaris 10 to stop delivering 32-bit system archives? It turns out the merge of libc and libthread put the last nail in the coffin.

In earlier releases of Solaris, threaded applications needed to build against libthread. This library offered the true thread interfaces that allow you to create and manage threads. However, to allow library developers to create libraries that were thread aware (ie. that could be used in a threading environment and a non-threading environment), libc also provided all the thread interfaces. These interfaces were basically no-op stubs. The gyrations we had to go through to make these two libraries cooperate was a story in itself.

Now, if you built an application against libc.a and happened to reference the thread interfaces, you ended up with the no-op stubs becoming part of the application. This turned out to be a major problem when the application moved to another release and became bound to new dynamic objects.

In Solaris 10, libthread and libc have been merged. Effectively all applications are thread capable, but until any new threads are created, the application remains single threaded. This model simplifies application and especially library development because there are no longer three process models to contend with (threaded, non-threaded, and statically linked). There is now only one model, one that is thread-capable. Libraries can be assured they always operate in a thread-capable application environment. These libraries can take advantage of threading interfaces and features like thread-local storage that cannot be provided in a non-thread-capable environment.

The merger of libthread and libc makes any partially static application doomed to failure, even if a stripped-down, crippled archive version of libc.a were made available.

Therefore, to put to rest the consistent failure of partially static applications from release to release, we've now made it impossible to make such applications.

Some folks thought of static applications as being a means of insulating themselves from system changes. But as I explained above, they were not insulating themselves from user/kernel interface changes.

Note that there is some flexibility even in a dynamic linking environment. Although applications are built to encode the interpretor /usr/lib/ld.so.1, applications can be built to specify an alternative interpretor:

    % cp  /usr/lib/ld.so.1  /local/safe/lib
    % cp  /usr/lib/libc.so.1  /local/safe/lib
    ...
    % LD_OPTIONS=-I/local/safe/lib/ld.so.1 \\
        cc -o main main.c -R/local/safe/lib ...

Or, you can invoke an alternative interpretor directly:

    % /local/safe/lib/ld.so.1  main ...

But, even if you create an environment with an alternate runtime linker and dynamic libc, it must be in-sync with the kernel on which it will execute. Otherwise, this technique is no more robust than using an archive version of libc.a.

Surfing with the Linker-Aliens

Comments

Nick Towers — Tuesday December 07, 2004
Thanks for interesting comments on static linking. Note for a lot of people the reason static linking was used was not to try to avoid versioning issues - from my side there were two other reasons. Firstly, from a security side I can bring in a static binary in which I have more trust if I have any reason to believe a machine may have been compromised. Secondly in case of disk corruption or hardware errors a static binary has less dependencies within the system. The approach of bringing along another interpreter and libc with the binary is certainly a possibility though. BTW note I'm well aware about the libc interface changes in general, from my side it is perfectly reasonable to rebuild "static" apps for a version change.
Rod Evans — Wednesday December 08, 2004

Sadly, a number of vocal customers have been rather upset when their static binary has failed.

Surfing with the Linker-Aliens

Published Elsewhere

https://blogs.sun.com/rie/entry/static_linking_where_did_it/
https://blogs.oracle.com/rie/entry/static_linking_where_did_it/
https://blogs.oracle.com/rie/static-linking-where-did-it-go/

Surfing with the Linker-Aliens

[10] Shared Object Filters
Blog Index (rie)
[12] Interface Creation (compilers)