/MD と /MTの話

投稿者: | 2017/9/29 金曜日

上のようなエラーがどうして起こるのかの話。ただしどこからはじめても要素が絡み合ってまとまらないので適当に書く。

コンパイルフェーズ

ソースコードからどのように最終成果物のexeやdllが生成されるか?libは最終成果物というよりも開発途上物と呼ぶべきもの(一般ユーザに配布するわけじゃないから)。

1、ソースファイルから*.objをつくる。
clコマンドでつくる。/cをつくるとコンパイルだけしてリンクしない。ここで/MDや/MTの指定ができる。デフォルトは/MT。デバッグの話を混ぜるとさらに複雑になるので省略。マルチスレッドも省略(今はほとんどマルチスレッド)。

/MTや/MDはCRT(Cランタイムライブラリ(printf()とか))の扱いを決める。ほとんどのプログラムがCRTをつかう。CRTはmain()を呼んだり、__argcをつくったりするのでないと大変。

/MTを指定すると、スタティックライブラリのCRTを使うことを指定する。これは最終成果物のexeやdllにCRTの実装が入ることを意味する。clで*.objをつくった際、このobjにはリンカーへの指示としてLIBCMT.libとともにリンクするよう伝えるように書き込まれる。libコマンドで*.libをつくる際にも同じ情報が書き込まれる。後々linkで最終成果物を作るときにLIBCMT.libがコピー(みたいに)される。

プロジェクトがいくつものライブラリをリンクする際(外部ライブラリも含めて)すべて/MTで統一されていれば、リンクエラーは起きない(VCのバージョンが違う場合はどうなるか知らない)。すべての*.objや*.libがLIBCMT.libとリンクしてほしいと指示しているので。

/MDを使ってobjをつくるとCRTのダイナミックライブラリとリンクしたいことを指示する。objやlibにはmsvcrt.libとリンクするように指示される。msvcrt.libはインポートライブラリで実装はmsvcrtXXX.dllにある。

link時に/MTと/MDが混在しているとLIBCMT.libとmsvcrt.libが両方指定されて、この2つは元は同じもののはずだから(ソースコードレベルでは)同じシンボルを持ち、上記のようなエラーが発生する。よって上記のようなエラーが出た場合には、/MTや/MDを直すべきのはず。

シナリオ

ただ難しいのはあるソースから作られるものはexeとdllとlibがあって、dllをつくるときもlibができて、dllをつくるときの/MTや/MDをどうするのがいいのかということになると思う。

自分が作るのがexeで使うスタティックライブラリが/MTで作られていたなら自分も/MTにするしかない(はず)。しかしちなみにC++/CLIでは/MDしか許されないのでこれができない。

自分が作るのがスタティックライブラリのときはどうなるだろうか。/MTを指定すると、自分を使う人にも/MTを使ってほしいことになる。/MDを指定すると自分を使う人にも/MDを使ってほしいことになる?(はず)

さらに難しいのがdllをつくるときに外部ライブラリも必要な場合どうなるのかということだ。MSのドキュメントにはit is not recommended to link statically to the CRT in a DLL unless the consequences of this are specifically desired and understoodと書かれているので/MDを使うことになるが、その外部ライブラリが/MTなら自分も/MTにするしかなくそうなった場合いったい何が起こるのか。簡単に言うとMSが推奨してない/MTでdllをつくってそのdllつかうexeをコンパイルしたり実行したときどうなるのかの問題。

よくわからないが結論

スタティックライブラリを作るときは/MTを使う。DLLをつくるときは/MDをつかう。

しかしこうなると2つのライブラリを自分のexeに取り込みたいとき、そしてその2つがスタティックライブラリとDLLしか提供していない場合に、どちらも取り込むということができなくなるのではないか?

よってライブラリ作成者はスタティックバージョンとDLLバージョンの両方を用意するのが普通ということになるのだろうか。

LoadLibraryはどうなる

LoadLibraryが失敗するとき、シンボルが見つからないからとか言うエラーはよく見るが、シンボルが重複しているからというのは見たことがない。もしかして2重に持つのだろうか?(上記のような矛盾の場合)もしそうなら上で書いたできないの内いくつかは解決するはず。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です