gccでコンパイルとリンクを手を動かして学ぶ
コンパイル・リンク・ライブラリの作成等について、実際にコマンドを打ちながら学んだのでメモ。深いところには踏み入れないので注意です。
普段なんとなくソースコードから実行ファイルを作っているけど雰囲気でやっているなぁって方には参考になるかと思います。
ソースコードから実行ファイルを生成する
C言語ソースコードを用意する。
#include <stdio.h>
void func1()
{
printf("func1\n");
}
void func2()
{
printf("func2\n");
}
int main()
{
func1();
func2();
}
人間が読める「プログラミング言語」の形から、機械が読める「機械語」に翻訳する(コンパイル)。すると、オブジェクトファイルが生成される。
$ ls
test1.c
$ gcc -c test1.c
$ ls
test1.c test1.o
オブジェクトファイルのままでは実行できない。実行ファイルを生成し、実行しよう。
-oは生成されるファイル名を指定するためのオプション。
$ gcc -o program test1.o
$ ls
program test1.c test1.o
$ ./program
func1
func2
ソースコードを複数のファイルに分割する
全ての処理を1ファイルに書かれているのはさすがにナンセンス。
関数のみ別ファイルに移し、ファイルを分割してみよう。
#include <stdio.h>
void func1()
{
printf("func1\n");
}
#include <stdio.h>
void func2()
{
printf("func2\n");
}
#include <stdio.h>
void func1();
void func2();
int main()
{
func1();
func2();
}
オブジェクトファイルを生成しよう。
$ ls
func1.c func2.c test2.c
$ gcc -c test2.c
$ gcc -c func1.c
$ gcc -c func2.c
$ ls
func1.c func1.o func2.c func2.o test2.c test2.o
オブジェクトファイルを1つずつ生成するのが面倒な場合は、以下のようにやってもいい。
$ gcc -c test2.c func1.c func2.c
または、ワイルドカードを使って書くこともできる。覚えたら他の工程でも使ってみよう。
$ gcc -c *.c
3つのオブジェクトファイルができたら、それらを使って実行ファイルを作り、実行してみよう。
$ gcc -o program test2.o func1.o func2.o
$ ls
func1.c func1.o func2.c func2.o program test2.c test2.o
$ ./program
func1
func2
オブジェクトファイルをまとめて静的ライブラリを作る
いま、func1.oとfunc2.oを使って実行ファイルを作った。これからどんどんオブジェクトファイルが増えていくと困る。
実は、複数のオブジェクトファイルを1つのファイルにまとめることができるようになっている。複数のオブジェクトファイルをまとめた、拡張子.aのファイルのことを、静的ライブラリと呼ぶ。
まずはオブジェクトファイルを作っておこう。
$ ls
func1.c func2.c test2.c
$ gcc -c *.c
$ ls
func1.c func1.o func2.c func2.o test2.c test2.o
この中の、func1.oとfunc2.oをまとめて、libfunc.aという静的ライブラリを作ってみよう。
$ ar rcs libfunc.a func1.o func2.o
$ ls
func1.c func1.o func2.c func2.o libfunc.a test2.c test2.o
生成された静的ライブラリlibfunc.aと、test2.oを使って実行ファイルを作ってみよう。この作業をリンクという。
$ gcc -o program test1.o libfunc.a
$ ./program
func1
func2
リンクは以下の記法でもよい。
-Lは.aが配置されているパス指定、-lはlibXXXのXXX部分を指定する。
gcc -o program test1.o -L./ -lfunc
動的ライブラリを作る
静的ライブラリlibfunc.aに含まれる情報は、実行ファイルにも全て含まれている。もし、libfunc.aが複数の実行ファイルから呼び出される場合には、実行ファイルそれぞれにlibfunc.aの情報が含まれることになり、実行ファイルのサイズが無駄に大きくなってしまう。
そこで、実行ファイルにはlibfuncの情報は(あまり)含めずに、実行ファイルが実行されるときにライブラリを参照すればいい、という考えがある。このライブラリを動的ライブラリという(共有ライブラリとも言う)。
動的ライブラリ(libfunc.soファイル)を作ってみよう。
$ ls
func1.c func2.c test2.c
$ gcc -c *.c
$ gcc -shared -fPIC -o libfunc.so func1.o func2.o
$ ls
func1.c func1.o func2.c func2.o libfunc.so test1.c test1.o
できた動的ライブラリlibfunc.soと、オブジェクトファイルtest1.oをリンクして、実行してみよう。
$ gcc -o program test1.o libfunc.so
$ ./program
./program: error while loading shared libraries: libfunc.so: cannot open shared object file: No such file or directory
エラーになってしまった。実行ファイルは、libfunc.soがどこにあるかわからなくなっているらしい。
実は、実行時に動的ライブラリをどこから探せばいいのかについては、リンク時に教えてあげることができる。以下のようにしてリンクをやり直そう。/home/user
は適宜変更して、作業中のディレクトリ(実行時にlibfunc.soが置かれているディレクトリ)を指定しよう。
$ gcc -o program -Wl,-rpath /home/user test1.o libfunc.so
$ ./program
func1
func2
ちなみに、動的ライブラリを参照する方法には色々とあるので、調べてみてほしい。
共有ライブラリが見つからない時にやること
次のステップ
いちいち全部コマンドで実行していくのは現実的ではないので、Makefile等にまとめて書く方法を学ぶといいかも。