スタティックリンクとダイナミックリンクによって作成される実行バイナリサイズの違い

前回スタティックリンクとダイナミックリンクの違いを述べ、それぞれの方法でリンクした時のファイルのサイズを見ようとしたが、printfのスタティックライブラリが見つからなかったので、途中で断念してしまった。そこで今回は自分で関数を作ってそれをライブラリにして、スタティック・ダイナミックの二つの方法でリンクしてみようと思う。

注意点

私はMacを使っているのだが、Macgccを使おうとするとデフォルトでclangを使おうとするようだ。具体的に以下のコマンドを実行するとわかる。

$ gcc -v 
Apple clang version 13.1.6 (clang-1316.0.21.2.5)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

そこで、brewなどを使ってGNUgccを使えるようにすることを推奨する。なぜなら今回はgccを想定したリンクを行うのだが、gccのオプションはclangでは使えないからだ。私は/usr/local/bin/gcc-12を今回使用した。

準備

以下の2つのC言語ソースコードを準備した。add関数の方をライブラリにするつもりだ。

main.c

#include "Add.c"

int main() {
    int a = 1;
    int b = 2;
    int n = 100;
    int c;
    c = add(a, b, n);
    return c;
}
// Add.c

int add(int a, int b, int n) {
    int c = 0;
    for (int i = 0; i < n; i++) {
        c -= a;
        c += b;
    }
    return c;
}

ダイナミックリンク

まずは、ダイナミックライブラリを以下のコマンドで作る。

gcc-12 -shared -fPIC -o libAdd.so Add.c

ここで、-fPICオプションは、共有ライブラリを作るときに使われるオプションで、メインメモリのどこに配置されても絶対アドレスに関わらず正しく実行されることを保証するものである。Position-Independent Codeの略称である。また共有ライブラリを作るため、オプションとして-sharedも忘れずにつける。

最後に以下のコマンでmain.cのコンパイルと共有ライブラリとのダイナミックリンクを行う。

$ gcc-12 -L. -o main.dynamic main.c -lAdd

スタティックリンク

まず、Add.cのオブジェクトファイルを作る。

$ gcc-12 -c Add.c -o Add.o

これを以下のコマンドで静的ライブラリに変換する。

$ ar rcs libAdd.a Add.o

最後に、このライブラリを用いて、スタティックリンクを行う。

$ gcc-12 -o main.static main.o -L. -lAdd

その後、ダイナミックリンクをした実行バイナリとスタティックリンクをした実行バイナリのファイルサイズを比較した結果が以下だ。

16568 11  1 09:44 main.dynamic
49456 11  1 09:49 main.static

スタティックリンクをした実行バイナリの方が3倍程度も大きくなっていることがわかる。ダイナミックリンクの方がメモリ効率が良いことをこれらの実験で確認することができた。

参照