libc++/libc++abi on Linux

LibreOffice bug report “Build failure with clang++/libc++ on Linux x86_64” and reports about a similar failure when trying to build on NetBSD made me look at building LibreOffice on Linux with Clang against libc++/libc++abi (instead of the usual libstdc++) myself.

The libc++ web site warns: “Unfortunately you can’t simply run clang with ‘-stdlib=libc++’ at this point [when you have built Clang to be able to use libc++ in combination with libc++abi], as clang is set up to link for libc++ linked to libsupc++.” But that appears to no longer be true (at least on current LLVM trunk). All it takes is to include the libcxx and libcxxabi repositories in the projects sub-directory of the LLVM/Clang source tree and build it. The resulting includes the necessary -lc++abi, so building LibreOffice with CXX=clang++ -stdlib=libc++ just works. (You need to make sure that the gets found at runtime, of course, e.g., by setting LD_LIBRARY_PATH.)

One remaining problem is that libc++abi and libstdc++ (resp. its ABI-related libsupc++ part) differ in how they determine type equivalence. On the one hand, libc++abi sticks to the original C++ ABI semantics: “It is intended that two type_info pointers point to equivalent type descriptions if and only if the pointers are equal.” (That is, type equivalence is determined by comparing _ZTI* type_info pointers for equality.) On the other hand, libstdc++ relaxed this quite a while ago to comparing type_infos’ name fields (_ZTS*) for string equality (via strcmp).

While both approaches have pros and cons, they do lead to different behaviour. One example is when a dynamic library is dlopen’ed RTLD_LOCAL. If code in that library (or any library that gets loaded dependent of it) throws an exception of type T, and code beyond that RTLD_LOCAL “wall” shall catch it, then that will work with the libstdc++ approach but fail with the libc++abi approach (even if all the relevant _ZTI*, _ZTS* symbols are weakly exported).

That is the reason why e.g. the UNO runtime makes sure to dlopen all relevant libraries as RTLD_GLOBAL, but LibreOfficeKit/LibreOfficeKitInit.h somewhat ignorantly tries to get away with dlopen’ing the sofficeapp library as RTLD_LOCAL. Now LibreOffice’s cppunittester executable (used to run CppUnit tests during the LibreOffice build) links against the sal library. So by the time CppunitTest_libreofficekit_tiledrendering dlopen’s the sofficeapp library as RTLD_LOCAL, the sal library has already been loaded into the process (implicitly as RTLD_GLOBAL), so any UNO components that happen to get instantiated in that process (via the dlopen in sal’s osl_loadModule) will be outside that “sofficeapp RTLD_LOCAL walled garden,” so that when code in ucb/source/ucp/file/bc.cxx throws a css::ucb::CommandFailedException, code in ucbhelper/source/client/content.cxx (inside the walled garden) won’t catch it (but would instead proceed to std::unexpected, std::terminate, std::abort).

That problematic dlopen with RTLD_LOCAL had already been addressed for UBSan builds (where runtime type identification code in at least Clang’s UBSan implementation always operates under the original C++ ABI semantics, regardless of compiler ABI library used), “Ensure RTTI symbol visibility for Linux Clang -fsanitize=function,vptr.” The only problem this time is to find a suitable #if condition to distinguish usage of libc++abi from usage of other compiler ABI libraries, “[cfe-dev] Feature-test macro whether compiling against libc++abi?”


2 thoughts on “libc++/libc++abi on Linux

  1. Jörg Sonnenberger

    Your argument makes no sense. If you want to catch a specific exception across shared library boundaries, the correct approach is and has always been to define it in a common base library. That works correctly independent of RTLD_LOCAL. The GNU hack only ever helped when people don’t understand *when* types are defined, i.e. that you need a non-inlined key function. All cases where comparing by name gives the correct result are at best violations of ODR. At worst, you get two plugins accidently defining a class with the same name and now it gets aliased for exception handling purposes. That’s quite tricky to debug and IMO much more obnoxious than the properly designing the library tree.

  2. stbergmann Post author

    Yes, throwing instances of classes with non-inline virtual destructors is indeed the better choice. I should have made it clearer that the “exception of type T” I’m referring to is one of those UNO exceptions that are all-inline by the (arguably poor and problematic, but not easily changed for backwards compatibility reasons) design of LibreOffice/ UNO.
    Also, RTTI gets used in other places than exception handling (e.g., by some UBSan checkers), and there’s not always a unique place where the RTTI for the involved types can be emitted.


Leave a Reply

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

You are commenting using your 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 )

Connecting to %s