第 6 章 C 言語入門
6.13 コンパイルとリンク
6.13.2 リンク
プログラムファイルからコンパイラを利用して生成したオブジェクトコードを結合して, シンボルを解 決して,オブジェクトを配置することによって,実行可能コードを作成することが出来る. この操作をリン ク(link)とよぶ.
だが,ちょっと待った!Section 6.13.1.2での例を見ればわかるように,file1.oとfile2.oを結合した だけでは,シンボルprintfが解決できない. 関数printfはCの標準ライブラリ関数であるため,この関 数の実体を含むライブラリ(library)もついでに結合しておかなければ,すべてのシンボルを解決し,その 実体を明らかにすることが出来ない. つまり,リンクとは必要であれば,ライブラリも結合するという操作 を含むことになる.
54アセンブラコード中で,textセクション内でglobalと定義されていることに対応する.
55アセンブラコード内(file1.s内)で,dataセクション内に定義され,0で初期化されていることに相当している.
56アセンブラコード中でも,内部自動変数と同じ扱いを受けている.
57オブジェクトコード(アセンブラコード)中での扱いは,内部静的変数とほとんど同じ扱いであることに注意.したがって, Cプ ログラムのレベルではstaticの意味が多少異なるが,生成するオブジェクトコードレベルになると,内部リンケージを持つファイ ルスコープの変数と,内部静的変数は全く同じ扱いになることに注意しよう.
# だから,同じstaticという記憶クラス指定子を持つ.
58正確には,そのオブジェクトコード中の“text”セグメントの先頭からの「オフセット」と呼ばれる値である.
Id: C6-1.tex,v 1.2 2001-03-19 12:45:11+09 naito Exp
6.13.2.1 実行形式の作成
Section 6.13.1.2での例で作成した2つのオブジェクトコードfile1.oとfile2.o,さらに, Cの標準ラ
イブラリをリンクするには, gcc file1.o file2.o -o target
とする. ここで,-o targetに書かれたtargetが,リンカが出力する実行形式のファイル名となる.
でも, この命令では「標準ライブラリ」を指定していないが?通常のリンカは「標準ライブラリ」を必 ず結合するようになっているため,明示的に標準ライブラリを指定しなくても良い. 標準関数ライブラリは UNIXの場合,通常/usr/lib/libc.aというファイルである.
しかし, Cの標準関数の中には,「数学関数」と呼ばれる関数群があり59,これらの実体は標準関数には 入っていない. 数学関数ライブラリは UNIXの場合には通常は /usr/lib/libm.aであり, 数学関数ライ ブラリ60 を必要とする場合には,
gcc file1.o file2.o -o target -lm
のように,-lmというオプションをリンカに渡す必要がある61 . 最後に, なぜ-lmオプションを最後につ けるかという理由を考えてみよう. 例えば,file1.oは関数sinの呼び出しを含むとき,
gcc -lm file1.o file2.o -o target としても,リンカは正しく動作する. しかし,
Undefined first referenced
symbol in file
sin file1.o
ld: fatal: Symbol referencing errors.
というエラーを出力するだろう. これは,先にlibm.aのシンボルが評価され,file1.oにある未解決シン ボルsinの解決が出来なくなっていることを表している. したがって, ライブラリの指定は一番最後にし なくてはならない. また, 同様にライブラリの指定の順序によっては,シンボルの解決が出来ない場合があ りうるので注意が必要である.
6.13.2.2 動的リンクライブラリ
Cで作成したプログラムをコンパイル(リンク)すると,必ず標準ライブラリがリンクされてしまう. 標 準ライブラリの大きさは,
• Sun MicrosystemsのSolaris 2.6 (SunOS 5.6 Generic 105181-05 sparc SUNW)で,約 1.6 MB,
• Sun MicrosystemsのSunOS 4.1.4 (SunOS 4.1.4-JL 1)で,約670 KB,
• FreeBSD 4.2-Release (4.2-RELEASE FreeBSD)で,約1.1 MB
と非常に巨大なファイルである. すべてのプログラムの実行コードにこの大きさのライブラリがリンクさ れると,巨大なディスクスペースが必要となる.
59例えば,三角関数の値を求めるsinや,対数関数logがある.
60なぜ数学関数ライブラリのリンクを明示的に指定しなければいけないのだろうか?数学関数ライブラリは,ユーザの目的によっ ては,その精度や速度に問題がある可能性が否定できない.標準的な数学関数ライブラリの場合には,精度と速度が適切になるような コードから生成されていることが多く,より高い精度や,より高速な実行を求める場合には,必要に応じて,異なった数学関数ライブ ラリを用いることが考えられる.そのため,数学関数ライブラリが標準関数ライブラリから独立していると考えられる.
しかし, Darwin (MacOS X)のPublic Beta Versionでは,数学関数は標準関数ライブラリに組み込まれていた. Darwinの元
となったNeXTSTEPでどのような構成になっていたかは,私は良く知らないが,まあ, Darwinでは数値計算はするなということ
なのだろう.
61-lの後に空白なしに指定した文字をXXXXとすると,リンカは指定されたディレクトリからlibXXXX.aという名前のライブラ リを探し,それをリンクする.指定されたディレクトリとは,通常は/usr/libであり,それ以外のディレクトリもライブラリの検索 対象としたい場合には,-L/usr/local/libのように-Lオプションで明示的にディレクトリを指定する必要がある.
Id: C6-1.tex,v 1.2 2001-03-19 12:45:11+09 naito Exp
そのため, 最近の UNIX システムや Windows, MacOS 等では, 動的リンクライブラリ (Dynamic
Linking Library)を用いて, 標準ライブラリなどをプログラム実行時にリンクするという方法をとって
いる.
動的リンクライブラリを用いるもう一つの長所として, もし, 標準ライブラリなどにバグがあった場合, プログラムを再リンクすることなく,動的リンクライブラリだけを入れ替えることにより,バグを解消可能 となる. しかしながら,動的リンクライブラリを用いると,プログラムの実行時でのライブラリのリンクの 時間だけ実行時間が大きくなるという欠点があり, 現状では, 標準的なライブラリに関しては動的リンク ライブラリを,いくつかの特殊な(そのプログラムだけで利用するものなど)ライブラリでは,静的リンク
(static link)(リンク時にライブラリをリンクしてしまう方法)を用いるという使い分けをしている.
6.13.2.2.1 ライブラリの作成方法 オブジェクトコードを静的リンクライブラリとしてまとめる(アー
カイブ(archive)するという)時には,コマンドarを用いて,
ar -q libx.a file2.o file3.o
とすれば,file2.o,file3.oをlibx.aにアーカイブでき,リンク時に-lxオプションで静的にリンクで きる62. また,オブジェクトコードを動的リンクライブラリにアーカイブするときには,
gcc -o libx.so -G file2.o file1.o とすればよい63 64 .
6.13.2.3 インターポジショニング
インターポジショニング(interpositioning)とは,ライブラリ中で定義されている関数を自前の関数で 置き換えてしまうことを指す. Cでは,標準関数の識別子名は予約(reserve)されているが,その識別子を 使ってはいけないという意味ではない65 . 例えば, Cの標準関数islowerを考えてみよう. 次のようなプ ログラムを書いたら何が起こるかということである66.
#include <stdio.h>
extern int islower(int) ; int main(int argc, char **argv) {
int c=’a’ ;
if (islower(c)) printf("%c is lower character\n",c) ; else printf("%c is not lower character\n",c) ;
return 0 ; }
int islower(int c) {
if ((c >= ’A’)&&(c <= ’Z’)) return 1 ; return 0 ;
}
62静的リンクライブラリの拡張子.aはarchiveの略であるのは明らかだろう.
63動的リンクライブラリの拡張子.soはshared objectの略である. また,あるプログラムがどのような動的リンクライブラリを 用いているかは,lddコマンドで知ることが出来る.ldd /usr/bin/cpとしてみるとよい.
64しかし, Solaris 2.xの動的リンクライブラリには少々面倒なところがあり,実行時の動的リンクライブラリの検索パスを指定するた めに,プログラムのリンク時に-Rオプションにより明示的に動的リンクライブラリを指定するか,シェルの環境変数LD LIBRARY PATH で動的リンクライブラリのあるディレクトリを指定する必要がある. SunOS 4.xなどでは,動的リンクライブラリのリンクキャッシュ
ld.soがあり,そこに動的リンクライブラリのハッシュテーブルを構成できた. 個人的にはこちらの方が好みなのだが,ld.soをつ
ぶしてしまうと悲惨なことが起きるという欠点がある.
# 実際,私はld.soを間違って消してしまった経験がある.
65[6]にも書かれている通り,これは「警告」対象とはならない.せめて警告くらいはしてくれる仕様にして欲しいのは誰でも思う ことなのだが...
66islowerはctype.hで宣言されている.
Id: C6-1.tex,v 1.2 2001-03-19 12:45:11+09 naito Exp
当然, “a is not lower character”という出力を得る. これだけであれば,正しく動作するのだが,もし, 他の標準関数でislower関数を利用している関数67 を利用したらどうなるのだろうか?この場合には,標 準ライブラリのislowerではなく,このプログラムファイル中にあるislowerが利用される. ということ は, 悲惨な結末を向かえることになるのは明らかである.
このように,標準関数内で定義されているシンボル名を関数名に利用してはいけない.