Categories

Thu, 08 May 2014 - Static library linking behavior

A static library (an .a file) is, at its heart, nothing special—it's
just a bunch of object files (.o files) in an archive, with an optional
index. Thus, I've always treated them mentally the same to a bunch of object
files, but it turns out there's an edge case where there is a difference between linking to
an .a file and a bunch of .o files (even if you discount the fact that
the .a file is a linker group and the .o files would need -( and -) around it
to get the same effect).

The difference manifests itself when you are building a shared library
(a .so file).
For an .o file, the linker does what you'd expect; any symbol that has
global linkage (ie., for C or C++: it's not marked as static or in an
anonymous namespace) and has default visibility (which, unfortunately,
means almost all symbols unless you're clever enough to use
-fvisibility=hidden and explicitly mark your exports
with __attribute__((visibility("default"))) or similar),
will be exported in your shared library to external clients. This means,
among others, that it will take up space because the name and a few other
things need to be stored.

However, for an .a file, things are different! If you have a symbol in an .a file, and it's not used by anything
from an .o file (directly or indirectly), the linker will silently just
discard it as unused. It doesn't matter what visibility it has; it will
just not be there. (To be honest, I haven't checked if this is on the level
of a symbol, a section, an .o file within the library, or the entire .a file.)

The workaround for this is to add --whole-archive before the
.a file in question (and presumably --no-whole-archive after it
to negate the effect for the following ones), which will negate this
behavior. However, I am unsure if it also has other effects, like messing
with visibility, so beware.

To be honest, I think this is insane behavior, but given that so few people
set visibility explicitly, I guess there would never be any pruning if
the behavior were different (and ELF symbol visibility is a relatively new
invention anyway), so I can understand why it is like that.

Of course, I figured this out the hard way. Poof, there goes a few hours of
my life.