The Linux GCC HOWTO <author>Daniel Barlow <tt><daniel.barlow@linux.org></tt> <date>v1.17, 28 February 1996 <trans>中野武雄 <tt/<nakano@apm.seikei.ac.jp>/ <tdate>v1.17j2, 29 January 1997 <abstract> この文書では linux の下で GNU C コンパイラとコンパイルに必要なライブラ リを設定する方法について記しています。またこれらの環境を用いたプログラ ムのコンパイル、リンク、実行およびデバッグについても概要を述べます。こ の文書の内容の多くは Mitch D'Souza の GCC-FAQ から得たものであり、この 文書は GCC-FAQ の代わりとなるものです。また ELF-HOWTO からも多くの題材 を採用しており、同じくこの文書は ELF-HOWTO の大部分を置きかえるもので す [訳注:Daniel さんは ELF-HOWTO の著者でもあります。] この版は公式リリースとしては最初のものになります(バージョン番号からは そうは見えないかもしれませんが、これは RCS が自動的につけているものな んです)。ご意見ご感想をお待ちしています。 </abstract> <p> <bf>注意: この文書はかなり以前に書かれたものなので、 いまどきの Linux 環境にはあてはまらない箇所があります。 (JF Project)</bf> </p> <!-- *********************** SECTION ************************** --> <!-- Preliminaries --> <sect>はじめに <p> <sect1>ELF 対 a.out <p> <label id="index.0"> <!-- elf --> <label id="index.1"> <!-- a.out --> 現在 Linux におけるプログラム開発環境はどんどん変化しています。 Linux で実行できるバイナリには二種類の形式が在り、システムの設定 によってこれらを使い分けることが出来ます。この HOWTO を読む前に、読者 のシステムではどちらを使っているかを調べておくと良いでしょう。 <label id="index.2"> <!-- <tt/file/ --> さて、どうすればわかるでしょうか?答えは `file' コマンドを使う、です (<tt>file /bin/bash</tt> のようにします)。ELF の実行ファイルなら 「<tt/ELF/」 という文字列がメッセージにあらわれますし、 a.out の実行ファ イルなら「<tt>Linux/i386</tt>」という文字列を含む表示が出ます。 ELF と a.out の違いについてはこの文書の後ほどの部分で詳細に解説します。 ELF の方が新しいフォーマットであり、一般にはより良いものとされています。 <sect1>この文書について <p> <label id="index.3"> <!-- chewing gum --> 著作権などの法的な件に関しては、この文書の<em/最後/に示してあり ます。また Usenet で間抜けな質問をしたり、バグでないことをバグだと報告 して C 言語についての無知をさらしたり、ガムを噛みながら歩きまわったり することに関する法的な警告も示してあります。 訳注:言うだけ野暮ですが、後の方のは冗談です <tt/:-)/。 <sect1>フォントについて <p> この文書を Posstscript、dvi、html のどれかでお読みの方は、テキスト 版を読んでいる方より少々多くのフォントを見ることになります。ファイル名、 コマンド名、コマンド出力、ソースコードなどからの引用は <tt/typewriter/ フォントで、その他の強調すべき内容は <em/emphasized/ フォントで示します。 便利な索引も用意しました。 dvi と Postscript では index の数字は章や節 の番号です。HTML では数字は単に出てきた順番ですが、クリックによってそ の内容に飛ぶことが出来ます。テキスト版では本当に「単なる」順番になって しまいます。他の形式で見て下さい! 例には Bourne シェルを用いています。 C シェルをお使いの方は、 <tscreen><verb> $ FOO=bar; export FOO </verb></tscreen> となっているところを <tscreen><verb> % setenv FOO bar </verb></tscreen> と読み変えて下さい。 プロンプトに <tt/$/ でなく <tt/#/ を用いているところでは、その コマンドは root で実行する必要があることを示しています。もちろんこの文 書での例を実行した結果にたいする責任は私はとれません。幸運を <tt/:-)/ <!-- *********************** SECTION ************************** --> <!-- Where to get things --> <sect>入手先 <p> <sect1>この文書 <p> この文書は Linux HOWTO シリーズの一つですので、 Linux HOWTO が置か れているところ(例えば <url url="http://sunsite.unc.edu/pub/linux/docs/HOWTO/"> )ならばどこにでもあるはずです。 HTML 版(おそらくは多少新しい版)をお望みでしたら <url url="http://ftp.linux.org.uk/˜barlow/howto/gcc-howto.html"> をどうぞ。 <sect1>他の文書 <p> <label id="index.4"> <!-- documentation --> gcc に関する公式な文書は配布ソース(後述)に texinfo 形式およ び <tt>*.info</tt> ファイルとして入っています。CD-ROM がある方、充分高 速なネットワークを利用できる(あるいは辛抱強い)方は配布パッケージを手 に入れて untar し、適宜必要なものを <tt>/usr/info</tt> にコピーするだけ です。以上のいずれも不可能な方は、 <url url="ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/" name="tsx-11"> でもこの文書を入手できます。ただしこちらは常に最新版であるとは限りませ ん。 <label id="index.5"> <!-- manual pages --> libc に関する情報源は二つあります。 GNU libc には info ファイルが付属 しており、これには Linux の libc の内容が(stdio を除いて)比較的正確 に記述されています。また <url name="man ページ" url="ftp://sunsite.unc.edu/pub/Linux/docs/"> のアーカイブは Linux 向けに書かれたもので、システムコール(セクション 2)と libc の関数(セクション 3)に関する記述がたくさんあります。 <sect1>GCC <p> <label id="index.6"> <!-- gcc --> 二つの選択があります。 <enum> <item> Linux 用の GCC 公式配布版が常にバイナリ形式(コンパイル済み)で <url url="ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/"> に置かれています。この文書を書いている時点での最新版は 2.7.2 で、 <tt/gcc-2.7.2.bin.tar.gz/ というファイルです。 訳注:翻訳時では 2.7.2.1 が最新版です。 <!-- (b) The latest source distribution of GCC from the Free Software Foundation can be had from <url name="GNU archives" url="ftp://prep.ai.mit.edu/pub/gnu/">. This is not necessarily always the same version as above, though it is just now. The Linux GCC maintainer(s) have made it easy for you to compile the latest version available yourself -\-\- the <tt/configure/ script should set it all up for you. Check <url name="tsx-11" url= "ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/"> as well, for patches which you may want to apply. --> <item> Free Software Foundation からの GCC ソースの最新配布版が <url name="GNU archives" url="ftp://prep.ai.mit.edu/pub/gnu/"> にあります。こちらのバージョンは常にバイナリ配布のものと同じであるとは 限りませんが、現在のところは同じ 2.7.2 です。 Linux GCC をメンテナンス してくれている人々は、最新バージョンのコンパイルを容易にするための 作業をしてくれており、 <tt/configure/ スクリプトですべての設定が完了し ます。 <url name="tsx-11" url="ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/"> にも有用なパッチが置かれていることがありますので、チェックしておきましょ う。 </enum> ごく初歩的なコード以外をコンパイルするには以下のようなものも必要になり ます(実はほとんどの初歩的なコードにも必要です)。 <sect1>C ライブラリとヘッダファイル <p> <label id="index.7"> <!-- libc --> ここで必要になるものは、システムで ELF と a.out のどちらを用いてい るか、あるいは読者がどちらを用いたいかによって変わります。 libc 4 か ら 5 にアップデートするときは ELF-HOWTO を読んでおくようにして下さい。 この文書を入手したのと同じところにあるはずです。 以下に示すものも今までと同じく <url name ="tsx-11" url="ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/"> にあります。 <descrip> <tag/<tt/libc-5.2.18.bin.tar.gz// ELF 共有ライブラリと静的リンクライブラリ、インクルードファイルです。 C ライブラリと math ライブラリが入っています。 <tag/<tt/libc-5.2.18.tar.gz// 上記のソースです。ヘッダファイルを入手するには <tt/.bin/ のパッケージ も必要です。 C ライブラリを自分でコンパイルするかバイナリを使うかで悩 むかもしれませんが、ほとんどの場合はバイナリを使うのが正解です。しかし NYS か shadow password を利用したい場合は自分でライブラリを作る必要が あるかもしれません。 <tag/<tt/libc-4.7.5.bin.tar.gz// a.out 形式の共有ライブラリと静的リンク用のライブラリです。 バージョン 4.7.5 の C および関連のライブラリとなっています。このパッケージは上記 のバージョン 5 ライブラリと共存できるようになっていますが、a.out 形式 のプログラムの作成や利用をしない限りはこのパッケージは必要ありません。 </descrip> <sect1> 関連ツール(as, ld, ar, strings 等) <p> <label id="index.8"> <!-- <tt/as/ --> <label id="index.9"> <!-- <tt/ld/ --> <label id="index.10"> <!-- <tt/ar/ --> <label id="index.11"> <!-- <tt/strings/ --> 今までのものと同じく、 <url name ="tsx-11" url="ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/"> から入手します。現在のバージョンは <tt/binutils-2.6.0.2.bin.tar.gz/ です。 訳注:翻訳時の最新版は 2.7.0.3 です。 現在 binutils は ELF 対応のものしかありません。また最新版の libc も ELF ですので、 a.out の libc は ELF の libc と一緒に使う必要があるでしょ う。現在では C ライブラリの開発はほとんど ELF ベースになってきています から、特に a.out にこだわる理由がない限り ELF を用いるようにしましょう。 <!-- to insert: 29) Where can I get Objective C for Linux ?--> <!-- *********************** SECTION ************************** --> <!-- installation and setup --> <sect>GCC のインストールと設定 <p> <sect1>GCC のバージョン <p> <label id="index.12"> <!-- version numbers --> <label id="index.13"> <!-- gcc, flags --> <label id="index.14"> <!-- gcc -v --> 現在利用している GCC のバージョンはシェルプロンプトから <tt/gcc -v/ と入力することでわかります。またこのコマンドによって現在のセットアッ プが ELF か a.out かもわかります。私のシステムでは以下のようになります。 <tscreen><verb> $ gcc -v Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs gcc version 2.7.2 </verb></tscreen> <p>出力のうち注意すべきキーワードは以下のようなものです。 <descrip> <tag/<tt/i486// この gcc が 486 プロセッサ向けにビルドされたことを示しています。読者の システムでは 386 や 586 が表示されるかもしれません。これらのシステム のどれかでコンパイルされたバイナリは他のどのチップでも実行させるこ とが出来ます。 486 gcc でコンパイルされたプログラムでは、 486 チップで 高速に動作するようなコードがあちこちに埋め込まれています。このことによっ て 386 下での実行時の性能に有害な影響が出ることはありませんが、バイナ リのサイズは少々大きくなります。 <tag/<tt/box// これはまったく重要ではありませんで、他のものに置き変わっている (<tt/slackware/ や<tt/debian/ など)ことや、そもそもまったく現れない こともあります(つまり全体で <tt/i486-linux/ のようになります)。 もし自分自身で gcc をビルドする場合には、好きな値をセットして格好良く 見せることもできます(私はそうしてます <tt/:-)/)。 <tag/<tt/linux// これは <tt/linuxelf/ や <tt/linuxaout/ となることもあ り、更に面倒なことには gcc のバージョンによってこれらの意味あいが変わっ ています。 <itemize> <item> <tt/linux/ は 2.7.0 以降のバージョンなら ELF、その前の場合は a.out を意味します。 <item> <tt/linuxaout/ は a.out 対応です。このエントリは <tt/linux/ の 内容が 2.7.0 以降で a.out から ELF に変更されたことにより導入 されました。したがって 2.7.0 より前のバージョンではこのエントリが現れ ることはありません。 <label id="index.15"> <!-- gcc, bugs --> <item><tt/linuxelf/ というのは古い書式です。これが現れるのはほとんどの 場合 ELF バイナリを出力するように設定された 2.6.3 の gcc です。ところ で gcc 2.6.3 は ELF コードを作成するにあたってバグがあることが知られて います。したがってこの表示が現れた場合はアップグレードをお薦めします。 </itemize> <tag/<tt/2.7.2// バージョン番号です。 </descrip> したがって私は ELF コードを出力する 2.7.2 版の gcc を使っていることに なります。そうだったのか! <sect1>インストール先のディレクトリ <p> gcc のインストールの時によそ見をしていた場合や、バイナリパッケージ の一部として gcc をインストールした場合、ファイルシステムのどこを探せ ば gcc があるのかを知りたいことがあるかもしれません。以下重要なものを 示します。 <itemize> <item> <tt>/usr/lib/gcc-lib/</tt><em>target</em><tt>/</tt><em/version/<tt>/</tt> とそのサブディレクトリにはコンパイラ本体の大部分が置かれています。コン パイラ本体のプログラムとバージョンに特有のライブラリ、インクルードファ イルがあります。 <item> <tt>/usr/bin/gcc</tt> はコンパイラのドライバです。コマンドライ ンからはこれが実行されます。複数のバージョンのコンパイラが上記のような ディレクトリに別々にインストールされている場合、このプログラムでバージョ ンを使い分けることが出来ます。デフォルトのバージョンを表示するには <tt/gcc -v/ とします。他のバージョンを用いる場合には <tt/gcc -V/ <em/version/ とします。以下に例を示します。 <tscreen><verb> # gcc -v Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs gcc version 2.7.2 # gcc -V 2.6.3 -v Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.6.3/specs gcc driver version 2.7.2 executing gcc version 2.6.3 </verb></tscreen> <item> <tt>/usr/</tt><em>target</em><tt>/(bin|lib|include)/</tt>。複 数のターゲットシステム(例えば a.out と ELF、あるいは各種のクロスコン パイラなど)に応じて gcc を複数インストールした場合、Linux 以外向 けのライブラリや binutils (<tt/as/、 <tt/ld/ など)、ヘッダファイルな どはこのディレクトリ下に置かれます。一種類の gcc しかインストールして い場合でも、まあ要するにここに関連ファイルが置かれるわけです。ここ にない場合は <tt>/usr/(bin|lib|include)</tt> にあります。 <item> <tt>/lib</tt> や <tt>/usr/lib</tt> などは native なシステムのた めのライブラリが置かれるディレクトリです。コンパイラ以外のアプリケーショ ンの中には、ここに <tt>/lib/cpp</tt> が置かれることを想定しているものが あります(特に X でよく使われます)。ない場合は <tt>/usr/lib/gcc-lib/</tt><em>target</em><tt>/</tt><em/version/<tt>/</tt> からコピーしてくるかシンボリックリンクを張りましょう。 <label id="index.16"> <!-- /lib/cpp --> </itemize> <sect1>ヘッダファイルの在処 <p> <label id="index.17"> <!-- header files --> 利用者各自が <tt>/usr/local/include</tt> にインストールするものを 除くと、 Linux で主に利用されるヘッダファイルは以下の 3 つになります。 <itemize> <item> <tt>/usr/include/</tt> とそのサブディレクトリにあるヘッダファイ ルのほとんどは H. J. Lu が提供している libc バイナリパッケージのもので す。「ほとんど」と書いたのは、他にもここにヘッダファイルを置くパッケー ジがあるからです。例えば <tt/curses/ や <tt/dbm/ などがそうです。古い libc パッケージは curses と dbm もいっしょになっていましたが、最新のも のでは別パッケージになっています。 <label id="index.18"> <!-- <linux/*.h> --> <label id="index.19"> <!-- <asm/*.h> --> <item> <tt>/usr/include/linux</tt> と <tt>/usr/include/asm</tt> ディレ クトリのファイル(これらは <tt><linux/*.h></tt>、 <tt><asm/*.h></tt> のようにインクルードされます)。これらのディ レクトリはカーネルソースツリーのうちの <tt>linux/include/linux</tt> ディ レクトリおよび <tt>linux/include/asm</tt> へのシンボリックリンクとなっ ています。少々複雑なプログラムを開発する場合はインストールしておく必要 があります。これらはカーネルのコンパイルのためだけのものではないのです。 カーネルソースを展開するだけではだめで、 <tt>make config</tt> を実行す る必要があります。このコマンドによってはじめて作成される <tt><autoconf.h></tt> は多くのファイルによって参照されていますし、 カーネルのバージョンによっては <tt/asm/ ディレクトリがシンボリックリン クになっていて、 <tt/make config/ のときにのみリンクが作成されるように なっています。 したがってカーネルソースを <tt>/usr/src/linux</tt> 以下に展開した後の 作業は次のようになります。 <tscreen><verb> $ cd /usr/src/linux $ su # make config [質問に答えます。この後に続けてカーネルを作るのでない限り ここでの答を気にする必要はあまりありません。] # cd /usr/include # ln -s ../src/linux/include/linux . # ln -s ../src/linux/include/asm . </verb></tscreen> <label id="index.20"> <!-- <float.h> --> <label id="index.21"> <!-- <limits.h> --> <label id="index.22"> <!-- <varargs.h> --> <label id="index.23"> <!-- <stdarg.h> --> <label id="index.24"> <!-- <stddef.h> --> <item> <tt/<float.h>/ や <tt/<limits.h>/、 <tt/<varargs.h>/、 <tt/<stdarg.h>/、 <tt/<stddef.h>/ といったファイルはコンパイラのバージョンによって内容が変わっています。 したがってこれらは <tt>/usr/lib/gcc-lib/i486-box-linux/2.7.2/include/</tt> か、あるいは似 たような名前の場所に置かれています。 </itemize> <sect1> クロスコンパイラの作成 <p> <sect2> Linux で実行するバイナリを他のホストでコンパイルする <p> gcc のソースコードがあればその中の INSTALL ファイルの指示に従うだ けでできるはずです。コンパイルを行うコンピュータの種類が <tt/XXX/ でし たら <tt/configure --target=i486-linux --host=XXX/ として <tt/make/ す れば、すべてやってくれるはずです。ただし実際のコンパイルには Linux 特 有のヘッダファイル、カーネルのヘッダファイルが必要になりますし、クロス アセンブラ、クロスリンカのソースを <url url="ftp://tsx-11.mit.edu/pub/linux/packages/GCC/"> から入手してそれぞれビルドする必要があります。 <!-- <sect2> Linux as the source platform, MSDOS as the target --> <sect2> MSDOS の実行バイナリを Linux でコンパイルする <p> うーん。 &dquot;emx&dquot; パッケージとか &dquot;go&dquot; エクステン ダなどを使うと可能になるそうです。 <url url="ftp://sunsite.unc.edu/pub/Linux/devel/msdos"> を見てみて下さい。 私はこれらを試したことがないので、性能について保証することはできません。 <!-- *********************** SECTION ************************** --> <!-- * Porting and Compiling --> <sect>移植とコンパイル <p> <sect1>自動定義されるシンボル <p> <label id="index.25"> <!-- gcc, flags --> 手元の gcc でどんなシンボルが自動定義されているかを調べるには <tt/-v/ スイッチをつけて gcc を実行します。私の場合は以下のようになり ました。 <tscreen><verb> $ echo 'main(){printf("hello world\n");}' | gcc -E -v - Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs gcc version 2.7.2 /usr/lib/gcc-lib/i486-box-linux/2.7.2/cpp -lang-c -v -undef -D__GNUC__=2 -D__GNUC_MINOR__=7 -D__ELF__ -Dunix -Di386 -Dlinux -D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__i386 -D__linux -Asystem(unix) -Asystem(posix) -Acpu(i386) -Amachine(i386) -D__i486__ - </verb></tscreen> Linux 特有の機能に依存したコードを書いている場合はその部分を以下のよう に囲っておくと良いでしょう。 <tscreen><verb> #ifdef __linux__ /* ... funky stuff ... */ #endif /* linux */ </verb></tscreen> この目的には <tt/__linux__/ を用います。 <tt/linux/ は用いるべきでは <em/ありません/。後者は POSIX 準拠ではないからです。 <!-- what does this mean? are we talking namespace issues here? --> <sect1> コンパイラへの指示 <p> コンパイラのスイッチに関する文書は gcc の info に書かれています。 (Emacs からは <tt/C-h i/ として `gcc' オプションを選びます)。インス トールに用いたバイナリ配布パッケージによってはこれが入っていなかったり 古かったりすることがあります。その場合は gcc のソースアーカイブを <url url="ftp://prep.ai.mit.edu/pub/gnu"> やミラーサイトから入手し、その中の info をコピーして使いましょう。 gcc の man ページ <tt/gcc.1/ は、一言で言ってしまうと内容が古いです。 これは man ページそのものの中でも警告されています。 <sect2>コンパイラのフラグ <p> <label id="index.26"> <!-- gcc, flags --> <label id="index.27"> <!-- optimisation --> <p> gcc のコマンドラインに <tt/-O/<em/n/ をつけると出力される コードを最適化することができます。 <em/n/ は整数です(省略すると 1 と みなされます)。意味のある <em/n/ の値とそれぞれの値に対する実質的な効 果はコンパイラのバージョンによって変わりますが、通常は 0 (最適化なし) から 2(たくさん)あるいは 3(とてもたくさん)までが意味を持ちます。 gcc 内部ではこれらの値は <tt/-f/ や <tt/-m/ オプション群に展開されます。 <tt/-O/ オプションのそれぞれのレベルにどのようなオプションが対応してい るかを調べるためには gcc を <tt/-v/ と <tt/-Q/ オプションをつけて実行 します(後者のオプションはマニュアルには載っていません)。例えば私の場 合 <tt/-O2/ に対しては以下のようになります。 <tscreen><verb> enabled: -fdefer-pop -fcse-follow-jumps -fcse-skip-blocks -fexpensive-optimizations -fthread-jumps -fpeephole -fforce-mem -ffunction-cse -finline -fcaller-saves -fpcc-struct-return -frerun-cse-after-loop -fcommon -fgnu-linker -m80387 -mhard-float -mno-soft-float -mno-386 -m486 -mieee-fp -mfp-ret-in-387 </verb></tscreen> 使っているコンパイラでの最も高い最適化レベルよりも高い数値を指定した場 合(例えば <tt/-O6/ など)の動作は、最高レベルの最適化を指定したのと同 じになります。しかし配布するコードにこのようなやり方で最適化を指定する のは良いアイディアとは言えません。もし将来リリースされるコンパイラで更 なる最適化が導入された場合、コードがうまく動かなくなる可能性があるから です。 <label id="index.28"> <!-- gcc, bugs --> gcc 2.7.0 および 2.7.2 のユーザは、これらの版の <tt/-O2/ にはバグがあ ることに気をつけて下さい。具体的には strength reduction が動作しないの です。 gcc を再コンパイルする場合はパッチを当てることによってこの問題 は解決できます。そうしない場合はコンパイルの際に常に <tt/-fno-strength-reduce/ を指定するようにして下さい。 <!-- I need a source for this patch --> <sect3>プロセッサ固有のフラグ <p> <tt/-O/ オプションのどのレベルでも指定はされませんが、 <tt/-m/ は 有用なフラグ群です。その最たるものは <tt/-m386/ と <tt/-m486/ で、それ ぞれ 386 と 486 に有利なコードを出力するように指定します。これらのオプ ションを指定してコンパイルしたコードは、それぞれ他のチップでも動作しま す。 486 のコードは大きくなりますが、386 の上でも遅くなることはありま せん。 現在はまだ <tt/-mpentium/ あるいは <tt/-m586/ というオプションは存在し ません。 Linus によれば <tt/-m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2/ を使うとアラインメントのための大きなギャップを作 ることなく 486 のコードを最適化できるそうです(Pentium ではそも そもアラインメントが必要とされません)。 Cygnus の Michael Meissner は こう言っています。 <quote> 個人的には <tt/-mno-strength-reduce/ を x86 のコードに指定すると速度は 向上すると思う(strength reduction のバグのことを言っているわけではな いことに注意されたい。それはまた別の話である)。 x86 CPU ではレジスタ が不足しやすいため、 gcc で用いている手法(レジスタ群を spill レジスタとそれ以外へグループ分けする)と相性が悪いからである。 strength reduction では乗算を加算で置き換える際により多くのレジスタを 使用する。私は <tt/-fcaller-saves/ も同様に性能低下の原因になると考える。 </quote> <quote> もう一つ私見を。 <tt/-fomit-frame-pointer/ は有利に働く場合も 不利に働く場合もあると思う。このオプションは他のレジスタを割り当て可能 にする。一方 x86 が命令セットを解釈するやり方から考えて、スタック相対 アドレスはフレーム相対アドレスよりも大きなスペースを必要とする。したがっ てプログラムで利用できる I キャッシュが少なくなってしまう。同様に <tt/-fomit-frame-pointer/ を指定するとコンパイラはスタックポインタを命 令コールのたびに再配置するが、フレームがある場合はスタックアキュムレー タを数命令で使いきってしまうことになる。 </quote> この話題の最後は再び Linus の言葉で締めくくりましょう。 <quote> 最高の性能を得るには私を信じちゃいけません。テストして下さい。 gcc コ ンパイラにはたくさんのオプションが在り、その組み合せのうちの一つがあな たにとってのベストな最適化となるはずです。 </quote> <!-- TODO: url for pentium gcc--> <sect2> <tt/Internal compiler error: cc1 got fatal signal 11/ <p> <label id="index.29"> <!-- gcc, bugs --> <label id="index.30"> <!-- segmentation fault --> <label id="index.31"> <!-- SIGSEGV --> <label id="index.32"> <!-- SIGSEGV, in gcc --> <label id="index.33"> <!-- segmentation fault, in GCC --> シグナル 11 は SIGSEGV または「セグメント違反」を意味します。通常こ れはプログラム中でポインタが混乱し、プログラムで管理していないメモリ領 域に書き込みを行おうとした結果です。したがってこれは gcc のバグである 可能性もあります。 しかし gcc (のほとんどの部分)は細部までテストされた信頼すべきソフト ウェアと言えます。一方 gcc では数多くの複雑なデータ構造や無数のポイン タを用いています。つまり通常手に入る中で最も優秀な RAM のテスターであ るとも言えるのです。もしバグが再現されなければ(コンパイルを再び行なっ たときに同じところで止まるのでなければ)それは多分間違いなく使っている ハードウェア(CPU、メモリ、マザーボードまたはキャッシュ)の障害です。 システムが電源投入時のチェックをパスするからといって、あるいは Windows で問題なく動作するからといってこの障害をバグと言ってはいけません。これ らの『テスト』は一般に価値が無いとみなされているからです(正当な判断と 言えます!)。またカーネルのコンパイルがいつも `<tt/make zImage/' の途 中で停止するからといって、これをバグだと言ってこないでください --- そ りゃ確かにバグかもしれませんけどね。 `<tt/make zImage/' はおそらく 200 以上のファイルをコンパイルします。我々が知りたいのはもう少し小さな範囲 なのです。 もしバグが再現できたら、また(より望むらくは)バグを引き起こす短いプロ グラムがあったら、その問題に関するバグレポートを FSF か linux-gcc メー リングリストに送りましょう。 gcc の文書を良く読んで、彼らが必要とする 情報に関して理解してからにしましょう。 <!-- pointer to the sig11 faq --> <sect1>移植性 <p> <!-- 25) How do I port program XXX to Linux ? --> 最近では『もし Linux に移植されていないプログラムがあったとしたら、 それはそもそも移植されるべき価値が無いのだ』とも言われています。 <tt/:-)/ もう少し真面目に。しかし一般に Linux の 100% POSIX 準拠を満たすには ソースを少々変更するだけで良いはずです。行なった変更はプログラムの原著 者にフィードバックすると良いでしょう。以降は `make' だけで実行ファイ ルができるようにしてもらえるかもしれません。 <sect2> BSD 系(<tt/bsd_ioctl/、 <tt/daemon/ および <tt/<sgtty.h>/) <p> <!-- <p> You can compile your program with <tt>-I/usr/include/bsd</tt> and link it with <tt>-lbsd</tt> (i.e. add <tt>-I/usr/include/bsd</tt> to <tt>CFLAGS</tt> and <tt>-lbsd</tt> to the <tt>LDFLAGS</tt> line in your Makefile). There is <em/no/ need to add <tt>-D__USE_BSD_SIGNAL</tt> any more if you want BSD type signal behavior, as you get this automatically when you have <tt>-I/usr/include/bsd</tt> and include <tt/<signal.h>/. --> プログラムは <tt>-I/usr/include/bsd</tt> をつければコンパイルでき、 また <tt>-lbsd</tt> をつければリンクできます(つまり Makefile の <tt/CFLAGS/ に <tt>-I/usr/include/bsd</tt> を加え、 <tt/LDFLAGS/ に <tt>-lbsd</tt> を加えるわけです)。 BSD 形式のシグナルの振る舞いを用い るために <tt>-D__USE_BSD_SIGNAL</tt> を加える必要はもうありません。 <tt>-I/usr/include/bsd</tt> を加えて <tt/<signal.h>/ をインクルー ドすれば自動的に選択されます。 <sect2>「失われた」シグナル(<tt/SIGBUS/, <tt/SIGEMT/, <tt/SIGIOT/, <tt/SIGTRAP/, <tt/SIGSYS/ など ) <p> <label id="index.34"> <!-- <tt/SIGBUS/ --> <label id="index.35"> <!-- <tt/SIGEMT/ --> <label id="index.36"> <!-- <tt/SIGIOT/ --> <label id="index.37"> <!-- <tt/SIGTRAP/ --> <label id="index.38"> <!-- <tt/SIGSYS/ --> Linux は POSIX に準拠しています。これらは POSIX で定義されているシ グナルではありません。 ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990) の B.3.3.1.1 から引用します。 <quote> 「SIGBUS、 SIGEMT、 SIGIOT、 SIGTRAP、 SIGSYS の各シグナルは POSIX.1 から削除されます。これらのシグナルの振舞いは実装によって異なっており、 適当な分類ができないからです。 POSIX 準拠の実装でもこれらのシグナル を発行することは許されていますが、発行される状況は文書化しなければなり ませんし、これらシグナルの発行に関するあらゆる制限を記述しておく必要が あります。」 </quote> <p>これを回避する安直な方法はこれらのシグナルを <tt/SIGUNUSED/ として 再定義することです。<em/正しい/方法はこれらを扱っているコードを適当な <tt/#ifdef/ の組で囲うことです。 <tscreen><verb> #ifdef SIGSYS /* ... non-posix SIGSYS code here .... */ #endif </verb></tscreen> <sect2>K & R のコード <p> <label id="index.39"> <!-- <tt/-fwritable-strings/ --> GCC は ANSI のコンパイラです。しかし現在存在する C のコードはほと んどが ANSI 準拠ではありません。 K & R のコードに関して GCC ができ ることはコンパイラのフラグに <tt/-traditional/ を付けることぐらいです。 もう少し精密なコントロールをすることも可能ですが、これらをエミュレート するのは各種の頭痛の種になるでしょう。詳しくは gcc の info を参照して 下さい。 <tt/-traditional/ は gcc の文法を変えるだけでなく、副作用を生じること に注意して下さい。例えば <tt/-traditional/ によって <tt/-fwritable-string/ が有効になります。このスイッチにより文字列定数 はデータ領域に書き込まれます(スイッチがないとプログラムによる変更が行 われないテキスト領域に書き込まれます)。これにより、プログラムのメモリ 使用量が増加します。 <sect2>プリプロセッサのシンボルがコード中のプロトタイプと衝突する <p> <label id="index.40"> <!-- <tt/atoi()/ --> <label id="index.41"> <!-- <tt/atol()/ --> ありがちなのは、汎用の関数が Linux のヘッダファイルでもマクロとして定 義されているため、プリプロセッサがコード中の同様なプロトタイプ宣言を 認めなくなるという問題です。良くあるのは <tt/atoi()/ と <tt/atol()/ です。 <sect2> <tt>sprintf()</tt> <p> <label id="index.42"> <!-- <tt/sprintf()/ --> (特に SunOS から移植する際に)気をつけなければならないのは、 <tt/sprintf(string, fmt, ...)/ の戻り値は多くの Unix では <tt/string/ へのポインタであるのに対して、 Linux では(ANSI に従い)文字列へ書き込 まれた文字数になっていることです。 <sect2><tt/fcntl/ など。 <tt/FD_*/ の定義はどこにあるの? <p> <label id="index.43"> <!-- <tt/FD_SET/ --> <label id="index.44"> <!-- <tt/FD_CLR/ --> <label id="index.45"> <!-- <tt/FD_ISSET/ --> <label id="index.46"> <!-- <tt/FD_ZERO/ --> <label id="index.47"> <!-- <tt/fcntl/ --> <label id="index.48"> <!-- <tt><sys/time.h></tt> --> <label id="index.49"> <!-- <tt><unistd.h></tt> --> <tt><sys/time.h></tt> で定義されています。 <tt/fcntl/ を用い る場合は <tt/<unistd.h>/ も一緒にインクルードする必要があるでしょ う。実際のプロトタイプはここで定義されています。 一般に、関数に必要な <tt/#include/ は man ページの SYNOPSIS セクショ ンに記述されています。 <!-- These courtesy of Bruno Haible--> <sect2><tt/select()/ が一度タイムアウトするとプログラムがウェイトしなくなる <p> <label id="index.50"> <!-- select() --> 昔は <tt/select()/ の timeout 引数は変更されませんでした。し かし当時でもマニュアルには以下のように書かれていました。 <quote> select() は与えられた timeout から(もしあれば)残った時間を、time の 値を置き換えることによって返すべきです。これはシステムの将来のバージョ ンでインプリメントされるでしょう。従って timeout のポインタが select() の呼び出しによって変更されないことを仮定したコードを書くのは良くありま せん。 </quote> その将来が来たわけです、少なくともここでは。 <tt/select()/ から戻るとき、 timeout 引数には待ち時間の残りがセットさ れます。データが最後まで到着しなけ れば timeout は 0 になりますので、 timeout 構造体をそのままにしてもう 一度 <tt/select()/ を呼ぶと、すぐに制御が返って来てしまいうというわけ です。 この問題を修正するには <tt/select()/ を呼ぶ度にタイムアウトの値を timeout 構造体に代入してやれば良いのです。今までのコードが以下のような ものだとしたら、 <tscreen><verb> struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; while (some_condition) select(n,readfds,writefds,exceptfds,&ero;timeout); </verb></tscreen> このように変えて下さい。 <tscreen><verb> struct timeval timeout; while (some_condition) { timeout.tv_sec = 1; timeout.tv_usec = 0; select(n,readfds,writefds,exceptfds,&ero;timeout); } </verb></tscreen> Mosaic のあるバージョンではこの問題が残っていたことがありました。回転 する地球のアニメーションが、ネットワークから到着するデータの速度と反比 例した速さで回転したのです! <sect2>システムコールが割り込まれる <p> <label id="index.51"> <!-- interrupted system calls --> <label id="index.52"> <!-- EINTR --> <sect3>症状 <p> プログラムが Ctrl-Z で停止されてから再開される(あるいはシグナルを 発生する他の状況: Ctrl-C による中断や子プロセスの終了など)と、プログ ラムが &dquot;interruputed system call&dquot; や &dquot;write: unknown error&dquot; と言ったようなメッセージを出します。 <sect3>問題点 <p> POSIX のシステムでは古い UNIX よりもシグナルチェックをする局面が多 くなっています。 Linux は以下のようなシグナルハンドラを実行します。 <itemize> <item> 非同期なチェック(タイマ割り込みの際) <item> システムコールからの戻り時 <item> 以下のシステムコールの実行時: <!--JC 2nd level itemize --> <itemize> <item> ターミナル、ソケット、パイプおよび <tt>/proc</tt> のファイルに対する <tt/select()/、 <tt/pause()/、 <tt/connect()/、 <tt/accept()/、 <tt/read()/。 <item> ターミナル、ソケット、パイプおよびラインプリンタへの <tt/write()/。 <item> FIFO、 PTY およびシリアルラインの <tt/open()/。 <item> ターミナルへの <tt/ioctl()/。 <item> <tt/fcntl()/ への <tt/F_SETLKE/ コマンド。 <item> <tt/wait4()/、 <tt/syslog()/、 その他全ての TCP および NFS 動作。 </itemize> </itemize> <!-- What does this MEAN? --> 他の OS では以下のようなシステムコールが対象になる場合もあります。 <tt>creat()</tt>、 <tt>close()</tt>、 <tt>getmsg()</tt>、 <tt>putmsg()</tt>、 <tt>msgrcv()</tt>、 <tt>msgsnd()</tt>、 <tt>recv()</tt>、 <tt>send()</tt>、 <tt>wait()</tt>、 <tt>waitpid()</tt>、 <tt>wait3()</tt>、 <tt>tcdrain()</tt>、 <tt>sigpause()</tt>、 <tt>semop()</tt>。 <p> もしプログラムがハンドラを持っているシグナルがシステムコールの 途中で発生すると、そのシグナルハンドラが呼び出されます。ハンドラからの 制御が(システムコールに)戻ると、システムコールは自分に対する割り込み を検知し、ただちに -1 を返すとともに <tt/errno = EINTR/ を セットします。プログラムはこのようなことが起こるとは思っていませんから、 異常終了します。 対処法は二つあります。 (1) 導入したシグナルハンドラごとに <tt/SA_RESTART/ を sigaction フラグに追加します。例えば <tscreen><verb> signal (sig_nr, my_signal_handler); </verb></tscreen> のようなものを以下のように書き換えます。 <tscreen><verb> signal (sig_nr, my_signal_handler); { struct sigaction sa; sigaction (sig_nr, (struct sigaction *)0, &ero;sa); #ifdef SA_RESTART sa.sa_flags |= SA_RESTART; #endif #ifdef SA_INTERRUPT sa.sa_flags &ero;= ~ SA_INTERRUPT; #endif sigaction (sig_nr, &ero;sa, (struct sigaction *)0); } </verb></tscreen> これはほとんどのシステムコールに適用できますが、 <tt/read()/、 <tt/write()/、 <tt/ioctl()/、 <tt/select()/、 <tt/pause/、 <tt/connect()/ の各システムコールに対しては <tt/EINTR/ のチェックをプ ログラム中で行なう必要があります。以下を参考にして下さい。 <!-- (2) Check for <tt/EINTR/ explicitly, yourself: --> (2) <tt/EINTR/ をプログラム中で明示的にチェックする。 <tt/read()/ と <tt/ioctl()/ に対する二つの例を示します。 まず <tt/read()/ の場合です。 <tscreen><verb> int result; while (len > 0) { result = read(fd,buffer,len); if (result < 0) break; buffer += result; len -= result; } </verb></tscreen> のようなコードを以下のように書き換えます。 <tscreen><verb> int result; while (len > 0) { result = read(fd,buffer,len); if (result < 0) { if (errno != EINTR) break; } else { buffer += result; len -= result; } } </verb></tscreen> 次は <tt/ioctl()/ の例です。 <tscreen><verb> int result; result = ioctl(fd,cmd,addr); </verb></tscreen> これを以下のように書き換えます。 <tscreen><verb> int result; do { result = ioctl(fd,cmd,addr); } while ((result == -1) && (errno == EINTR)); </verb></tscreen> BSD Unix のバージョンによっては、デフォルトでシステムコールをやり直す ことになっていることもあります。この場合システムコールを中断するには、 <tt/SV_INTERRUPUT/ か <tt/SA_INTERRUPT/ フラグを用いる必要があります。 <!-- according to some comments in /usr/include this may no longer be --> <!-- the case. Should check --> <sect2>書き込み可能な文字列(プログラムがランダムに停止する) <p> <label id="index.53"> <!-- SIGSEGV --> <label id="index.54"> <!-- segmentation fault --> <label id="index.55"> <!-- <tt/mktemp()/ --> <label id="index.56"> <!-- <tt/-fwritable-strings/ --> GCC はユーザを信頼しており、文字列定数はあくまで定数として扱われる ものとみなしています。従って GCC では文字列定数はテキスト(コード)領 域に保持されます。ここはプログラムのディスクイメージにページングされま す(スワップ領域に take up される代わりに)ので、この文字列定数を書き 換えようとするとセグメント違反となります。これは仕様です! 古いプログラムの場合ではこれが問題になることがあります。例えば <tt/mktemp()/ を文字列定数を引数にして呼び出す場合などです。 <tt/mktemp()/ は引数を書き換えようとするためです。 修正するには二つの方法があります。 (a) <tt/-fwritable-string/ を付けて コンパイルして gcc に定数をデータ領域に保持するよう伝える。 (b) 問題と なる部分を定数でない文字列に strcpy して、こちらを用いる。 <sect2><tt/execl()/ が失敗する <p> <label id="index.57"> <!-- <tt/execl()/ --> 間違った呼び出し方をしているからです。 <tt/execl/ の最初の引数は実 行したいプログラムです。2番目以降の引数は呼び出すプログラムの <tt/argv/ 配列になります。ここで <tt/argv[0]/ はプログラムの パスそのものであることに注意して下さい。従って<tt/execl/ の呼び出しは 以下のように書く必要があります。 <tscreen><verb> execl("/bin/ls","ls",NULL); </verb></tscreen> 単に以下のように書くのは間違いです。 <tscreen><verb> execl("/bin/ls", NULL); </verb></tscreen> 少なくとも a.out の場合は、引数を全くセットせずにプログラムを実行する と、依存しているダイナミックライブラリを表示するようになっています。 ELF ではまた違った動作となります。 もしこのライブラリの情報が必要でしたら、もっと簡単なインターフェースが あります。ダイナミックロードの節か <tt/ldd/ の man ページを見て下さい。 <!-- *********************** SECTION ************************** --> <!-- * Debugging --> <sect>デバッグとプロファイリング <p> <sect1>予防的メンテナンス(lint) <p> <label id="index.58"> <!-- lint --> Linux では広く用いられている lint はありません。ほとんどの人が gcc の出す warning に満足しているからです。おそらく <tt/-Wall/ スイッチが もっとも役にたつでしょう。これは `Warnings, all' の意味です。しかしこ のスイッチからは、どちらかというと頭を叩き付けたくなるもの(wall)を連 想しそうですね。 パブリックドメインの lint が <url url="ftp://larch.lcs.mit.edu/pub/Larch/lclint"> から入手できます。どのくらい良いかについては私は知りません。 <sect1>デバッグ <p> <label id="index.59"> <!-- debugging --> <sect2>プログラムにデバッグ情報を埋め込むには <p> <label id="index.60"> <!-- <tt/gcc -g/ --> <label id="index.61"> <!-- <tt/gcc -fomit-frame-pointer/ --> <label id="index.62"> <!-- <tt/libg.a/ --> コンパイルとリンクの全ての段階において <tt/-g/ スイッチを付け、 <tt/-fomit-frame-pointer/ スイッチは付けない必要があります。実際には全 ての部分を再コンパイルする必要はなく、デバッグしたい部分のみをすれば OK です。 a.out の共有ライブラリは <tt/-fomit-frame-pointer/ を付けてコンパイル されており、これに対して gdb を用いることはできません。 <tt/-g/ オプショ ンをリンクのときに付けると static リンクになってしまうのはこうした理由 からです。 「libg.a が見つからない」というメッセージが出てリンカが動かない場合は、 <tt>/usr/lib/libg.a</tt> がないのです。これはデバッグが可能になっている 特殊な C ライブラリです。 libc のバイナリパッケージに入っているか、あ るいは(最近の版の C ライブラリでは) libc のソースコードを手に入れて 自分でビルドする必要があります。しかし実際にはこれは必要なく、ほとんど の場合は <tt>/usr/lib/libc.a</tt> に対するシンボリックリンクを張れば、 必要な情報は得られるはずです。 <sect3>埋め込まれたデバッグ情報を除去するには <p> <label id="index.63"> <!-- binaries too big --> GNU のソフトウェアの多くは <tt/-g/ フラグがついた状態でコンパイル、 リンクされています。そのため実行ファイルは巨大(かつしばしば static リ ンク)になっています。これはあまりありがたくないですね。 <!-- <p> If the program has an autoconf generated <tt/configure/ script, you can usually turn off debugging information by doing <tt>./configure CFLAGS=</tt> or <tt>./configure CFLAGS=-O2</tt>. Otherwise, check the Makefile. Of course, if you're using ELF, the program is dynamically linked regardless of the <tt/-g/ setting, so you can just <tt/strip/ it. --> プログラムに autoconf で作った <tt/configure/ スクリプトが付属している 場合は、<tt>./configure CFLAGS=</tt> または <tt>./configure CFLAGS=-O2</tt> とすることでデバッグ用のコードを付加しないようにできま す。その他 の場合は Makefile を書き換えることになるでしょう。もちろん ELF を使っ ている場合はプログラムは <tt/-g/ の有無に関わらずダイナミックリンクと なっていますから <tt/strip/ すれば良いだけです。 <!--- gdb; where on sunsite? xxgdb almost certainly has wrong url--> <sect2>役に立つソフト <p> <label id="index.64"> <!-- gdb --> <bf/gdb/ が最も広く使われています。ソースは <url url="ftp://prep.ai.mit.edu/pub/gnu" name="GNU archive sites"> から、バイナリは <url url="ftp://tsx-11.mit.edu/pub/linux/packages/GCC" name="tsx-11"> から入手できます。 <bf/xxgdb/ は X 用のフロントエンドで(したがって先 に gdb をインストールしておく必要があります) 、ソースは <url url="ftp://ftp.x.org/contrib/xxgdb-1.08.tar.gz"> にあります。 <bf/UPS/ デバッガも Rick Sladkey によって移植されています。これも X で 動作しますが、 xxgdb とは違って単なるテキストベースデバッガのフロント エンドではありません。多くの便利な機能がありますので、デバッグ作業を行 う人はチェックしてみると良いでしょう。 Linux 用のコンパイル済みバイナ リと、オリジナルの UPS のソースコードへのパッチとが <url url="ftp://sunsite.unc.edu/pub/Linux/devel/debuggers/"> にあります。オリジナルのソースは <url url="ftp://ftp.x.org/contrib/ups-2.45.2.tar.Z">. です。 デバッグに便利なツールをもう一つ紹介しましょう。 <bf/strace/ はプログ ラムが発行するシステムコールを表示してくれます。またコンパイル済みの バイナリから呼び出されるパス名を表示してくれるので、ソースがなくてもど のファイルが使われているかを知ることができますし、プログラムが引き起こ す競合状態(race condition)のレポートをしてくれるなど、いろいろ便利に 使うことができます。プログラムがどのように動作しているかの学習にも適し ています。 strace の最新バージョン(現在 3.0.8)は <url url="ftp://ftp.std.com/pub/jrs/"> にあります。 訳注:現在は 3.1.0.1 のようです。 <sect2>バックグラウンド(デーモン)プログラム <p> 典型的なデーモンプログラムでは <tt/fork()/ を実行し、親プロセスを終 了します。するとデバッグのセッションもあっという間に終わってしまいます。 これを回避する一番簡単な方法は <tt/fork/ にブレークポイントをセットし、 プログラムが停止したときに 0 を返すように強制することです。 <tscreen><verb> (gdb) list 1 #include <stdio.h> 2 3 main() 4 { 5 if(fork()==0) printf("child\n"); 6 else printf("parent\n"); 7 } (gdb) break fork Breakpoint 1 at 0x80003b8 (gdb) run Starting program: /home/dan/src/hello/./fork Breakpoint 1 at 0x400177c4 Breakpoint 1, 0x400177c4 in fork () (gdb) return 0 Make selected stack frame return now? (y or n) y #0 0x80004a8 in main () at fork.c:5 5 if(fork()==0) printf("child\n"); (gdb) next Single stepping until exit from function fork, which has no line number information. child 7 } </verb></tscreen> <sect2>core ファイル <p> Linux はブート時の設定では core ファイルを作らない設定になっていま す。作る設定にしたい場合は、シェルの組み込みコマンドを使って再設定して ください。 C シェル系(tcsh など)では <tscreen><verb> % limit core unlimited </verb></tscreen> とします。 Bourne シェル系(sh、bash、zsh、pdksh)では次のようにします。 <tscreen><verb> $ ulimit -c unlimited </verb></tscreen> core ファイルの命名法をもうちょっと便利にしたい場合(例えばそれ自身良く 落ちるようなデバッガで他のプログラムが吐いた core ファイルをデバッグす るときなど)は、カーネルを少々変更すれば可能です。 <tt>fs/binfmt_aout.c</tt> と <tt>fs/binfmt_elf.c</tt> のコードの中に、 以下のような部分があるはずです(新しいカーネルの場合です。古いカーネル では少々 grep してまわる必要があるかもしれません)。 <tscreen><verb> memcpy(corefile,"core.",5); #if 0 memcpy(corefile+5,current->comm,sizeof(current->comm)); #else corefile[4] = '\0'; #endif </verb></tscreen> <!-- and change the <tt/0/s to <tt/1/s. --> この <tt/0/ を <tt/1/ に変えて <tt/memcpy(corefile+5../ の行の方を有効 にしてください。 <sect1>プロファイリング <p> プロファイリングとはプログラムのどの部分が最も多く呼ばれ、最も長い時間 を食っているのかを調べることです。無駄な時間を使っているコードを最適化 するのに良い方法です。プロファイルを行うにはタイミング情報が必要なオブ ジェクトファイルを <tt/-p/ をつけてコンパイルします。出力されたファイ ルから情報を得るには <tt/gprof/ が必要になります(binutils のパッケー ジに入っています)。詳細は <tt/gprof/ の man ページを見てください。 <!-- **************************** SECTION ********************* --> <!-- * Linking --> <sect>リンク <p> あらかじめお断りしておきますが、この章の構成は複雑になっています。 互換性のないバイナリの形式が二つあること、 static なリンクと共有ライブ ラリを使う場合があること、「リンク」という言葉が「コンパイルの後に行う 作業」と「コンパイルされたプログラムが呼び出されたときに行われること」 という二つの意味で用いられること(「ロード(load)」という言葉も使われま す。ただし逆の意味で) などが原因です。でも今あなたが読んだばかりの文よ りごちゃごちゃしている部分は少ないはずですから、それほど心配しないでく ださい。 <!--JC この文章より簡単... というのは日本語にしにくい(^_^;--> 多少なりとも混乱を少なくするために、実行時に行われることは「動的ロード」 と呼ぶことにして、その内容は次の章に書きます。同じ内容を「動的リンク」 と書いている文書もありますが、この文書では「動的リンク」は用いま せん。要するに、この章ではコンパイルの最終段階として行うリンクに ついてのみを扱います。 <sect1>共有ライブラリ対 static なライブラリ <p> <!-- <p> The last stage of building a program is to `link' it; to join all the pieces of it together and see what is missing. Obviously there are some things that many programs will want to do -\-\- open files, for example, and the pieces that do these things are provided for you in the form of libraries. On the average Linux system these can be found in <tt>/lib</tt> and <tt>/usr/lib/</tt>, among other places. --> プログラム作成の最終段階は「リンク」と呼ばれます。全ての部品を結合 して、足りないものがないかどうか調べる作業です。「ファイルを開く」といっ たような類の作業は、多くのプログラムで行われます。したがってこのよ うな機能を持つ「部品」はライブラリの形で提供されています。普通の Linux システムでは、ライブラリは <tt>/lib</tt> と <tt>/usr/lib</tt> にありま す(他の場所にあることもままありますが)。 <label id="index.65"> <!-- binaries too big --> <label id="index.66"> <!-- statically linked binaries, unexpected --> static なライブラリを用いる場合は、リンカはプログラムが必要とする部品 を探し、出力する実行ファイルにその部品をコピーします。共有ライブラリの 場合は違った作業が行われます。リンカは出力ファイルに「このプログラムが 実行されるときには、まずこれこれのライブラリがロードされていないといけ ませんよ」といったメッセージを埋め込みます。したがって明らかに共有ライ ブラリを用いる方が実行ファイルのサイズは小さくなります。また消費するメ モリやディスク容量も小さくなります。 Linux におけるのデフォルトの振る 舞いでは、共有ライブラリがあればそちらを用い、なければ static なリンク を行います。実行ファイルを共有ライブラリ形式にしたいのに static になっ てしまった場合は、正しい位置に共有ライブラリのファイルがあって(a.out では <tt/*.sa/、 ELF では <tt/*.so/ です)、それらが読み込み可能になっ ているかどうかをチェックしてください。 Linux では static なライブラリは <tt/libname.a/ といったような名前を持っ ており、共有ライブラリは <tt/libname.so.x.y.z/ となっています。 <tt/x.y.z/ はバージョン番号を示します。共有ライブラリにはリンクが張ら れることが多く、これは重要な機能を持っています。また a.out を利用する 設定では <tt/.sa/ という拡張子を持ったファイルもあるはずです。標準ライ ブラリは共有形式のものと static な形式の両方が含まれています。 あるプログラムがどのような共有ライブラリを必要とするかを調べるには <tt/ldd/ コマンド(List Dynamic Dependencies)を用います。 <tscreen><verb> $ ldd /usr/bin/lynx libncurses.so.1 => /usr/lib/libncurses.so.1.9.6 libc.so.5 => /lib/libc.so.5.2.18 </verb></tscreen> これは WWW ブラウザのプログラム lynx が <tt/libc.so.5/ (C ライブラリ) と <tt/libncurses.so.1/ (端末制御ライブラリ) を用いていることを示して います。もし利用する共有ライブラリがなければ、 <tt/ldd/ の表示は 「<tt/statically linked/]か「<tt/statically linked (ELF)/」となります。 <sect1>ライブラリに尋ねる(<tt/sin()/ はどこにいるの?) <p> <label id="index.67"> <!-- <tt/sin()/ --> <label id="index.68"> <!-- <tt/cos()/ --> <label id="index.69"> <!-- maths --> <tt/nm/ <em/libraryname/ とすると <em/libraryname/ が参照している シンボルのリストが表示されます。 static なライブラリにも共有ライブラリ にも有効です。例えば <tt/tcgetattr()/ が定義されているライブラリが知り たい場合としましょう。この場合はまず以下を実行します。 <tscreen><verb> $ nm libncurses.so.1 |grep tcget U tcgetattr </verb></tscreen> <tt/U/ は「定義されていない(undefined)」ことを意味します。したがって ncurses ライブラリでは tcgetattr を用いていますが、定義はしていないこ とにことになります。続いて以下のように実行します。 <tscreen><verb> $ nm libc.so.5 | grep tcget 00010fe8 T __tcgetattr 00010fe8 W tcgetattr 00068718 T tcgetpgrp </verb></tscreen> <tt/W/ は「弱い定義(weak) 」であることを示します。すなわちこのシンボ ルは定義されてはいますが、他のライブラリの定義によって上書きされること を示しているのです。通常の定義は <tt/T/ で示されます(<tt/tcgetpgrp/ がそうです)。 <label id="index.70"> <!-- <tt/<math.h>/ --> ところでこの節のタイトルに対する解答は <tt/libm(so|a)/ です。 <tt><math.h></tt> で定義されている関数の本体は全て math ライブラ リにあります。したがってこのような関数を用いるには、リンクの際に <tt/-lm/ が必要になるわけです。 <!-- 13) Why are my binaries so huge and how do I reduce them ? --> <!-- covered static/shared, need to look at -g, -N --> <sect1>ファイルを探す <p> <tt/ld: Output file requires shared library `libfoo.so.1`/ ld などのプログラムにおけるファイル検索の方法はバージョンによって異な ります。しかし全てのバージョンにおいて、<tt>/usr/lib</tt> は検索対象に 入っています。もしここ以外のディレクトリをライブラリ検索の対象にしたけ れば、 gcc や ld などの <tt/-L/ オプションを用いてください。 これで見つからなければ、正しいファイルがそのディレクトリにあるかを確認 してください。 a.out では <tt/-lfoo/ を指定してリンクすると、 ld はま ず <tt/libfoo.sa/ (共有ライブラリ)を探し、失敗すると <tt/libfoo.a/ (static ライブラリ)を検索します。 ELF の場合は <tt/libfoo.so/、 <tt/libfoo.a/ の順に探します。 <tt/libfoo.so/ は通常 <tt/libfoo.so.x/ へのシンボリックリンクとなっています。 <!-- n+1) How/when to recompile libc --> <sect1>自分のライブラリを作る <p> <sect2>バージョン管理 <p> プログラムと同様、ライブラリにもバグはつきもので、時間とともに修正 されていきます。また新たな機能が追加されたり、関数の仕様が変更されたり 古いものが削除されたりもします。これはライブラリを使用するプログラムに は問題です。もし古い仕様に基づいている場合はどうすれば良いのでしょうか? したがってライブラリにはバージョン管理を用います。ライブラリに対して行っ た変更を「小さい(minor) 」ものと「大きい(major)」ものに分けます。 minor なな変更では、そのライブラリを用いるプログラムがちゃんと動くことを保証 することにします。ライブラリのバージョンはファイル名でわかるようにしま す。(本当の事を言うとこれは ELF には当てはまりません。理由は後述。) <tt/libfoo.so.1.2/ は major バージョンが 1 で、 minor バージョンが 2 であることを示します。 minor バージョンは少々違った構造を持つこともあ ります。 libc では「パッチレベル」を minor バージョンに追加し、ライブ ラリの名前を <tt/libc.so.5.2.18/ のようにしています。 ASCII 端末で表示 可能な文字なら、英字でも _ でもつけてかまいません。 ELF と a.out の大きな違いの一つは、共有ライブラリの作り方にあります。 まず簡単な ELF のほうから見ることにしましょう。 <label id="index.71"> <!-- elf --> <sect2>ELF って結局なに? <p> ELF (Executable and Linking Format)はもともと USL(UNIX System Laboratories)で開発されたバイナリ形式で、現在では Solaris と System V Release 4 で用いられています。 ELF は以前 Linux で用いられていた a.out 形式よりも柔軟性に富んでいたので、 GCC と C ライブラリの開発者たちは昨 年に ELF を Linux の標準バイナリ形式としても採用することに決めました。 <sect3>だからなんだって? <p> この節は `/news-archives/comp.sys.sun.misc' にある文書から抜粋した ものです。 <quote> ELF(Executable Linking Format)は SVR4 に導入された「進歩した最新の」 オブジェクトファイル形式です。 ELF はユーザによる拡張が可能であり、 straight COFF よりもずっと強力です。 ELF では、オブジェクト ファイルを任意の長さを持ったセクションからなるものします(決まったサ イズの要素からなる配列とはみなされません)。これらのセクションは(COFF とは異なり) 決まった場所に置く必要がなく、また順番も任意です。ユーザがオブジェクト ファイルに新たなデータを導入したければ、新しいセクションを追加するだけ で良いのです。 ELF にはこれまでのものよりずっと強力なデバッグ支援用の形式も導入されて います。これは DWARF(Debugging With Attibute Record Format)と呼ばれ ています。現在のところ linux ではこの機能は完全にはサポートされていま せん(しかし作業は着々と継続中です)。 DWARF DIE(Debugging Information Entries)のリンクリストは ELF バイナリの中の .debug という セクションに収められています。小さく、サイズも固定されたデバッグ情報と 異なり、DWARF DIE はそれぞれ任意の長さの複雑な属性を持ち、スコープに依 存したツリー形式のプログラムデータとして記述されています。 DIE は COFF の .debug セクションでは不可能であったような、巨大な情報(C++ の継承関 係リストなど)を保有することができるのです。 </quote> <quote> ELF ファイルは SVR4 (Solaris 2.0 ?)の ELF アクセスライブラリを通じて アクセスされます。このライブラリは ELF で取り扱いが厄介になっている部 分に対して、簡単で高速なインターフェースを提供しています。このライブラ リを用いれば、 ELF ファイルの実体そのものを見なくても済みます。 Elf ファ イルとしてアクセスされた UNIX のファイルは、最初に elf_open() コールを 実行すれば、後はその中身に elf_foobar() コールでアクセスすることができ ます。今まで COFF ユーザが強制されてきたように、実際のディスク上の位置 を求めてさまよう必要はもう無いのです。 </quote> ELF の有利/不利な点、および a.out のシステムを ELF システムにアップグ レードする際に必要な内容は ELF-HOWTO に記述されています。ここにカット & ペーストするつもりはありません。 ELF-HOWTO は、この文書と同じと ころにあるはずです。 <sect3>ELF 共有ライブラリ <p> <tt/libfoo.so/ のような共有ライブラリを作成するための基本的な手順 は以下のようになります。 <tscreen><verb> $ gcc -fPIC -c *.c $ gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o $ ln -s libfoo.so.1.0 libfoo.so.1 $ ln -s libfoo.so.1 libfoo.so $ LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH ; export LD_LIBRARY_PATH </verb></tscreen> これによって <tt/libfoo.so.1.0/ という名前の共有ライブラリができ、ld のためのリンク(<tt/libfoo.so/)とダイナミックローダのためのリンク (<tt/libfoo.so.1/)が作成されます。テストするにはカレントディレクトリ を <tt/LD_LIBRARY_PATH/ に追加します。 <label id="index.72"> <!-- weird things --> ライブラリがちゃんと動いたら、これを移動する必要があります。 <tt>/usr/local/lib</tt> あたりが適当でしょう。上で作ったようなリンクも それぞれ作り直す必要があります。 <tt/libfoo.so.1/ と <tt/libfoo.so.1.0/ のリンクは <tt/ldconfig/ によって常に最新のものに更 新されます。 通常 <tt/ldconfig/ はブートプロセスの一部で実行されているはずです。 <tt/libfoo.so/ のリンクはマニュアルで更新する必要があります。几帳面な 方はライブラリの全て(ヘッダファイルなども含む)を同時にアップデートし たくなるでしょうが、その場合は <tt>libfoo.so -> libfoo.so.1</tt> とい うリンクを張っておき、 ldconfig が両者を同時に細心にしてくれるようにし ておくのが最も簡単でしょう。<em/そうしない/人は、後に<em/あらゆる種類 の不可思議な現象/に見舞われることになるでしょう。後で「聞いてないよ」なん て言わないように! <tscreen><verb> $ su # cp libfoo.so.1.0 /usr/local/lib # /sbin/ldconfig # ( cd /usr/local/lib ; ln -s libfoo.so.1 libfoo.so ) </verb></tscreen> <sect3>バージョンの番号付けと soname、シンボリックリンク <p> <label id="index.73"> <!-- soname --> <label id="index.74"> <!-- version numbers --> 各々のライブラリは <em/soname/ という情報を持っています。リンカがライ ブラリを検索する際にこの soname を見つけると、実行バイナリにはライブラ リのファイル名ではなく soname が埋め込まれます。するとプログラムの実行 時に、動的ローダはリンク時に用いられたファイルではなく soname で指定されるファイルを探索します。例えば <tt/libfoo.so/ という ライブラリが <tt/libbar.so/ という soname を持つことも可能で、すると <tt/libfoo.so/ にリンクされたプログラムは実行時に <tt/libbar.so/ の方 を検索します。 意味の無い機能だと思いますか?実は同じライブラリの複数バージョンをシス テムに共存させるための鍵となる機能なのです。 Linux におけるライブラリ 命名法のデファクト・スタンダードは <tt/libfoo.so.1.2/ といったようなも ので、これに対する soname は <tt/libfoo.so.1/ となります。このライブラ リが標準のライブラリディレクトリ(例えば <tt>/usr/lib</tt>)に置かれる と、 <tt/ldconfig/ は <tt/libfoo.so.1 -> libfoo.so.1.2/ というシン ボリックリンクを作り、実行時に適当なファイルが利用されるようにします。 また <tt/libfoo.so -> libfoo.so.1/ というリンクも必要で、これにより ld はリンク時に用いるべき正しい soname を見つけることができるようになりま す。 ライブラリのバグを修正したり新機能を追加(今までのプログラムに影響を与 えない範囲で)したりしたときには、 soname はそのままにしてファイル名を 変更するのです。ライブラリの上位互換性がなくなったときには soname の番 号を一つ増やします。この場合新しいバージョンのライブラリはファイル名が <tt/libfoo.so.2.0/、 soname が <tt/libfoo.so.2/ となります。 <tt/libfoo.so/ のリンクも新しいバージョンへ張りなおせばライブラリの更 新に伴う手続きがすべて完了したことになります。 絶対にこの規則でライブラリの命名を行わなければならないわけではありませ んが、良い慣習ですので利用する方が良いと思います。 ELF ではライブラリ の命名に関しても柔軟性がありますから、人がうんざりするほど複雑な命名ルー ルを使うことだってできますが、実際にそうするかどうかはまた別の話ですよね。 実行方法をまとめます。伝統に従い major なアップグレードは互換性が失わ れたとき、 minor なアップグレードはそうでないときということにしましょ う。この場合は以下のようにリンクしてください。 <tscreen><verb> gcc -shared -Wl,-soname,libfoo.so.major -o libfoo.so.major.minor </verb></tscreen> これでうまく動くはずです。 <sect2>a.out、汝古き形式よ <p> ライブラリが ELF へ移行した主な理由は、共有ライブラリを簡単に作れ るという点にありました。しかし a.out でも作成が不可能なわけではありません。 <url url="ftp://tsx-11.mit.edu/pub/linux/packages/GCC/src/tools-2.17.tar.gz"> を入手して、パッケージの中にある 20 ページのドキュメントを読みましょう。 私は一方の党派に偏るつもりはありませんが、でも今まで書いてきた文章で、 私がもう a.out を使うつもりが無いことは明らかでしょうかね <tt/:-)/ <sect3>ZMAGIC と QMAGIC <p> <label id="index.75"> <!-- ZMAGIC --> <label id="index.76"> <!-- QMAGIC --> 古い a.out の実行バイナリは ZMAGIC と呼ばれます。 QMAGIC は ZMAGIC と 似ていますが、最初のページをマップしない点が違っています。 したがって 0〜4096 番地がどこにもマッピングされてないため、NULL ポインタ から変数を参照するという間違いを見つけやすくなっています(0 番地をアク セスすると、トラップされるわけです)。 また別の利点としてバイナリが少々(1K くらい)小さくなります。 非常に古いリンカでは ZMAGIC のみに、やや古いものは両方に、最近のものは QMAGIC のみに対応しています。カーネルは両方のフォーマットを実行させる ことができますので、実際には気にする必要はありません。 プログラムが QMAGIC かどうかは、`file' コマンドを実行することによって 表示されます。 <sect3>ファイルの配置 <p> a.out の共有ライブラリは二つのファイルと一つのシンボリックリンクから構 成されます。この文書でこれまで使ってきた「foo」ライブラリを例にとりま すと、 <tt/libfoo.sa/ と <tt/libfoo.so.1.2/ が実際のファイル、 <tt/libfoo.so.1/ が <tt/libfoo.so.1.2/ へのシンボリックリンクです。 これらの役割はどんなものでしょうか? コンパイルの際に、 <tt/ld/ は <tt/libfoo.sa/ を探します。このファイル はライブラリの「stub」ファイルと呼ばれ、外から参照できるデータと 実行時リンクに必要な関数へのポインタとを保持しています。 実行時には、ダイナミックローダは <tt/libfoo.so.1/ を探索します。これは 実ファイルではなくシンボリックリンクになっていて、バグフィックスなどに よるアップデートの際に、このライブラリを使っていたプログラムに問題が生 じないようにしています。新しいバージョンのライブラリ (<tt/libfoo.so.1.3/ など)があれば、 ldconfig を実行することによって リンク先を変更でき、このライブラリを用いていたプログラムには影響を与え なくてすみます。 DLL ライブラリ(トートロジーだということは承知しています <tt/:-p/)は static なライブラリに比べて大きくなる場合が多いです。 DLL ライブラリに は将来の拡張に備えて、「hole」という形式の領域が確保されています。この hole 領域が実際にはディスクを消費しないようにすることもできます。単に <tt/cp/ するか、あるいは <tt/makehole/ コマンドを用います。(a.out で は)アドレスは固定されているので、ライブラリ構築後に strip することも できます。なお<bf/ ELF ライブラリは strip してはいけません!/ <sect3>「libc-lite」とは? <p> libc-lite は libc ライブラリの軽量版です。フロッピーでの利用に適し、 UNIX の基本的なタスクのほとんどをカバーしています。 libc-lite には curses、 dgm、 termcap などのコードは入っていません。もしお使いのシス テムの <tt>/lib/libc.so.4</tt> がこの lite 版へのリンクでしたら、 full サイズの版に置き換えた方が良いでしょう。 <sect2>リンク:よくある問題 <p> リンクの際に障害が起こったら私に教えてください!私が手助けできることは あまり無いかもしれませんが、同じのがたくさんきたらここに載せることはで きます... <descrip> <tag/共有形式でリンクしたいのに static になってしまう/ <label id="index.77"> <!-- binaries too big --> <label id="index.78"> <!-- statically linked binaries, unexpected --> <tt/ld/ が共有ライブラリを検索できるよう、それぞれリンクがちゃんと張ら れているか確認してください。 ELF の場合はシンボリックリンク <tt/libfoo.so/ が、 a.out では <tt/libfoo.sa/ が実ファイルへ張られていな ければなりません。この問題は ELF binutils を 2.5 から 2.6 に更新したと きに非常に多く報告されました。以前のバージョンではライブラリの検索を 「賢く」行っており、全てのリンクがなくても動作することがありました。新 しいバージョンでは他のアーキテクチャとの整合性をとるために、この機能を 削除しました。また推測を誤ると、より深刻な問題を引き起こす可能性が生じ ることも削除された理由の一つです。 <tag/ DLL のツール `mkimage が libgcc の検索に失敗します/ <label id="index.79"> <!-- libgcc --> <tt/libc.so.4.5.x/ 以上では、 libgcc は共有ライブラリではなくなりまし た。したがって問題が生じた行の「<tt/-lgcc/」は「<tt/`gcc -print-libgcc-file-name`/」に置き換えてください。バッククォート「`」を お忘れなく。 また <tt>/usr/lib/libgcc*</tt> は全て削除してください。こちらも重要で す。 <tag/ <tt/__NEEDS_SHRLIB_libc_4 multiply defined/ というメッセージが出る/ <p> これは上と同種の問題です。 <tag/ DLL を再構築するときに ``Assersion failure'' というメッセージがでる/ この謎のメッセージは、恐らくジャンプテーブルの slot のうちの一つがオー バーフローしてしまったことを意味しています。オリジナルの <tt/jump.vars/ ファイルに予約した領域が小さすぎたことが原因と考えられ ます。問題の個所は <tt/getsize/ によって特定できます(<tt/getsize/ は tools-2.17.tar.gz パッケージにあります)。この場合残念ながら、ライブラ リの major バージョンを上げ、下位互換性をあきらめることが唯一の解決法 となるでしょう。 <tag/<tt/ld: output file needs shared library libc.so.4// これは libc 以外のライブラリ(X 関係のライブラリなど)を用いており、か つ <tt/-g/ をつけて <tt/-static/ をつけていない場合に生じます。 共有ライブラリに対応した stub ファイル(<tt/*.sa/)には、通常 <tt/_NEEDS_SHRLIB_libc_4/ という未定義のシンボルが含まれています。これ は <tt/libc.sa/ stub によって解決されています。 <tt/-g/ が指定されてい ると <tt/libg.a/ または <tt/libc.a/ とのリンクが行われますが、ところが これらのライブラリでは上のシンボルは解決されていないのです。したがって 表題のエラーとなります。 結局 <tt/-g/ をつけてコンパイルするときは <tt/-static/ を追加するか、 あるいはリンクの際に <tt/-g/ を用いないか、が解答です。リンクの際に <tt/-g/ を用いなくても、各々のソースを <tt/-g/ でコンパイルしておけば、 ほとんどの場合は充分なデバッグ情報が得られます。 </descrip> <!-- *********************** SECTION ************************** --> <!-- * Dynamic Loading --> <sect>ダイナミックロード(Dynamic Loading) <p> この章は今のところまだ短いです。私が ELF HOWTO の内容を消化するに連れ、 だんだん拡張されていくでしょう。 <sect1>概念 <p> 前の章全部を一気に読んだ人はもう聞き飽きたかもしれませんが、 Linux は 共有ライブラリを利用しています。「名前を検索して置き換える」という動作 が、リンク時から実行時に先送りされるようになったのです。 <sect1>エラーメッセージ <p> リンクエラーが起こったら送ってください!解決はできないかもしれませんが、 ここにまとめたいと思います... <descrip> <tag><tt> can't load library: /lib/libxxx.so, Incompatible version</tt></tag> (a.out のみ)これは xxx ライブラリの major バージョンがあっていないか らです。ただシンボリックリンクを違うバージョンのライブラリに張っただけ ではだめですよ(そんなことをして segfault くらいで済んだら幸運というも のです)。新しいバージョンを入手してください。 ELF の場合、同じような 状況では以下のようなメッセージとなります。 <tscreen><verb> ftp: can't load library 'libreadline.so.2' </verb></tscreen> <tag/<tt/ warning using incompatible library version xxx// (a.out のみ)このメッセージを吐いたプログラムをコンパイルした人よりも minor バージョンが古いライブラリを使っている場合に出ます。多分プログ ラムはちゃんと動くと思います。でもアップグレードした方が良いでしょうね。 </descrip> <sect1>ダイナミックローダの動作をコントロールする <p> <label id="index.80"> <!-- <tt/LD_*/ environment variables --> <label id="index.81"> <!-- ldd --> ダイナミックローダが参照する環境変数は多岐に渡っています。これらのほと んどは <tt/ldd/ だけが使うものなので、 <tt/ldd/ に対応するスイッチをつ けて実行する方が便利かもしれません。 <itemize> <item> <tt/LD_BIND_NOW/ 通常ライブラリ中の関数は、呼び出されるまでアク セスされません。このフラグを設定しておくと、ライブラリがロードされたと きに全ての関数が確認されます( したがって時間がかかります)。このオプショ ンはプログラムをテストするときに、全てがちゃんとリンクされているかを確 認するときに役に立ちます。 <item> <tt/LD_PRELOAD/ を使うと関数定義を「上書き」するファイルを指定 できます。例えばメモリ割り当て関数を実装して、これを `malloc' と置き換 えたい場合には、その実装を <tt/malloc.o/ にコンパイルして以下のように します。 <tscreen><verb> $ LD_PRELOAD=malloc.o; export LD_PRELOAD $ some_test_program </verb></tscreen> <tt/LD_ELF_PRELOAD/ と <tt/LD_AOUT_PRELOAD/ も同じような機能を持ちます が、それぞれのバイナリ形式に対してのみ有効になります。 <tt/LD_/<em/something/<tt/_PRELOAD/ と <tt/LD_PRELOAD/ が同時に定義さ れている場合は、範囲の狭い指定の方が有効になります。 <item> <tt/LD_LIBRARY_PATH/ はコロンで区切られたリストで、共有ライブラ リを検索するディレクトリを指定します。この指定は実行時のみに用いられ、 <tt/ld/ には影響しません。また setuid や setgid されたプログラムには無 効です。これにも <tt/LD_ELF_LIBRARY_PATH/ と <tt/LD_AOUT_LIBRARY_PATH/ があり、バイナリ形式によって異なる検索リストを指定できます。通常は <tt/LD_LIBRARY_PATH/ は必要ないでしょう。ディレクトリを <tt>/etc/ld.so.conf</tt> に加えて ldconfig を実行しなおす方をお勧めし ます。 <item> <tt/LD_NOWARN/ は a.out のみに適用されます。設定されていると (つまり <tt/LD_NOWARN=true; export LD_NOWARN/)、ローダは致命的でない 警告メッセージ(minor バージョンがあっていないなど)を出力しなくなりま す。 <item> <tt/LD_WARN/ は ELF にのみ適用されます。設定されていると通常は 致命的エラーである ``Can't find library'' をワーニングに変えます。通常 はあまり利用されることはないでしょうが、 ldd には重要な設定です。 <item> <tt/LD_TRACE_LOADER_OBJECTS/ は ELF にのみ適用されます。指定す ると、それぞれの実行プログラムは、自分が <tt/ldd/ の下で実行されている ものと考えるようになります。 <tscreen><verb> $ LD_TRACE_LOADED_OBJECTS=true /usr/bin/lynx libncurses.so.1 => /usr/lib/libncurses.so.1.9.6 libc.so.5 => /lib/libc.so.5.2.18 </verb></tscreen> </itemize> <sect1>ダイナミックロードを用いたプログラムを書く <p> <label id="index.82"> <!-- <tt/dlopen()/ --> <label id="index.83"> <!-- <tt/dlsym()/ --> <!-- <p> This is very close to the way that Solaris 2.x dynamic loading support works, if you're familiar with that. It is covered extensively in H J Lu's ELF programming document, and the <tt>dlopen(3)</tt> manual page, which can be found in the ld.so package. Here's a nice simple example though: link it with <tt>-ldl</tt> --> Solaris 2.x でのダイナミックロード機能と非常に似ています(といっても Solaris ユーザにしかわかりませんね)。具体的な内容は H. J. Lu の ELF programming document および <tt/dlopen(3)/ の man ページで詳述されてい ます。後者は ld.so のパッケージに入っています。以下にもちょっとした例 を示します。 <tt/-ldl/ をつけてリンクしてください。 <tscreen><verb> #include <dlfcn.h> #include <stdio.h> main() { void *libc; void (*printf_call)(); if(libc=dlopen("/lib/libc.so.5",RTLD_LAZY)) { printf_call=dlsym(libc,"printf"); (*printf_call)("hello, world\n"); } } </verb></tscreen> <!-- *********************** SECTION ************************** --> <!-- * The developers --> <sect>開発者に連絡を取るには <p> <sect1>バグレポートを送る <p> <label id="index.84"> <!-- gcc, bugs --> まず問題を<bf/絞り込んで/ください。 Linux に特有の問題か、他のシステム の gcc でも起こるか?カーネルのバージョンに固有の問題か?ライブラリの バージョンは?リンクを static にすれば解決するか?問題を起こすプログラ ムを<bf/短い/デモンストレーション版に切りつめることができるか?などで す。 これが済んだら、どのプログラムにバグがいるかがはっきりすると思います。 GCC の場合は、バグレポートの手続きは info ファイルで説明されています。 ld.so や C ライブラリ、 math ライブラリの場合は <tt/linux-gcc@vger.rutgers.edu/ にメールを送ってください。可能ならば短 く、バグの存在をはっきりと示すプログラムを添付し、そのプログラムで想定し ていた動作と実際の動作についての説明も送ってください。 <sect1>開発に参加する <p> GCC や C ライブラリの開発に参加したい場合は、まずメーリングリスト <tt/linux-gcc@vger.rutgers.edu/ に参加してください。どんなことが議論さ れているかを知りたいだけならば、リストのアーカイブが <url url="http://homer.ncm.com/linux-gcc/"> にあります。その次になすべきことはあなた次第です! <!-- Maybe there should be a TODO list for gcc/libc/whatever, for --> <!-- people who want to play with it? Moot the idea on the list --> <!-- *********************** SECTION ************************** --> <!-- * Clearing up --> <!--@@@--> <sect>その他 <p> <sect1>謝辞 <p> <quote> 「我々」と言う言葉を使うことができるのは、大統領と編集者、そして 体内にサナダ虫を飼っている人々のみである </quote> (Mark Twain) この HOWTO は Mitchum DSouza の GCC-FAQ から非常に多くの題材を取ってい ます。 GCC-FAQ の大部分の(本当に大部分の)情報は、この文書にそのまま の形で導入されています。この HOWTO の文章中、一人称(「私」)が指す実 体は我々のどちらの場合もありえます。 「私はこれらを試していません。これらを試したことによってあなたのディス クやシステムや奥さんが焼けてしまっても私のせいにしないでください」といっ た内容は、特に指定が無ければ我々の両方に当てはまります。 この文書に助力くださった人々です(ファーストネームの ASCII 順です)。 Andrew Tefft, Axel Boldt, Bill Metzenthen, Bruce Evans, Bruno Haible, Daniel Barlow, Daniel Quinlan, David Engel, Dirk Hohndel, Eric Youngdale, Fergus Henderson, H.J. Lu, Jens Schweikhardt, Kai Petzke, Michael Meissner, Mitchum DSouza, Olaf Flebbe, Paul Gortmaker, Rik Faith, Steven S. Dick, Tuomas J Lukka, そしてもちろん Linus Torvalds。彼がいなければこの文書に関連した全ての 活動は意味を持たなかったですし、そもそも行われなかったでしょう <tt/:-)/ もしこの文書(HOWTO にでも FAQ にでも)助力くださった方で、リストから 漏れている方がいらっしゃいましたら、どうかお知らせくださいますようお願 いします。私にメールしていただければ修正します。 訳注:日本語訳に当たっては、 水原さん、 こやまさん、 鴨澤さん、 堀江さん、 菅原さん、 山崎さん、 伊藤さん、 をはじめとする JF メーリングリストの皆さんに有益なご指摘を頂きました。 <!-- of course, lots of this is due to other people; the copyright had --> <!-- better be looked at --> <sect1>翻訳について <p> 現時点ではこの文書の翻訳版はありません。翻訳してみようと思う方、どうぞ お願いします。でも私に知らせてください!私が翻訳先の言葉を話せる可能性 は(残念なことに)100 対 1 以下でしょうが、それはともかく私は喜んでお 手伝いするつもりです。 <sect1>フィードバック <p> 歓迎します。私のアドレス <htmlurl url="mailto:daniel.barlow@linux.org" name="daniel.barlow@linux.org"> 宛にメールしてください。 私の PGP 公開鍵(ID 5F263625)は <url url="http://ftp.linux.org.uk/˜barlow/" name="web ページ"> にあります。通信の秘密を必要とする方は利用してください。 訳注:原文でのメールアドレスは <tt/dan@detached.demon.co.uk/ と なっていますが、 <tt/daniel.barlow@linux.org/ の方がアクセスが良い そうです。 <sect1>法的条項 <p>訳注:この節は原文も示します。 <p> All trademarks used in this document are acknowledged as being owned by their respective owners. This document is copyright (C) 1996 Daniel Barlow <tt/<dan@detached.demon.co.uk>/ It may be reproduced and distributed in whole or in part, in any medium physical or electronic, as long as this copyright notice is retained on all copies. Commercial redistribution is allowed and encouraged; however, the author would like to be notified of any such distributions. All translations, derivative works, or aggregate works incorporating any Linux HOWTO documents must be covered under this copyright notice. That is, you may not produce a derivative work from a HOWTO and impose additional restrictions on its distribution. Exceptions to these rules may be granted under certain conditions; please contact the Linux HOWTO coordinator at the address given below. In short, we wish to promote dissemination of this information through as many channels as possible. However, we do wish to retain copyright on the HOWTO documents, and would like to be notified of any plans to redistribute the HOWTOs. If you have questions, please contact Greg Hankins, the Linux HOWTO coordinator, at <tt/gregh@sunsite.unc.edu/ via email. [日本語訳] この文書で引用している商標はそれぞれの保有者に帰するものです。 この文書の著作権は (C) 1996 Daniel Barlow <tt/<dan@detached.demon.co.uk>/ が保有しています。 この文書の全体あるいは一部は、物理的電子的を問わず、あらゆるメディアに 自由に複写することができます。ただしその際にはこの著作宣言を全てのコピー に付加する必要があります。商業的な配布も許可し、また奨励します。しかし その際には著者にお知らせくださるようお願いします。 Linux HOWTO の文書を含んだ翻訳、修正および編集作業の成果は、全てこの著 作権条項に従う必要があります。すなわちこの HOWTO を修正した後に、配布 条件に追加項を加えることはできません。但し適当と認められた場合は例外と することもできます。 Linux HOWTO の管理者に連絡してください。アドレス は以下に示します。 要するに我々はこれらの情報を可能な限りの方法で広めたいと思っているので す。しかし我々は HOWTO 文書に関する著作権を保持し続けることを望んでい ますし、HOWTO を再配布する計画について知らせてもらうことも望んでいます。 もし疑問点があったら、Linux HOWTO 管理者の Greg Hankins に連絡してくだ さい。電子メールのアドレスは <tt/gregh@sunsite.unc.edu/ です。 <sect>索引 <p> アルファベット以外の文字ではじまる言葉は ASCII 配列の順に並べてありま す。 <itemize> <item> <tt/-fwritable-strings/ <ref id="index.39" name="39"> <ref id="index.56" name="56"> <item> /lib/cpp <ref id="index.16" name="16"> <item> a.out <ref id="index.1" name="1"> <item> <tt/ar/ <ref id="index.10" name="10"> <item> <tt/as/ <ref id="index.8" name="8"> <item> <asm/*.h> <ref id="index.19" name="19"> <item> <tt/atoi()/ <ref id="index.40" name="40"> <item> <tt/atol()/ <ref id="index.41" name="41"> <item> binaries too big <ref id="index.63" name="63"> <ref id="index.65" name="65"> <ref id="index.77" name="77"> <item> chewing gum <ref id="index.3" name="3"> <item> <tt/cos()/ <ref id="index.68" name="68"> <item> debugging <ref id="index.59" name="59"> <item> <tt/dlopen()/ <ref id="index.82" name="82"> <item> <tt/dlsym()/ <ref id="index.83" name="83"> <item> documentation <ref id="index.4" name="4"> <item> EINTR <ref id="index.52" name="52"> <item> elf <ref id="index.0" name="0"> <ref id="index.71" name="71"> <item> <tt/execl()/ <ref id="index.57" name="57"> <item> <tt/fcntl/ <ref id="index.47" name="47"> <item> <tt/FD_CLR/ <ref id="index.44" name="44"> <item> <tt/FD_ISSET/ <ref id="index.45" name="45"> <item> <tt/FD_SET/ <ref id="index.43" name="43"> <item> <tt/FD_ZERO/ <ref id="index.46" name="46"> <item> <tt/file/ <ref id="index.2" name="2"> <item> <float.h> <ref id="index.20" name="20"> <item> gcc <ref id="index.6" name="6"> <item> <tt/gcc -fomit-frame-pointer/ <ref id="index.61" name="61"> <item> <tt/gcc -g/ <ref id="index.60" name="60"> <item> gcc -v <ref id="index.14" name="14"> <item> gcc, bugs <ref id="index.15" name="15"> <ref id="index.28" name="28"> <ref id="index.29" name="29"> <ref id="index.84" name="84"> <item> gcc, flags <ref id="index.13" name="13"> <ref id="index.25" name="25"> <ref id="index.26" name="26"> <item> gdb <ref id="index.64" name="64"> <item> header files <ref id="index.17" name="17"> <item> interrupted system calls <ref id="index.51" name="51"> <item> <tt/ld/ <ref id="index.9" name="9"> <item> <tt/LD_*/ environment variables <ref id="index.80" name="80"> <item> ldd <ref id="index.81" name="81"> <item> libc <ref id="index.7" name="7"> <item> <tt/libg.a/ <ref id="index.62" name="62"> <item> libgcc <ref id="index.79" name="79"> <item> <limits.h> <ref id="index.21" name="21"> <item> lint <ref id="index.58" name="58"> <item> <linux/*.h> <ref id="index.18" name="18"> <item> manual pages <ref id="index.5" name="5"> <item> <tt/<math.h>/ <ref id="index.70" name="70"> <item> maths <ref id="index.69" name="69"> <item> <tt/mktemp()/ <ref id="index.55" name="55"> <item> optimisation <ref id="index.27" name="27"> <item> QMAGIC <ref id="index.76" name="76"> <item> segmentation fault <ref id="index.30" name="30"> <ref id="index.54" name="54"> <item> segmentation fault, in GCC <ref id="index.33" name="33"> <item> select() <ref id="index.50" name="50"> <item> <tt/SIGBUS/ <ref id="index.34" name="34"> <item> <tt/SIGEMT/ <ref id="index.35" name="35"> <item> <tt/SIGIOT/ <ref id="index.36" name="36"> <item> SIGSEGV <ref id="index.31" name="31"> <ref id="index.53" name="53"> <item> SIGSEGV, in gcc <ref id="index.32" name="32"> <item> <tt/SIGSYS/ <ref id="index.38" name="38"> <item> <tt/SIGTRAP/ <ref id="index.37" name="37"> <item> <tt/sin()/ <ref id="index.67" name="67"> <item> soname <ref id="index.73" name="73"> <item> <tt/sprintf()/ <ref id="index.42" name="42"> <item> statically linked binaries, unexpected <ref id="index.66" name="66"> <ref id="index.78" name="78"> <item> <stdarg.h> <ref id="index.23" name="23"> <item> <stddef.h> <ref id="index.24" name="24"> <item> <tt/strings/ <ref id="index.11" name="11"> <item> <tt><sys/time.h></tt> <ref id="index.48" name="48"> <item> <tt><unistd.h></tt> <ref id="index.49" name="49"> <item> <varargs.h> <ref id="index.22" name="22"> <item> version numbers <ref id="index.12" name="12"> <ref id="index.74" name="74"> <item> weird things <ref id="index.72" name="72"> <item> ZMAGIC <ref id="index.75" name="75"> </itemize> <!-- ************** E N D O F G C C - H O W T O **************** --> </article>