先ほど、g++でコンパイルしたC++プログラムで例外が発生したらtry-catchで捕まえることができずにabortしてしまったというわけのわからないことが起きてしまいました。
まるまる一日かけて原因らしきものがややわかってきたので、備忘録代わりに書いておきます。
発生環境
- Windows10
- MinGW-W64 (i686-8.1.0-win32-dwarf-rt_v6-rev0)
- gcc version 8.1.0 (i686-win32-dwarf-rev0)
発生現象
C++ プログラムで throw した例外が catch できずにプログラムが abort する。
発生条件
- 32bit でコンパイルされており、かつ
- プログラムが複数のモジュール(DLLまたはEXE)から構成されており、かつ
- 例外の throw 側モジュールと catch 側モジュールが別々であり、
- throw 側と catch 側のモジュールの少なくとも片方が -static オプション付きでリンクされている。
再現性
必ず再現する。
原因
-static オプションでリンクされているとモジュール間の例外の通知に支障があるのではないかと想像しているが、根本的な原因は不明。
32bit 版でしか再現しない原因も不明。
サンプルプログラム
以下のファイル (Makefile×1, C++ソールファイル×2, インクルードファイルx1) をファイルに落としてから make して UncaughtException_exe.exe を実行してください。
# include <iostream>
# include <exception>
# include "HelloClass.h"
extern "C" int main()
{
HelloClass hello;
try
{
hello.Hello();
}
catch (const std::exception& ex)
{
std::cout << "2:" << ex.what() << "\n";
}
}
# pragma once
# ifdef EXPORT_HELLOCLASS_DLL
# define __DLLEXPORT __declspec(dllexport)
# else
# define __DLLEXPORT __declspec(dllimport)
# endif
class __DLLEXPORT HelloClass
{
public:
HelloClass();
~HelloClass();
void Hello();
};
# include <iostream>
# include <exception>
# include <stdexcept>
# include "HelloClass.h"
HelloClass::HelloClass()
{
}
HelloClass::~HelloClass()
{
}
void HelloClass::Hello()
{
try
{
throw std::runtime_error("Hello, world.");
}
catch (const std::exception& ex)
{
std::cout << "1:" << ex.what() << "\n";
throw;
}
}
# コマンド行の行頭のTABに注意
all: UncaughtException_dll.dll UncaughtException_exe.exe
UncaughtException_dll.dll : HelloClass.o Makefile
g++ -o UncaughtException_dll.dll HelloClass.o -shared -static
HelloClass.o: HelloClass.cpp HelloClass.h Makefile
g++ -c -o HelloClass.o HelloClass.cpp -DEXPORT_HELLOCLASS_DLL -O
UncaughtException_exe.exe : main.o UncaughtException_dll.dll Makefile
g++ -o UncaughtException_exe.exe main.o UncaughtException_dll.dll -static
main.o: main.cpp Makefile
g++ -c -o main.o main.cpp -O
実行すると以下のように例外が catch されずに abort します。
1:Hello, world.
terminate called after throwing an instance of 'std::runtime_error'
what(): Hello, world.
Makefile中の -static オプションを2か所ともコメントアウトすると、以下のように例外が通知されるようになります。
1:Hello, world.
2:Hello, world.
追記 (2019.03.21)
ちょっと調べてみたら、gcc は 32bit と 64bit で例外の実装方法が異なるみたいですね。その辺に原因があるのかも。
これ以上調べても -static リンクオプションを外すよりましな対策が出てこないような気がするので、ここまでにしておきます。
さて、MinGW のランタイム DLL の配布可否について調べねば・・・