C++11 and ACAP

As a C++11 developer, you may find yourself stymied by the C++ compiler provided in the official ACAP SDK toolchain.  Full C++11 support was not available in GCC until version 4.8; the ACAP SDK provides GCC version 4.3.  This post provides a high-level overview of solving what is an enduring issue for most embedded Linux development environments: building your own bleeding-edge toolchain.

For the remainder of this post I will assume that you have a functional Linux development environment with the ACAP SDK already installed, and an Axis ARTPEC-4 or ARTPEC-5 camera (the following instructions can be directly adapted for Ambarella-based cameras, as well).


In order to provide C++11 support, we must compile several new system libraries (notably libstdc++) for the camera.  We use Buildroot, a tool for generating embedded Linux systems through cross-compilation:

git clone git://git.buildroot.net/buildroot
cd buildroot

Buildroot provides a GTK-based configuration tool that compiles on your machine.  To use this, you will need to install development versions of libglade2, libglib2.0, and libgtk2.0.  Make and run the configuration tool:

make gconfig

For ARTPEC processors, select the following configuration options:

  • Target options → Target Architecture ⇒ “MIPS (little endian)”
  • Target options → Target Architecture Variant ⇒ “mips32r2”
  • Target options → Use soft-float ⇒ enabled
  • Toolchain → custom toolchain vendor name ⇒ a string of your choice, such as “acap”
  • Toolchain → Kernel Headers ⇒ “Manually specified Linux version”
  • Toolchain → linux version ⇒ “2.6.35”
  • Toolchain → C library ⇒ “glibc”
  • Toolchain → GCC compiler Version ⇒ “gcc 5.x”.
  • Toolchain → Enable C++ support ⇒ enabled
  • Toolchain → Build cross gdb for the host ⇒ enabled

Save and exit the configuration utility, then build your toolchain and target libraries:

nice make


When this is complete, GCC 5.x for mips32r2 will be available in your Buildroot output directory.  Add this to your path:

export PATH="$AXIS_BUILDROOT/output/host/usr/bin:$PATH"

We are now ready to compile some C++11!  Save the following text as test_program.cpp:

#include <cstdlib>
#include <iostream>
#include <thread>

int main(int argc, char *argv[])
    auto message = { "Hello", " ", "world", "!" };
    std::thread t([&]()
        for(const auto& word : message)
            std::cout << word;
        std::cout << std::endl;

    return EXIT_SUCCESS;

Compile test_program.cpp with your new compiler:

mipsel-acap-linux-gnu-g++ \
    -std=c++11 \
    -pthread \
    test_program.cpp -o test_program

Now upload test_program to your camera (I prefer rsync, but FTP works as well).  For the remainder of this discussion, I will assume that you have an SD card mounted in the camera’s filesystem at /var/spool/storage and that you upload test_program to this location.  Now telnet or SSH into your camera and run test_program.  The results are not what we hoped for:

 ./test_program: /lib/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by ./test_program)
 ./test_program: /lib/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./test_program)

Let’s check what libraries test_program is linked against:

  ldd ./test_program

    libstdc++.so.6 => /lib/libstdc++.so.6 (0x2aaaa000)
    libm.so.6 => /lib/libm.so.6 (0x2abb6000)
    libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x2ac96000)
    libpthread.so.0 => /lib/libpthread.so.0 (0x2acd0000)
    libc.so.6 => /lib/libc.so.6 (0x2acfa000)
    /lib/ld.so.1 (0x55550000)

Notice that even though we compiled our new program with GCC 5.x, the dynamic loader on the camera will link our new C++11 executable to the old libraries provided by the camera. We want our executable to use the libraries we just compiled with Buildroot, so copy the entire $AXIS_BUILDROOT/output/target/staging directory tree to the camera at /var/spool/storage/mips32r2_buildroot.

Compile Again

Now let’s recompile our test program, but specify the rpath to tell the linker to preferentially use the libraries that we just uploaded rather than the camera’s system libraries:

mipsel-acap-linux-gnu-g++ \
   -std=c++11 \
   -pthread \
   -Wl,-rpath,$CAMERA_BUILDROOT/lib:$CAMERA_BUILDROOT/usr/lib \
   test_program.cpp -o test_program

Upload your new executable to the camera and run it to get another error:


    ./test_program: relocation error: /var/spool/storage/mips32r2_buildroot/lib/libc.so.6: symbol _dl_find_dso_for_object, version GLIBC_PRIVATE not defined in file ld.so.1 with link time reference

Unfortunately, the dynamic loader (an executable responsible for satisfying our program’s dynamic library dependencies at runtime) on the camera is too old for our new libraries. All executables that use shared libraries are hard-coded to use the dynamic loader at /lib/ld.so.1, but we can manually call a different dynamic loader. From the command line we’ll use the dynamic loader compiled by Buildroot:

/var/spool/storage/mips32r2_buildroot/lib/ld.so.1 ./test_program
    Hello world!

It works!

Compile Yet Again

As a final step, we recompile our test program one last time to manually specify the location of our Buildroot-provided dynamic loader:

mipsel-axis_acap-linux-gnu-g++ \
 -std=c++11 \
 -pthread \
 -Wl,dynamic-linker=$CAMERA_BUILDROOT/lib/ld.so.1 \
 test_program.cpp -o test_program

With our Buildroot tree installed on the camera, we can now execute our C++11 program like any other executable!


    Hello world!

Jamming all of the foregoing into an ACAP installation package is left as an exercise for the reader. 🙂


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s