Monday, 24 June 2013

Clap & Clang

Note: Much of this post uses terms and concepts from C++ programming and ABI implementation. If you don't know about things like that, just nod, smile and try to take in the conclusion at the end. 

So I had a bug report from a Mac user. He tried to load a slightly broken AIFF file in Logic Audio 9, something that should normally have led to a little message box saying "Could not load file", and nothing more. Instead however, the whole application crashed, without so much as an apology.
He sent me the file, and I fired up AU lab and loaded it into the TX. As I expected, I got the error message I knew should be there. So what is going on?
Firing up Logic Audio, I could indeed reproduce the behaviour he described; As a matter of fact, even the trusty Reaper exhibited the same pattern. Some debugging showed that while the appropriate exception was indeed thrown, the catch clause did not take at all. In these two hosts. But they did in AU Lab. Oy wei...
So, some more debugging, and stepping through the disassembly of the exception handling framework for C++ compiled against libc++ in OSX, which is libcxxabi. Now, the exception is thrown properly, and the stack unwinding happens as it should, using libunwind. But the type matching breaks and fails to recognize that std::runtime_error is a subtype of std::exception. For this unfamiliar with how this is done in exception handling, basically an application or library exports a type table which identifies exported types, in this case the table comes from libc++. The exception handling will use soft type analysis to determine if types are related and compatible, which in turn means that to be able to handle exceptions across DLL boundaries for example, you must make sure that you both agree on the types you are exchanging (i.e. link to the same C++ runtime). But here is the funny thing; In my case, the exception handling is not across DLL boundaries, but inside a single plug-in library. And still, the type handling breaks when the plug-in is loaded in certain hosts?
I ran a number of different compiles to ensure that symbol visibility was not broken at my end, all symbols required for exception handling properly pointed out etc. No difference. I also made sure to test the behaviour on a much smaller example, the Apple "FilterDemo" sample AU plug-in.

This is where I found the culprit: OSX toolset 10.7.

I seems that a plug-in compiled with the XCode 4.6 Clang compiler, C++11 and libc++ while linking against the OSX 10.7 system headers will have broken exception handling when loaded in some applications. My guess is that something in these headers contain declarations that are not fully llvm compatible and causes a conflict in the somewhat fragile system that is the dynamic type handling in C++ exceptions when certain other compatibility libraries are loaded. And this is pretty much where I will stop my debugging of this, because it is exhausting.

And now for the conclusion:
The bottom line is; There is what I would categorise as a bug in the c++11 toolchain when targeting OSX 10.7 (unless this is explicitly unsupported, though I did not find any docs to that fact), so from the next build TX16Wx will only support OSX 10.8 and above. It might still work on a 10.7 machine, since I am not making use of any particular 10.8 features, but given that this is Apple, I would not hold my hopes up. I'm sorry for those on 10.7 eager to use TX16Wx, but since I cannot find any other way to provide a stable functioning plug-in this is how it will be.

New service release will be out in a few days.
Happy sampling
/C

Edit: So, it seems that while the above is correct, there is a relatively simple solution, namely usign the 10.8 SDK but set targeted version 10.7. This is documented, though with a slightly pouty face I'd still like to point out that the documentation in question is not very close to the settings themselfes. So, at least partially this seems to have been a little bit of SBS on my part. Though, seriously, its still quite a weird thing to happen. Anyhow, the good news here is that there will be a 10.7 version of TX16Wx after all.