The New C++11 Stack
FreeBSD 10 ships with a new C++ stack, replacing the old GNU one.
Rationale
Why do we need a new C++ stack? There are two main reasons: functionality and licensing. FreeBSD 9.0 includes the last GPLv2 (+ linking exemption) release of GNU libstdc++. GPLv2 isn't ideal for FreeBSD and GPLv3 (the license of the newer versions) is considered unacceptable, so a replacement is desirable.
In 2011, a new version of the C++ standard was introduced. C++11 is not supported at all by the 2007 version of libstdc++ that we currently ship, yet is going to be important moving forward.
Overview
A C++ implementation requires several components. The major ones are:
- The compiler.
- The Standard Template Library (STL) implementation.
- The ABI library.
The STL implementation sits on top of the ABI library, which provides things like guards for initialising static variables, exceptions, and so on. The new stack uses the following components:
These components have all been imported into the base system in -CURRENT. They were merged into 9-STABLE prior to 9.1 and are in 9.1 and later, but libc++ is not built by default in 9.x. They are all built by default in 10.0, replacing their GNU equivalents, on any platforms where clang is the default compiler. Our old GCC is not capable of building libc++ (which uses C++11 internally), so this stack is not built by default on other architectures.
Building the New Stack on 9.x
Because libc++ uses a number of C++11 features, it needs to be built with a compiler that supports C++11. In 9.x, the base system defaults to building with gcc. You need to follow the instructions for building with clang to switch to a more modern compiler. You also need to add this line to /etc/make.conf:
WITH_LIBCPLUSPLUS=yes
Using the New Stack on 9.x
To use the new stack, you will need to use clang++ as your C++ compiler and linker-driver. Add -stdlib=libc++ to your compile and link flags and, in theory, it will Just Workâ˘. In practice, please report any problems that you encounter to theraven@.
Mixing Libraries using Libc++ and Libstdc++
Although both libc++ and libstdc++ provide a largely overlapping set of source-language symbols, they are mangled differently for the linker. This means that (as long as both are using libcxxrt) you can link against libraries that use either. The only proviso is that STL symbols should not cross the language barrier.
For example, here is a trivial shared library:
$ cat test.cc #include <iostream> void print1(std::string &str) { std::cout << str; } void print1(const char *str) { std::cout << str; }
And here is a program that uses it:
$ cat testmain.cc #include <string> int print1(std::string&); int print1(const char*); int main() { std::string msg = "Hello world\n"; #ifdef WORK print1(msg.c_str()); #else print1(msg); #endif }
To show how the interoperability works, we'll first compile the library against the old C++ stack:
$ g++ -fPIC -shared test.cc -o libtest.so
Now, if we compile the test program with the old stack:
$ g++ testmain.cc -L. -ltest $ setenv LD_LIBRARY_PATH . $ ./a.out Hello World
As expected, that works fine. The same will happen if you use clang++ instead of g++. Now, let's try using the new C++ stack:
$ clang++ -stdlib=libc++ testmain.cc -L. -ltest /tmp/testmain-9COPu7.o: In function `main': testmain.cc:(.text+0x83): undefined reference to `print1(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)' clang++: error: linker command failed with exit code 1 (use -v to see invocation)
Note that this is referring to the std::__1 namespace, not just std::. This is the inline namespace that libc++ uses for symbol versioning. The version compiled against libstdc++ does not use this namespace, so the symbols are different. This includes the mangling of functions that use these types, so the print1() function will have different mangling - and therefore be a different symbol - depending on whether you use libc++ or libstdc++ headers.
The simplest fix is to just use the version of the function that takes a C string:
$ clang++ -stdlib=libc++ -DWORK testmain.cc -L. -ltest $ ldd ./a.out ./a.out: libtest.so => ./libtest.so (0x80081a000) libc++.so.1 => /usr/lib/libc++.so.1 (0x800a1b000) libm.so.5 => /lib/libm.so.5 (0x800cc9000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x800ee9000) libc.so.7 => /lib/libc.so.7 (0x8010f6000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x801455000) libcxxrt.so.1 => /lib/libcxxrt.so.1 (0x801765000) $ ./a.out Hello world
This now links against both libc++ and libstdc++, but there are no conflicts. There are a few caveats to make this work:
- You must have the version of libstdc++ that links against libcxxrt, not libsupc++. By default, libstdc++ in trunk and 9-STABLE dynamically link libsupc++: you must use libmap.conf to fix this.
- Statically linking both libc++ and libstdc++ or statically linking one and dynamically linking the other is unsupported.
- Throwing an STL type as an exception in one library and catching it in another is unlikely to work if the two libraries are not compiled with the same STL implementation (although throwing other exceptions is fine).
Still To Do
This new stack contains all of the parts of the system that are specific to C++. The ABI library implements the C++ parts of the unwinder, but not the generic parts. These are currently provided by libgcc_s. This can be replaced by libunwind, but that is not done. It probably requires some ARM-specific functions to be implemented on top of libunwind too.
Currently, libstdc++ statically links against libsupc++, which implements the same specification as libcxxrt. This means that code that links the two is going to end up with two definitions of these symbols and stuff is likely to break. Unfortunately, the symbols in libsupc++ are not versioned. To be able to mix code between the two, we need to make libstdc++ link against libcxxrt instead of libsupc++. In theory, this is trivial, but there may be unforeseen complications.
Finally, the libstdc++ shipped with FreeBSD 9 and earlier, modified to use libcxxrt, needs moving into a port so that it can be installed for ABI compatibility.