2

I am porting a project from MSVC to mingw. The project contains C and C++. However, whenever an exception is thrown, instead of being caught, std::terminate gets called and the application crashes. I can't understand why so any advice would be appreciated.

My toolchain is cmake/ninja/mingw32 in a MSYS2 environment installed in Windows.

MCVE:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.6)
project(FailedExceptions)
add_executable(FailedExceptions c_funcs.c main.cpp)

//main.cpp
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

int main() {
    try  {
        boost::property_tree::ptree pt;
        std::printf("reading file\n");
        boost::property_tree::read_xml("nonexistant-file", pt);
        std::printf("provider file read\n");
    } catch (...) {
        std::printf("Exception caught\n");
    }
    return 0;
}

// c_funcs.c
int SomeCFunction()
{
    return 0;
}

Output

$ cmake .. -GNinja
-- The C compiler identification is GNU 7.2.0
-- The CXX compiler identification is GNU 7.2.0
-- Check for working C compiler: C:/msys64/mingw32/bin/cc.exe -- works
-- Check for working CXX compiler: C:/msys64/mingw32/bin/c++.exe -- works
-- Configuring done
-- Generating done
-- Build files have been written to: C:/msys64/home/sferguson/src/vis/build

$ ninja -v
[1/3] C:\msys64\mingw32\bin\cc.exe  -MD -MT c_funcs.c.obj -MF c_funcs.c.obj.d -o c_funcs.c.obj   -c ../c_funcs.c
[2/3] C:\msys64\mingw32\bin\c++.exe -MD -MT main.cpp.obj -MF main.cpp.obj.d -o main.cpp.obj -c ../main.cpp
[3/3] C:\msys64\mingw32\bin\c++.exe  c_funcs.c.obj main.cpp.obj  -o FailedExceptions.exe -Wl,--major-image-version,0,--minor-image-version,0  -lgcc_eh -lgcc_eh -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32

$ ./FailedExceptions.exe
reading file

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
$

Trace:

I can get this trace from Dr. Mingw. It really does appear that the crash happens between construction of the exception and the actual throw.

ntdll.dll!_NtTerminateProcess@8
ntdll.dll!_RtlExitUserProcess@4
kernel32.dll!_ExitProcessStub@4
msvcrt.dll!___crtExitProcess
msvcrt.dll!__cinit
msvcrt.dll!__exit
msvcrt.dll!_abort
FailedExceptions.exe!uw_init_context_1
FailedExceptions.exe!boost::property_tree::xml_parser::xml_parser_error::xml_parser_error
FailedExceptions.exe!boost::property_tree::xml_parser::read_xml<boost::property_tree::basic_ptree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >
FailedExceptions.exe!main
FailedExceptions.exe!__tmainCRTStartup  [D:/develop/scripts/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c @ 334]
kernel32.dll!@BaseThreadInitThunk@12
ntdll.dll!___RtlUserThreadStart@8
ntdll.dll!__RtlUserThreadStart@8

Troubleshooting:

  • I found some posts from 5-10 years ago suggesting that this could be a conflict between mingw's dw2 and sjlj libraries, but I only have the libgcc_s_dw2-1.dll binaries installed which come with the mingw-w64-i686-gcc-libs package in the msys pacman repository.
  • I tried changing my CMakeLists.txt file to compile everything with C++ with project(FailedExceptions LANGUAGES CXX). This prevents cmake from building my C files. So it does work for the MCVE, but my full project is missing all C content.
  • I've added set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -fexceptions) but it seems to have no effect. I've confirmed with ninja -v that this flag is added to the C-file compilation command.
  • As soon as I remove the C file from the build, everything works. But even though I'm not using the C-file in this MCVE, I still use it in my big project.
  • I've found another smaller example here. I can reproduce that problem IFF I also compile a C file in that same project.
Stewart
  • 4,356
  • 2
  • 27
  • 59
  • Where is `int SomeCFunction()` called? – YSC Oct 11 '17 at 12:28
  • 1
    is `boost::property_tree::read_xml` not taking a stream as parameter instead of a string? – 463035818_is_not_an_ai Oct 11 '17 at 12:29
  • 1
    @YSC int SomeCFunction() isn't called anywhere. It's simply linked. That's one of the strange details here. I dont NEED to call SomeCFunction() to destroy my exceptions in the main.cpp. I just need to link to it. – Stewart Oct 11 '17 at 12:34
  • @tobi303 There is an overload of `read_xml` which accepts a `const string&`, then opens a file with that name. That is the overload I am invoking. See [this](http://www.boost.org/doc/libs/1_63_0/doc/html/boost/property_tree/xml_parser/read_xml_idp910001728.html) – Stewart Oct 11 '17 at 12:36
  • @Stewart ah ok didnt see that one. thx – 463035818_is_not_an_ai Oct 11 '17 at 12:36
  • and what about boost ptree ? is it essential to the MCVE ? – Massimiliano Janes Oct 11 '17 at 12:37
  • 1
    Since C does not have exceptions, the likely answer is whenever a thrown C++ exception encounters a stack frame from a C function, it explicitly fails, since the exception cannot be properly propagated. This was the case, at least, with early versions of gcc, where an explicit compilation option was needed to compile C code with proper support for C++ exceptions. – Sam Varshavchik Oct 11 '17 at 12:37
  • @Massimiliano Janes I couldn't reproduce it by throwing a primitive, but the boost stuff I chose to show is all header-only so it should be fairly simple. I haven't figured out a pattern between what crashes and what doesn't. I'll see if I can find a simpler way to reproduce it. – Stewart Oct 11 '17 at 12:39
  • [Here](https://stackoverflow.com/questions/7765599/strange-problems-with-c-exceptions-with-mingw)'s a good example of a very similiar situation (if not identical). The cause of that problem is a mix of static/dynamic libraries. That isn't the problem here, but it shows the same symptoms. – Stewart Oct 11 '17 at 12:44
  • I'm not sure why cmake generates two -lgcc_eh flags. – Stewart Oct 11 '17 at 12:47

3 Answers3

1

It's a feature(bug?) in cmake implicit library detection introduced in cmake 3.1. CMake thinks that in C mode GCC needs to link with gcc_eh, which breaks C++ exception handling.

You can disable implicit library detection by adding this to CMakeLists.txt:

set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "")

(Don't know how to exclude just gcc_eh from the list)

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • It turns out that when I use this command, `gcc_eh` is the only library excluded. The others are all included with the CXX link and so this solution is perfect. – Stewart Oct 18 '17 at 07:10
0

RustyX's answer is the accepted one, but I also found another number of work-arounds that seem to work:

  • set_source_files_properties(filename.c PROPERTIES LANGUAGE CXX)
  • set(CMAKE_C_COMPILER /path/to/cpp/compiler)
  • set(APPEND CMAKE_CXX_SOURCE_FILE_EXTENSIONS c).

But RustyX's is the best answer so far.

Edit: The last one doesn't work. It still builds with the C compiler. I'd also need to remove c from CMAKE_C_SOURCE_FILE_EXTENSIONS.

Stewart
  • 4,356
  • 2
  • 27
  • 59
0

To add to rustyx answer above (I'm not allowed to comment), this is fixed in cmake 3.10 per this pull-request: https://gitlab.kitware.com/cmake/cmake/merge_requests/1460

I tested with CMake 3.10.2, and it seems like exceptions now work without having to specify the work-around cmake stanza rustyx is mentioning.

Atokon
  • 29
  • 4