Help us understand the problem. What is going on with this article?

[C言語] 共有ライブラリと静的ライブラリを整理する

More than 1 year has passed since last update.

なんとなく分かった気でいるふたつのライブラリ。
それを小さなテストコードを使いながら整理してみます。
(ちなみに今回紹介するのはMacでのものです。Linuxではフォーマット、コンパイル方法などが異なります)

静的ライブラリをコンパイルする

まずは静的ライブラリです。
ちなみにMacではMach-Oというフォーマットを使います。
(Linuxでは「ELF(Executable and Linking Format)」というらしい)

こちらの記事が分かりやすかったです。

また、静的ライブラリはオブジェクトファイルをまとめたただのアーカイブです。
(なのでar(アーカイブ)コマンドを使う)

例として以下のように静的ライブラリの中身を定義しました。

static-library.c
#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

最後に、この静的ライブラリを使うファイルを作成します。

main.c
#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版

次に共有ライブラリをコンパイルします。

shared-lib.c
#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関数を作ります。

main.c
#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.

と出力されます。

ただ、これだと静的ライブラリとの違いが分からないので、今度は共有ライブラリを修正してみます。

shared-lib.c
#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 LinkingDynamic Loadingという2種類があるようです。

MacのMach-Oフォーマットではこれらは厳密に区別されており、前者を.dylibで、後者を.bundleで区別するようです。
(ちなみに.bundleはAppleが推奨しているだけで、拡張子はなんでもいいようです。Linuxと合わせるために.soで生成している場合もあるみたいです)

このあたりの詳しい話はこちら

共有ライブラリを動的にロードしてみる

上記の例ではコンパイル時にライブラリをリンクしていましたが、実行時にロードする方法もあります。

dynamic-load
#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;
}

dlopendlsymdlcloseを使って動的に読み込ませています。

ライブラリはいくつものファイルやオブジェクトを効率的にまとめる方法です。
また動的ライブラリによって再コンパイルがいらない点もメリットです。

ライブラリは普通に使えるし、なにをしてくれているかも把握していましたが、実際に自分で書いてみるとさらに色々分かるからいいですね。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away