位置独立コード

コンピューティングにおいて、位置独立コード[ 1 ] ( PIC [ 1 ] ) または位置独立実行ファイル( PIE ) [ 2 ]は、メモリアドレスに関係なく適切に実行されるマシンコードの本体です。[ a ] PIC は共有ライブラリでよく使用され、同じライブラリコードを各プログラムのアドレス空間内の、たとえば他の共有ライブラリが使用しているメモリと重複しない場所にロードできます。PIC はMMUのない古いコンピュータシステムでも使用されており、[ 3 ]オペレーティングシステムはMMU のないシステムの単一のアドレス空間内であってもアプリケーションを互いに分離することができました。

位置独立コードは、変更なしに任意のメモリアドレスで実行できます。これは、正しく機能するために特定の位置にロードする必要がある絶対コード[ 1 ]リンカーまたはプログラムローダー実行にプログラムを変更し、特定のメモリ位置からのみ実行できるようにするロード時配置可能(LTL)コード[ 1 ]とは異なります。後者の用語は、位置依存コードと呼ばれることもあります。[ 4 ]位置独立コードの生成は、多くの場合コンパイラのデフォルトの動作ですが、絶対アドレス使用を禁止するなど、一部の言語機能の使用に制限が課される場合があります(位置独立コードは相対アドレス指定を使用する必要があります特定のメモリアドレスを直接参照する命令は実行速度が速い場合があり、それらを同等の相対アドレス指定命令に置き換えると実行速度がわずかに低下する可能性がありますが、最近のプロセッサではその差は実質的に無視できます。[ 5 ]

歴史

IBM 701 [ 6 ] (1952年4月29日) やUNIVAC I (1951年3月31日)といった初期のコンピュータでは、コードは位置独立ではありませんでした。つまり、各プログラムは特定のアドレスにロードされ、そこから実行されるように構築されていました。これらの初期のコンピュータにはオペレーティングシステムがなく、マルチタスク機能もありませんでした。プログラムは主記憶装置にロードされ(あるいは磁気ドラムに保存され、そこから直接実行された)、一度に1つずつ実行されました。このような操作環境では、位置独立コードは必要ありませんでした。

CDC 6600GE 625UNIVAC 1107などのベースアンドバウンド[ b ]システムでも 、OSがジョブのストレージにコードをロードすると、ロードされた相対アドレスからしか実行できませんでした。

バローズはセグメント化システムであるB5000 (1961年)を導入した。このシステムでは、プログラムはスタック上の制御ワードまたはプログラム参照テーブル(PRT)を介して間接的にセグメントをアドレス指定する。共有セグメントは、異なるプロセス内の異なるPRTアドレスを介してアドレス指定できる。同様に、後のB6500では、すべてのセグメント参照はスタックフレーム内の位置を介して行われるようになった。

IBM System/360(1964年4月7日)は、UNIVAC III [ 7 ] と同様の切り捨てアドレッシングを採用して設計され、コード位置独立性が考慮されている。切り捨てアドレッシングでは、メモリアドレスはベースレジスタとオフセットから計算される。プログラムの開始時に、プログラマはベースレジスタをロードしてアドレス指定を確立する必要がある。通常、プログラマはUSING疑似命令を使用してアセンブラにも通知する。プログラマは、エントリポイントアドレス(通常はR15)を含むことがわかっているレジスタからベースレジスタをロードするか、BALR(分岐リンク、レジスタ形式)命令(R2の値が0)を使用して、次の連続命令のアドレスをベースレジスタに格納することができる。このベースレジスタは、プログラム内の記憶場所を参照する各命令で明示的または暗黙的にコード化される。コード用またはデータ用に、複数のベースレジスタを使用することができた。このような命令は、完全な 24、31、32、または 64 ビットのアドレス (4 バイトまたは 8 バイト) を保持する必要がなく、代わりにベース レジスタ番号 (4 ビットでエンコード) と 12 ビットのアドレス オフセット (12 ビットでエンコード) を保持する必要があり、必要なのは 2 バイトだけなので、メモリが少なくて済みます。

このプログラミング手法はIBM S/360系システムでは標準であり、今日のIBM System/zにも採用されています。アセンブリ言語でコーディングする場合、プログラマーは前述のようにプログラムのアドレス指定を確立するとともに、動的に割り当てられた記憶域として他のベースレジスタを使用する必要があります。コンパイラはこの種のアドレス指定を自動的に処理します。

IBM の初期のオペレーティング システムDOS/360 (1966) では仮想ストレージは使用されていませんでした (System S/360 の初期モデルが仮想ストレージをサポートしていなかったため)。ただし、PHASE 名、* JCL (ジョブ制御言語)ステートメントを使用して、ロード中にプログラムを任意の (または自動的に選択された) ストレージの場所に配置できる機能はありました。

そのため、仮想ストレージを持たないS/360システムでは、プログラムを任意のストレージ位置にロードできましたが、そのためにはプログラムを格納するのに十分な大きさの連続したメモリ領域が必要でした。サイズの異なるモジュールのロードとアンロードによって、メモリの断片化が発生することがありました。仮想ストレージは、設計上、そのような制限はありません。

DOS/360 とOS/360は PIC をサポートしていませんでしたが、OS/360 の一時SVC ルーチンには再配置可能なアドレス定数を含めることができず、再配置なしで任意の一時領域で実行できました。

IBMは1965年、 IBM System/360モデル67で初めて仮想記憶を導入しました。これは、IBM初のマルチタスク・オペレーティング・システムおよびタイムシェアリング・オペレーティング・システムTSS/360をサポートするためでした。その後のDOS/360(DOS/VSなど)およびそれ以降のIBMオペレーティング・システムはすべて仮想記憶を採用していました。切り捨てアドレス指定は基本アーキテクチャの一部として残され、複数のモジュールを同一の仮想アドレス空間にロードする必要がある場合に依然として有利です。

比較すると、Burroughs B5000(1961年)やMultics (1964年)のBurroughs MCPなどの初期のセグメント化システムや、IBM TSS/360(1967年)などのページングシステムでは、 [ c ]コードも本質的に位置に依存しませんでした。これは、プログラム内のサブルーチン仮想アドレスが、プログラム参照テーブル、リンケージセグメント、プロトタイプセクションなどのコード外部のプライベートデータに配置されていたためです。

動的アドレス変換( MMUが提供する機能)の発明により、各プロセスが独自のアドレス空間(アドレス範囲)を持つことができるようになったため、位置独立コードの必要性は当初減少しました。しかし、複数のジョブが同時に同じコードを実行すると、物理メモリが無駄に消費されていました。2つのジョブが全く同一のプログラムを実行する場合、動的アドレス変換は、システムが2つの異なるジョブのアドレス32Kを、プログラムの単一のコピーを含む同じバイトの実メモリにマッピングするだけで済むという解決策を提供します。

異なるプログラムが共通のコードを共有する場合があります。例えば、給与計算プログラムと売掛金計算プログラムの両方に、同一のソートサブルーチンが含まれている場合があります。共有モジュール(共有ライブラリは共有モジュールの一種です)は一度ロードされ、2つのアドレス空間にマッピングされます。

SunOS 4.x と ELF

共有ライブラリ内の手続き呼び出しは、通常、小さな手続きリンクテーブル(PLT)スタブを介して行われ、そこから最終的な関数が呼び出されます。これにより、共有ライブラリは、独自の関数呼び出しを使用するのではなく、以前にロードされたライブラリから特定の関数呼び出しを継承することが可能になります。[ 8 ]

位置独立コードからのデータ参照は通常、アクセスされたすべてのグローバル変数のアドレスを格納するグローバルオフセットテーブル(GOT)を介して間接的に行われます。コンパイル単位またはオブジェクトモジュールごとに1つのGOTがあり、コードからの固定オフセットに配置されます(ただし、このオフセットはライブラリがリンクされるまでわかりません)。リンカーはモジュールをリンクして共有ライブラリを作成する際に、GOTをマージし、コード内の最終的なオフセットを設定します。後で共有ライブラリをロードする際に、オフセットを調整する必要はありません。[ 8 ]

グローバルデータにアクセスする位置独立コードは、GOTのエントリからグローバル変数のアドレスをフェッチすることでアクセスします。GOTはコードから固定オフセットにあるため、コード内の特定の命令のアドレスと特定のグローバル変数のGOTエントリのアドレス間のオフセットも固定です。そのため、位置独立コードがロードされるアドレスに応じてオフセットを変更する必要はありません。グローバル変数のGOTエントリをフェッチする命令は、コード内の特定の命令に対するオフセットを含むアドレッシングモードを使用します。これは、命令セットアーキテクチャがサポートしている場合はPC相対アドレッシングモード、関数が関数プロローグ内の命令のアドレスをレジスタにロードするレジスタ相対アドレッシングモードになります。[ 8 ] [ 9 ] [ 10 ] [ 11 ]

Windows DLL

Microsoft Windowsのダイナミックリンクライブラリ(DLL)は、CALL命令のバリアントE8(次の命令を基準とした相対位置指定によるニアアドレス呼び出し)を使用します。これらの命令は、DLLのロード時に変更する必要はありません。

一部のグローバル変数(文字列リテラルの配列、仮想関数テーブルなど)には、動的ライブラリのデータセクションまたはコードセクションにあるオブジェクトのアドレスが格納されることが想定されています。そのため、グローバル変数に格納されているアドレスは、DLLがロードされたアドレスを反映するように更新する必要があります。ダイナミックローダーは、グローバル変数が参照するアドレスを計算し、その値をグローバル変数に格納します。これにより、そのグローバル変数を含むメモリページのコピーオンライトがトリガーされます。コードを含むページと、コードまたはグローバルデータへのポインタを含まないグローバル変数を含むページは、プロセス間で共有されたままです。この操作は、任意のアドレスに動的ライブラリをロードできるすべてのOSで実行する必要があります。

Windows Vista以降のWindowsでは、 DLLと実行ファイルの再配置はカーネルメモリマネージャによって行われ、再配置されたバイナリは複数のプロセス間で共有されます。イメージは常に優先ベースアドレスから再配置されるため、アドレス空間レイアウトのランダム化(ASLR)が実現されます。[ 12 ]

Vistaより前のバージョンのWindowsでは、実行時のイメージの再配置を回避するため、システムDLLをリンク時に競合しない固定アドレスに事前リンクする必要がありました。これらの旧バージョンのWindowsでは、実行時の再配置は各プロセスのコンテキスト内でDLLローダーによって実行され、その結果、各イメージの再配置された部分はプロセス間で共有できなくなります。

WindowsにおけるDLLの扱いは、その元となったOS/2の手順とは異なります。OS/2は3つ目の選択肢を提示し、位置独立でないDLLをメモリ内の専用の「共有領域」にロードし、ロード後にマッピングしようとします。DLLを使用するすべてのユーザーは、同じメモリ内コピーを使用できます。

マルチクス

Multicsでは、各プロシージャは概念的に[ d ]コードセグメントとリンケージセグメントを持っています。[ 13 ] [ 14 ]コードセグメントにはコードのみが含まれ、リンケージセクションは新しいリンケージセグメントのテンプレートとして機能します。ポインタレジスタ4(PR4)は、プロシージャのリンケージセグメントを指します。プロシージャの呼び出しは、呼び出し先のリンケージセグメントへのポインタをロードする前に、PR4をスタックに保存します。プロシージャ呼び出しは、間接ポインタペア[ 15 ]と最初の呼び出しでトラップを発生させるフラグを使用して、動的リンケージメカニズムが新しいプロシージャとそのリンケージセグメントを既知セグメントテーブル(KST)に追加し、新しいリンケージセグメントを構築し、呼び出し元のリンケージセクションにセグメント番号を配置し、間接ポインタペアのフラグをリセットします。

TSS

IBM S/360タイムシェアリングシステム(TSS/360およびTSS/370)では、各プロシージャは読み取り専用の公開CSECTと書き込み可能な非公開プロトタイプセクション(PSECT)を持つことができます。呼び出し元は、ルーチンのV定数を汎用レジスタ15(GR15)にロードし、ルーチンのPSECTのR定数をGR13が指す保存領域の19番目のワードにコピーします。[ 16 ]

ダイナミックローダー[ 17 ]は、最初のページフォールトが発生するまでプログラムページをロードしたり、アドレス定数を解決したりしません。

位置独立実行ファイル

位置独立実行ファイル(PIE)は、完全に位置独立コードから作成された実行可能バイナリです。PIC実行ファイルのみを実行するシステムもありますが、PIEが使用される理由は他にもあります。PIEバイナリは、セキュリティ重視のLinuxディストリビューションで使用され、 PaXまたはExec Shieldがアドレス空間配置ランダム化(ASLR)を使用できるようにします。これにより、バイナリ内の実行コードのオフセットを知ることに依存するエクスプロイト(例えば、Return-to-libc攻撃)を用いたセキュリティ攻撃の際に、攻撃者が既存の実行コードの場所を知ることを防ぎます。(2005年の2.6.12以降の公式Linuxカーネルには、PIEでも動作するより弱いASLRが搭載されています。このASLRの弱点は、ELFファイルユニット全体にランダム性が適用されることです。)[ 18 ]

AppleのmacOSiOSは、それぞれバージョン10.7と4.3からPIE実行ファイルを完全にサポートしています。PIE非対応のiOS実行ファイルがAppleのApp Storeに承認のために提出されると警告が出されますが、まだ厳格な要件はなく、PIE非対応のアプリケーションが拒否されることはありません。[ 19 ] [ 20 ]

OpenBSDでは、2013年5月1日にリリースされたOpenBSD 5.3以降、ほとんどのアーキテクチャでPIEがデフォルトで有効になっています。[ 21 ]実行ファイルやディレクトリなどの静的にリンクされたバイナリ でのPIEのサポートは、2014年末に追加されました。 [ 22 ] openSUSEは2015-02年にPIEをデフォルトで追加しました。Fedora 23以降、 Fedora のメンテナーはPIEをデフォルトで有効にしてパッケージをビルドすることを決定しました。[ 23 ] Ubuntu 17.10では、すべてのアーキテクチャでPIEがデフォルトで有効になっています。[ 24 ] Gentooの新しいプロファイルは、現在PIEをデフォルトでサポートしています。[ 25 ] 2017年7月頃、DebianはPIEをデフォルトで有効にしました。[ 26 ]/bin/sbin

AndroidはJelly BeanでPIEのサポートを有効にし[ 27 ] 、 Lollipopで非PIEリンカーのサポートを削除しました[ 28 ]

参照

注記

  1. ^これにより、共有コピーを使用する各プロセスは、異なる仮想アドレスでそれを参照できるようになります。
  2. ^ただし、ジョブごとに個別のコードのコピーがロードされました。
  3. ^ TSS/360は共有PICをサポートしていたが、すべてのページングシステムに当てはまるわけではない。
  4. ^パフォーマンス上の理由により、いくつかの技術的な逸脱がありますが、これはこの記事の範囲外です。

参考文献

  1. ^ a b c d e f「オブジェクトコードの種類」。iRMX 86 アプリケーションローダーリファレンスマニュアル(PDF)。Intel 。pp . 1-2, 1-3 2017年8月21日閲覧。[…]絶対コードおよび絶対オブジェクトモジュールは、LOC86によって処理され、メモリ内の特定の位置でのみ実行されるコードです。ローダーは、絶対オブジェクトモジュールを、そのモジュールが占有する必要がある特定の位置にのみロードします。位置独立コード(一般にPICと呼ばれる) は、PICが任意のメモリ位置にロードできるという点で絶対コードとは異なります。PICが絶対コードに対して優れている点は、PICでは特定のメモリブロックを予約する必要がないことです。ローダーはPICをロードする際に、呼び出し元タスクのジョブのプールからiRMX 86メモリセグメントを取得し、そのセグメントにPICをロードします。 PICに関する制約は、PL/M-86 COMPACTセグメント化モデル[…]と同様に、コードセグメントとデータセグメントをそれぞれ1つしか持てず、これらのセグメントのベースアドレス、ひいてはセグメント自体が動的に変化しないことです。つまり、PICプログラムの長さは必然的に64Kバイト未満になります。PICコードは、LINK86のBIND制御によって生成できます。ロード時ロケータブルコード(一般にLTLコードと呼ばれる)は、オブジェクトコードの3番目の形式です。LTLコードは、メモリ内の任意の場所にロードできるという点でPICに似ています。ただし、LTLコードをロードする際に、ローダーはポインタのベース部分を変更し、ポインタがマイクロプロセッサのレジスタの初期内容に依存しないようにします。この修正(ベースアドレスの調整)により、LTLコードは複数のコードセグメントまたは複数のデータセグメントを持つタスクで使用できます。つまり、LTLプログラムの長さは64Kバイトを超える可能性があります。FORTRAN 86Pascal 86は、短いプログラムであっても自動的にLTLコードを生成します。LTLコードはLINK86のBIND制御によって生成できます。[…]
  2. ^ 「位置独立実行可能ファイル(PIE)」
  3. ^ Levine, John R. (2000) [1999年10月]. 「第8章 ロードとオーバーレイ」 .リンカーとローダー. モルガン・カウフマン著『ソフトウェア工学とプログラミング』(第1版). サンフランシスコ、米国: Morgan Kaufmann . pp.  170– 171. ISBN 1-55860-496-0. OCLC  42413382 . ISBN 978-1-55860-496-4. 2012年12月5日にオリジナルからアーカイブ2020年1月12日閲覧。コード: [1] [2]エラッタ: [3]
  4. ^ 「位置独立コード」。Oracle 動的実行可能ファイル内のコードは通常、位置に依存し、メモリ内の固定アドレスに関連付けられています
  5. ^ Gabert, Alexander (2004年1月). 「Position Independent Code internals」 . Hardened Gentoo . 2009年12月3日閲覧. […] PIC非対応の直接アドレス指定は、PICアドレス指定よりも常に安価(つまり高速)です。[…]
  6. ^ 「701発表」IBM、1952年4月29日
  7. ^ UNIVAC IIIデータ処理システムリファレンスマニュアル(PDF)。Sperry Rand Corporation。1962年。UT-2488。
  8. ^ a b c Gingell, Robert A.; Lee, Meng; Dang, Xuong T.; Weeks, Mary S. SunOSの共有ライブラリ(PDF)。1987年夏季USENIX技術会議および展示会。pp.  131– 146。
  9. ^ System V アプリケーションバイナリインターフェース Motorola 68000 プロセッサフ​​ァミリー補足(PDF) . Prentice-Hall. 1990. pp.  3-32 – 3-35 . ISBN 0-13-877663-6
  10. ^ System V アプリケーションバイナリインターフェイス i386 アーキテクチャプロセッササプリメント(PDF) (第4版)。pp.  3-35 – 3-39
  11. ^ System V アプリケーションバイナリインターフェイス AMD64 アーキテクチャプロセッササプリメント (LP64 および ILP32 プログラミングモデル付き) バージョン 1.0 (PDF) . 2021 年 9 月 28 日。
  12. ^ 「Windowsのメモリ管理の進歩」View.officeapps.live.com . 2017年6月23日閲覧
  13. ^オーガニク、エリオット・アーヴィング(1972). Multicsシステム;その構造の考察. MITプレス. ISBN 9780262150125LCCN  78157477
  14. ^ Daley, Robert C.; Dennis, Jack B. (1968年5月). 「Multicsにおける仮想メモリ、プロセス、および共有」 . Communications of the ACM . 11 (5). Association for Computing Machinery : 306– 312. doi : 10.1145/363095.363139 . 2024年7月21日閲覧
  15. ^「Section 6 Virtual Address Formation」、DPS/LEVEL 68 & DPS 8M MULTICS PROCESSOR MANUAL (PDF) (Rev. 1 ed.)、Honeywell Information Systems Inc.、1982年、pp.  6– 21、AL39 2023年3月25日取得
  16. ^「セクション3:TSS for the: Svslcm Programmer」IBMタイムシェアリングシステムの概念と機能(PDF)(第7版)。1978年4月。61ページ。GC28-2003-6。
  17. ^ IBM System/360 タイムシェアリングシステム ダイナミックローダー(PDF) (第4版). 1971年9月. GY28-2031-3.
  18. ^ Lettieri, G. 「アドレス空間レイアウトのランダム化」(PDF)
  19. ^ 「iphone - 非 PIE バイナリ - 実行可能ファイル 'プロジェクト名' は位置独立実行可能ファイルではありません。 - Stack Overflow 。stackoverflow.com
  20. ^ 「iOS 開発者ライブラリ」。apple.com
  21. ^ 「OpenBSD 5.3 リリース」 2013年5月1日. 2020年5月9日閲覧
  22. ^ 「Heads Up: Static PIE のスナップショットアップグレード」 2014年12月24日. 2014年12月24日閲覧
  23. ^ 「すべてのパッケージの変更/強化 - FedoraProject 。fedoraproject.org
  24. ^ 「Ubuntu Foundations Team - Weekly Newsletter, 2017-06-15」 . 2017-06-15 . 2017年6月17日閲覧
  25. ^ 「Gentooリポジトリの新しい17.0プロファイル」 . 2017年11月30日. 2017年12月10日閲覧
  26. ^ Liang, Mudong (2017年8月8日). 「DebianはいつPIEをデフォルトで有効にすることを決定したのか?」 debian.org . 2021年7月6日閲覧
  27. ^ 「Android 1.5から4.1までのセキュリティ強化 - Androidオープンソースプロジェクト」。Androidオープンソースプロジェクト。 2022年6月26日時点のオリジナルよりアーカイブ。 2016年7月25日閲覧
  28. ^ 「Android 5.0のセキュリティ強化 - Androidオープンソースプロジェクト」。Androidオープンソースプロジェクト。 2017年2月27日時点のオリジナルよりアーカイブ。 2016年7月25日閲覧