【iOS】ダイナミックリンカーdyld

参考: dyld の理解: 動的リンカー

ディルドの紹介

dyld (ダイナミック リンカー) は、macOS および iOS システムのダイナミック リンカーであり、実行時にダイナミック共有ライブラリ (dylib) または実行可能ファイルのロードとリンクを担当するコンポーネントです。macOS システムでは、dyld は にありますD/usr/lib/dyld

dyld ソースコードアドレス

値2

dyld 2 (ダイナミック リンカー 2) は、macOS および iOS システムの第 2 世代ダイナミック リンカーです。前世代の dyld 1 の進化版であり、性能、機能性、安全性の面で改良と最適化が行われています。dyld 2 は主に、実行時に動的共有ライブラリまたは実行可能ファイルのコンポーネントをロードしてリンクし、プログラムが正しく実行できるようにする役割を果たします。

dyld 2 の主な機能と改善点の一部を次に示します。

  • C++ イニシャライザ セマンティクスを正しくサポートし、Mach-O 形式を拡張し、C++ ライブラリを効率的にサポートするために dyld を更新します。
  • より多くのアーキテクチャとプラットフォームをサポートします。
    Power PC での dyld 2.0 のリリース以来、x86、x86 64 arm、arm64 およびその他のアーキテクチャが追加され、iOS、tvOS、watchOS プラットフォームをサポートしています。
  • セキュリティはいくつかの方法で向上します。
    • 共同設計: コード署名。アプリケーションのセキュリティを強化するために、dyld 2 はアプリケーションと共有ライブラリのコード署名の検証をサポートし、それらが改ざんされたり悪意を持って変更されていないことを確認します。
    • ASLR: アドレス空間レイアウトのランダム化 アドレス空間構成はランダムにロードされます。
    • 境界チェック: 悪意のあるバイナリ データの挿入を回避するために、Mach-O ヘッダーの多くのコンテンツに重要な境界チェック機能が追加されました。
  • シミュレータのサポート: dyld 2 は、iOS シミュレータでのパフォーマンスと機能のサポートを向上させ、開発者がシミュレータ上でアプリケーションのデバッグとテストを容易にします。
  • パフォーマンスの向上: 共有キャッシュ テクノロジを使用して、事前バインドを完全に置き換えます。
  • 増分ロード: dyld 2 は増分ロード (増分ロード) をサポートしています。つまり、アプリケーションの起動時に、すべての動的ライブラリを一度にロードするのではなく、必要な共有ライブラリとシンボルのみがロードされます。これにより、起動時間とメモリ使用量をさらに削減できます。
  • 同時ロード: dyld 2 はマルチコア プロセッサを最大限に活用し、動的共有ライブラリの同時ロードをサポートするため、動的リンク プロセスが高速化されます。
  • シンボル分離: セキュリティを向上させるために、dyld 2 ではシンボル分離メカニズムが導入されました。共有ライブラリのシンボル テーブルを分離して、共有ライブラリ間のシンボルが相互に影響を及ぼさないようにすることで、シンボルの競合やシンボル リークを回避します。

実装プロセス

ここに画像の説明を挿入します

  1. dyld 2 の初期化: dyld 2 がメモリにロードされると、いくつかの初期化操作が実行され、ダイナミック リンク タスクを実行する準備が整います。メインコードは でありdyldbootstrap::start、その後実行されますdyld::_main dyld::_main多くのコードがあり、これは dyld ロードの中心部分です。
  2. image headeバイナリ パスの取得、環境変数の確認、メイン バイナリやその他の情報の解析など、環境を確認して準備します。
  3. メイン バイナリをインスタンス化しimage loader、メイン バイナリと dyld のバージョンが一致するかどうかを確認します。
  4. shared cacheマッピングされているかどうかを確認し、マッピングされていない場合は、map shared cache最初に操作を実行します。
  5. DYLD_INSERT_LIBRARIES存在する場合は、挿入された動的ライブラリをロードします (インスタンス化image loader)を確認します。
  6. リンク操作を行います。このプロセスは比較的複雑です。すべての依存する動的ライブラリが最初に再帰的に読み込まれます (依存するライブラリはソートされ、依存するライブラリは常に先頭になります)。同時に、シンボルのバインドと操作がこの段階で実行されますrebasebinding;
  7. 初期化メソッドを実行します。Objective-C+loadおよび Cconstructorメソッドはこの段階で実行されます。
  8. Mach-OLC_MAINセグメントを読み取ってプログラムのエントリ アドレスを取得し、mainメソッドを呼び出します。

共有キャッシュをロードする

これFoundationは、他のいくつかの動的ライブラリにも依存し、これらの依存ライブラリはさらに多くのライブラリに依存するため、相互依存するシンボルが多くなり、処理時間が比較的長くなります。このシステムのダイナミック リンカーは共有キャッシュを使用します。共有キャッシュは にあります/var/db/dyld/Mach-O ファイルをロードするとき、ダイナミック リンカーはまず共有キャッシュがあるかどうかを確認します。各プロセスはこれらの共有キャッシュを独自のアドレス空間にマッピングするため、アプリの起動速度を最適化できます。

共有キャッシュのロードは、(Shared Cache)macOS および iOS システムの dyld 2 で動的リンクを高速化するために使用される最適化テクノロジです。共有キャッシュは、複数のアプリケーションで共通に使用される動的共有ライブラリを含む、事前に生成された動的ライブラリ コレクションです。アプリケーションの起動時に、dyld 2 はディスクから動的ライブラリを 1 つずつ再ロードすることなく、共有キャッシュから必要な動的ライブラリを直接ロードできるため、アプリケーションの起動が高速化されます。

共有キャッシュのロードプロセスは次のように簡単に要約できます。

  1. 共有キャッシュの生成: システムがインストールまたは更新されると、オペレーティング システムは、一般的に使用される複数のシステム動的共有ライブラリとフレームワークを含む共有キャッシュを事前に生成します。このプロセスは通常、デバイスが初めて起動されたとき、オペレーティング システムがアップグレードされたとき、または開発者がシステム ライブラリを再コンパイルしたときに発生します。
  2. 共有キャッシュ パス: 共有キャッシュはシステム ファイルに保存され、そのパスは/System/Library/Caches/com.apple.dyld/dyld_shared_cache_x86_64(macOS)または/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64(iOS)などです。具体的なパスはデバイスとアーキテクチャによって異なります。
  3. アプリケーションの起動: アプリケーションが起動すると、dyld 2 はまず共有キャッシュが利用可能かどうかを確認し、共有キャッシュのロードを試みます。
  4. 共有キャッシュをロードする: 共有キャッシュが使用可能な場合、dyld 2 は共有キャッシュをメモリに直接マップし、共有キャッシュ内のダイナミック ライブラリとアプリケーションの間のリンク関係を確立します。
  5. シンボルの解決と再配置: 共有キャッシュをロードした後、dyld 2 はシンボルの解決と再配置を実行して、アプリケーション内のシンボル参照を共有キャッシュ内のシンボル アドレスに関連付けます。
  6. 動的ライブラリ リンク: アプリケーションが共有キャッシュに含まれていない他の動的共有ライブラリにも依存している場合、dyld 2 は必要に応じてこれらの動的共有ライブラリを 1 つずつロードし、リンクとシンボル解決を実行します。
  7. アプリケーションの初期化: すべての動的共有ライブラリがロードされ、シンボルの解決が完了すると、dyld 2 はアプリケーションのmain()関数の実行を開始し、アプリケーションの実行が正式に開始されます。

動的なライブラリのロード

リンクされた共有ライブラリは、静的ライブラリと動的ライブラリに分けられます。

  • 静的ライブラリはコンパイル中にリンクされるライブラリであり、Mach-O ファイルにリンクする必要があります。更新する必要がある場合は再コンパイルする必要があり、動的にロードおよび更新することはできません。
  • 動的ライブラリは実行時にリンクされるライブラリであり、dyld を使用して動的ロードを実現できます。

Mach-O ファイルはコンパイルされた製品であり、ダイナミック ライブラリは実行時にリンクされ、Mach-O ファイルのコンパイルとリンクには関与しないため、Mach-O ファイルにはダイナミック ライブラリのシンボル定義は含まれません。 。つまり、これらのシンボルは「未定義」として表示されますが、その名前と対応するライブラリ パスは記録されます。実行時に dlopen および dlsym を介して動的ライブラリをインポートする場合、まず記録されたライブラリ パスに従って対応するライブラリを検索し、次に記録された名前シンボルを通じてバインディング アドレスを検索します。

dlopen は、実行中のプロセスのアドレス空間に共有ライブラリをロードします。ロードされた共有ライブラリには未定義のシンボルも含まれるため、さらに多くの共有ライブラリがロードされることになります。dlopen は、すべての参照をすぐに解決するか、遅延して解決するかを選択することもできます。dlopen がダイナミックライブラリを開いた後に返すのは参照ポインタであり、dlsym の機能は、dlopen が返すダイナミックライブラリポインタと関数シンボルを通じて関数のアドレスを取得して使用することです。

dyld2の問題

  • mach-o ヘッダーの解析では、変更された Mach-O ファイル ヘッダーを使用して攻撃することができます。
  • 依存関係の検索には、検索パスである @rpaths を使用できます。これらのパスを変更するか、ライブラリを適切な場所に挿入すると、プログラムが壊れる可能性があります。
  • シンボル ルックアップの実行 シンボル ルックアップ部分。ソフトウェアの更新がない限り、またはライブラリがディスク上で変更されない限り、特定のライブラリではシンボルが常にライブラリ内の同じオフセットにあるためです。

値3

dyld 3 は、ダイナミック リンクの概念を完全に変える、まったく新しいダイナミック リンカーです。WWDC-App Startup Time: Past, Present, and Future では、iOS 13 システムでは、iOS が以前のバージョンの dyld 2 を置き換えるために新しい dyld 3 を完全に採用すると述べました。dyld 3 はパフォーマンスを大幅に向上させ、APP の起動時間を短縮します。dyld 3 は dyld 2 と完全な互換性があり、API インターフェイスが同じであるため、ほとんどの場合、開発者は追加の調整を行わずにスムーズに移行できます。

実装プロセス

ここに画像の説明を挿入します

dyld 3 には次の 3 つの部分が含まれています。

  1. アウトプロセス Mach-O パーサーおよびコンパイラー (アウトプロセス Mach-O パーサー) dyld 2 に存在する問題のため、dyld 3 では、結果データをファイルにキャッシュするために事前に書き込む方法が使用されます。起動クロージャを形成します(キャッシュファイルとして理解できます)
  2. インプロセスエンジンは、「起動クロージャ」が正しいかどうかを検証する起動クロージャ処理(インプロセスエンジン)を行い、dylibをマッピングし、main関数を実行します。この時点で、mach-o ヘッダーを分析してシンボル検索を実行する必要がなくなり、時間を大幅に節約できます。
  3. 起動クロージャ キャッシュ サービス (起動クロージャ キャッシュ) システム プログラムの起動クロージャは、共有キャッシュに直接組み込まれています。サードパーティ APP の場合、APP がインストールまたは更新されるときに生成されます。これにより、起動クロージャが常に保証されます。アプリを開く前に準備が完了しています。

ほとんどのプログラムの起動では、アウトプロセスの mach-o アナライザーやコンパイラーを呼び出さずにキャッシュが使用され、起動クロージャは Mach-O よりも単純です。これらはメモリ マップされたファイルであり、分析に複雑なメソッドは必要ありません。単純に検証できます。目的は速度を向上させることです

dyld 3 でのシンボル欠落の問題

dyld 2 ではデフォルトで遅延シンボルのシンボル読み込み方式が採用されていますが、dyld 3 ではアプリの起動前にシンボル解析の結果がすでに起動クロージャに含まれているため、遅延シンボルは必要なくなりました。このとき、欠落しているシンボルがある場合、APP の動作は異なります。dyld 2 では、欠落しているシンボルが初めて呼び出されたときにアプリがクラッシュします。dyld 3 では、欠落しているシンボルによりアプリがクラッシュします。始まったらすぐに。

おすすめ

転載: blog.csdn.net/m0_63852285/article/details/131892703