dlopenで共有ライブラリを使ってみる

2012-05-14

共有ライブラリとは

共有ライブラリとは字のごとく、共有で使われるライブラリです。

通常、実行形式のバイナリは共有ライブラリを参照し、共有ライブラリに存在する関数を呼び出したりして実行します。

共有ライブラリの中の関数がいわゆるAPIと呼ばれるものです。

共有ライブラリの仕組がないと、実行形式に必要な全てのコードを含まなくてはならないため、実行形式のサイズは肥大化し、 実行中のメモリ消費量も大きくなってしまいます。

共有ライブラリによってメモリやディスクを節約できるわけです。

また、共有ライブラリが提供する機能はAPIとして外部のプログラムから呼び出すことができます。

今回は共有ライブラリのAPIをdlopenを使って手軽に呼び出す方法を紹介します。

dlopenを使う

それでは共有ラリブラリを呼び出す方法を見ていきましょう。

まずはせっかくなので呼び出す対象の共有ライブラリを作ってみましょう。

foo.cとして次の内容を保存します。

#include <stdio.h>

void foo()
{
  printf("foo called!\n");
}

"foo called!"だけ標準出力に表示する単純な関数fooを定義しています。

コンパイルします。

$ gcc -shared -fPIC -o foo.so foo.c

これで共有ライブラリfoo.soができました。

ついでなのでnmでシンボルを確認してみましょう。

$ nm foo.so
0000000000200658 a _DYNAMIC
00000000002007f0 a _GLOBAL_OFFSET_TABLE_
                 w _Jv_RegisterClasses
0000000000200630 d __CTOR_END__
0000000000200628 d __CTOR_LIST__
0000000000200640 d __DTOR_END__
0000000000200638 d __DTOR_LIST__
0000000000000620 r __FRAME_END__
0000000000200648 d __JCR_END__
0000000000200648 d __JCR_LIST__
0000000000200818 A __bss_start
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000000580 t __do_global_ctors_aux
00000000000004c0 t __do_global_dtors_aux
0000000000200650 d __dso_handle
                 w __gmon_start__
0000000000200818 A _edata
0000000000200828 A _end
00000000000005b8 T _fini
0000000000000458 T _init
00000000000004a0 t call_gmon_start
0000000000200818 b completed.6347
0000000000200820 b dtor_idx.6349
000000000000056c T foo
0000000000000540 t frame_dummy
                 U puts@@GLIBC_2.2.5

なにやらいろいろと出てきましたが、fooがあることが確認できますね。

次に呼び出し元のコードを書きましょう。 main.cとして以下の内容で保存します。

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

int main()
{
  void *handle = dlopen("./foo.so", RTLD_LAZY);

  if (handle == 0) {
    fprintf(stderr, "%s\n", dlerror());
    return 1;
  }

  void (*func)(void) = dlsym(handle, "foo");
  if (func == 0) {
    fprintf(stderr, "%s\n", dlerror());
    return 1;
  }

  func();

  if (dlclose(handle) != 0) {
    fprintf(stderr, "%s\n", dlerror());
    return 1;
  }

  return 0;
}

コンパイルして実行します。

$ gcc -ldl main.c
$ ./a.out
foo called!

foo.soの関数fooが呼び出されていることがわかります。

まとめ

今回はdlopenを使って手軽に共有ライブラリの関数を呼び出す方法を見ていきました。

dlopenは実行時にライブラリのシンボルから関数のアドレスを取得するため、 使いたい共有ライブラリをコンパイル時にリンクする必要がありません。

そのため、dlopenを使ってプラグインや機能のカスタマイズを行うことが可能となります。

ご質問等、何かありましたらこちらへ。