すべてのコードが位置独立にコンパイルされないのはなぜですか?
-
03-07-2019 - |
質問
gccで共有ライブラリをコンパイルする場合、-fPICオプションはコードを位置独立としてコンパイルします。すべてのコード位置に依存しないでコンパイルしない理由(パフォーマンスまたはその他)がありますか?
解決
インダイレクションを追加します。位置に依存しないコードでは、関数のアドレスを読み込んでからジャンプする必要があります。通常、関数のアドレスは既に命令ストリームに存在します。
他のヒント
はい、パフォーマンス上の理由があります。いくつかのアクセスは、メモリ内の絶対位置を取得するために、間接的に別のレイヤーの下に効果的にあります。
グローバル変数のオフセットを保存するGOT(グローバルオフセットテーブル)もあります。私には、これはIATフィックスアップテーブルのように見えます。これは、ウィキペディアや他のいくつかのソースによって位置依存として分類されています。
受け入れられた答えに加えて。 PICコードのパフォーマンスを大きく損なうことの1つは、「IP相対アドレス指定」の欠如です。 x86で。 「IP相対アドレス指定」の場合現在の命令ポインタからXバイトのデータを要求できます。これにより、PICコードがはるかに簡単になります。
ジャンプと呼び出しは、通常EIP相対であるため、実際には問題は発生しません。ただし、データにアクセスするには、少し余分なトリックが必要です。レジスタが一時的に「ベースポインタ」として予約される場合があります。コードが必要とするデータへ。たとえば、一般的な手法は、x86での呼び出しの動作を悪用することです。
call label_1
.dd 0xdeadbeef
.dd 0xfeedf00d
.dd 0x11223344
label_1:
pop ebp ; now ebp holds the address of the first dataword
; this works because the call pushes the **next**
; instructions address
; real code follows
mov eax, [ebp + 4] ; for example i'm accessing the '0xfeedf00d' in a PIC way
この手法および他の手法により、データアクセスに間接層が追加されます。たとえば、gccコンパイラで使用されるGOT(グローバルオフセットテーブル)。
x86-64は「RIP relative」を追加しました; 多くをシンプルにするモード。
完全に位置に依存しないコードを実装すると、コードジェネレーターに制約が追加され、より高速な操作の使用が妨げられるか、その制約を維持するための追加ステップが追加されるためです。
これは、仮想メモリシステムなしでマルチプロセッシングを実現するための許容可能なトレードオフであり、プロセスが相互のメモリに侵入しないことを信頼し、任意のベースアドレスで特定のアプリケーションをロードする必要がある場合があります。
最近の多くのシステムでは、パフォーマンスのトレードオフが異なります。また、再配置ローダーは、自由に統治できる場合にオプティマイザーができる最善の方法よりも安価です(コードが最初にロードされるたびにコストがかかります)。また、仮想アドレス空間が利用できるため、そもそも位置独立の動機付けのほとんどが隠されています。
また、ほとんどの最新のプロセッサ(ほとんどの最新のOSで使用)の仮想メモリハードウェアは、多くのコード(すべてのユーザースペースアプリ、mmapの風変わりな使用を除く)が位置に依存する必要がないことを意味します。すべてのプログラムは、ゼロから始まると考える独自のアドレス空間を取得します。
位置に依存しないコード
は、追加のレジスタを必要とするため、ほとんどのアーキテクチャでパフォーマンスのオーバーヘッドがあります。
つまり、これはパフォーマンスのためです。
デフォルトでは、オペレーティングシステムとコンパイラはデフォルトですべてのコードを位置に依存しないコードとして作成します。 -fPICフラグなしでコンパイルすると、コードは正常にコンパイルされますが、警告が表示されるだけです。OSのようなウィンドウでは、メモリマッピングと呼ばれる手法を使用してこれを実現します。