Linux で使用されるインターフェースは、基本的に Solaris のものと同じです。 これを「dlopen()」API と呼ぼうと思います。 しかし、このインターフェースは全てのプラットフォームでサポートされているわけではありません。 HP-UX では shl_load() という別の仕組みが使用され、Windows プラットフォームでは全く異なるインターフェースである DLL が使用されます。 広範な移植性を最終目標とするならば、おそらく、プラットフォーム間の差違を隠蔽するラッパーライブラリの使用を検討したほうがよいでしょう。 方法の一つとして、モジュールの動的ローディングをサポートする glib ライブラリがあります。 glib ライブラリは、プラットフォーム固有の動的ローディング・ルーチン群を内部で使用し、動的ローディング用の移植性の高いインターフェースを実装しています。 glib については、http://developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html を参照してください。 glib のインターフェースについては、glib のドキュメントで十分に説明されているので、ここではこれ以上言及しません。 もう一つの方法は、libltdl を使う方法です。 libltdl は、GNU libtool の一部です。 もっと多くの機能が必要ならば、CORBA Object Request Broker (ORB) を調べてみるのもよいでしょう。 Linux と Solaris でサポートされるインターフェースを直接使用することにまだ興味をお持ちならば、読み進んでください。
C++ と動的ライブラリを使用する開発者の方は、「C++ dlopen mini HOWTO」も参照してください。
dlopen(3) 関数は、ライブラリをオープンし、使用前の準備をおこないます。 C のプロトタイプは次のようになります。
void * dlopen(const char *filename, int flag); |
ライブラリが他のライブラリに依存しているなら (例えば、X が Y に依存している)、依存されているほうを先にロードしてください (この例で言えば、Y を先にロードし、それから X をロードします)。
dlopen() の戻り値は、他の動的ライブラリ・ルーチンによって使用される「ハンドル」です (このハンドルの実体は隠蔽されているものとして扱ってください)。 ロードが失敗した場合、dlopen() は NULL を返しますので、戻り値をチェックする必要があります。 同じライブラリが dlopen() で二回以上ロードされると、同じファイルハンドルが返されます。
古いシステムでは、ライブラリが _init という名前のルーチンをエクスポートしている場合、dlopen() から戻る前にそのコードが実行されます。 この仕組みを使って、ライブラリの初期化ルーチンを実装することができます。 しかし、ライブラリは、_init や _fini といった名前のルーチンをエクスポートすべきではありません。 これらの仕組みは古くなっており、望んでいない動作をする可能性があります。 かわりに、ライブラリでは、__attribute__((constructor)) 関数属性と __attribute__((destructor)) 関数属性を使用してルーチンをエクスポートしてください (gcc を使用しているものと仮定しています)。 詳細については Section 5.2 を参照してください。
void * dlsym(void *handle, char *symbol); |
dlerror(); /* エラーコードをクリアする。*/ s = (actual_type) dlsym(handle, symbol_being_searched_for); if ((err = dlerror()) != NULL) { /* ハンドルエラー。シンボルは見つからなかった。*/ } else { /* シンボルが見つかった。値は s に格納されている。*/ } |
dlopen() の逆が dlclose() です。dlclose() で動的ライブラリをクローズします。 dl ライブラリは動的なファイルハンドルへのリンク数を管理しているので、同一の動的ライブラリに対して、dlopen() が成功した回数と同じ数の dlclose() が呼ばれない限り、当該ライブラリは実際にはメモリ上から削除されません。 ですので、同じプログラムが同じライブラリを何回ロードしても、問題にはなりません。 ライブラリの割当てが解除されるとき、古いライブラリでは、(もしも存在するならば) _fini 関数が呼ばれます。 しかし、_fini は古い仕組みなので、これに依存してはいけません。 かわりに、ライブラリでは、__attribute__((constructor)) 関数属性と __attribute__((destructor)) 関数属性を使用してルーチンをエクスポートしてください。 詳細については Section 5.2 を参照してください。 注意:dlclose() は、成功ならば 0 を、エラーならば非ゼロを返します。 この返り値について言及していない Linux man ページもあります。
#include <stdlib.h> #include <stdio.h> #include <dlfcn.h> int main(int argc, char **argv) { void *handle; double (*cosine)(double); char *error; handle = dlopen ("/lib/libm.so.6", RTLD_LAZY); if (!handle) { fputs (dlerror(), stderr); exit(1); } cosine = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } printf ("%f\n", (*cosine)(2.0)); dlclose(handle); } |
このプログラムが "foo.c" という名前のファイルだとすると、次のコマンドでプログラムを作成することができます。
gcc -o foo foo.c -ldl |