Exception handling with RTTI disabled on macOS

Hi!

It turned out that I cannot catch std::bad_alloc& thrown by the operator new when compiling 1.cpp with -fno-rtti and linking against system’s libc++.1.dylib/libc++abi.dylib on macOS. This scenario works on Linux, and according to the official libc++ page it is supported (“However linking against it with -fno-rtti is supported”, https://libcxx.llvm.org/), however the following code is aborted due to an unhandled exception:

#include <cstdio>
#include <new>

int main() {
try {
::operator new(-1);
} catch (std::bad_alloc& e) {
printf("caught %s\n", e.what());
}
return 0;
}

Compile & run:

$ clang++ -fno-rtti 1.cpp -o 1_no_rtti
$ ./1_no_rtti
libc++abi.dylib: terminating with uncaught exception of type std::bad_alloc: std::bad_alloc
Abort trap: 6

I know about similar issues with exceptions when -fno-rtti on macOS (Polymorphically catching an exception in a -fno-rtti shared library on Mac OS X https://stackoverflow.com/questions/3638237/polymorphically-catching-an-exception-in-a-fno-rtti-shared-library-on-mac-os-x, Problems throwing and catching exceptions on OS X with -fno-rtti https://stackoverflow.com/questions/21737201/problems-throwing-and-catching-exceptions-on-os-x-with-fno-rtti). There was an issue in Android NDK with exceptions when -flto=thin and it was said that “The problem is that we have multiple copies of a symbol, where one of them is strong, and the others are linkonce_odr” https://github.com/android/ndk/issues/1046#issuecomment-514469559 (fixed now https://reviews.llvm.org/D61255).

Can anyone shed some light on it? It seems like a bug at the moment, and if your code uses any of libc++.1.dylib/libc++abi.dylib system libraries on macOS, you must leave RTTI enabled, except you have turned exceptions off as well.

FYI, the difference between non-RTTI (./1_no_rtti) and the default (./1_rtti, with RTTI) Mach-O executables:

$ diff <(nm -C 1_no_rtti) <(nm -C 1_rtti)
1c1
< 0000000100003f48 s GCC_except_table0

[ removing cfe-dev]

Unfortunately, there is no description for -fno-rtti flag on
https://releases.llvm.org/11.0.0/tools/clang/docs/ClangCommandLineReference.html but I assume it should work the same as in GCC:

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.

So it looks like in G++ we can use -fno-rtti, but the compiler will still generate it if needed. Is it true for Clang on macOS? If not, then what about Linux?

I also found the -fno-rtti-data option, without description as well.

Hi!

It turned out that I cannot catch std::bad_alloc& thrown by the operator new when compiling 1.cpp with -fno-rtti and linking against system’s
libc++.1.dylib/libc++abi.dylib on macOS. This scenario works on Linux, and
according to the official libc++ page it is supported (“However linking
against it with -fno-rtti is supported”, https://libcxx.llvm.org/), however
the following code is aborted due to an unhandled exception:

#include <cstdio>
#include <new>

int main() {
try {
::operator new(-1);
} catch (std::bad_alloc& e) {
printf("caught %s\n", e.what());
}
return 0;
}

Compile & run:

$ clang++ -fno-rtti 1.cpp -o 1_no_rtti
$ ./1_no_rtti
libc++abi.dylib: terminating with uncaught exception of type
std::bad_alloc: std::bad_alloc
Abort trap: 6

I know about similar issues with exceptions when -fno-rtti on macOS
(Polymorphically catching an exception in a -fno-rtti shared library on Mac
OS X
https://stackoverflow.com/questions/3638237/polymorphically-catching-an-exception-in-a-fno-rtti-shared-library-on-mac-os-x,
Problems throwing and catching exceptions on OS X with -fno-rtti
https://stackoverflow.com/questions/21737201/problems-throwing-and-catching-exceptions-on-os-x-with-fno-rtti).
There was an issue in Android NDK with exceptions when -flto=thin and it
was said that “The problem is that we have multiple copies of a symbol,
where one of them is strong, and the others are linkonce_odr”
https://github.com/android/ndk/issues/1046#issuecomment-514469559 (fixed
now https://reviews.llvm.org/D61255).

Can anyone shed some light on it? It seems like a bug at the moment, and if
your code uses any of libc++.1.dylib/libc++abi.dylib system libraries on
macOS, you must leave RTTI enabled, except you have turned exceptions off
as well.

Mechanically, the problem here is that exceptions rely on RTTI, so
combining -fno-rtti with -fexceptions forces the compiler to make
an awkward decision about whether to expect that classes with key
functions emit their RTTI objects eagerly. If the compiler trusts
classes to do this, then you can’t catch and throw exceptions of your
own class types that happen to have key functions; if the compiler
doesn’t trust classes to do this, then you can’t catch and throw
exceptions of system classes like std::bad_alloc (or any class that
happens to have a key function and is compiled with RTTI enabled).
The traditional decision is to favor the first, and we can’t really
change that.

Darwin is stricter about RTTI object matching than other platforms,
and we’re also not going to change that.

Ultimately, -fno-rtti -fexceptions is only a semi-supported
configuration. It causes language implementation problems which it
becomes your responsibility to work around. In this case, you can do
that by catching the exception in a file that does not disable RTTI.

John.