14

ソースからバイナリをコンパイルする場合、 PIC オブジェクトを生成する場合と生成しない場合の実際の違いは何ですか? 将来のどの時点で、「MySQL をコンパイルするときに PIC オブジェクトを生成/使用する必要があった」と誰かが言うでしょう。か否か?

Gentoo の Introduction to Position Independent CodePosition Independent Code internalsHOWTO fix -fPIC errorsLibtool の Creation object files、およびPosition Independent Codeを読みました。

PHP から./configure --help:

--with-pic: PIC/非 PIC オブジェクトのみの使用を試みます [デフォルト = 両方を使用]。

MySQL からcmake -LAH .:

-DWITH_PIC: PIC オブジェクトを生成

この情報は良い出発点ですが、多くの疑問が残ります。

私が理解していることから-fPIC、コンパイラでオンになり、結果のバイナリ/ライブラリに PIC オブジェクトが生成されます。なぜ私はそれをしたいのですか?またはその逆。多分それはより危険であるか、バイナリの安定性を低下させる可能性がありますか? 特定のアーキテクチャ (私の場合は amd64/x86_64) でコンパイルするときは、避けるべきではないでしょうか?

デフォルトの MySQL ビルドでは、PIC=OFF が設定されています。公式の MySQL リリースのビルドでは、PIC=ON が設定されています。そして、PHP は「両方を使おうとします」。私のテスト設定-DWITH_PIC=ONでは、バイナリがわずかに大きくなります。

          PIC=OFF     PIC=ON
mysql     776,160    778,528
mysqld  7,339,704  7,476,024
4

4 に答える 4

1

共有ライブラリと実行可能ファイルは、PIC コードを有効または無効にしてビルドできます。つまり、PIC なしでビルドした場合でも、他のアプリで使用できます。ただし、PIC 以外のライブラリはどこでもサポートされているわけではありませんが、Linux ではサポートされていますが、いくつかの制限があります。

=== これは必要のない簡単な説明です ;-) ===

PIC が行うことは、コードの位置を独立させることです。各共有ライブラリはメモリ内のある位置にロードされます - セキュリティ上の理由から、この場所はしばしばランダム化されます - したがって、コード内の「絶対」メモリ参照は実際には「絶対」にはなりません - 実際、それらはライブラリのメモリセグメントの開始に関連しています住所。ライブラリがロードされた後、それらを調整する必要があります。

これは、それらすべてを調べて (それらのアドレスはファイル ヘッダーに格納されます)、修正することで実行できます。しかし、これは遅く、ベースアドレスが異なる場合、「修正された」イメージをプロセス間で共有できません。

したがって、通常は別の方法が使用されます。メモリへの各参照は、特殊レジスタ (通常は ebx) を介して行われます。関数が呼び出されると、最初に、ebx 値をライブラリのメモリ セグメント アドレスに調整する特別なコード ブロックにジャンプします。次に、関数は [ebx + 既知のオフセット] を使用してそのデータにアクセスします。

したがって、すべての関数やメモリ参照ではなく、プログラムごとにこのコード ブロックのみを調整する必要があります。

関数が同じ共有ライブラリの他の関数から呼び出されることがわかっている場合、コンパイラ/リンカーは PIC レジスタ (ebx) の調整を省略できることに注意してください。これは、関数が既に正しい値を持っていることがわかっているためです。一部のアーキテクチャ (特に x86_64) では、プログラムは IP (現在の命令ポインター) に関連するデータにアクセスできます。IP は既に絶対調整されているため、ebx のような特別なレジスターとその調整の必要性がなくなります。

=== 読まずにスキップできるセクションの終わり ===

では、なぜPICなしで何かを構築したいのでしょうか?

まず第一に、各関数の開始時にレジスタを調整するために追加のコードが実行され、オプティマイザが貴重なレジスタを使用できないため、プログラムが数パーセント遅くなります (x86 のみ)。多くの場合、関数は同じライブラリから呼び出されたのか別のライブラリから呼び出されたのかを認識できないため、内部呼び出しでさえペナルティを受けます。したがって、速度を最適化したい場合は、PIC なしでコンパイルしてみてください。

次に、お気づきのように、コード サイズが少し大きくなります。これは、各関数に PIC レジスタをセットアップするための命令がさらにいくつか含まれるためです。

これは、リンク時の最適化 (--lto スイッチ) と保護された関数の可視性を使用して、外部からまったく呼び出されない関数をコンパイラが認識し、PIC コードを必要としない場合に、ある程度回避できます。しかし、私はそれを試していません(まだ)。

そして、なぜPICを使いたいのですか?安全性が高いため (これはアドレス空間のランダム化に必要です)。すべてのシステムが非 PIC ライブラリをサポートしているわけではないためです。PIC 以外のライブラリでは、起動時の読み込み時間が遅くなる可能性があるため (テーブル スタブだけでなく、コード セグメント全体を絶対アドレスに調整する必要があります)。また、ロードされたライブラリ セグメントは、別のスペースにロードされている場合は共有できません (つまり、より多くのメモリが使用される可能性があります)。次に、すべてのコンパイラ/リンカー フラグが非 PIC ライブラリと互換性があるわけではありません (私が覚えていることから、スレッド ローカル サポートについて何かがあると思います)。そのため、非 PIC コードをまったくビルドできない場合があります。

そのため、PIC 以外のコードは少しリスクが高く (安全性が低く)、常に取得できるとは限りませんが、必要な場合 (速度など) には理由があります。

于 2013-08-05T20:56:53.407 に答える
1

この方法でコンパイルする理由は、実際には 2 つあります。

1 つは、共有ライブラリを作成する場合です。通常、Linux では共有ライブラリは PIC でなければなりません。

2 つ目は、基本的に実行可能ファイルの PIC であるメイン実行可能ファイル "PIE" をコンパイルすることです。PIE は、アドレス空間のランダム化をメインの実行可能ファイルに適用できるようにするセキュリティ機能です。

于 2013-08-04T01:53:32.533 に答える