2009-04-03 4 views
9

ライブラリAにはa()とb()があるものとします。プログラムBをAとリンクしてa()を呼び出すと、b()はバイナリに含まれますか?コンパイラは、プログラム内の関数がb()を呼び出すかどうかを調べます(おそらくa()がb()を呼び出すか、別のlibがb()を呼び出します)もしそうなら、コンパイラはどのようにこの情報を得ますか?そうでない場合は、大きなライブラリにリンクしていてマイナー機能しか使用していないと、これは最終的なコンパイルサイズの大きな浪費ではありませんか?リンカーはどのようにライブラリのどの部分を含めるかを決定しますか?

答えて

0

リンカによって異なりますが、実際に呼び出される関数のみが最終実行ファイルに含まれます。リンカーは、ライブラリ内の関数名を検索し、その名前に関連付けられたコードを使用して動作します。

リンカーに関する本はごくわずかですが、その重要性を考えると変です。良いもののテキストはhereです。

+0

-1他の質問にはあなたの厄介なコメントがあります。 – Brian

0

最適化がなければ、はい、それが含まれます。ただし、リンカーは、コードを静的に分析し、到達不能なコードを削除しようとすると、最適化できる可能性があります。

9

link-time optimizationをご覧ください。これは必然的にベンダーに依存します。また、バイナリの構築方法にも依存します。 MSコンパイラ(少なくとも2005年以降)は、Function Level Linkingと呼ばれるものを提供しています。これは不要なシンボルを削除する別の方法です。 Thisポストは、GCCでどのように同じことが達成できるかを説明しています(これは古いものですが、GCCは動いたはずですが、コンテンツはあなたの質問に関係しているはずです)。

また、LLVMの実装(および例のセクション)もご覧ください。

また、私はあなたにも、ジョン・レヴァインのLinkers and Loadersを見てみることをお勧めします。

+0

通常、リンク時の最適化は、参照されていないオブジェクトや関数を削除する以外にも多くの問題に対処します。LTOは一般にリンク時に生成されたコードを最適化できるテクノロジを指します - 関数のインライン化やポインタがエイリアシングオブジェクトではないことの認識など。 –

+0

@Michael Burr:正しい。しかしそれは探し求める場所の1つです。また、これが私が非常に多くの文献を引用した理由です。 – dirkgently

+0

確かに - 答えに間違いがあったことを暗示するつもりはありませんでした。ちょうどLTOは一般的に機能レベルのリンク以上のものです。 –

1

リンカーによって異なります。

例えば、 Microsoft Visual C++には「機能レベルのリンクを有効にする」というオプションがあり、手動で有効にすることができます。

2

通常(静的)ライブラリは、ソースファイルから作成されたオブジェクトで構成されている(私は多分遅いか何かのリンク...彼らはちょうどそれをすべての時間を有効にしない理由があると仮定します)。リンカーが通常行うことは、そのオブジェクトによって提供される関数が参照されている場合にオブジェクトを含めることです。ソースファイルに1つの関数しか含まれておらず、その関数だけがリンカによって持ち込まれる場合。より洗練されたリンカーがありますが、Cベースのリンカーの多くは、概説したように動作します。複数の関数を含むCソースを人為的に小さなソースファイルに分割し、より細かい静的リンクを作成するツールがあります。

共有ライブラリを使用している場合、コンパイルされたサイズに多かれ少なかれ影響を与えません。ただし、ランタイムサイズにはそれらが含まれます。

0

リンカに渡されるオプションによって異なりますが、通常、リンカはオブジェクトファイルをどこでも参照されていないライブラリに残します。

$ cat foo.c 
int main(){} 

$ gcc -static foo.c 

$ size 
    text data  bss  dec  hex filename 
452659 1928 6880 461467 70a9b a.out 

# force linking of libz.a even though it isn't used 
$ gcc -static foo.c -Wl,-whole-archive -lz -Wl,-no-whole-archive 

$ size 
    text data  bss  dec  hex filename 
517951 2180 6844 526975 80a7f a.out 
1

アカデミック地球でこのlectureかなり良い概要を示し、リンクは話、IIRCの後半の近くに語られています。

8

によって異なります。

ライブラリが共有オブジェクトまたはDLLの場合、ライブラリ内のすべてがロードされますが、実行時にはロードされます。余分なメモリのコストは、そのライブラリを使用するメモリ内のすべてのプロセス間でライブラリ(実際にはコードページ)を共有することによって(うまくいけば)相殺されます。これは、libc .soのようなものにとって大きな勝利です。myreallyobscurelibrary.soではそれほどです。しかし、あなたはおそらく共有オブジェクトについては求めていないでしょう。

スタティックライブラリは、個々のオブジェクトファイルのコレクションであり、それぞれが別々のコンパイル(またはアセンブリ)の結果であり、同じソース言語で書かれていない場合もあります。各オブジェクトファイルにはエクスポートされたシンボルの数が多く、ほとんどの場合、インポートされたシンボルの数があります。

リンカーの仕事は、未定義のインポートされたシンボルが残っていない完成した実行可能ファイルを作成することです。 (私はもちろん、動的リンクが許可されていれば嘘つきですが、私には負担してください)。そうするためには、リンクコマンドラインで明示的に指定されたモジュールから始まり(場合によっては暗黙のうちに)、モジュール明示的にnamedは、実行可能な実行可能ファイルの一部でなければなりません。次に、すべての未定義シンボルの定義を探します。

通常、指定されたオブジェクトモジュールは、libc.aなどのライブラリからシンボルを取得することを想定しています。

この例では、a()という関数を呼び出すモジュールが1つあります。その結果、リンカーはa()をエクスポートするモジュールを探しています。

あなたはA(unix上におそらくlibA.a)という名前のライブラリがa()b()を提供していますが、方法は指定していないと言います。あなたはa()b()がお互いに電話をしていないことを暗示しています。

libA.aa.oと、それぞれが対応する単一の関数を定義b.oから構築された場合、リンカーは、a.oを含み、b.oを無視します。

しかし、libA.a場合、それは、a()の必要性を満たし、そして未使用の機能b()を含めて、リンクにab.oを含むであろう両方a()b()定義ab.oを含んでいました。

他にも言及したように、個々の機能をモジュールから分離し、実際に使用されているものだけを含むリンカーがあります。多くの場合、これは安全なことです。しかし、あなたが特定の文書を持っていなければ、あなたのリンカーがそれをしないと仮定するのが通常最も安全です。

その他の注意点として、ほとんどのリンカは、コマンドラインで指定されたファイルやライブラリを通過できるだけのパスを作成し、シンボルテーブルを構築します。実際問題として、リンクコマンドライン上のすべてのオブジェクトモジュールの後にライブラリを指定することをお勧めします。

0

リンカーとライブラリの作成方法によって異なります。通常、ライブラリはオブジェクトファイルの組み合わせです(インポートライブラリはこれに対する大きな例外です)。古いリンカーは、ライブラリーに入れられたオブジェクトファイルの細かさで、出力ファイルイメージに物を引っ張り出します。したがって、関数a()と関数b()が同じオブジェクトファイル内にある場合は、2つの関数のうちの1つだけが実際に参照されたとしても、それらは出力ファイルにあります。

これは、ソースファイルごとに1つのC関数のポリシーを持つライブラリ指向プロジェクトがよく見られる理由です。こうすることで、各関数はそれぞれのオブジェクトファイルにパッケージ化され、リンカーは参照されているものだけを引き出すことに問題はありません。

しかし、より新しいリンカー(確かに新しいMicrosoftリンカー)は、参照されているオブジェクトファイルの一部だけをプルする機能を持っているため、ソースファイルごとに1機能のポリシーを適用する必要性はあまりありませんとにかくメンテナンス性のために実行すべき妥当な議論があるが、

関連する問題