I'll look into merging with the wiki later when I have a bit more time, but for now below is a copy-paste of my notes on getting libsupc++ to work. Hope you guys find it useful. Happy to answer any questions.
libsupc++
Unfortunately, the gcc build system (at least up to v4.9.2) seems to be incapable of configuring a cross-compiler build for libsupc++, so we have to handle that ourselves. The only prerequisite is to have a few standard library pieces implemented already: some functions from stdlib.h (e.g., malloc, free, abort), and some functions from string.h (e.g., strcmp).
The actual steps to build are straightforward:
- Copy the source for libsupc++ to wherever we'll be building.
- Copy the file "unwind-pe.h" from the libgcc source directory to our libsupc++ source directory.
- Write a makefile to compile all the source files and link the resulting object files into a static library.
- Try to build, see what the errors are, resolve them, and try again.
Most of the compile errors will be caused by the compiler looking for bits/... and ext/... headers. The easiest way to resolve these is to search the gcc source tree for the required header, and copy it to libsupc++/bits/... or libsupc++/ext/... as needed. Note that some of the bits/... headers can in fact be found in the libsupc++ source directory itself (e.g., exception_defines.h), so for those we can just change the #include <bits/...> directive to remove the "bits/" prefix.
We'll likely also need to edit the bits/c++config.h header to set _GLIBCXX_HOSTED and _GLIBCXX_HAVE_TLS to 0, since we're building for a freestanding environment and there's no thread-local storage support unless we implement it.
Note that this can be a bit restrictive. For thread safety, exception support relies on thread-local variables (e.g., each thread is supposed to have an object of type
__cxa_eh_globals to hold thrown exceptions). If there are no thread-local variables, then the library falls back on a global static buffer. This is fine so long as we are dealing with just one thread, but if there are multiple threads and any of them can throw exceptions, then things might get messed up. If we set _GLIBCXX_HAVE_TLS to 1, then the library will still compile. However, in that case we will have to set up the thread-local storage correctly at runtime (which has a standard, architecture-dependent handling for ELF binaries).
And the fun doesn't stop there. In case we do add TLS support, any thread-local variable with a non-trivial destructor will need that destructor called when the thread exits. The compiler handles this by registering thread-local destructors with
__cxa_thread_atexit. In our freestanding world, the library implementation falls back on a single-threaded implementation, which can only lead to wonderful things once there are multiple threads. A possible way to deal with this might be to define _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL as 1 in bits/c++config.h, and provide our own implementation of
__cxa_thread_atexit_impl (see atexit_thread.cc for reference). Or we can just avoid having non-trivial destructors for thread-locals.
There is an additional potential concern around exceptions. The ABI functions to allocate/deallocate memory when an exception is thrown (
__cxa_allocate_exception and
__cxa_deallocate_exception) use malloc/free by default. But if malloc fails, the allocation falls back on an emergency static global buffer, and access to this buffer is protected via a mutex. In our freestanding setup, the mutex implementation is trivial, so the buffer access is unprotected. So, in the event that malloc fails and multiple threads are throwing exceptions, there is a race condition. But, of course, malloc would never fail...
There is also another thing to be aware of. Local static variables are initialized the first time the code is executed, and the C++ standard requires this to be thread-safe. The ABI handles this by putting guards around the initialization (
__cxa_guard_acquire and
__cxa_guard_release) to ensure that it happens only once. Since our freestanding build does not have thread support, it falls back on a single threaded implementation. So, once again, there's a potential issue there, but it's relatively straightforward to avoid (either don't use local statics or ensure that the initialization runs before multiple threads are active).