英語版
このページの英語版を見る

LinuxでのDを使った共有ライブラリの作成

比較目的で仕組みを理解するために、まずC言語での方法を説明する。

Cの静的リンク

CモジュールをCプログラムに静的にリンクするには、

main.c:
#include <stdio.h>

extern int dll();

int main()
{
    printf("+main()\n");
    dll();
    printf("-main()\n");
    return 0;
}
dll.c:
#include <stdio.h>

int dll()
{
    printf("dll()\n");
    return 0;
}
ビルド:
gcc -c dll.c
gcc -c main.c
gcc -o main main.o dll.o

./main
結果:
+main()
dll()
-main()

C言語で共有ライブラリを静的に読み込む

ビルド:
gcc -c dll.c -fpic
gcc -shared -o libdll.so dll.o
gcc -c main.c
gcc -L/home/username/tmp -Wl,-rpath=/home/username/tmp main.o -o main -ldll

(ソースファイル、実行、結果は同一であるべきである。)

rpathは、libdll.soへのパスをmainに埋め込むために使用され、実行時に見つけられるようにする。 他の方法としては、以下のようなものがある(ただし、ここではこれ以上説明しない):

-rpathは、libdll.soへのパスをmainに埋め込むために使用され、実行時に見つけられるようにする。 それを行う他の方法としては(ここでは説明しないが)、

  1. 環境変数 LD_LIBRARY_PATH を設定する。
  2. 共有ライブラリを標準の場所にコピーし、ldconfigを使用してld.soを変更する。

C言語での共有ライブラリの動的ロード

実行時にライブラリをロードするためのコードをmain.cに追加する。

main.c:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main()
{
    printf("+main()\n");

    void *lh = dlopen("/home/walter/tmp/libdll.so", RTLD_LAZY);
    if (!lh)
    {
        fprintf(stderr, "dlopen error: %s\n", dlerror());
        exit(1);
    }
    printf("libdll.so is loaded\n");

    int (*fn)() = dlsym(lh, "dll");
    char *error = dlerror();
    if (error)
    {
        fprintf(stderr, "dlsym error: %s\n", error);
        exit(1);
    }
    printf("dll() function is found\n");

    (*fn)();

    printf("unloading libdll.so\n");
    dlclose(lh);

    printf("-main()\n");
    return 0;
}
ビルド:
gcc -c dll.c -fpic
gcc -shared -o libdll.so dll.o
gcc -c main.c
gcc -rdynamic main.o -o main -ldl

./main
結果:
+main()
libdll.so is loaded
dll() function is found
dll()
unloading libdll.so
-main()

C++の共有ライブラリをCで動的にロードする

ここで問題となるのは、共有ライブラリの静的コンストラクタとデストラクタが実行されるタイミングを把握することである。 dll.c(現在はC++コード)に、それらの静的コンストラクタとデストラクタを挿入する。

dll.c:
#include <stdio.h>

extern "C" int dll()
{
    printf("dll()\n");
    return 0;
}

struct S
{
    S() { printf("libdll.so construction\n"); }
    ~S() { printf("~libdll.so destruction\n"); }
};

S ss;
ビルド:
g++ -c dll.c -fpic
g++ -shared -o libdll.so dll.o
gcc -c main.c
gcc -rdynamic main.o -o main -ldl

./main
結果:
+main()
libdll.so construction
libdll.so is loaded
dll() function is found
dll()
unloading libdll.so
libdll.so destruction
-main()

libphobos2.aとのDプログラムの静的リンク

main.d:
import core.stdc.stdio;
import dll;

int main()
{
    printf("+main()\n");
    dll.dll();
    printf("-main()\n");
    return 0;
}
dll.d:
import core.stdc.stdio;

extern (C) int dll()
{
    printf("dll()\n");
    return 0;
}
ビルド:
dmd -c dll.d
dmd -c main.d
dmd  main.o dll.o

./main
結果:
+main()
dll()
-main()

libphobos2.soとDプログラムの静的リンク

ビルド:
dmd -c dll.d
dmd -c main.d
dmd  main.o dll.o -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is

./main

D共有ライブラリの作成とlibphobos2.soとの静的リンク

D共有ライブラリを使用する際には、コードをlibphobos2.soとリンクする必要があり、 libphobos2.aではない。これは、ガベージコレクタのインスタンスが1つだけになるようにするためである。

ビルド:
dmd -c dll.d -fPIC
dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is
dmd -c main.d
dmd main.o -L-l:libdll.so -defaultlib=libphobos2.so -L-rpath=.:/path/to/where/shared/library/is

./main

DプログラムからC++ DLLを動的にロードする

main.d:
import core.stdc.stdio;
import core.stdc.stdlib;
import core.sys.posix.dlfcn;

extern (C) int dll();

int main()
{
    printf("+main()\n");

    void* lh = dlopen("libdll.so", RTLD_LAZY);
    if (!lh)
    {
        fprintf(stderr, "dlopen error: %s\n", dlerror());
        exit(1);
    }
    printf("libdll.so is loaded\n");

    int function() fn = cast(int function())dlsym(lh, "dll");
    char* error = dlerror();
    if (error)
    {
        fprintf(stderr, "dlsym error: %s\n", error);
        exit(1);
    }
    printf("dll() function is found\n");

    fn();

    printf("unloading libdll.so\n");
    dlclose(lh);

    printf("-main()\n");
    return 0;
}
dll.c:
#include <stdio.h>

extern "C" int dll()
{
    printf("dll()\n");
    return 0;
}

struct S
{
    S() { printf("libdll.so construction\n"); }
    ~S() { printf("libdll.so destruction\n"); }
};

S ss;
ビルド:
g++ -c dll.c -fpic
g++ -shared -o libdll.so dll.o
dmd -c main.d
dmd main.o -L-ldl -L-rpath=.

./main
結果:
+main()
libdll.so construction
libdll.so is loaded
dll() function is found
dll()
unloading libdll.so
libdll.so destruction
-main()

CプログラムからD DLLを動的にロードする

main.c:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main()
{
    printf("+main()\n");

    void *lh = dlopen("/home/walter/tmp/libdll.so", RTLD_LAZY);
    if (!lh)
    {
        fprintf(stderr, "dlopen error: %s\n", dlerror());
        exit(1);
    }
    printf("libdll.so is loaded\n");

    int (*fn)() = dlsym(lh, "dll");
    char *error = dlerror();
    if (error)
    {
        fprintf(stderr, "dlsym error: %s\n", error);
        exit(1);
    }
    printf("dll() function is found\n");

    (*fn)();

    printf("unloading libdll.so\n");
    dlclose(lh);

    printf("-main()\n");
    return 0;
}
dll.d:
import core.stdc.stdio;

extern (C) int dll()
{
    printf("dll()\n");
    return 0;
}

shared static this()
{
    printf("libdll.so shared static this\n");
}

shared static ~this()
{
    printf("libdll.so shared static ~this\n");
}
ビルド:
dmd -c dll.d -fPIC
dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is

gcc -c main.c
gcc -rdynamic main.o -o main -ldl

./main
結果:
+main()
libdll.so shared static this
libdll.so is loaded
dll() function is found
dll()
unloading libdll.so
libdll.so shared static ~this
-main()

libphobos2.soも自動的に動的に読み込まれることに注意。

DプログラムからD DLLを動的にロードする

メインプログラムをlibphobos2.soとリンクさせることが重要であり、libphobos2.aとリンクさせてはならない。そうでないと、 Dランタイムの複数のインスタンスが互いに競合する結果となる。

main.d:
import core.stdc.stdio;
import core.stdc.stdlib;
import core.sys.posix.dlfcn;

extern (C) int dll();

int main()
{
    printf("+main()\n");

    void* lh = dlopen("libdll.so", RTLD_LAZY);
    if (!lh)
    {
        fprintf(stderr, "dlopen error: %s\n", dlerror());
        exit(1);
    }
    printf("libdll.so is loaded\n");

    int function() fn = cast(int function())dlsym(lh, "dll");
    char* error = dlerror();
    if (error)
    {
        fprintf(stderr, "dlsym error: %s\n", error);
        exit(1);
    }
    printf("dll() function is found\n");

    fn();

    printf("unloading libdll.so\n");
    dlclose(lh);

    printf("-main()\n");
    return 0;
}

shared static this() { printf("main shared static this\n"); }

shared static ~this() { printf("main shared static ~this\n"); }
ビルド:
dmd -c dll.d -fPIC
dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is

dmd -c main.d
dmd main.o -L-ldl -defaultlib=libphobos2.so -L-rpath=.:/path/to/where/shared/library/is -map

./main
結果:
main shared static this
+main()
libdll.so shared static this
libdll.so is loaded
dll() function is found
dll()
unloading libdll.so
libdll.so shared static ~this
-main()
main shared static ~this

dlopen() が返す前に DLL の静的コンストラクタが呼び出され、 dlclose() によって静的デストラクタが呼び出されることに注目してほしい。