std::coutを使うと落ちる
2年前くらいから、Mingw-w64のg++でコンパイルしたプログラムが実行できない(数秒フリーズ後何も出力せず終了)することに気が付きました。
例えば、以下に示すようなプログラムが動きませんでした。
#include <iostream>
int main() {
int a = 10;
std::cout << a << std::endl;
return 0;
}
これをg++ test.cpp
でコンパイルした実行可能ファイルが落ちます。
色々試してみると、printfを使ったプログラムなら問題なく動作することが分かりました。
#include <iostream>
int main() {
int a = 10;
printf("%d\n", a);
return 0;
}
これに気づいた当初は焦りましたが、そこまでWindowsでg++を使わなかったので大きな問題にはならず、調査も面倒だったので何かの拍子に直ることを期待して放置対応を後回しにしていましたが、重い腰を上げて直すことにしました。
静的リンクを試す
この問題について調べていると、12年前に似たような問題が発生した記事を発見したので、この記事の流れに沿って進めていきました。
まず、g++自体がおかしいのか確認するため、静的リンクでコンパイルしてみました。g++ test.cpp -static
で共有ライブラリとのリンクを抑止してビルドを行うと、正しく標準出力されたのでg++自体がおかしいわけではなさそうです。
ところで、実行可能ファイルがどの共有ライブラリ(DLL)に依存しているかはdumpbin
コマンドで調べることができます1。
$ dumpbin /DEPENDENTS a.exe
Microsoft (R) COFF/PE Dumper Version 14.29.30151.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file a.exe
File Type: EXECUTABLE IMAGE
Image has the following dependencies:
libstdc++-6.dll
KERNEL32.dll
msvcrt.dll
Summary
1000 .CRT
1000 .bss
1000 .data
1000 .debug_abbrev
1000 .debug_aranges
1000 .debug_frame
1000 .debug_info
1000 .debug_line
1000 .debug_line_str
1000 .debug_str
1000 .eh_frame
1000 .idata
1000 .pdata
1000 .rdata
1000 .reloc
2000 .text
1000 .tls
1000 .xdata
-static
オプションを付けずにコンパイルした実行可能ファイルで試した結果によると、C++は3つの共有ライブラリに依存していることが分かります。
coutを使うと落ちることから、この中ではC++標準ライブラリの定義であるlibstdc++-6.dll
が怪しいです(実際printfを使って標準出力を行うと、libstdc++-6.dll
には依存しなくなるため、このDLLの読み込みに問題が生じていそうです)。
g++ test.cpp -static-libstdc++
で、libstdc++-6.dll
について静的リンクするようにすると、正しく標準出力されるようになったため、実行時のDLL読み込みがうまく行っていないことが分かりました。
DLLの検索順序
本来はg++と同じディレクトリ上のDLLが読み込まれるべきですが、別のものが読み込まれて落ちる状況が発生しています2。そのため、どのような流れでDLLが読まれているのかを知る必要がありました。
実行時のDLL検索順序はどうなっているのでしょう。
調べてみると以下のような順序で決まることが分かりました。
- アプリケーションのexeファイルが存在するディレクトリ
- SYSTEMディレクトリ
- カレントディレクトリ
- 環境変数PATHで設定されたディレクトリ
これを踏まえて、どのようにlibstdc++-6.dll
が検索されるかを調べてみます。
- アプリケーションのexeファイルが存在するディレクトリ
libstdc++-6.dll
は存在しません - SYSTEMディレクトリ(%WINDIR%\System, %WINDIR%\System32)
libstdc++-6.dll
は存在しません - カレントディレクトリ
今回、exeファイルのディレクトリとカレントディレクトリは同じなので、libstdc++-6.dll
は存在しません - 環境変数PATHで設定されたディレクトリ(%PATH%)
C:\anaconda3\Library\mingw-w64\bin
にlibstdc++-6.dll
を発見
上記の流れでlibstdc++-6.dll
が発見されて、読み込まれるようです。これは本来読み込まれるべきDLLではないので、結果落ちたということになります。
Anacondaはかなり前からインストールしていたものの、PATHを通したのは後からだったので気づかず、今回の問題が発生していたということでしょう。
対処法
- Anadondaを削除する
- AnacondaのPATHを変える
今回はとりあえず問題となっているC:\anaconda3\Library\mingw-w64\bin
を含むAnacondaのPATH優先順位を下げることで対応しました。AnacondaのPATHはシステム環境変数で、読み込んでほしいlibstdc++-6.dll
はユーザー環境変数に設定しているため、AnacondaのPATHはユーザー環境変数に設定し直しました。
いずれAnacondaのプログラムを動かしたときに問題が発生しそうですが、今のところAnacondaを使う予定はないのでとりあえずこれで解決したことにします。
参考資料
MinGWのg++でコンパイルしたstd::coutを含むプログラムが強制終了する問題について
what is libstdc++.so file
The GNU C++ Library
Locate a DLL
DLL 検索順序 (1)
DLL のロードの順序 教えて!goo(WebArchive)
Windowsでファイル検索(TECH+)
WindowsでAnacondaのPATHを通そう
-
Linuxでは
ldd
(「list dynamic dependencies」の略)が使われる。WindowsでもGit BashやMSYS2において使うことができる。Windowsで依存関係を調べる方法はDependency WalkerやDependencies、dumpbinが有名。 ↩ -
DLLが検索できなかった場合、システムエラーが発生してそもそも実行できません。今回の状況が発生するのは、同じ名前で想定していないDLLを誤って読み込んでしまっているためです。 ↩