なんとなく分かった気でいるふたつのライブラリ。
それを小さなテストコードを使いながら整理してみます。
(ちなみに今回紹介するのはMacでのものです。Linuxではフォーマット、コンパイル方法などが異なります)
静的ライブラリをコンパイルする
まずは静的ライブラリです。
ちなみにMacではMach-Oというフォーマットを使います。
(Linuxでは「ELF(Executable and Linking Format)」というらしい)
こちらの記事が分かりやすかったです。
また、静的ライブラリはオブジェクトファイルをまとめたただのアーカイブです。
(なのでar
(アーカイブ)コマンドを使う)
例として以下のように静的ライブラリの中身を定義しました。
#include <stdio.h>
int static_func() {
printf("This is a static library demo.\n");
return 0;
}
これをまずはオブジェクトファイルにします。
$ gcc -c static-library.c
すると、static-library.o
が生成されます。
これを静的ライブラリとするため、ar
コマンドを使ってアーカイブします。
ar r libstatic.a static-library.o
最後に、この静的ライブラリを使うファイルを作成します。
#include <stdio.h>
int static_func();
int main() {
static_func();
}
int static_func();
でプロトタイプを宣言しています。
本来はここにはヘッダファイルなどを読み込ませたりしますが、今回はサンプルなので直に書いています。
あとはコンパイル時にリンクしてやれば無事、静的ライブラリがリンクされます。
$ gcc main.c -lstatic
引数オプション(-l
)に、ファイル名からlib
と拡張子を取ったものを指定します。
(今回の例ではlibstatic.a
を生成しているので-lstatic
としています)
これを実行すると以下のようになります。
$ ./a.out
This is a static library demo.
ちなみに「リンカ」がやってくれることをざっくり説明してくれている記事がありました。
リンカがやっている概要を引用させてもらうと、
大雑把な流れ
リンカは未解決の参照のリストを保持している。はじめ、このリストにはmainが入っている [1]。 リンカはオブジェクトファイル(.o)を受け取り、参照を解決し、新しい未解決の参照がみつかったらリストに追加する(リンクする)。 静的ライブラリ(.a)を受け取った場合は、未解決の参照を定義しているオブジェクトファイルのみを静的ライブラリから抜き出し、リンクを行う。 いままで処理したオブジェクトファイルから参照されていないオブジェクトファイルは 静的ライブラリに含まれていても無視される。
ということのようです。
共有ライブラリをコンパイルする
Mac版
次に共有ライブラリをコンパイルします。
#include <stdio.h>
int hoge_func() {
printf("This is a shared library.\n");
return 0;
}
単純にprintf
を実行するだけの関数です。
これを以下のようにしてコンパイルします。
(ちなみに共有ライブラリの拡張子は.dylib
です)
$ gcc -dynamiclib -o libshared.dylib shared-lib.c
Windows版
[2018.02.20 追記]
Windowsで同様のことをやる場合は、Cygwinを利用すると楽です。
コマンドで以下のようにします。
$ gcc -c dlltest.c
$ ls
dlltest.c dlltest.o
-c
オプションは、「Compile and assemble, but do not link
」と書かれているので、コンパイルだけを行うオプションです。
コンパイルが成功するとオブジェクトファイル(拡張子が.o
のもの)が生成されます。
これを、DLLに変換します。
$ gcc -shared -o dlltest.dll dlltest.o
$ ls
dlltest.c dlltest.dll* dlltest.o
共有ライブラリをリンクする
Mac版
次に、今作った共有ライブラリをリンクして使うmain
関数を作ります。
#include <stdio.h>
int hoge_func();
int main() {
hoge_func();
}
これを以下のようにコンパイルします。
$ gcc -o main main.c libshared.dylib
Windows版
Windowsでは以下のようにリンクさせてコンパイルします。
$ gcc -I./ -L./ -o hoge.exe main.c -ldlltest
$ ls
dlltest.c dlltest.dll* dlltest.o hoge.exe* main.c
そして実行すると、
$ ./main
This is a shared library.
と出力されます。
ただ、これだと静的ライブラリとの違いが分からないので、今度は共有ライブラリを修正してみます。
#include <stdio.h>
int my_func() {
printf("This is a shared library. hogehoge\n");
return 0;
}
printf
の中身をちょっとだけ変えました。
これを、前回と同じようにライブラリだけコンパイルしなおします。
そのあとで、main
はコンパイルしなおさずに実行してみます。
$ ./main
This is a shared library. hogehoge
main.c
はコンパイルしなくても出力が変わるのが確認できます。
これが共有ライブラリの意味ですね。
共有ライブラリについてメモ
どうやらMacとLinuxでは共有ライブラリについての仕組みが異なるようです。
拡張子やフォーマットの違いから始まり、さらにはDynamic Linking
とDynamic Loading
という2種類があるようです。
MacのMach-Oフォーマットではこれらは厳密に区別されており、前者を.dylib
で、後者を.bundle
で区別するようです。
(ちなみに.bundle
はAppleが推奨しているだけで、拡張子はなんでもいいようです。Linuxと合わせるために.so
で生成している場合もあるみたいです)
このあたりの詳しい話はこちら。
共有ライブラリを動的にロードしてみる
上記の例ではコンパイル時にライブラリをリンクしていましたが、実行時にロードする方法もあります。
#include <stdio.h>
#include <dlfcn.h>
typedef void (*hoge_test)(void);
int main() {
void *handle = NULL;
hoge_test func = NULL;
handle = dlopen("./libshared.dylib", RTLD_LAZY);
if (handle == NULL) {
fprintf(stderr, "error: dlopen\n");
return -1;
}
// handleから`hoge_func`のシンボルを取り出す。
func = (hoge_test)dlsym(handle, "hoge_func");
if (func == NULL) {
fprintf(stderr, "error: dlsym\n");
return -1;
}
// 実行
func();
if (handle != NULL) {
if (dlclose(handle) != 0) {
fprintf(stderr, "error: dlclose\n");
return -1;
}
}
return 0;
}
dlopen
、dlsym
、dlclose
を使って動的に読み込ませています。
ライブラリはいくつものファイルやオブジェクトを効率的にまとめる方法です。
また動的ライブラリによって再コンパイルがいらない点もメリットです。
ライブラリは普通に使えるし、なにをしてくれているかも把握していましたが、実際に自分で書いてみるとさらに色々分かるからいいですね。