Blog

Working with UHD static library

Introduction

We have recently been working to support building and using static libraries for all GNU Radio components, which therefore means static libraries for our different dependencies as well. Part of this is to better support Android development work in GNU Radio, but there are other use-cases where people find it useful to build static applications for easier distribution and configuration control of systems. Whatever your feelings here, we want to support the capability.

Ettus Research will soon be updating the UHD library to also support building and using static libraries, though there are a couple of caveats. First, I'll go over some of the issues with static libraries and what we've done in GNU Radio. I'll then introduce the way they have handled it in the UHD library, which leads to other issues with libraries, at least in Ubuntu due to some decisions they have made. We'll cover these issues and show how to get around them, at least for right now.

As of the writing of this post, the static library support is not part of the mainline source code for UHD, so when I say "look at the UHD manual," I mean the future manual when they merge this concept into their distribution and release it in a future version.

GNU Radio and Static Libraries

One of the biggest issue that you run into with static libraries is in dealing with global variables. When you're doing shared library linking, you don't have to think about this, and so you can easily get lazy. We generally don't have too many of these kinds of variables as they are considered bad practice, but sometimes they are exactly what's required. The problem comes when you directly reference one of these variables. In a statically-linked library, there is no guarantee that the variable will be initialized before being called. So instead, we provide access functions for these variables. Where we might have had:

static int some_variable;

We switch to:

int get_some_variable()
{
static int some_variable = init_me;
return some_variable;
}

The difference is that when you call 'get_some_variable()' the first time, it will initialize the variable for us as a static variable. All calls after that will return the same static variable we have defined here. And then we jump through some hoops to provide access to the some_variable in another way -- like with a #define that maps to the call get_some_variable().

In GNU Radio, we have been patching it to remove any direct calls to global variables using the above method. Doing so makes it possible to build and use a libgnuradio-<component>.a file to build statically-built applications. We've supported the building of static libraries in GNU Radio for a while, now, by using the -DENABLE_STATIC_LIBS=True as a CMake argument. When this flag is enabled, we build both the shared and static libraries. So, for instance, we will build a libgnuradio-runtime.so and libgnuradio-runtime.a.

However, we do not build static versions of gr-fcd or gr-uhd because of underlying problems with the hardware libraries themselves. We did not have a static library for libuhd.a that we could use in order to statically link with libgnuradio-uhd.a. So we kept those disabled during the static library build.

Static UHD Library

Working with Ettus Research, they have added their own -DENABLE_STATIC_LIBS flag when building libuhd. This is a big help to us as we can now enable static libraries for gr-uhd. However, this one comes with a bit of a caveat and the reason for this post. LibUHD still uses static globals like I discussed above. Some of these are tied pretty tightly into the code, and so the task of updating the code is a bit more difficult. At least here, though, we have another way around it, and I felt it was worth publishing the way to handle it here.  The short of it is the use of the -whole-archive flag to the linker for libuhd.

Because they are good and thoughtful programmers, the Ettus developers, Martin in this case, made sure to put this information into the UHD manual for doing static library builds. Basically, it says that when linking your application against libuhd.a, you'll need to do the following:

g++ <source code>.cc -static <cxxflags for include and linker dirs, others> -Wl,-whole-archive -luhd -Wl,-no-whole-archive -lboost_system -lboost_filesystem -lboost_serialization -lboost_thread -lboost_date_time -lboost_regex -ldl <other libs> -o <output name>

Notice the use of "-Wl,-whole-archive -luhd -Wl,-no-whole-archive'. This forces loading everything in the libuhd.a archive into the executable, but then turns this off for the following libraries. Linking in the whole archive forces all static variables in that archive to be initialized at runtime before anything else is called. This avoids the problem that I discussed above, but it creates two other issues. First, larger executables because it pulls in everything in the archive instead of just what the binary needs from it. Second, pulling in the whole archive also means that we need to link against all static libraries that libuhd links against

On Ubuntu, the second problem is difficult because we need libusb-1.0, which in turns relies on libudev. Ok fine, so we'll install libusb-dev and libudev-dev, which install both the headers and static libraries on Debian/Ubuntu systems. Oh, but wait, no it doesn't. Fairly recently, the libudev-dev install no longer installs libudev.a. You can search for this and find various questions about where libudev.a is and how to install it in Ubuntu, and you'll see them mention something about no longer supporting it because "why" and if you do you're "doing it wrong." Ok, awesome. But we still need it if we want to use libuhd.a even if we aren't using a USB-connected USRP.

Luckily, it turns out that installing udev's static library on Ubuntu isn't that bad, at least not as of 14.04. Here's how you do it.

  1. Download the version of systemd used by your OS
    1. http://www.freedesktop.org/software/systemd/
    2. Ubuntu 14.04 uses version 204, so get that tarball.
  2. Unpack the tarball and build just the udev portion:
    1. tar xf systemd-204.tar.xz
    2. cd system-204
    3. mkdir build; cd build
    4. ../configure --prefix=<somplace safe> --enable-static
      1. Make sure you set a good prefix and not /usr since we probably don't want to overwrite the any libraries installed by apt-get.
      2. There should be a way to just build libudev.a and not all the systemd stuff as well, but that way seems to be mysterious. Neither --enable-udev-only nor --disable-systemd seem to work right.
    5. make && make install
      1. Make sure that you don't run this as sudo!
      2. Make the install prefix writable by you as a user. The build system will try to do stuff in /usr/lib/systemd regardless of what prefix you set. But just running 'make install' will install the libraries into your prefix and then error out when trying to do stuff in /usr/lib. Don't worry, everything you need is installed where you need it.
  3. Use this to link your application:
    1. g++ gr_uhd_test.cc -I<path-to-gr>/include -fpic -static -L<path-to-gr>/lib -Wl,-whole-archive -luhd -Wl,-no-whole-archive -lgnuradio-uhd -lgnuradio-blocks -lgnuradio-runtime -lgnuradio-pmt -lvolk  -lboost_system -lboost_filesystem -lboost_serialization -lboost_thread -lboost_date_time -lboost_regex  -lorc-0.4 -lpthread -lusb-1.0 -ludev -ldl -llog4cpp -lrt -o gr_uhd_test

This builds a C++ example that I called gr_uhd_test that creates a GNU Radio application, so I have to link against all of the appropriate GNU Radio libraries as well as libuhd.a using the whole archive flags. I also needed to include all of these Boost libraries, liborc, libusb-1.0 and libudev as discussed, the logging library liblog4cpp, and some system libraries for pthread, dynamic linking, and realtime support.

Wrap-up

I wanted to have all of this information chronicled somewhere, partly so that I don't forget it later in life. Hopefully, this will help understand some of these issues and help us build static applications, at least under Ubuntu with the libudev.a problem. On a side note, it sounds like CentOS does not have this problem.

One of the other nice things that Martin did when adding static library support to libuhd was also provide CMake settings that we can use. Again, take a look at the UHD manual for building static library support and it will show you how to set up your CMake project to properly link in the static libuhd. Part of this is that they set the -whole-archive and -no-whole-archive for us, so it does the right thing without us having to know what and why. I wanted to write this post so that we both know why and have some idea of what to do if we run into problems with requiring these other libraries, like libusb and libudev.