Exception handling with RTTI disabled on macOS

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Exception handling with RTTI disabled on macOS

Deep Majumder via cfe-dev
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:

```1.cpp
#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
---
> 0000000100003f68 s GCC_except_table0
4,9c4
< 0000000100004028 S typeinfo for std::bad_alloc
< 0000000100004018 S typeinfo for std::exception
< 0000000100003f74 S typeinfo name for std::bad_alloc
< 0000000100003f81 S typeinfo name for std::exception
<                  U vtable for __cxxabiv1::__class_type_info
<                  U vtable for __cxxabiv1::__si_class_type_info
---
>                  U typeinfo for std::bad_alloc
11c6
< 0000000100003ed0 t ___clang_call_terminate
---
> 0000000100003ef0 t ___clang_call_terminate
17c12
< 0000000100003e20 T _main
---
> 0000000100003e40 T _main
$ diff <(clang++ -fno-rtti 1.cpp -S -o -) <(clang++ 1.cpp -S -o -)
143,169d142
<       .section        __TEXT,__const
<       .globl  __ZTSSt9bad_alloc       ## @_ZTSSt9bad_alloc
<       .weak_definition        __ZTSSt9bad_alloc
< __ZTSSt9bad_alloc:
<       .asciz  "St9bad_alloc"
<
<       .globl  __ZTSSt9exception       ## @_ZTSSt9exception
<       .weak_definition        __ZTSSt9exception
< __ZTSSt9exception:
<       .asciz  "St9exception"
<
<       .section        __DATA,__const
<       .globl  __ZTISt9exception       ## @_ZTISt9exception
<       .weak_definition        __ZTISt9exception
<       .p2align        3
< __ZTISt9exception:
<       .quad   __ZTVN10__cxxabiv117__class_type_infoE+16
<       .quad   __ZTSSt9exception
<
<       .globl  __ZTISt9bad_alloc       ## @_ZTISt9bad_alloc
<       .weak_definition        __ZTISt9bad_alloc
<       .p2align        3
< __ZTISt9bad_alloc:
<       .quad   __ZTVN10__cxxabiv120__si_class_type_infoE+16
<       .quad   __ZTSSt9bad_alloc
<       .quad   __ZTISt9exception
<
```

Linked issues: https://reviews.llvm.org/D46665, https://reviews.llvm.org/D47092, https://reviews.llvm.org/rG2405bd6898151e0a7ffede78b0d0c7c85c0b66d3, https://github.com/Amanieu/asyncplusplus/commit/2360c8993951afab67ab0414288dacae1dbd7195

--
- Ilia

_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Exception handling with RTTI disabled on macOS

Deep Majumder via cfe-dev

On 26 Jan 2021, at 10:25, Ilia K wrote:

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:

```1.cpp
#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.

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
---

0000000100003f68 s GCC_except_table0

4,9c4
< 0000000100004028 S typeinfo for std::bad_alloc
< 0000000100004018 S typeinfo for std::exception
< 0000000100003f74 S typeinfo name for std::bad_alloc
< 0000000100003f81 S typeinfo name for std::exception
< U vtable for __cxxabiv1::__class_type_info
< U vtable for __cxxabiv1::__si_class_type_info
---

U typeinfo for std::bad_alloc

11c6
< 0000000100003ed0 t ___clang_call_terminate
---

0000000100003ef0 t ___clang_call_terminate

17c12
< 0000000100003e20 T _main
---

0000000100003e40 T _main

$ diff <(clang++ -fno-rtti 1.cpp -S -o -) <(clang++ 1.cpp -S -o -)
143,169d142
< .section __TEXT,__const
< .globl __ZTSSt9bad_alloc ## @_ZTSSt9bad_alloc
< .weak_definition __ZTSSt9bad_alloc
< __ZTSSt9bad_alloc:
< .asciz "St9bad_alloc"
<
< .globl __ZTSSt9exception ## @_ZTSSt9exception
< .weak_definition __ZTSSt9exception
< __ZTSSt9exception:
< .asciz "St9exception"
<
< .section __DATA,__const
< .globl __ZTISt9exception ## @_ZTISt9exception
< .weak_definition __ZTISt9exception
< .p2align 3
< __ZTISt9exception:
< .quad __ZTVN10__cxxabiv117__class_type_infoE+16
< .quad __ZTSSt9exception
<
< .globl __ZTISt9bad_alloc ## @_ZTISt9bad_alloc
< .weak_definition __ZTISt9bad_alloc
< .p2align 3
< __ZTISt9bad_alloc:
< .quad __ZTVN10__cxxabiv120__si_class_type_infoE+16
< .quad __ZTSSt9bad_alloc
< .quad __ZTISt9exception
<
```

Linked issues: https://reviews.llvm.org/D46665,
https://reviews.llvm.org/D47092,
https://reviews.llvm.org/rG2405bd6898151e0a7ffede78b0d0c7c85c0b66d3,
https://github.com/Amanieu/asyncplusplus/commit/2360c8993951afab67ab0414288dacae1dbd7195

--
- Ilia


_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev