Polymorphically catching an exception in a -fno-rtti shared library on Mac OS X
Asked Answered
B

2

6

I'm building a shared library with f-no-rtti. Internally, this library throws std:invalid_argument and catches std::exception, but the catch clause is never entered.

The following code reproduces the problem (g++ 4.2, Mac OS X 10.6):

// library.cpp: exports f(), compiled with -fno-rtti
#include <stdexcept>
#include <iostream>
extern "C" {
    void f() {
        try {
            throw std::invalid_argument("std::exception handler");
        } catch( std::exception& e) {
            std::cout << e.what() << "\n";
        } catch(...) {
            std::cout << "... handler\n";
        }
    }
}

// main.cpp: the main executable, dynamically loads the library
#include <dlfcn.h>
typedef void(*fPtr)();

int main() {
    void* handle = dlopen( "./libexception_problem.dylib", RTLD_LAZY );
    fPtr p_f = reinterpret_cast<fPtr>( dlsym( handle, "f" ) );
    p_f();
}

Output:

MacBook-Pro:teste pfranco$ # works fine with rtti
MacBook-Pro:teste pfranco$ g++ -c library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ main.cpp -o main && ./main
std::exception handler
MacBook-Pro:teste pfranco$ # breaks with -fno-rtti
MacBook-Pro:teste pfranco$ g++ -c -fno-rtti library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ -fno-rtti main.cpp -o main && ./main
... handler
MacBook-Pro:teste pfranco$ #-no_dead_strip_inits_and_terms doesn't change anything
MacBook-Pro:teste pfranco$ g++ -c -no_dead_strip_inits_and_terms -fno-rtti library.cpp && g++ -no_dead_strip_inits_and_terms -shared -o libexception_problem.dylib library.o && g++ -fno-rtti -no_dead_strip_inits_and_terms main.cpp -o main && ./main
... handler
MacBook-Pro:teste pfranco$ # linking against the shared library works, but this isn't always an option
MacBook-Pro:teste pfranco$ g++ -c -fno-rtti library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ -fno-rtti main.cpp -o main -L. -lexception_problem && ./main
std::exception handler

This only happens if the code that throws is in a shared library, and only if the caught type is a base class of the actual exception - catch(std::invalid_argument&) works fine, std::logic_error& doesn't.

Interestingly, this doesn't happen on Linux, even when running the exact same commands.

Questions:

  1. Why does this happen? Is this a bug, undefined behavior or by design?
  2. How could I make it work, short of linking against the library?

Thanks a lot.

Bk answered 3/9, 2010 at 17:46 Comment(1)
If by "undefined behaviour" you mean according to the C++ standard, then it's (at best) implementation-defined what happens when you use a compiler option which puts the compiler in a non-compliant mode. I doubt that helps you much, but you can't disable bits of the standard and then expect the standard to help you ;-)Danieladaniele
B
5

Turns out this a bug on Apple's gcc. They've recently replied to my bug report saying it won't be fixed, though.

Bk answered 23/3, 2011 at 12:26 Comment(0)
T
-2

From the info page for gcc (my highlighing).

-fno-rtti Disable generation of information about every class with virtual functions for use by the C++ runtime type identification features (dynamic_cast and typeid). If you don't use those parts of the language, you can save some space by using this flag. Note that exception handling uses the same information, but it will generate it as needed. The dynamic_cast operator can still be used for casts that do not require runtime type information, i.e. casts to void * or to unambiguous base classes.

RTTI is a core part of the language. If the compiler allows you to disable it, you're working outside of the rules of the language so aren't necessarily going to have everything work as you expect.

Thyrsus answered 3/9, 2010 at 17:50 Comment(4)
Yeah, I'd read that, but isn't "it will generate it as needed" the key part? Thanks.Creolized
@Pedro d'Aquino: To be honest, I don't know. The difficulty is that you're now using a language variant that is defined implicitly by gcc's actually behaviour. I recommend against trying to disable RTTI.Thyrsus
I was about to post the same thing. It suggests to me that exception-handling is supposed to generate all necessary type information, regardless of whether -fno-rtti is specified, as far as GCC is concerned. But I didn't run man on an OSX or BSD system, so it might say something different there. It could be that they've deliberately changed the behaviour (and documented somewhere), it could be they've silently changed it (and not documented it anywhere - very naughty), or it could be a bug in their gcc fork and/or their C++ runtime support.Danieladaniele
@Pedro: also comb the docs for dlls on OSX - it might mention something there, since apparently it only happens with dynamic-link. Otherwise, sounds like a candidate for a bug. Whether that's a bug in the compiler ("-fno-rtti should not break catch") or just in the documentation ("-fno-rtti should mention that it breaks catch") probably depends whether it's a mistake or a genuine limitation of their implementation.Danieladaniele

© 2022 - 2024 — McMap. All rights reserved.