ルギア君の戯言

雑多な記事。

5年2月18日 (土)

みんな「みーっつ! みんな笑顔であかるい世界!」
ルギア君「いま、5年2月18日って言った?」
ドンちゃん「ああ、そう言ったよ。」
ルギア君「1年ずれてるじゃないか!」
ドンちゃん「ええっと、5年が正しい。*1
ルギア君「本当?」
ドンちゃん「今度はまちがいない。」
ルギア君「もう年明けから1ヶ月経っていると言うのに。」
ドンちゃん「去年はあまり活動してなかったからこんなことになったんだ。」
ルギア君「しっかりしてくれ。」
ドンちゃん「よし、わかったら仕事始め! 今日は5年2月18日の土曜日だ。頭の中を塗り替えておけ。」
みんな「はい!」


・・・


ルギア君「うーん・・・」
キリルン「どうしたんですか。」
ルギア君「CMake で作った C++ のライブラリにおなじプロジェクトの別のライブラリからリンクすると undefined reference になっちゃうんだよね・・・」
キリルン「それは難しい話ですね。」
ルギア君「そのライブラリの nm はこうだ。」
キリルン「はい。」
ルギア君「見安いようにマングルは解除しておく。」

[lugia@lugia-castle ~]$ nm /usr/lib/libdragonscript.so -C                       
000032a4 d DW.ref.__gxx_personality_v0                                          
00003108 a _DYNAMIC                                                             
00003224 a _GLOBAL_OFFSET_TABLE_                                                
         w _Jv_RegisterClasses                                                  
         U _Unwind_Resume@@GCC_3.0                                              
000032c0 b guard variable for dragon::IntlCommon::convert(void*, std::string const&)::ret                                                                       
000032b0 b guard variable for dragon::IntlCommon::convert(std::string const&, std::string const&, std::string const&)::ret                                      
000032d0 b guard variable for dragon::IntlCommon::mbstowcs(std::string const&)::ret                                                                             
000032e0 b guard variable for dragon::IntlCommon::wcstombs(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&)::ret         
00000d40 t dragon::IntlCommon::convert(void*, std::string const&)               
00000db0 t dragon::IntlCommon::convert(std::string const&, std::string const&, std::string const&)                                                              
00001070 t dragon::IntlCommon::mbstowcs(std::string const&)                     
00000e60 t dragon::IntlCommon::wcstombs(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&)                                 
000013b0 t dragon::DragonExcept::getLineNum()                                   
000013a0 t dragon::DragonExcept::getFileName()                                  
000013c0 t dragon::DragonExcept::getDescription()                               
00001570 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, dragon::DragonExcept::DragonCode, unsigned int)                                  
000013d0 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, std::string&)                                                                    
00001650 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, dragon::DragonExcept::DragonCode, unsigned int)                                  
000014a0 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, std::string&)                                                                    
00001920 t dragon::DragonExcept::~DragonExcept()                                
00001730 t dragon::DragonExcept::~DragonExcept()                                
00001b10 t dragon::DragonExcept::~DragonExcept()                                
         U __gnu_cxx::__exchange_and_add(int volatile*, int)@@GLIBCXX_3.4       
         U std::exception::what() const@@GLIBCXX_3.4                            
         U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::_Rep::_S_empty_rep_storage@@GLIBCXX_3.4                             
         U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::assign(wchar_t const*, unsigned int)@@GLIBCXX_3.4                   
         U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::reserve(unsigned int)@@GLIBCXX_3.4                                  
         U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::~basic_string()@@GLIBCXX_3.4                                        
         U std::string::_Rep::_M_destroy(std::allocator<char> const&)@@GLIBCXX_3.4                                                                              
         U std::string::_Rep::_S_empty_rep_storage@@GLIBCXX_3.4                 
         U std::string::append(char const*, unsigned int)@@GLIBCXX_3.4          
         U std::string::assign(char const*, unsigned int)@@GLIBCXX_3.4          
         U std::string::assign(std::string const&)@@GLIBCXX_3.4                 
         U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)@@GLIBCXX_3.4          
         U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@@GLIBCXX_3.4                                                 
         U std::exception::~exception()@@GLIBCXX_3.4                            
         U std::terminate()@@GLIBCXX_3.4                                        
000030fc d typeinfo for dragon::DragonExcept                                    
         U typeinfo for std::exception@@GLIBCXX_3.4                             
00001d5c r typeinfo name for dragon::DragonExcept                               
         U vtable for __cxxabiv1::__si_class_type_info@@CXXABI_1.3              
000030e8 d vtable for dragon::DragonExcept                                      
000032c8 b dragon::IntlCommon::convert(void*, std::string const&)::ret          
000032b8 b dragon::IntlCommon::convert(std::string const&, std::string const&, std::string const&)::ret                                                         
000032d8 b dragon::IntlCommon::mbstowcs(std::string const&)::ret                
000032e8 b dragon::IntlCommon::wcstombs(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&)::ret                            
         U operator delete[](void*)@@GLIBCXX_3.4                                
         U operator delete(void*)@@GLIBCXX_3.4
         U operator new[](unsigned int)@@GLIBCXX_3.4
000030cc d __CTOR_END__
000030c8 d __CTOR_LIST__
000030d4 d __DTOR_END__
000030d0 d __DTOR_LIST__
00001f78 r __FRAME_END__
000030d8 d __JCR_END__
000030d8 d __JCR_LIST__
000032a8 A __bss_start
         U __ctype_get_mb_cur_max@@GLIBC_2.0
         U __cxa_allocate_exception@@CXXABI_1.3
         U __cxa_atexit@@GLIBC_2.1.3
         U __cxa_call_unexpected@@CXXABI_1.3
         w __cxa_finalize@@GLIBC_2.1.3
         U __cxa_throw@@CXXABI_1.3
00001d00 t __do_global_ctors_aux
00000c60 t __do_global_dtors_aux
000030e0 d __dso_handle
         U __errno_location@@GLIBC_2.0
         w __gmon_start__
         U __gxx_personality_v0@@CXXABI_1.3
00000d17 t __i686.get_pc_thunk.bx
000032a8 A _edata
000032ec A _end
00001d38 T _fini
00000a48 T _init
000032a8 b completed.5731
000032ac b dtor_idx.5733
00000ce0 t frame_dummy
         U iconv_close@@GLIBC_2.1
         U iconv_open@@GLIBC_2.1
00000d20 t main
         U mbrtowc@@GLIBC_2.0
         w pthread_cancel
         U strlen@@GLIBC_2.0
         U wcrtomb@@GLIBC_2.0
         U wcslen@@GLIBC_2.0
[lugia@lugia-castle ~]$

ルギア君「この dragon:: *2で始まる関数群が、僕が作ったモノなんだが、これを、」

#include "DragonExcept"

#include <iostream>

using namespace std;

int main(int argc, char** argv) {
  try {
    throw dragon::DragonExcept("", 0, false, dragon::DragonExcept::SeeErrno, 0);
    return 0;
  } catch(dragon::DragonExcept& ex) {
    cout << argv[0] << ": ";
    if(ex.getFileName() != "") {
      cout << ex.getFileName();
      if(ex.getLineNum() != 0) {
        cout << "(" << ex.getLineNum() << ")";
      }
      cout << ": ";
    }
    cout << ex.getDescription();
  }
}

ルギア君「とリンクしてコンパイルすると、」

[ 80%] Building CXX object cdragon/src/CMakeFiles/cdragon.dir/main.cpp.o
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:26: 警告: unused parameter ‘argc’
Linking CXX executable cdragon
CMakeFiles/cdragon.dir/main.cpp.o: In function `main':
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:28: undefined reference to `dragon::DragonExcept::DragonExcept(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned int, bool, dragon::DragonExcept::DragonCode, unsigned int)'
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:28: undefined reference to `dragon::DragonExcept::~DragonExcept()'
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:28: undefined reference to `typeinfo for dragon::DragonExcept'
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:32: undefined reference to `dragon::DragonExcept::getFileName()'
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:33: undefined reference to `dragon::DragonExcept::getFileName()'
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:34: undefined reference to `dragon::DragonExcept::getLineNum()'
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:35: undefined reference to `dragon::DragonExcept::getLineNum()'
/home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:39: undefined reference to `dragon::DragonExcept::getDescription()'
CMakeFiles/cdragon.dir/main.cpp.o:(.gcc_except_table+0x28): undefined reference to `typeinfo for dragon::DragonExcept'
collect2: ld はステータス 1 で終了しました
make[2]: *** [cdragon/src/cdragon] エラー 1
make[1]: *** [cdragon/src/CMakeFiles/cdragon.dir/all] エラー 2
make: *** [all] エラー 2

ルギア君「となってしまうんだ・・・。」
キリルン「うーん、私にはわかりませんね。」
ルギア君「CMakeLists.txt を弄れば何とかなるのかな・・・」
キリルン「さあ。試しに名前空間を止めてみるとかはどうでしょうか。」
ルギア君「やり甲斐はあるかね・・・」


・・・


ルギア君「アーカイブにしたらとりあえずできた。」
キリルン「でも、共有ライブラリのほうが良いんでしょ?」
ルギア君「うーん、どうだろ。」
キリルン「速さに関してはわからないということですか?」
ルギア君「そうだな。」

/usr/bin/c++    -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef
-Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security 
-fno-exceptions -fno-check-new -fno-common -Woverloaded-virtual 
-fno-threadsafe-statics -fvisibility=hidden -fvisibility-inlines-hidden 
-fmessage-length=0 -fexceptions -O2 -g -DNDEBUG -DQT_NO_DEBUG  
-fPIC -Wl,--enable-new-dtags  CMakeFiles/cdragon.dir/main.cpp.o CMakeFiles/cdragon.dir/cdragon.cpp.o  
-o cdragon -rdynamic -L/usr/lib/kde4/devel -L/home/lugia/Projects/DragonScript_build/lib
../../lib/libdragonscript.a 
-Wl,-rpath,/usr/lib/kde4/devel:/home/lugia/Projects/DragonScript_build/lib

ルギア君「共有ライブラリにすると libdragonscript.a が libdragonscript.so になるだけなんだがね。」
キリルン「ふうん。」


・・・


ルギア君「あ、わかった!」
キリルン「何?」
ルギア君「CMake で KDE4 を使おうとするといろいろオプションがつくけど。」
キリルン「はい。」
ルギア君「この中のどこかに原因があるな。」

/usr/bin/c++                                                                                                                                                                                                                                                                      -fmessage-length=0 -fexceptions                                -fPIC                         CMakeFiles/cdragon.dir/main.cpp.o CMakeFiles/cdragon.dir/cdragon.cpp.o  -o cdragon -rdynamic                       -L/home/lugia/Projects/DragonScript_build/lib ../../libdragonscript/src/libdragonscript.so -Wl,-rpath,/home/lugia/Projects/DragonScript_build/lib:/home/lugia/Projects/DragonScript_build/libdragonscript/src 
/usr/bin/c++    -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-exceptions -fno-check-new -fno-common -Woverloaded-virtual -fno-threadsafe-statics -fvisibility=hidden -fvisibility-inlines-hidden -fmessage-length=0 -fexceptions -O2 -g -DNDEBUG -DQT_NO_DEBUG  -fPIC -Wl,--enable-new-dtags  CMakeFiles/cdragon.dir/main.cpp.o CMakeFiles/cdragon.dir/cdragon.cpp.o  -o cdragon -rdynamic -L/usr/lib/kde4/devel -L/home/lugia/Projects/DragonScript_build/lib ../../lib/libdragonscript.so                 -Wl,-rpath,/usr/lib/kde4/devel:/home/lugia/Projects/DragonScript_build/lib 

ルギア君「上が KDE4 なし、下があり。」
キリルン「確かに、いっぱいくっついていますね。」
ルギア君「ひとつづつ調べてみるか。」

だいぶ遅い追記

この原因は -fvisibility=hidden でした。このオプションをつけて共有ライブラリをビルドするには、ソースコード中で、エクスポートする関数名を指定する必要があります。

普通なら、

#include <iostream>

int foo()
{
  std::cout << "エクスポートする関数\n";
  return 0;
}

こう書く関数を、

#include <iostream>

__attribute__((visibility("default"))) int foo()
{
  std::cout << "エクスポートする関数\n";
  return 0;
}

と、追加して、

$ g++ -shared -fvisibility=hidden -o foo.so foo.cpp

でビルドします。

この __attribute__ 記述は gcc 専用なので、他のコンパイラでもコンパイルが通るようにするためには、マクロを使って gcc の場合だけ設定されるようにすると良いでしょう。詳しい方法と、Windows の対応については このページ を見てちょ。

visibility のデフォルトは "default" (基本は公開) なのですが、非公開の関数やクラスにつけるよりも __declspec(...) をつける必要のある Windows (正確には VC) との関係の都合上、こっちのほうがやりやすいみたいです。

ちなみに、gcc であれば、CygwinMinGW のデフォルトもやはり "default" なので、__declspec(...) をつけなくても DLL を作れます。ちょこっと作りたいときには便利(かも)。

*1:2005年7月7日 が 1年1月1日。

*2:dragon は名前空間