Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SIGABRT on throwing and catching exceptions with GCC on Mac OS X

I'm currently working on proper error management in a C++ library I'm maintaining. When writing a unit test for some negative cases (i. e. testing the proper throwing of exceptions), the unit test suite simply aborted with SIGABRT. I went on a hunt and tried to boil down the error with simpler exceptions being thrown and experimenting with various catch statements. But even a catch-all block could not prevent the crash (for a MWE see below).

My setup is this: I'm working on a Mac with the most current OS X Big Sur 11.1 with the latest XCode Command Line Tools installed. I'm using GCC from Homebrew, currently v10.2.0_1.

$ g++-10 -v
Using built-in specs.
COLLECT_GCC=g++-10
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/10.2.0_1/libexec/gcc/x86_64-apple-darwin20/10.2.0/lto-wrapper
Target: x86_64-apple-darwin20
Configured with: ../configure --build=x86_64-apple-darwin20 --prefix=/usr/local/Cellar/gcc/10.2.0_1 --libdir=/usr/local/Cellar/gcc/10.2.0_1/lib/gcc/10 --disable-nls --enable-checking=release --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-10 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-system-zlib --with-pkgversion='Homebrew GCC 10.2.0_1' --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --disable-multilib --with-native-system-header-dir=/usr/include --with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk SED=/usr/bin/sed
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 10.2.0 (Homebrew GCC 10.2.0_1)

I also compiled GCC myself using Apples system toolchain. The output of the self-compiled GCC is this:

$ /opt/gcc/10.2.0/bin/g++-10 -v
Using built-in specs.
COLLECT_GCC=/opt/gcc/10.2.0/bin/g++-10
COLLECT_LTO_WRAPPER=/opt/gcc/10.2.0/libexec/gcc/x86_64-apple-darwin20/10.2.0/lto-wrapper
Target: x86_64-apple-darwin20
Configured with: ../configure --build=x86_64-apple-darwin20 --prefix=/opt/gcc/10.2.0 --libdir=/opt/gcc/10.2.0/lib/gcc/10 --disable-nls --enable-checking=release --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-10 --with-system-zlib --disable-multilib --with-native-system-header-dir=/usr/include --with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk SED=/usr/bin/sed
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 10.2.0 (GCC)

The result is still the same: exceptions abort the program.

My minimum working example is this:

#include <iostream>
#include <stdexcept>

int main()
{
    try {
        throw "this is an exception text";
    }
    catch(const char* e)
    {
        std::cerr << e << std::endl;
    }
    catch(...)
    {
        std::cerr << "Unknown error!" << std::endl;
    }

    return 0;
}

This compiles fine and produces the expected output on my Linux VM.

I'm using the following commands to compile it on my Mac:

$ g++-10 -o bin/main.o -c -std=c++11 main.cpp
$ g++-10 -o bin/main bin/main.o
$ ./bin/main
[1]    60310 abort      ./bin/main

Using LLDB yields:

(lldb) run
Process 61177 launched: './bin/main' (x86_64)
Process 61177 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00007fff202fa462 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
->  0x7fff202fa462 <+10>: jae    0x7fff202fa46c            ; <+20>
    0x7fff202fa464 <+12>: movq   %rax, %rdi
    0x7fff202fa467 <+15>: jmp    0x7fff202f46a1            ; cerror_nocancel
    0x7fff202fa46c <+20>: retq   
Target 0: (main) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff202fa462 libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff20328610 libsystem_pthread.dylib`pthread_kill + 263
    frame #2: 0x00007fff2027b720 libsystem_c.dylib`abort + 120
    frame #3: 0x000000010048b00a libgcc_s.1.dylib`uw_init_context_1.cold + 5
    frame #4: 0x0000000100488475 libgcc_s.1.dylib`_Unwind_RaiseException + 69
    frame #5: 0x00000001001382f7 libstdc++.6.dylib`__cxa_throw + 55
    frame #6: 0x0000000100003d55 main`main + 52
    frame #7: 0x00007fff20343621 libdyld.dylib`start + 1

It seems to me as if another error happens during the unwind phase which then leads to the termination. Which would also explain, why no catch block is ever reached.

This is way out of my realm of expertise, so any ideas are welcome.

Edit: Updated question after the latest GCC Homebrew release.

like image 894
lepus2589 Avatar asked Nov 06 '22 03:11

lepus2589


1 Answers

I confirm the unexpected behaviour on Big Sur, Homebrew GCC 10.2.0_2 setup. Changing the linked brew libstdc++ to the system one (assuming the one found in /usr/lib is installed by macOS) solved the problem on my setup:

$ g++-10 main.cpp -o main -std=c++11
$ ./main
Abort trap: 6
$ otool -L main
main:
    /usr/local/opt/gcc/lib/gcc/10/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.28.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
    /usr/local/lib/gcc/10/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
$ install_name_tool -change /usr/local/opt/gcc/lib/gcc/10/libstdc++.6.dylib /usr/lib/libstdc++.6.dylib main
$ ./main
this is an exception text

Alternatively, setting export DYLD_LIBRARY_PATH=/usr/lib before running main has the same effect.

Update: The bug has been fixed and the patch is included in brew's gcc-10.2.0_3.

like image 182
mlund Avatar answered Nov 14 '22 21:11

mlund