mangle、マングル、名前修飾

これは何の話?

C/C++言語のヘッダファイルの役割extern C のことを理解するときは、mangle のことを知った方が手っ取り早い。ソースコード上の関数名は、とある規則にしたがって変換された名前で、コンパイル後のバイナリ(exe/dll)に埋め込まれている。この名前変換のことをmangle(マングル)と呼びます。

mangle

Wikipediaだと名前修飾 という呼び方で説明されていますね。C/C++言語のソースコードに書かれている関数の名前が、コンパイル後のバイナリ(exe/dll)では、どのような名前になっているか
mangleされた名前のことを、ここではmangle名と呼ぶことにしよう。

lib や dll では、mangle名が埋め込まれている

dumpbin.exe /export XXXX.dll で、mangle名を確認しておこう。

dumpbin.exe /exports XXXX.lib

File Type: LIBRARY
     Exports
       ordinal    name
                  ?piyo@@YAHXZ (int __cdecl piyo(void))

?piyo@@YAHXZ がmangle名だね。カッコは関数piyo のシグネチャだね。

ヘッダファイルはなんで必要なの?

ヘッダファイルがあることで、コンパイラはmangle名をつくることができる。mangle名があれば、それを頼りに、関数を使う側のソースコードから作られたオブジェクトファイルと、lib をリンカがリンクして、一つの実行ファイルを作り上げることができる。

GetProcAddress

mangle名がわかれば、後述のインポートライブラリがなくても、C/C++のdllの関数を呼ぶことができる。 dll をLoadLibraryして、mangle名をつかってGetProcAddressすればいい。 GetProcAddressで得られる関数ポインタの型がわからないと、関数を呼ぶことはできないので、ヘッダファイルは必要だね。ヘッダファイルが分かれば、GetProcAddressの結果をどの関数ポインタ型で受け取ればよいか分かりますね。

インポートライブラリ

C/C++言語でdllをつくると、それとセットでインポートライブラリとよばれるlibができるよね。 とてもおおざっぱにいうと、インポートライブラリは、「同名のdllをLoadLibraryして、mangle名でGetProcAddressして得た関数ポインタを返す関数」が定義された静的ライブラリだと考えると理解しやすいと思います。

mangleの規則はコンパイラによって異なる

C++ Builder で作ったlib-A を、Visual C++ で作ったexe とリンクすることはできないよね。ヘッダファイルがあっても、VC++がつくるmangle名と、lib-A に埋め込まれたmangle名があわないからなんだね。

extern C すると、mangle規則はコンパイラが異なっても同じ

extern C された関数のmangle規則は、C言語風になります。 この規則はとても単純で、関数名がそのままmangle名になる、と思ってよいです。 引数がなんだろうが、戻り値がなんだろうがそんなものには影響されず、関数名だけ がmangle名につかわれます。ライブラリと、ライブラリを使用する側のコンパイラが違うならば、ライブラリを作る側は extern C しておくと、呼び出し側はGetProcAddressをしやすいと思います。 これには副作用があって、C言語風のmangle規則は、引数をmangle名に使わないので、同名の引数違いの関数の定義(関数のオーバーロード)ができなくなります。

__declspec(dllexport)  int piyo();

の dll を dumpbin.exe で見てみる。

C++だとこんなmangle名になる

dumpbin.exe /exports XXXX.dll

    ordinal hint RVA      name
          1    0 000111F4 ?piyo@@YAHXZ = @ILT+495(?piyo@@YAHXZ)

extern "C" だと、こんなmangle名になる

dumpbin.exe /exports XXXX.dll

    ordinal hint RVA      name
          1    0 000112A8 piyo = @ILT+675(_piyo)

自己記述性

これは何の話?

他人がつくったC言語のdll を使うときは、ヘッダファイルがいるよね。ヘッダファイルがないと、dllにどんな関数があるか分からないからね。 でも、他人がつくったC#の(.NETの) dll はリフレクションで、そのdllにどんなメソッドが含まれているか調べることができるし、呼ぶこともできるよね。そういう技術をリフレクションと読んでいるね。リフレクションできるように、プログラミング言語がそなえている性質を、自己記述性と呼ぶことにする。

自己記述性

C#に限らず現代的なプログラミング言語は、ビルドしたバイナリ(dll や classファイル)から、そこにどんなメソッドが含まれているか分かるようになっている。これを自己記述性がある、とか呼ぶことにする。

Webサービスにも似たような性質があって、たとえばSOAPサービスが自身の提供するメソッドの情報をWSDLとして利用者に提示できるのも、この性質の親戚みたいなものだと思います。

「ビルドしたバイナリ」と「Webサービス」は、自己記述性を備えている、と捉えるのがよいと思う。

正確な用語ではない

この性質を自己記述性と呼ぶのは、あまり確立された呼び名ではないかもしれない。ググっても、「自己記述性」という言葉で説明している文章にはあまり出会わないんだよね。 また、この性質を、「ビルドしたバイナリ」と「Webサービス」が共通して備えている性質、と捉えることも、どれほど一般的なのかわかりません。
個人的には、自己記述性は、最近のプログラム言語とWebサービスが備えている、自身が提供するメソッドの情報の開示を実現する性質、と思っています。

Visual Studio で作ったC言語のプログラム を他のPCで実行する

これは何の話?

初学者が、Visual StudioC言語の学習のためにプログラムつくって、それを他のPCで動かそうとしたら、MSVCR***.dll が見つからなかったため、アプリケーションを開始できませんでした。 みたいなメッセージがでて、困りますよね。

こうしよう

「構成プロパティ」-「C/C++」-「コード生成」を選択して、 右側の「ランタイムライブラリ」を 「マルチスレッド DLL (/MD)」から「マルチスレッド (/MT)」に、もしくは 「マルチスレッド デバッグDLL (/MDd)」から「マルチスレッド デバッグ (/MTd)」に変える。

なんで?

既定の設定(/MD や /MDd) でビルドしたexeは、単独では動作しない。 Visual Studio 20XX の Visual C++ 再頒布可能パッケージ を入れれば動作するんだけど、ビルド時にランタイムをexeに組み込んだ方がらくだよね。
組み込んだ方がexeのファイルサイズは大きくなってしまうけど、ここでは気にしないことにしておく。

個人的なIT用語の連想グラフ

私の知っていることの連想をvisでグラフにしてみました。
PCスペックにもよりますが、表示に10秒ほどかかるかもしれません。

このグラフは、「あるキーワードから私が連想するキーワード」のグラフなので、
個人的な経験や思考、思索の結果が反映されているので
一般的に受け入れられた繋がりの表現ではありません。

もう少し一般的な意味を持つように手直しするつもりですが
満足を追求しているといつまでたっても記事にできないので
ひとまず公開。

こんな図を描く経緯

よくある「学習しないエンジニア問題」への対策というと大げさなのですが
中小企業の新卒SEの育成ロードマップを考えるために、自分の経験を整理しようと
思って描きました。

「広く浅く」で器用貧乏になってしまうのは問題ですが、
インハウスSE的な環境の当社では、だいたいの人は
10年ほどたっても「狭く浅く」なってしまうのです。