::::: : the wood : davidrobins.net

Shared libraries and symbol versions

News, Technical ·Saturday June 18, 2011 @ 15:20 EDT (link)

I was doing some more work on XBMC recently; I set it up to build in two parts: as a shared library, libxbmc.so, and a small binary, xbmc.bin (same as now, but built only from xbmc/xbmc.cpp). (This is similar to how Word is structured, with winword.exe and wwlib.dll, except Word loads wwlib.dll dynamically at runtime.) The plan is to be able to use XBMC functionality from other programs and utilities, without having to boot the GUI (so the various interfaces such as HTTP are unsatisfactory here).

Originally I was going to break XBMC into several shared libraries, but it's so intertwingled—GUI calls are everywhere, for example—that that swiftly became infeasible, although I hope further splits are possible later. I learned a few things while I was separating out this new library and other tasks; I learned how to use git better, and GitHub for the first time; when incorporating some other patches into a pull request, I taught myself a little about autoconf. When making Makefile changes, I had to change Makefile.in, or create one if one didn't exist (as for my new test harness). I also noticed that GCC generates two copies of constructors and three of destructors, which I thought was some problem with my build until I saw it was a rather old bug nobody cared to fix yet.

The first problem I ran into was that the link complained about missing symbols from the FreeType library. It turned out that mysql_config --libs emitted a -Wl,--as-needed as part of its library specification, but never terminated it with -Wl,--no-as-needed to restore the default behavior: so that was fixed in configure.in, using awk to check for the condition (since bash's string matching isn't guaranteed to be available but awk was already being used). This poor practice had always been there but only became a problem with the shared library refactor.

After that everything seemed to work well but there was a strange error: no sound; failed to enable audio device popup, etc.—just for video sound; the sound effects in the menus worked. I tracked this down (with a debug reinstall of ALSA) to an innocuous-looking call to snd_pcm_hw_params_set_rate_near from CALSADirectSound::Initialize. Oddly enough, it was coming back with -EINVAL, invalid argument. Apparently the ALSA 0.9 version of this function took an intenger value, and then was later upgraded to take a pointer to an integer (so the actual set value could be passed back). And, incomprehensibly, we were calling the old compatibility version, slicing all over the place and passing in a large number (32 bits of the address that was passed in) which was naturally rejected. I don't know why the loader defaulted to use that version of the function, since the default symbol, marked with @@, was the newer one.

You can believe I played around a lot with readelf, objdump, nm, etc.; and everything looked fine. I even enabled loader debugging (LD_DEBUG=all). One of my searches, however, found an online version of the ldd manpage that mentioned that --verbose would show library symbol versions—the ones following @ or @@ in symbol names, not the version of the library itself. And it showed that libxbmc.so had very few library dependencies (the loader, pthreads, C library) while xbmc.bin had the usual suspects (a stack of video, audio, decoder, display, and other libraries). This is how I though it was meant to work: and apparently it almost does but not quite. Documentation on the runtime linker is scarce, but presumably since libxbmc.so didn't specify its dependencies explicitly, it picked the oldest (lexicographically?) version for versioned symbols. Fix: move the $(LIBS) specification from the Makefile line for xbmc.bin to libxmbc.so. And now audio works.

Books finished: Naked Empire, The Checklist Manifesto: How To Get Things Right.