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)