• 検索結果がありません。

リンク

ドキュメント内 note.dvi (ページ 92-95)

6.13 コンパイルとリンク

6.13.2 リンク

プログラムファイルからコンパイラを利用して生成したオブジェクトコードを結合して,シンボルを解決 して,オブジェクトを配置することによって,実行可能コードを作成することが出来る. この操作をリンク

(link)とよぶ.

だが,ちょっと待った!Section 6.13.1.2での例を見ればわかるように,file1.oとfile2.oを結合した だけでは,シンボルprintfが解決できない. 関数printfは Cの標準ライブラリ関数であるため,この関 数の実体を含むライブラリ(library)もついでに結合しておかなければ,すべてのシンボルを解決し,その 実体を明らかにすることが出来ない. つまり,リンクとは必要であれば,ライブラリも結合するという操作 を含むことになる.

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の標準関数の中には, 「数学関数」と呼ばれる関数群があり62, これらの実体は標準関数には 入っていない. 数学関数ライブラリはUNIX の場合には通常は/usr/lib/libm.aであり,数学関数ライ ブラリ63を必要とする場合には,

gcc file1.o file2.o -o target -lm

60オブジェクトコード(アセンブラコード)中での扱いは,内部静的変数とほとんど同じ扱いであることに注意.したがって, C ログラムのレベルではstaticの意味が多少異なるが,生成するオブジェクトコードレベルになると,内部リンケージを持つファイル スコープの変数と,内部静的変数は全く同じ扱いになることに注意しよう.

# だから,同じstaticという記憶クラス指定子を持つ.

61正確には,そのオブジェクトコード中の“text”セグメントの先頭からの「オフセット」と呼ばれる値である.

62例えば,三角関数の値を求めるsinや,対数関数logがある.

63なぜ数学関数ライブラリのリンクを明示的に指定しなければいけないのだろうか?数学関数ライブラリは,ユーザの目的によっ ては,その精度や速度に問題がある可能性が否定できない. 標準的な数学関数ライブラリの場合には,精度と速度が適切になるような コードから生成されていることが多く,より高い精度や,より高速な実行を求める場合には,必要に応じて,異なった数学関数ライブ ラリを用いることが考えられる.そのため,数学関数ライブラリが標準関数ライブラリから独立していると考えられる.

しかし, Darwin (MacOS X)Public Beta Versionでは,数学関数は標準関数ライブラリに組み込まれていた. Darwinの元

となったNeXTSTEPでどのような構成になっていたかは,私は良く知らないが,まあ, Darwinでは数値計算はするなということ

なのだろう.

のように,-lmというオプションをリンカに渡す必要がある64. 最後に,なぜ-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

と非常に巨大なファイルである. すべてのプログラムの実行コードにこの大きさのライブラリがリンクさ れると,巨大なディスクスペースが必要となる.

そのため, 最近の 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オプションで静的にリンクで きる65. また,オブジェクトコードを動的リンクライブラリにアーカイブするときには,

64-lの後に空白なしに指定した文字をXXXXとすると,リンカは指定されたディレクトリからlibXXXX.aという名前のライブラ リを探し,それをリンクする.指定されたディレクトリとは,通常は/usr/libであり,それ以外のディレクトリもライブラリの検索 対象としたい場合には,-L/usr/local/libのように-Lオプションで明示的にディレクトリを指定する必要がある.

65静的リンクライブラリの拡張子.aarchiveの略であるのは明らかだろう.

gcc -o libx.so -G file2.o file1.o とすればよい6667.

6.13.2.3 インターポジショニング

インターポジショニング(interpositioning)とは,ライブラリ中で定義されている関数を自前の関数で 置き換えてしまうことを指す. Cでは,標準関数の識別子名は予約(reserve)されているが,その識別子を 使ってはいけないという意味ではない68. 例えば, C の標準関数islowerを考えてみよう. 次のようなプ ログラムを書いたら何が起こるかということである69.

#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 ;

}

当然, “a is not lower character”という出力を得る. これだけであれば,正しく動作するのだが,もし, 他の標準関数でislower関数を利用している関数70を利用したらどうなるのだろうか?この場合には, 標 準ライブラリのislowerではなく,このプログラムファイル中にある islowerが利用される. ということ は, 悲惨な結末を向かえることになるのは明らかである.

このように,標準関数内で定義されているシンボル名を関数名に利用してはいけない.

66動的リンクライブラリの拡張子.soshared objectの略である.また,あるプログラムがどのような動的リンクライブラリを 用いているかは,lddコマンドで知ることが出来る.ldd /usr/bin/cpとしてみるとよい.

67しかし, Solaris 2.xの動的リンクライブラリには少々面倒なところがあり,実行時の動的リンクライブラリの検索パスを指定するた めに,プログラムのリンク時に-Rオプションにより明示的に動的リンクライブラリを指定するか,シェルの環境変数LD LIBRARY PATH で動的リンクライブラリのあるディレクトリを指定する必要がある. SunOS 4.xなどでは,動的リンクライブラリのリンクキャッシュ

ld.soがあり,そこに動的リンクライブラリのハッシュテーブルを構成できた. 個人的にはこちらの方が好みなのだが,ld.soをつ

ぶしてしまうと悲惨なことが起きるという欠点がある.

# 実際,私はld.soを間違って消してしまった経験がある.

68[6]にも書かれている通り,これは「警告」対象とはならない.せめて警告くらいはしてくれる仕様にして欲しいのは誰でも思う ことなのだが...

69islowerctype.hで宣言されている.

70FreeBSD 4.2 RELEASEのライブラリ群のソースコード(/usr/src/lib以下)を見てみると,libc/net/inet network.c どで利用されている.

ドキュメント内 note.dvi (ページ 92-95)