C++のマングルされたシンボルをC言語で使う

C++のシンボルはマングルされている

C++では関数のオーバーロードや名前空間があるため、シンボルはマングルされ、それぞれの関数が一意に識別されるようになっています。

今回はそのマングルされたシンボルを直接利用してC++で定義された関数をC言語から呼び出してみます。

下ごしらえ

foo.ccとして次のコードを保存します。

#include <iostream>

void foo()
{
  std::cout << "foo called" << std::endl;
}

コンパイルします。

$ g++ -c foo.cc

void foo()がどのようになっているか確認しましょう。

$ nm foo.o
0000000000000062 t _GLOBAL__I__Z3foov
0000000000000000 T _Z3foov
0000000000000022 t _Z41__static_initialization_and_destruction_0ii
                 U _ZNSolsEPFRSoS_E
                 U _ZNSt8ios_base4InitC1Ev
                 U _ZNSt8ios_base4InitD1Ev
                 U _ZSt4cout
                 U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
0000000000000000 b _ZStL8__ioinit
                 U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
                 U __cxa_atexit
                 U __dso_handle
                 U __gxx_personality_v0

_Z3foovが見つかります。

c++filtで確認してみましょう。

$ c++filt _Z3foov
foo()

つまり_Z3foov がマングルされたシンボルです。

次に呼び出す側のC言語のソースを用意します。

void _Z3foov();

int main()
{
  _Z3foov();
  return 0;
}

それではコンパイルしましょう。

$ gcc foo.o main.c
foo.o: In function `foo()':
foo.cc:(.text+0xa): undefined reference to `std::cout'
foo.cc:(.text+0xf): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<c\
har, std::char_traits<char> >&, char const*)'
foo.cc:(.text+0x14): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<c\
har, std::char_traits<char> >&)'
foo.cc:(.text+0x1c): undefined reference to `std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >&\
 (*)(std::basic_ostream<char, std::char_traits<char> >&))'
foo.o: In function `__static_initialization_and_destruction_0(int, int)':
foo.cc:(.text+0x45): undefined reference to `std::ios_base::Init::Init()'
foo.cc:(.text+0x4a): undefined reference to `std::ios_base::Init::~Init()'
foo.o:(.eh_frame+0x12): undefined reference to `__gxx_personality_v0'
collect2: ld はステータス 1 で終了しました

あれれ、リンクエラーが発生しました。

g++でコンパイルしたソースをgccでリンクする場合、libstdc++を明示的に指定してやる必要があります。

$ gcc -lstdc++ foo.o main.c

コンパイル完了。

実行してみましょう。

$ ./a.out
foo called

無事に呼ばれていることがわかります。

まとめ

今回はマングルされたシンボルを直接利用してC++で定義された関数をC言語から呼び出す方法を紹介しました。

ご質問等、何かありましたらこちらへ。