Cygwinでの共有オブジェクト (DLL) の作り方

2003.11.29新規作成。

Cygwinでは、共有オブジェクト(動的リンクライブラリ;DLL)の拡張子は.dllとなる。ELFフォーマットを使うUNIXでは拡張子は.so であり、また、コンパイルオプションも若干変えないといけない。以下、UNIXと比較しつつ、Cygwinでライブラリを生成する方法を書く。

ファイル名の違い

UNIXでは、次のようなファイル名となる。.so の後ろの .1.2.3 でライブラリのインターフェイスのバージョンを表すことになっている。インターフェイスに非互換な変更があった場合には、foo.so.2 として、foo.so.1 と区別できるようにする。

  foo.so.1.2.3

Cygwin、というかWindowsでは、次のようなファイル名となる。ファイル名にインターフェイスのバージョンが含まれないため、トラブルが起こることが多い。拡張子が異なるため、UNIXと両対応にするためにはMakefileに工夫がいる。あるいはlibtoolなど何かしらツールを使ったほうがいいかも。libtool

  bar.dll

ライブラリの生成

ごく簡単なソースを用意する。ファイル名はmydll.ccとする。

#include <stdio.h>
extern "C" {
void testfunc();
}

void testfunc() {
  printf("test ok.\n");
}

Cygwinでは、ライブラリ用のソースをコンパイルするときに -fPICオプションを付けなくてもいい。Windows(PEフォーマット)では、常に位置独立コード (position-independent code; PIC) が生成される。

$ gcc -c mydll.cc -o mydll.o

リンクは、次のようにする。これでライブラリ mydll.dllが生成される。-sharedを付けないと、WinMain()を探そうとしてエラーになる。UNIXでは libfoo.so のように、ファイル名の頭にlibを付けないといけないが、Cygwinではその必要はない。

$ gcc -shared -o mydll.dll mydll.o

ライブラリを使うメインプログラムを書く。次のファイルは名前をmain.ccとする。

extern "C" {
extern void testfunc();
}

int main() {
  testfunc();
  return 0;
}

コンパイルする。-lオプションで探すライブラリ名にはlibは付けられない。

$ gcc main.cc -L. -lmydll

実行してみる。

$ ./a
test ok.

シンボルの表示

ところで、nmコマンドでエクスポート、インポートの状況を確認できる。I が付いているものは、外部リンクであることを示す。

$ nm mydll.dll | grep testfunc
1000101a T _testfunc
$ nm a.exe | grep testfunc
004040e8 I __imp__testfunc
004016f0 T _testfunc

ちなみに静的にリンクした場合のnmの結果は次のとおり。I の付くものは表示されない。

$ nm a.exe | grep testfunc
0040108a T _testfunc

動的にリンクする

ライブラリは、プログラムの実行を開始してから、動的にリンクすることもできる。

動的にリンクするには、dlopen()、dlsym()、dlclose()を使う。これらは、<dlfcn.h>で宣言されている。

void *dlopen(const char *file, int mode);
void *dlsym(void *restrict handle, const char *restrict name);
int dlclose(void *handle);

先ほどのライブラリを動的にリンクし、testfunc()を呼び出すプログラムは、次のようになる。Cygwinは、dlopen()のモードのうち、RTLD_LOCAL をサポートしていない。

#include <assert.h>
#include <dlfcn.h>

int main() {
    void (*testfunc)();

    void* mylib_handle = dlopen("mydll.dll", /*RTLD_LOCAL |*/ RTLD_LAZY);
    assert(mylib_handle);
    testfunc = (void (*)()) dlsym(mylib_handle, "testfunc");
    assert(testfunc);

    testfunc();

    dlclose(mylib_handle);
    return 0;
}