void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
そのライブラリが他の共有ライブラリに依存している場合は、 依存しているライブラリも動的リンカーが同じ検索ルールに基づいて 自動的にロードする (それらのライブラリにさらに依存関係がある場合などは この処理は再帰的に行われる)。
flag には以下の 2 つの値のいずれかを含めなければならない:
以下の値のうち 0 個以上を論理和 (OR) の形で flag に追加することもできる:
filename が NULL である場合は、 返されるハンドルはメインプログラムのものになる。 このハンドルが dlsym() に渡されると、シンボルの検索は、メインプログラム内、 プログラムの起動時にロードされる全ての共有ライブラリ、 dlopen() によって RTLD_GLOBAL フラグ付きでロードされた全ての共有ライブラリ、の順序で行われる。
オープンされたライブラリ中での外部参照は、 そのライブラリの依存リストにあるライブラリか、 RTLD_GLOBAL フラグ付きで既にオープンされているライブラリを使って解決される。 実行ファイルが "-rdynamic" フラグ ("--export-dynamic" も同義) 付きでリンクされている場合は、実行ファイル中のグローバルシンボルも、 動的にロードされるライブラリ内の参照解決に用いられる。
同じライブラリが dlopen() によって再度ロードされた場合には、同じライブラリハンドルが返される。 dl ライブラリはライブラリハンドルのリンク数を管理している。 したがって動的ライブラリは dlclose() が dlopen() と同じ回数だけ呼び出されない限りアンロードされない。 _init() ルーチンは一度だけ呼び出される (_init() が存在する場合のみ)。 RTLD_NOW が指定されて dlopen() が呼び出された場合、 RTLD_LAZY で以前にロードされたライブラリのシンボル解決が実行されることがある。
dlopen() は、何らかの理由で失敗すると NULL を返す。
RTLD_DEFAULT と RTLD_NEXT という二つの特別な擬似ハンドルがある。 RTLD_DEFAULT は、デフォルトのライブラリ検索順序にしたがって、 検索対象のシンボルが最初に現れるところを探す。 RTLD_NEXT は、ライブラリ検索順序の中で現在のライブラリ以降で最初に 関数が現れるところを探す。この機能を使うことで、別の共有ライブラリの 関数へのラッパーを提供することができる。
関数 dlclose() は、成功した場合は 0 を返し、エラーの場合 0 以外を返す。
このルーチンや、gcc のオプション -nostartfiles や -nostdlib は使用しないことを推奨する。 これらを使うと、望ましくない動作をすることがある。 なぜなら、(特別な措置が行われない限り) これらの constructor/destructor ルーチンは実行されないからである。
代わりに、ライブラリは __attribute__((constructor)) や __attribute__((destructor)) の関数属性を使って必要なルーチンをエクスポートするのがよい。 これらについては gcc の info ページを参照のこと。 constructor ルーチンは dlopen() が復帰する前に実行され、 destructor ルーチンは dlclose() が復帰する前に実行される。
#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 symbol whose definition overlaps addr */ void *dli_saddr; /* Exact address of symbol named in dli_sname */ } Dl_info;
addr にマッチするシンボルが見つからなかった場合、 dli_sname と dli_saddr は NULL にセットされる。
dladdr() は、エラー時には 0 を返し、成功した場合は 0 以外を返す。
関数 dlvsym() は dlsym() と同じ動作をするが、バージョンの文字列を渡す引き数が 追加されている点が異なる (dlvsym() はバージョン 2.1 以降の glibc で提供されている)。
glibc 2.2.3 以降では、 atexit(3) を使って、ライブラリがアンロードされる際に自動的に呼び出される 終了ハンドラー (exit handler) を登録することができる。
問題は、関数ポインターの解決は今なおコンパイル時に行われるが、 そのポインターは元のオブジェクトの plt (Procedure Linkage Table) セクションを指しているだけだという点にある (オブジェクト自体は、ダイナミックリンカーによってシンボルの解決が行われた後に、 関数の呼び出しを行う)。 これに対処する方法としては、 コードを position-independent でコンパイルするという方法がある。 そうすると、コンパイラはコンパイル時にポインターを用意することができず、 今日の gcc(1) では、実行時に dladdr() に関数ポインターを渡す前に、 got (Global Offset Table) から最終的なシンボルのアドレスをロードするだけの コードが生成される。
#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 */ cosine = (double (*)(double)) dlsym(handle, "cos"); /* ISO の C 標準によれば、上のような、関数ポインターと 'void *' 間の キャストを行った場合に得られる結果は不定である。 POSIX.1-2003 と POSIX.1-2008 では、この状況は認められており、 以下のようなワークアラウンドが提案されている。 *(void **) (&cosine) = dlsym(handle, "cos"); この (ぶかっこうな) キャストは ISO の C 標準に従っており、 コンパイラの警告を避けることができる。 POSIX.1-2008 の 2013 Technical Corrigendum (別名 POSIX.1-2013) では、 POSIX に準拠する実装では 'void *' から関数ポインターへの キャストをサポートすることが要求されるようになり、状況が改善 された。にもかかわらず、('-pedantic' オプションを指定した gcc などの) いくつかのコンパイラは、このプログラムで使用されている キャストについて文句を言うのだ。 error = dlerror(); if (error != 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.so info pages, gcc info pages, ld info pages
[man1]
[man2]
[man3]
[man4]
[man5]
[man6]
[man7]
[man8]
[a]
[b]
[c]
[d]
[e]
[f]
[g]
[h]
[i]
[j]
[k]
[l]
[m]
[n]
[o]
[p]
[q]
[r]
[s]
[t]
[u]
[v]
[w]
[x]
[y]
[z]