Today, virtually all binaries used on linux systems are dynamically linked to several libraries. While it is commonly accepted statically linking applications is bad – most notably in terms of security concerns: fixing a library's bug means you won't have to recompile all applications that are using that special library, they'll simply load the version available at run-time – there are in fact good reasons to use static linking. (And for those who claim statically linked binaries occupy much disk space: yeah, sure. As if a few megs compared to a few hundred kilobytes make that much a difference today, plus you don't have the overhead of looking up and loading the libs in the first place.)
As I mentioned in my post about tmux already, there's a huge advantage to static linking: you can compile bleeding edge software with bleeding edge library functions and still use them on reasonably outdated systems (think: Debian stable).
One division of rapidly evolving software I could never successfully link statically was window managers like dwm or awesome. However, especially considering the XCB development and adoption over the past few years, to me it makes perfect sense. I'll just distribute a copy of the window manager I use to different systems and have a guarantee it'll work there, no matter the libxcb version (or if it's available at all).
Usually, however, it's not possible to just pass a -static
or
-Wl,-Bstatic
flag to the compiler (in my case, gcc). It'll fail to
find several symbols that are located in libraries that don't have to
be explicitly linked in. Such an error message might look like this:
/usr/lib/libXinerama.a(Xinerama.o): In function `find_display':
(.text+0x89): undefined reference to `XextCreateExtension'
/usr/lib/libXinerama.a(Xinerama.o): In function `XineramaQueryScreens':
(.text+0x255): undefined reference to `XMissingExtension'
To find the appropriate library, you may try to use pkg-config.
I use a different approach, however. I have a shell function defined
called findsym
(beware, Z-Shell specialties apply):
findsym () {
[[ -z $1 ]] && return 1
SYMBOL=$1
LIBDIR=${2:-/usr/lib}
for lib in $LIBDIR/*.a
do
nm $lib &> /dev/null | grep -q $SYMBOL && \
print "symbol found in $lib\n -L$LIBDIR -l${${lib:t:r}#lib}"
done
}
Thus, I can simply go looking for the missing XMissingExtension
symbol like this:
$ findsym XMissingExtension
symbol found in /usr/lib/libXext.a
-L/usr/lib -lXext
symbol found in /usr/lib/libXi.a
-L/usr/lib -lXi
symbol found in /usr/lib/libXinerama.a
-L/usr/lib -lXinerama
symbol found in /usr/lib/libXrandr.a
-L/usr/lib -lXrandr
Now, I use the readme file, some common sense or symple try'n'error to
find out which library I'd best link in, too. In this case, it's
adding a simple -lXext
to the LDFLAGS
part.
Thus, I come up with the following diff to dwm's config.mk
:
--- a/config.mk
+++ b/config.mk
@@ -16,7 +16,7 @@ XINERAMAFLAGS = -DXINERAMA
# includes and libs
INCS = -I. -I/usr/include -I${X11INC}
-LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS}
+LIBS = -L/usr/lib -L${X11LIB} -static -lX11 ${XINERAMALIBS} -lxcb -lXau -lXext -lXdmcp -lpthread -ldl
# flags
CPPFLAGS = -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
There's one important point here: libX11 will (to me, it seems,
inevitably) load another library, not sure why or which one. Thus, it
is vitally important to statically link in libdl
, the library that
dynamically loads another library. Otherwise, the follwing error
messages appear:
/usr/lib/libX11.a(CrGlCur.o): In function `open_library':
(.text+0x3b): undefined reference to `dlopen'
/usr/lib/libX11.a(CrGlCur.o): In function `fetch_symbol':
(.text+0x6b): undefined reference to `dlsym'
/usr/lib/libX11.a(CrGlCur.o): In function `fetch_symbol':
(.text+0x88): undefined reference to `dlsym'
With the above modification to config.mk
, dwm will compile and link
just fine:
$ file dwm
dwm: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
statically linked, for GNU/Linux 2.6.18, not stripped
You can reduce the binary's size by a few hundred kilobytes by
manually calling strip(1)
.
The binary works very well for me. I'll try to use it on different systems over the next few weeks and see what happens. If that works out well, I'll also try to get lucky with awesome and zathura, as these (and the libraries needed) are not installed on many systems, either.