Provided by: manpages-ja-dev_0.5.0.0.20131015+dfsg-2_all bug

名前

       dlclose, dlerror, dlopen, dlsym - 動的リンクを行うローダへの プログラミングインターフェース

書式

       #include <dlfcn.h>

       void *dlopen(const char *filename, int flag);

       char *dlerror(void);

       void *dlsym(void *handle, const char *symbol);

       int dlclose(void *handle);

       -ldl でリンクする。

説明

       dlopen(), dlsym(), dlclose(), dlerror()  の 4つの関数は、動的リンク (dynamic linking) を行うローダへの イ
       ンタフェースを実装したものである。

   dlerror()
       関数 dlerror()  は、前回 dlerror()  が呼び出された後に、 dlopen(), dlsym(), dlclose()  のいずれかで最後に
       発生したエラーについての説明メッセージを返す。 初期化後または前回呼び出された後で、エラーが発生していなけ
       れば NULL を返す。

   dlopen()
       関数   dlopen()    は、NULL  終端された文字列  filename  で指定されたファイル名の動的ライブラリ  (dynamic
       library) をロードし、 その動的ライブラリへの内部「ハンドル」を返す。 filename が NULL  の場合、メイン・プ
       ログラムへのハンドルが返される。 filename がスラッシュ ("/") を含む場合、(相対か絶対かの)パス名として解釈
       される。 それ以外の場合、動的リンカは以下の手順でライブラリを検索する (詳細は ld.so(8) を参照):

       o   (ELF のみ) 呼び出し元プログラムの実行ファイルに DT_RPATH タグが含まれており、 DT_RUNPATH タグが含まれ
           ていない場合、DT_RPATH タグに書かれている ディレクトリ・リストを検索する。

       o   プログラムの開始時に環境変数 LD_LIBRARY_PATH にコロン区切りのディレクトリのリストが定義されていれば、
           この環境変数に定義されたディレクトリが検索される   (セキュリティ上の理由で、この変数は   set-UID   や
           set-GID された プログラムの場合は無視される)。

       o   (ELF のみ) 呼び出し元プログラムの実行ファイルに DT_RUNPATH タグが含まれて  いる場合、そのタグに書かれ
           ているディレクトリ・リストを検索する。

       o   キャッシュファイル    /etc/ld.so.cache    の中に    filename   のエントリが入っているかをチェックする
           (/etc/ld.so.cacheldconfig(8)  によって管理されている)。

       o   ディレクトリ /lib/usr/lib をこの順番で検索する。

       そのライブラリが他の共有ライブラリに依存している場合は、 依存しているライブラリも動的リンカが同じ検索ルー
       ルに基づいて 自動的にロードする (それらのライブラリにさらに依存関係がある場合などは  この処理は再帰的に行
       われる)。

       flag には以下の 2 つの値のいずれかを含めなければならない:

       RTLD_LAZY
              lazy  binding (手抜きなシンボルの結び付け) が行う。 シンボルの解決はそのシンボルを参照するコードが
              実行されるときにのみ 行われる。シンボルが一度も参照されなかった場合には、そのシンボルは 解決されな
              いままとなる。 (lazy binding は関数参照についてのみ実施される; 変数への参照は常に ライブラリがロー
              ドされた時点で直ちに解決される。)

       RTLD_NOW
              この値が指定されるか、環境変数 LD_BIND_NOW に空でない文字列が設定された場合、 ライブラリ中の未定義
              のシンボルを全て解決してから dlopen()  は復帰する。解決できなかったときにはエラーが返される。

       以下の値のうち 0 個以上を論理和 (OR) の形で flag に追加することもできる:

       RTLD_GLOBAL
              このライブラリで定義されているシンボルが、これより後でロードされる  ライブラリのシンボル解決で利用
              できるようになる。

       RTLD_LOCAL
              このフラグは RTLD_GLOBAL の反対の意味であり、どちらのフラグも指定されなかった場合は こちらがデフォ
              ルトとなる。 このライブラリで定義されているシンボルは、これより後でロードされる ライブラリでのシン
              ボル参照で利用できない。

       RTLD_NODELETE (glibc 2.2 以降)
              dlclose()  中にそのライブラリをアンロードしない。 そのため、同じライブラリをこれ以降に dlopen() で
              再度ロードした場合に、ライブラリ内の静的変数は再初期化されない。  このフラグは POSIX.1-2001 では規
              定されていない。

       RTLD_NOLOAD (glibc 2.2 以降)
              そのライブラリをロードしない。  このフラグはそのライブラリがすでに組み込まれているかを検査するのに
              利用できる (dlopen() は、ライブラリが組み込まれていなければ NULL を返し、 すでに組み込まれていれば
              そのライブラリのハンドルを返す)。 また、すでにロードされているライブラリのフラグを昇格させるのにも
              利用できる。例えば、過去に  RTLD_LOCAL でロードしたライブラリを RTLD_NOLOAD | RTLD_GLOBAL で再オー
              プンすることができる。 このフラグは POSIX.1-2001 では規定されていない。

       RTLD_DEEPBIND (glibc 2.3.4 以降)
              このライブラリ内のシンボルの参照領域をグローバル領域よりも前に配置する。  つまり、内蔵型のライブラ
              リでは、すでにロードされたライブラリに含まれる  同じ名前のグローバルなシンボルよりも自ライブラリ内
              のシンボルが優先して 使われる。 このフラグは POSIX.1-2001 では規定されていない。

       filename が NULL ポインタである場合は、 返されるハンドルはメイン・プログラムのものになる。 このハンドルが
       dlsym() に渡されると、シンボルの検索は、メイン・プログラム内、 プログラムの起動時にロードされる全ての共有
       ライブラリ、 dlopen()  によって RTLD_GLOBAL  フラグ付きでロードされた全ての共有ライブラリ、の順序で行われ
       る。

       オープンされたライブラリ中での外部参照は、  そのライブラリの依存リストにあるライブラリか、 RTLD_GLOBAL フ
       ラグ付きで既にオープンされているライブラリを使って解決される。    実行ファイルが    "-rdynamic"    フラグ
       ("--export-dynamic"  も同義)  付きでリンクされている場合は、実行ファイル中のグローバルシンボルも、 動的に
       ロードされるライブラリ内の参照解決に用いられる。

       同じライブラリが dlopen()  によって再度ロードされた場合には、同じファイルハンドルが返される。 dl ライブラ
       リはライブラリハンドルのリンク数を管理している。 したがって動的ライブラリは dlclose()  が dlopen() と同じ
       回数だけ呼び出されない限りアンロードされない。 _init()  ルーチンは一度だけ呼び出される (_init()  が存在す
       る場合のみ)。 RTLD_NOW が指定されて dlopen()  が呼び出された場合、 RTLD_LAZY で以前にロードされたライブラ
       リのシンボル解決が実行されることがある。

       dlopen()  は、何らかの理由で失敗すると NULL を返す。

   dlsym()
       関数 dlsym()  は、 dlopen()  が返した動的ライブラリの「ハンドル」と、 NULL 終端されたシンボル名の文字列を
       引き数に取り、  そのシンボルがロードされたメモリのアドレスを返す。 シンボルが、指定されたライブラリと、指
       定されたライブラリがロードされる際に dlopen()  が自動的にロードしてライブラリのいずれにも見つからない場合
       には、 dlsym()  は NULL を返す (dlsym() による検索は、これらのライブラリの依存関係のツリーを先頭から 辿っ
       て行われる)。  実際にはシンボルの値自体が  NULL  になることもある (そのため、 dlsym()  の返り値が NULL で
       あったとしても必ずしもエラーという訳ではない)。     エラーかどうかを確認する正しい方法は以下の通りである:
       dlerror()  を呼び出して以前のエラー状態をクリアしてから、 dlsym()  を呼び出す。その後でもう一度 dlerror()
       を呼び出して、 dlerror()  の返り値を変数に保存し、保存した値が NULL であるか判定する。

       RTLD_DEFAULTRTLD_NEXT という二つの特別な擬似ハンドルがある。 RTLD_DEFAULT は、デフォルトのライブラリ
       検索順序にしたがって、 検索対象のシンボルが最初に現れるところを探す。 RTLD_NEXT  は、ライブラリ検索順序の
       中で現在のライブラリ以降で最初に  関数が現れるところを探す。この機能を使うことで、別の共有ライブラリの 関
       数へのラッパーを提供することができる。

   dlclose()
       関数 dlclose()   は動的ライブラリのハンドル  handle  の参照カウントを  1  減らす。参照カウントが  0  にな
       り、ロードされている  他のライブラリからそのライブラリ内のシンボルが使われていなければ、 その動的ライブラ
       リをアンロードする。

       関数 dlclose()  は、成功した場合は 0 を返し、エラーの場合 0 以外を返す。

   廃止されたシンボル _init()  _fini()
       リンカは _init_fini を特別なシンボルと解釈する。 ある動的ライブラリで _init()  という名前のルーチンが
       エクスポートされていれば、  そのコードは、ライブラリのロード後、かつ dlopen()  が復帰する前に実行される。
       その動的ライブラリで _fini()  という名前のルーチンがエクスポートされていれば、 ライブラリがアンロードされ
       る直前にそのルーチンが呼び出される。 システムの起動ファイルに対するリンクを避ける必要がある場合、  gcc(1)
       のコマンドラインに -nostartfiles オプションを指定すればよい。

       このルーチンや、gcc  のオプション  -nostartfiles-nostdlib は使用しないことを推奨する。 これらを使う
       と、望ましくない動作をすることがある。          なぜなら、(特別な措置が行われない限り)           これらの
       constructor/destructor ルーチンは実行されないからである。

       代わりに、ライブラリは __attribute__((constructor))__attribute__((destructor)) の関数属性を使って必要
       なルーチンをエクスポートするのがよい。 これらについては gcc の info ページを参照のこと。 constructor ルー
       チンは dlopen()  が復帰する前に実行され、 destructor ルーチンは dlclose()  が復帰する前に実行される。

   GNU での拡張: dladdr()  dlvsym()
       glibc では POSIX には記載されていない関数が 2つ追加されている。 プロトタイプは以下の通りである。

       #define _GNU_SOURCE         /* feature_test_macros(7) 参照 */
       #include <dlfcn.h>

       int dladdr(void *addr, Dl_info *info);

       void *dlvsym(void *handle, char *symbol, char *version);

       関数  dladdr()   は、関数のポインタを引き数にとり、関数の名前と関数が定義されている  ファイルの解決を試み
       る。情報は Dl_info 構造体に格納される。

           typedef struct {
               const char *dli_fname;  /* Pathname of shared object that
                                          contains address */
               void       *dli_fbase;  /* Address at which shared object
                                          is loaded */
               const char *dli_sname;  /* Name of nearest symbol with address
                                          lower than addr */
               void       *dli_saddr;  /* Exact address of symbol named
                                          in dli_sname */
           } Dl_info;

       addr にマッチするシンボルが見つからなかった場合、 dli_snamedli_saddr は NULL にセットされる。

       dladdr()  は、エラー時には 0 を返し、成功した場合は 0 以外を返す。

       関数 dlvsym()  は dlsym()  と同じ動作をするが、バージョンの文字列を渡す引き数が  追加されている点が異なる
       (dlvsym()  はバージョン 2.1 以降の glibc で提供されている)。

準拠

       POSIX.1-2003 には dlclose(), dlerror(), dlopen(), dlsym().  の記載がある。

注意

       シンボル   RTLD_DEFAULTRTLD_NEXT<dlfcn.h>  で定義されており、  <dlfcn.h>  のインクルード前に
       _GNU_SOURCE が定義されている場合のみ有効となる。

       glibc 2.2.3 以降では、 atexit(3)  を使って、ライブラリがアンロードされる際に自動的に呼び出される 終了ハン
       ドラ (exit handler) を登録することができる。

   歴史
       dlopen インターフェースの標準は SunOS をもとにしている。 SunOS には dladdr()  もあったが、 dlvsym()  はな
       かった。

バグ

       時として、  dladdr()  に渡した関数ポインタは驚くような値になることがある。 いくつかのアーキテクチャ (特に
       i386 と x86_64) では、 引き数として使用した関数が動的リンクライブラリで定義されるもので  あったとしても、
       dli_fnamedli_fbasedladdr()  を呼び出したオブジェクトを参照した状態で終わっていることがある。

       問題は、関数ポインタの解決は今なおコンパイル時に行われるが、     そのポインタは元のオブジェクトの     plt
       (Procedure Linkage Table) セクションを指しているだけだという点にある  (オブジェクト自体は、ダイナミックリ
       ンカによってシンボルの解決が行われた後に、  関数の呼び出しを行う)。 これに対処する方法としては、 コードを
       position-independent でコンパイルするという方法がある。  そうすると、コンパイラはコンパイル時にポインタを
       用意することができず、  今日の  gcc(1)   では、実行時に  dladdr()  に関数ポインタを渡す前に、 got (Global
       Offset Table) から最終的なシンボルのアドレスをロードするだけの コードが生成される。

       math ライブラリをロードし、2.0 の余弦を表示する

       #include <stdio.h>
       #include <stdlib.h>
       #include <dlfcn.h>

       int
       main(int argc, char **argv)
       {
           void *handle;
           double (*cosine)(double);
           char *error;

           handle = dlopen("libm.so", RTLD_LAZY);
           if (!handle) {
               fprintf(stderr, "%s\n", dlerror());
               exit(EXIT_FAILURE);
           }

           dlerror();    /* Clear any existing error */

           /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
              would seem more natural, but the C99 standard leaves
              casting from "void *" to a function pointer undefined.
              The assignment used below is the POSIX.1-2003 (Technical
              Corrigendum 1) workaround; see the Rationale for the
              POSIX specification of dlsym(). */

           *(void **) (&cosine) = dlsym(handle, "cos");

           if ((error = dlerror()) != NULL)  {
               fprintf(stderr, "%s\n", error);
               exit(EXIT_FAILURE);
           }

           printf("%f\n", (*cosine)(2.0));
           dlclose(handle);
           exit(EXIT_SUCCESS);
       }

       このプログラムを "foo.c" に書いたとすると、以下のコマンドでプログラムを ビルドできる。

           gcc -rdynamic -o foo foo.c -ldl

       _init()  と _fini()  をエクスポートするライブラリの場合は 以下のようにしてコンパイルする必要がある。 例と
       して bar.c をコンパイルする場合:

           gcc -shared -nostartfiles -o bar bar.c

関連項目

       ld(1), ldd(1), dl_iterate_phdr(3), rtld-audit(7), ld.so(8), ldconfig(8)

       ld.so info pages, gcc info pages, ld info pages

この文書について

       この man ページは Linux man-pages プロジェクトのリリース 3.54 の一部 である。プロジェクトの説明とバグ報告
       に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。

Linux                                              2008-12-06                                          DLOPEN(3)