title: Why can a thrown exception with hidden visibility still be caught
body: <p>I'm having a CI failure of my program which uses Boost.ProgramOptions. It fails to catch an exception thrown inside the Boost shared library. But this only happens on OSX.</p>
  <p>The problem boils down to cross-boundary exception handling with shared objects and hidden visibility.</p>
  <p>What happens is:</p>
  <ul>
  <li>Boost is build with hidden visibility, the exception thrown is marked with default visibility</li>
  <li>When consumed the exception in the Boost header is not marked with anything</li>
  <li>When the consuming program is build with default visibility all works as expected</li>
  <li>When building the consuming program with hidden visibility it fails to catch the exception on OSX only (windows and Linux work across multiple compilers)</li>
  </ul>
  <p>I found:
  <a href="https://gcc.gnu.org/wiki/Visibility" rel="nofollow noreferrer">https://gcc.gnu.org/wiki/Visibility</a></p>
  <blockquote>
  <p>Symbol visibility is &quot;default&quot; by default but if the linker encounters just one definition with it hidden - just one - that typeinfo symbol becomes permanently hidden (remember the C++ standard's ODR - one definition rule).</p>
  </blockquote>
  <p>So this (and the rest of the C++ exception paragraph) says, that the OSX behavior is what is expected.</p>
  <p>This should also be shown by &quot;Version 3&quot; of <a href="http://www.ce.unipr.it/people/medici/visibility_and_shared_object.html" rel="nofollow noreferrer">http://www.ce.unipr.it/people/medici/visibility_and_shared_object.html</a> which is basically an MWE:</p>
  <p>my_hidden_exception.h</p>
  <pre><code>class my_exception {
    public:
    virtual ~my_exception() throw() { }
    virtual const char*
            what() const throw() { return &quot;my_exception&quot;; }
  };
  </code></pre>
  <p>main.cpp:</p>
  <pre><code>#include &lt;iostream&gt;
  #include &quot;my_hidden_exception.h&quot;
  
  int my_object();
  
  int main()
  {
    try{
      my_object();
    }
    catch(my_exception &amp; e)
    {
      std::cout &lt;&lt; e.what() &lt;&lt; std::endl;
    }
  }
  </code></pre>
  <p>shared.cpp:</p>
  <pre><code>#include &quot;my_hidden_exception.h&quot;
  
  int __attribute__ ((visibility(&quot;default&quot;))) my_object()
  {
    throw my_exception();
  }
  </code></pre>
  <p>Compile and run with</p>
  <pre><code>g++ -shared -o libshared.so shared.cpp -fvisibility=hidden -fvisibility-inlines-hidden
  g++ -o main main.cpp -lshared -L. -fvisibility=hidden -fvisibility-inlines-hidden
  ./main 
  </code></pre>
  <p>My problem now is that I'm unable to reproduce this with any GCC (7, 10) or Clang (6, 9) I tested, in all cases the exception is successfully caught which according to all sources shouldn't be possible.</p>
  <p>Can anyone explain what I'm missing here? How can I &quot;force&quot; the correct behavior on Linux for better testing (OSX testers are hard(er) to come by)?</p>