|
PGI コンパイラに限らず、一般的なコンパイラでは、アセンブル機能とリンク機能は、GNUのユーティリティを使用します(Linux の場合)。一般に、プログラムのコンパイルから実行モジュールの生成までは、以下の内部的な手続きが行われています。PGI
コンパイラは以下の手続きを自動的に行っています。以下の手続きの内容と各コマンドのオプションを知るための方法は、こちらで説明しています。
- コンパイル&コード生成 (pgi のコンパイラコマンド・コマンド)
- アセンブラによるオブジェクト生成 (GNU as コマンド)
- リンカによるライブラリ等のリンクを行って実行モジュールの生成 (GNU ld コマンド)
上記の手続きの中で、1,2 に関してはユーザが直接操作することができませんが、3 のリンケージの処理に関しては、ユーザ独自で GNU の ld
コマンドのオプションを操作して、リンク時の問題等に対応することが可能です。
以下に説明する内容は、リンクにおける様々な裏技の例を示したものです。「リンク時における問題」の多くは、必要とするライブラリが見つからない問題、同じ関数・サブプログラムを別のライブラリ・ファイルからリンクしたい等の対処が多いため、リンケージに関する基本的な知識を要します。
ここでは、リンク時における「ライブラリ」の対処法に関してフォーカスし、その裏技を説明します。
● ランタイム・ライブラリに関する基礎知識
まず始めに、実行モジュールにリンクされる、ライブラリに関する基礎的な事項について説明します。ユーザ・プログラム(オブジェクト)にリンクされるライブラリには、大きく以下の二つのライブラリ形式があります。
- スタティック(静的)ライブラリ
- シェアード(共有)ライブラリ
二つ目の共有ライブラリの範疇には、一般にダイナミックライブラリと言う形式のものもありますが、この共有ライブラリの中の特殊な例です。スタティックライブラリは、リンク時に実行モジュールの中に組み込まれます。モジュールサイズは大きくなりますが、別のシステム上で動作させたい場合は、全て必要なライブラリが組み込まれているため、そのままの形で動作します。一般に、ファイル名形式として、lib***.a
と言う .a の名称がつけられております。スタティックライブラリを作成するためには、一般に、ar コマンドでオブジェクト(*.o)をまとめ、
ranlib コマンドでリンクを高速化する先頭子をつけ、strip コマンドでシンボルテーブルを削除します。
一方、シェアード(共有)ライブラリは、プログラムの実行時にシステム上に備えている、そのライブラリをロードして使用するためのものであり、システムライブラリのバージョンが異なったり、システム上に存在しない場合は、実行モジュールが動作しない場合があります。一般に、ファイル名形式として、lib***.so
と言う .so の名称がつけられております。シェアードライブラリの一つである、ダイナミックライブラリとは、実行時に load/unload することが可能な「位置独立なライブラリ形式」のものを言います。これは、リンク時のオプションに
-fPIC を指定して作成されております。
一般の実行モジュールは、ほとんどが「シェアード(共有)ライブラリ」をリンクするとの前提で生成され、実行時に必要とされるシステム共有ライブラリとPGI
の場合は、PGI 用の共有ライブラリがロードされる形式になっています。このデフォルトのリンク方式を使用する場合には、「リンク時における問題」は、ほとんど発生しませんが、明示的にスタティック(静的)ライブラリをリンクする際に未解決なオブジェクト・ルーチンが存在する場合や作為的にあるライブラリを組み込みたい等の操作をした場合、リンク時の問題が発生します。
● リンク時におけるライブラリの組込み順序
デフォルトでは、リンカはまず、シェアード(共有)ライブラリを検索し、必要となるルーチンを組み込もうとします。次に、スタティック(静的)なライブラリの中から探し出し、実行モジュールに組み込みます。従って、一般のコンパイル(リンク)オプションでは、ある配慮を行わなければ、常に、シェアード(共有)ライブラリが組み込まれると言うことになります。この点を理解しておくが、「リンクする順序」と共に重要なポイントとなります。
ちなみに、実行モジュールの中でどのような dynamic shared library が使用されているかは、以下のコマンド (ldd) で確かめることができます。以下の例では、PGI
関連の libpgc.so だけでなく、システムに備えられたシェアード(共有)ライブラリもリンクされるべき実行モジュールであることが分かります。特に
libpgc.so ファイルは、PGI をインストールしたマシン上でしか存在しません。例えば、他のシステムでこのような実行モジュールを動作させるためには、このlibpgc.so
ファイルを予めコピーしておくことが必要となります。
$ ldd a.out
libc.so.6 => /lib/i686/libc.so.6 (0x4002d000)
libpgc.so => /usr/pgi/linux86/5.2/lib/libpgc.so (0x4015d000)
libm.so.6 => /lib/i686/libm.so.6 (0x40172000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
一方、スタティックなライブラリをリンクして作成された実行モジュールでは、ldd コマンドでどのように表示されるでしょうか?以下のように表示されます。
$ ldd a.out
not a dynamic executable
リンカが暗黙で行うシステム上のライブラリ検索順序は上記に述べたとおりですので、これを作為的に変更することによって、所望とするライブラリ・ルーチンを先に組み込むことができます。一般に、リンカ(コマンド)のオプションの指定で、ライブラリの検索順位を明示的に指定することができます。とは言っても、この操作は簡単で、単にリンクするライブラリ名をコマンド列の中で先に指定するだけの操作となります。このことを理解すれば、後は試行錯誤で、リンクエラーが起きないように所望とするライブラリを組み込むことができます。複数のライブラリ間で呼び出し/参照の順序の依存関係がある場合、ライブラリ名をコマンド列で指定する順序は、必ず以下のようにしなければなりません(順序はセンシティブです)。
例えば、 ユーザのスタティックライブラリ libA.a の中の関数が、libB.a の中の関数を必要とする場合の例を記します。
libA.a ---> libB.a (共有ライブラリの場合は、 libA.so ---> libB.so)
リンカのコマンド列では、以下のような順番でリンク検索順序を指定します。ライブラリ(*.a) 内の参照の解決は、コマンド行に記されたものを順番に、1回限りの検索を行います。特に大事なことは、コマンド行上で先行するアーカイブ・ライブラリを、後続の入力ファイル(ライブラリを含む)内の新しい参照の解決に使用することはありません。このリンケージにおける暗黙の決まりごとが、コマンド行のライブラリ名指定の順序に関係します。以下の例では、最初に
test.o の未解決の参照をコマンド行の次以降に記された *.a を順番に検索して解決すれば問題なく、test.o を実行モジュールにできますが、これで全てOKとは言えません。次のコマンド行に出てくる
libA.a 内部の未解決参照があるかどうか、リンカはチェックします。リンカは、コマンド行のlibA.a の右側に記述されているライブラリのみしか、未解決参照シンボルを検索しません。もし、その前にアーカイブ名記述されていたとしても、例えば、この場合libB.a
がコマンド行の前に記述されていても、「検索」自体を行いません。常に、コマンド行を1回限り、常に右にあるものを検索するのみです。
pgCC test.o -Bstatic -L{directory} -lA -lB ( -lB -lA の順番では libB.a の関数が未解決になります)
あるいは、明示的にライブラリ名を指定します。
pgCC test.o -Bstatic libA.a libB.a
共有ライブラリ(libA.so、libB.so)の場合は、
pgCC test.o -L{directory} -lA -lB あるいは、pgCC test.o libA.so libB.so
● 静的にリンクする場合のコンパイル・オプション
PGI コンパイラでは、リンカに静的リンケージを行うことを指示するために、 -Bstatic と言うオプションがあります。基本的には、この指定によって、リンカは静的ライブラリを先に検索しようとします。但し、-Bstatic オプションを指定して実行モジュールを作成する際に、リンカのレベルで、「未解決な参照ルーチン」が存在して、実行モジュールが作成できないエラーが生じる場合があります。これは、PGI コンパイラ(ld リンカ)のデフォルトのライブラリの検索順序が適当でない場合に生じます。この対策については、以下に例を示しました。
|