GCC -fPICオプション


436

コード生成規約に関するGCCのオプションについて読みましたが、「位置に依存しないコード(PIC)を生成する」の機能を理解できませんでした。例を挙げて、どういう意味か説明してください。


25
Clangは-fPICも使用します。
なくなっ

回答:


525

位置独立コードとは、生成されたマシンコードが特定のアドレスに配置されていることに依存しないことを意味します。

たとえば、ジャンプは絶対ではなく相対として生成されます。

疑似アセンブリ:

PIC:これは、コードがアドレス100でも1000でも機能します

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

非PIC:これは、コードがアドレス100にある場合にのみ機能します

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

編集:コメントに応じて。

コードが-fPICでコンパイルされている場合は、ライブラリに含めるのに適しています。ライブラリは、メモリ内の適切な場所から別のアドレスに再配置できる必要があります。ライブラリが優先するアドレスに別のライブラリが既にロードされている可能性があります。


36
この例は明らかですが、ユーザーがオプションなしで共有ライブラリ(.so)ファイルを作成すると、どのような違いがありますか?-fPICを使用しないと、私のlibが無効になる場合がありますか?
Narek、2011年

16
はい、PICではない共有ライブラリをビルドすると、エラーになる可能性があります。
John Zwinck、2011年

92
具体的には、共有ライブラリはプロセス間で共有されることになっていますが、両方の同じアドレスにライブラリをロードできるとは限りません。コードが位置に依存しない場合、各プロセスには独自のコピーが必要になります。
Simon Richter

19
@Narek:1つのプロセスが同じ仮想アドレスで複数の共有ライブラリをロードしようとすると、エラーが発生します。ライブラリは他にロードできるライブラリを予測できないため、この問題は従来の共有ライブラリの概念では避けられません。ここでは仮想アドレス空間は役に立ちません。
Philipp

6
-fPICプログラムまたは静的ライブラリをコンパイルする場合は省略できます。これは、プロセス内に存在するメインプログラムは1つだけであるため、実行時の再配置は不要です。一部のシステムでは、セキュリティを強化するために、プログラムは位置に依存しません。
Simon Richter

61

すでに言われていることをもっと簡単に説明しようと思います。

共有ライブラリが読み込まれると、ローダー(実行するプログラムを読み込むOS上のコード)は、オブジェクトが読み込まれた場所に応じて、コード内の一部のアドレスを変更します。

上記の例では、非PICコードの「111」は、最初にロードされたときにローダーによって書き込まれます。

共有されていないオブジェクトについては、コンパイラーがそのコードに対していくつかの最適化を行うことができるため、そのようにすることが必要な場合があります。

共有オブジェクトの場合、別のプロセスがそのコードに「リンク」する場合は、同じ仮想アドレスに読み取る必要があります。そうしないと、「111」は意味がありません。しかし、その仮想空間はすでに2番目のプロセスで使用されている可能性があります。


Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.これは、-fpicでコンパイルした場合、および-fpicが存在する理由、つまりパフォーマンス上の理由から、または再配置できないローダーがあるか、別の場所に複数のコピーが必要なため、またはさらに多くの理由により、正しくないと思います。
robsn

なぜ常に-fpicを使用しないのですか?
ジェイ

1
@Jay-関数呼び出しごとにもう1つの計算(関数アドレス)が必要になるため。したがって、パフォーマンスに関しては、必要がない場合は使用しない方がよいでしょう。
Roee Gavirel

45

共有ライブラリに組み込まれているコードは通常、位置に依存しないコードである必要があります。これにより、共有ライブラリを(多かれ少なかれ)メモリ内の任意のアドレスに簡単にロードできます。この-fPICオプションにより、GCCがそのようなコードを確実に生成します。


-fPICフラグをオンにしないと、共有ライブラリがメモリ内のどのアドレスにも読み込まれないのはなぜですか?プログラムにリンクされていませんか?プログラムの実行中、オペレーティングシステムはそれをメモリにアップロードします。何か不足していますか?
Tony Tannous

1
-fPICこのlibをリンクしているプロセスの任意の仮想アドレスに確実にロードできるように、フラグが使用されていますか?申し訳ありませんが、5分経過すると、前のコメントを編集できません。
トニーTannous

1
共有ライブラリの構築(作成libwotnot.so)とそれとのリンク(-lwotnot)を区別します。リンクしている間、あなたはについて大騒ぎする必要はありません-fPIC。以前は、共有ライブラリをビルドするときに-fPIC、すべてのオブジェクトファイルが共有ライブラリにビルドされるために使用されることを確認する必要がありました。最近、コンパイラーはデフォルトでPICコードを使用してビルドするため、ルールが変更された可能性があります。つまり、20年前に重要であったこと、7年前には重要であったかもしれないことは、最近ではそれほど重要ではないと私は信じています。o / sカーネル外のアドレスは「常に」仮想アドレスです。
ジョナサンレフラー、

したがって、以前はを追加する必要がありました-fPIC。このフラグを渡さない場合、.soのビルド時に生成されたコードは、使用中の可能性がある特定の仮想アドレスにロードする必要がありますか?
Tony Tannous

1
はい、PICフラグを使用しなかった場合、コードは確実に再配置できませんでした。ASLR(アドレススペースレイアウトのランダム化)のようなことは、コードがPICでない場合は不可能です(または、少なくとも達成するのが非常に難しいため、事実上不可能です)。
ジョナサンレフラー、

21

さらに追加しています...

すべてのプロセスに同じ仮想アドレス空間があります(Linux OSでフラグを使用して仮想アドレスのランダム化を停止した場合)(詳細については、アドレス空間レイアウトのランダム化を無効にして再度有効にしてください)

したがって、共有リンクのない1つのexe(仮定のシナリオ)の場合、害を及ぼすことなく、常に同じ仮想アドレスを同じasm命令に与えることができます。

しかし、共有オブジェクトをexeにリンクする場合、共有オブジェクトがリンクされた順序に依存するため、共有オブジェクトに割り当てられた開始アドレスはわかりません。リンク先のプロセスに応じて異なる仮想アドレス。

したがって、1つのプロセスが独自の仮想空間で0x45678910として.soに開始アドレスを与えることができ、同時に他のプロセスが0x12131415の開始アドレスを与えることができ、相対アドレス指定を使用しない場合、.soはまったく機能しません。

したがって、常に相対アドレッシングモードを使用する必要があるため、fpicオプションを使用する必要があります。


1
仮想アドレスの説明をありがとう。
Hot.PxL 2014

2
これが静的ライブラリの問題ではない理由を誰かが説明できますか?静的ライブラリで-fPICを使用する必要がないのはなぜですか?リンクはコンパイル時(または実際の直後)に行われることを理解していますが、位置依存コードを持つ2つの静的ライブラリがある場合、それらはどのようにリンクされますか?
マイケルP

3
@MichaelPオブジェクトファイルには、位置に依存するラベルのテーブルがあり、特定のobjファイルがリンクされると、すべてのラベルがそれに応じて更新されます。これは共有ライブラリに対して行うことはできません。
スラバ

16

動的ライブラリ内の関数へのリンクは、ライブラリが読み込まれたとき、または実行時に解決されます。したがって、プログラムを実行すると、実行可能ファイルと動的ライブラリの両方がメモリに読み込まれます。固定アドレスが同じアドレスを必要とする別の動的ライブラリと競合する可能性があるため、動的ライブラリがロードされるメモリアドレスを事前に決定することはできません。


この問題を処理するために一般的に使用される2つの方法があります。

1.移転。コード内のすべてのポインタとアドレスは、必要に応じて、実際のロードアドレスに合わせて変更されます。再配置は、リンカーとローダーによって行われます。

2.位置に依存しないコード。コード内のすべてのアドレスは、現在の位置を基準にしています。Unixライクなシステムの共有オブジェクトは、デフォルトで位置に依存しないコードを使用します。これは、特に32ビットモードでプログラムが長時間実行される場合、再配置よりも効率的ではありません。


位置に依存しないコード」という名前は、実際には次のことを意味します。

  • コードセクションには、再配置が必要な絶対アドレスは含まれず、自己相対アドレスのみが含まれます。したがって、コードセクションを任意のメモリアドレスにロードして、複数のプロセス間で共有できます。

  • データセクションには書き込み可能なデータが含まれていることが多いため、データセクションは複数のプロセス間で共有されません。したがって、データセクションには、再配置が必要なポインタまたはアドレスが含まれる場合があります。

  • Linuxでは、すべてのパブリック関数とパブリックデータをオーバーライドできます。メイン実行可能ファイル内の関数が共有オブジェクト内の関数と同じ名前である場合、メインからバージョンが呼び出されるときだけでなく、共有オブジェクトから呼び出されるときも、メインのバージョンが優先されます。同様に、メインのグローバル変数の名前が共有オブジェクトのグローバル変数と同じである場合、共有オブジェクトからアクセスされた場合でも、メインのインスタンスが使用されます。


このいわゆるシンボル挿入は、静的ライブラリの動作を模倣することを目的としています。

共有オブジェクトには、この「オーバーライド」機能を実装するために、プロシージャリンケージテーブル(PLT)と呼ばれるその関数へのポインタのテーブルと、グローバルオフセットテーブル(GOT)と呼ばれるその変数へのポインタのテーブルがあります。関数とパブリック変数へのすべてのアクセスは、このテーブルを通過します。

ps動的リンクを回避できない場合、位置に依存しないコードの時間のかかる機能を回避するさまざまな方法があります。

あなたはこの記事からもっと読むことができます:http : //www.agner.org/optimize/optimizing_cpp.pdf


9

すでに投稿されている回答へのマイナーな追加:位置に依存しないようにコンパイルされていないオブジェクトファイルは再配置可能です。これらには、再配置テーブルのエントリが含まれています。

これらのエントリにより、ローダー(プログラムをメモリにロードするコード)が絶対アドレスを書き換えて、仮想アドレス空間の実際のロードアドレスを調整できます。

オペレーティングシステムは、メモリに読み込まれた「共有オブジェクトライブラリ」の単一のコピーを、同じ共有オブジェクトライブラリにリンクされているすべてのプログラムと共有しようとします。

コードアドレススペース(データスペースのセクションとは異なり)は連続している必要はありません。また、特定のライブラリにリンクするほとんどのプログラムには、かなり固定されたライブラリ依存関係ツリーがあるため、これはほとんどの場合成功します。不一致があるまれなケースでは、はい、共有オブジェクトライブラリの2つ以上のコピーをメモリに置くことが必要になる場合があります。

明らかに、プログラムやプログラムインスタンス間でライブラリのロードアドレスをランダム化しようとすると(悪用可能なパターンが作成される可能性を減らすため)、そのようなケースが一般的であり、まれではないため、システムがこの機能を有効にしている場合、すべての共有オブジェクトライブラリをコンパイルして、位置に依存しないようにする必要があります。

メインプログラムの本体からこれらのライブラリへの呼び出しも再配置可能になるため、共有ライブラリをコピーする必要がほとんどなくなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.