[cfe-dev] Problem with libc++/libstdc++ interoperability on OS X

Florian Pflug fgp at phlo.org
Mon Jan 28 19:39:33 PST 2013


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 :-(

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






More information about the cfe-dev mailing list