title: Problems throwing and catching exceptions on OS X with -fno-rtti
body: <p>The issue is somewhat similar to <a href="https://stackoverflow.com/questions/3638237/polymorphically-catching-an-exception-in-a-fno-rtti-shared-library-on-mac-os-x">this question</a> but the accepted answer does not really propose a solution or workaround.</p>
  
  <p>In our project, we have a dylib and the main executalble. The dylib is compiled with <code>-fno-rtti</code>, while the executable does use RTTI. The problem happens when an exception (e.g. <code>std::bad_alloc</code>) is thrown from the dylib and is caught in the exe.</p>
  
  <p>(Before you yell "exceptions need RTTI so you must have it on!", please note that the RTTI necessary for exceptions is always generated <em>regardless</em> of the <code>-frtti</code> or <code>-fno-rtti</code> setting. This is actually documented in the <code>-fno-rtti</code> flag description. The issue on OS X is that it's not generated in the same way)</p>
  
  <p>After some investigation, the following was discovered:</p>
  
  <ul>
  <li>In the dylib (<code>-fno-rtti</code>), there is a local copy of the exception's RTTI structures; in particular, the <code>__ZTISt9bad_alloc</code> symbol (<code>typeinfo for std::bad_alloc</code>).</li>
  <li>The exe (<code>-frtti</code>) imports the typeinfo symbol from <code>libstdc++.6.dylib</code> and does not have a local copy</li>
  </ul>
  
  <p>Since the exception handling code relies on comparing typeinfo pointers to determine exception match, the matching fails, and only the <code>catch(...)</code> succeeds.</p>
  
  <p>So far I see the following options:</p>
  
  <p>1) compile everything, or at least the files that throw and catch exceptions, with <code>-frtti</code>. This is doable but I don't like the idea of generating RTTI for everything even if we don't use it; and the list of files which work with exceptions is prone to get stale.</p>
  
  <p>2) when linking the dylib, somehow make the linker throw away the weak exception definition from the object file and use the one from <code>libstdc++.6.dylib</code>. So far I was not successful.</p>
  
  <p>3) ???</p>
  
  <p>I made a small test illustrating the problem.</p>
  
  <pre><code>--- throw.cpp ---
  #include &lt;iostream&gt;
  
  #if defined(__GNUC__)
  #define EXPORT __attribute__((visibility("default")))
  #else
  #define EXPORT __declspec(dllexport)
  #endif
  
  EXPORT void dothrow ()
  {
     std::cout &lt;&lt; "before throw" &lt;&lt; std::endl;
     throw std::bad_alloc();
  }
  
  --- main.cpp ---
  #include &lt;stdio.h&gt;
  #include &lt;iostream&gt;
  
  #if defined(__GNUC__)
  #define IMPORT extern
  #else
  #define IMPORT __declspec(dllimport)
  #endif
  
  IMPORT void dothrow ();
  
  int main (void) {
   try {
     std::cout &lt;&lt; "trying lib-&gt;main exception" &lt;&lt; std::endl;
     dothrow ();
   }
   catch ( const std::bad_alloc&amp; )
   {
     std::cout &lt;&lt; "caught bad_alloc in main - good." &lt;&lt; std::endl;
   }
   catch (...)
   {
     std::cout &lt;&lt; "caught ... in main - bad!" &lt;&lt; std::endl;
   }
  }
  
  --- makefile ---
  # for main exe
  CFLAGS_RTTI=-m32 -frtti -fvisibility=hidden -fvisibility-inlines-hidden -shared-libgcc -funwind-tables
  # for dylib
  CFLAGS_NORTTI=-m32 -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden -shared-libgcc
  # for linking; some switches which don't help
  CLFLAGS=-Wl,-why_live,-warn_commons,-weak_reference_mismatches,error,-commons,error
  
  all: test
  
  test: libThrow.dylib main.o
      g++ $(CFLAGS_RTTI) -o test main.o -lthrow -L./ $(CLFLAGS)
  
  main.o: main.cpp
      g++ $(CFLAGS_RTTI) -c -o main.o main.cpp
  
  throw.o: throw.cpp
      g++ $(CFLAGS_NORTTI) -c -o throw.o throw.cpp
  
  libThrow.dylib: throw.o
      g++ $(CFLAGS_NORTTI) -dynamiclib -o libThrow.dylib throw.o
  
  clean:
      rm test main.o throw.o
  </code></pre>
  
  <p>Running:</p>
  
  <pre><code>$ ./test
  trying lib-&gt;main exception
  before throw
  caught ... in main - bad!
  </code></pre>
  
  <p>Symbols of the files involved:</p>
  
  <pre><code>$ nm -m throw.o | grep bad_alloc
  000001be (__TEXT,__textcoal_nt) weak private external __ZNSt9bad_allocC1Ev
  000001be (__TEXT,__textcoal_nt) weak private external __ZNSt9bad_allocC1Ev
  00000300 (__TEXT,__eh_frame) weak private external __ZNSt9bad_allocC1Ev.eh
           (undefined) external __ZNSt9bad_allocD1Ev
  00000290 (__DATA,__const_coal) weak external __ZTISt9bad_alloc
  000002a4 (__TEXT,__const_coal) weak external __ZTSSt9bad_alloc
           (undefined) external __ZTVSt9bad_alloc
  
  $ nm -m libThrow.dylib | grep bad_alloc
  00000ce6 (__TEXT,__text) non-external __ZNSt9bad_allocC1Ev
           (undefined) external __ZNSt9bad_allocD1Ev (from libstdc++)
  00001050 (__DATA,__const) weak external __ZTISt9bad_alloc
  00000e05 (__TEXT,__const) weak external __ZTSSt9bad_alloc
           (undefined) external __ZTVSt9bad_alloc (from libstdc++)
  
  $ nm -m main.o | grep bad_alloc
           (undefined) external __ZTISt9bad_alloc
  
  $ nm -m test | grep bad_alloc
           (undefined) external __ZTISt9bad_alloc (from libstdc++)
  </code></pre>
  
  <p>Note: similar compilation options on Linux and Windows works fine. I can throw exceptions from a shared object/dll and catch them in the main exe, even if they're compiled with different <code>-frtti</code>/<code>-fno-rtti</code> options.</p>
  
  <hr>
  
  <p><strong>EDIT</strong>: here's how I ended up solving it for the specific case of <code>bad_alloc</code>:</p>
  
  <pre><code>#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
   #define throw_nomem std::__throw_bad_alloc
  #else
   #define throw_nomem throw std::bad_alloc
  #endif
  
  EXPORT void dothrow ()
  {
     std::cout &lt;&lt; "before throw" &lt;&lt; std::endl;
     throw_nomem();
  }
  </code></pre>
  
  <p>The function <code>__throw_bad_alloc</code> is imported from <code>libstdc++.6.dylib</code> and so always throws a correct type.</p>