Problem with libc++/libstdc++ interoperability on OS X

Hi

While developing with clang (Xcode 4.5.2) and libstdc++ on OS X (10.8), debug builds have for some time now spuriously crashed, reporting "pointer being freed was not allocated". Usually, rebuilding with more optimizations solves the problem, as do random changes to the compiler flags. Since builds done on older version of OS X where fine, I never gave it much thought - until today, that is.

After spending a few intimate hours with gdb (not fun!), and reading through libstdc++'s and libc++'s sources (more fun!), I think I begin to understand the problem.

It seems that libc++ is designed to be *partially* compatible with libstdc++. In particular, it looks like the standard exception classes, one of them being std::runtime_error, are supposed to be ABI-compatible with libstdc++. Which, I guess, is the reason why those classes live directly in the std namespace, and not within std::__1 like the rest of libc++.

Now, libstdc++'s std::runtime_error contains an std::string which contains the error message, but libc++'s std::string is ABI-compatible with libstdc++'s. libc++ seemingly tries to overcome that obstacle by implementing a simple non-mutable string which is supposed to be ABI-compatible with libstdc++, called libcpp_nmstr.

Unfortunately, this is where things start to break. My version of libc++ doesn't seem to get libcpp_nmstr quite right. libstdc++ seems to use a 4-byte refcount field, followed by 4 bytes of padding. According to the dissassembler, my libc++ OTOH seems to have it the other way around, and ends up incrementing the padding instead of the refcount. Since these increments are invisible to libstdc++'s std::string destructor, it ends up freeing the same block multiple times :frowning:

To make things worse, the relevant code in libc++ isn't in the headers, but instead compiled into the library. Thus, whoever links against libc++ on OS X potentially gets the broken version of std::runtime_error. Which, as it turns out, is everybody, since libSystem pulls in libdispatch which in turn pulls in libc++. So that finally explains the crashes I've been seeing - if a build is unlucky enough to not inline std::runtime_error's copy-constructor, operator= or destructor *and* the dynamic linker chooses to pick the symbol from libc++, things go south…

Has anyone an idea how to work around this?

best regards,
Florian Pflug

The only workaround I'm aware of at the moment is to not mix libstdc++ and libc++. This logic for this code has been both moved and fixed. It is now in the libcxxabi project:

http://libcxxabi.llvm.org

Howard

The only workaround I'm aware of at the moment is to not mix libstdc++ and libc++.

Unfortunately, since libc++ gets pulled in (indirectly) via libSystem on OS X 10.8, that is easier said than done. I had hoped that there might be a way to convince the dynamic linker to ignore the broken symbols in libc++.

This logic for this code has been both moved and fixed. It is now in the libcxxabi project:

Oh, good to know. It seems that the broken stdexcept.cpp wasn't removed from libc++ HEAD though.

BTW, I reported this at Bug Reporting - Apple Developer as issue 13100815.

best regards,
Florian

FWIW, I reported this to Apple (bug 13100815) and was just informed that the bug is supposed to be fixed in OS X Mavericks, and will not be fixed in earlier releases :-(.

Since you *cannot* avoid mixing libstdc++ and libc++ on OS X nowadays, due to libSystem's habit of pulling in libc++ (via libdispatch), that leaves std::runtime_error broken on at least 10.8 Mountain Lion and probably on earlier versions too. The only "workaround" seems to be to juggle compiler flags until you hit a combination that hides the problem (e.g. combination that inlines std::runtime_error's copy constructor). Unfortunately, and such workaround is very fragile - arbitrary code changes can cause the problem to resurface.

best regards,
Florian Pflug