Windows 共有ライブラリ (DLL) と Linux 共有ライブラリ (SO) について簡単な質問があります。
Windows DLL を作成する場合、クライアント プログラムは静的ライブラリ (.lib ファイル) に対してもリンクする必要がありますが、Linux で作成されたアプリケーションはそのような静的ライブラリに対してリンクする必要がないのはなぜですか。
コードの再配置などと関係がありますか? ありがとう。
Windows 共有ライブラリ (DLL) と Linux 共有ライブラリ (SO) について簡単な質問があります。
Windows DLL を作成する場合、クライアント プログラムは静的ライブラリ (.lib ファイル) に対してもリンクする必要がありますが、Linux で作成されたアプリケーションはそのような静的ライブラリに対してリンクする必要がないのはなぜですか。
コードの再配置などと関係がありますか? ありがとう。
Windows DLL を作成する場合、クライアント プログラムは静的ライブラリ (.lib ファイル) に対してもリンクする必要がありますが、Linux で作成されたアプリケーションはそのような静的ライブラリに対してリンクする必要がないのはなぜですか。
これは、リンク時に特定のバージョンの DLL が存在しなくても、リンカーが DLL 参照を実行可能ファイルに追加できるようにするために、Microsoft が行った歴史的な設計上の決定です。この理由は、さまざまなバージョンの DLL を使用して、さまざまなバージョンの Windows が常に存在していたためです。また、当時 Microsoft は OS/2 で IBM と協力しており、Windows プログラムを OS/2 でも実行できるようにする計画がありました。Microsoft は、NT カーネルに基づいて独自のプロフェッショナル グレードの OS を展開することにより、OS/2 を「バックスタブ」することにしました。しかし、これは開発のために、開発者がシステム DLL にリンクできるようにすることを意味し、DLL のさまざまなバリアントをすべて利用できるようにする必要はありませんでした。代わりに動的リンケージ「テンプレート」.lib
これらはライブラリではなく、単なるシンボルと序数のテーブルです (これはあまり知られていない事実ですが、PE バイナリ シンボルは、文字列識別子だけでなく整数、いわゆる序数によってもロードできます)。
序数の副作用は、人間が読めるシンボルを隠すことができるため、序数 ←→ 関数の関係を知っている場合にのみ DLL を使用できることです。
Unix では、「実行するシステム上でビルドする」、または「すべてのターゲット システム ファイルを配置する」という伝統がありました。そのため、ライブラリとリンケージ情報を分離するインセンティブはありませんでした。技術的には、同じことが DLL でも機能します。PE は、DLL が行うシンボルと再配置テーブルをエクスポートでき、リンカはそこから必要なすべての情報を取得できます。
Unix 共有オブジェクトでシンボルを非表示にする場合は、通常struct
、すべての関数ポインターを含む単一のオブジェクトを使用し、この構造体のグローバル定数インスタンスを名前でエクスポートするだけでこれを行います。ポインター。ただし、Windows DLL でもまったく同じことができます。
TL;DR: この理由は技術的なものではなく、マーケティングと流通の決定です。
実際にはコードの再配置ではありません。それはまったく別の問題です。それはアーキテクチャの違いについてです:
Windows では、DLL は実行可能ファイル (EXE) のようなものです。EXE と DLL の主な違いは、EXE にはエントリ ポイント (main/WinMain 関数) があるため、プロセスの開始に使用できるのに対し、DLL は既存のプロセスにしかロードできないことです。しかし、(1)を参照してください
Linux では、.so はスタティック ライブラリ (.a) と同様に機能します。主な違いは、.so ファイルは実行中のプログラムとリンクできるのに対し、.a ファイルはプログラムのコンパイル時にしかリンクできないことです。
このアプローチの結果、Linux では同じファイルを使用してプログラムをビルドおよび実行できます。ただし、Windows では、プログラムをリンクするために適切なライブラリ (LIB) が必要です。実際、DLL に対応するライブラリには通常、リンカを満たす関数の名前と、再配置を行うためのスタブしかありません。しかし、(2)を参照してください
(1) DLL にもエントリ ポイントがありますが、これはメイン関数としては使用されず、ある種の初期化/ファイナライズ フックとして使用されます。
(2) 一部のリンカは、いくつかの単純なケースでは、追加の LIB ファイルを必要とせずに、DLL 自体を使用して DLL にリンクできるほど十分にスマートです。少なくともMinGWリンカーはそれができると思います。
Windows では、クライアント プログラムは、DLL 内の関数にアクセスするためにスタティック ライブラリとリンクする必要はありません。動的リンクは、クライアント プログラムがコンパイル時に DLL の存在を認識しなくても、完全に実行時に発生する可能性があります。
たとえば、「bar.dll」という名前の DLL で関数名「foo」を呼び出したい場合、次のようなコードを記述できます。
HINSTANCE hinst = LoadLibrary("bar.dll");
FARPROC foo = GetProcAddress(hinst, "foo");
foo();
また、「foo」と「bar.dll」は、たとえば構成ファイルやその他のユーザー入力を介して、実行時にのみ確立される値である可能性があります。
静的ライブラリの目的は、クライアント プログラムに関する限り通常の関数のように見えるが、実行時に DLL にリンクされるスタブを作成することによって、この動的読み込みプロセスを自動化することです。通常、このリンクはクライアント プロセスのロード時に発生しますが、必要に応じてロードおよびリンクするライブラリを生成することもできるため、実際に必要になるまで DLL はメモリに取り込まれません。リンクが発生するタイミングを決定するのは静的ライブラリです。
ほとんどの場合、コンパイラはこれらのライブラリを自動的に生成できるため、技術的には、DLL 関数にリンクするだけの場合は必要ありません。ただし、これに対する 1 つの例外 (私が認識している) は、共有変数にリンクする場合です。
Windows DLL では、その DLL をロードした任意のプロセスがアクセスできる変数を持つ共有データ セグメントを作成できます。これらの変数のサイズと型に関する情報は、関連するスタティック ライブラリに格納されており、DLL だけでは判断できません。これらの変数にアクセスするには、クライアント プログラムがその DLL のスタティック ライブラリにリンクする必要があります。
私の知る限り、Linux 共有ライブラリはそのような概念をサポートしていません。
アップデート
また、Windows では、関数のエントリ ポイントが名前ではなく序数 (数値) によってのみエクスポートされる DLL を作成できることにも言及する必要があります。これは、データ隠蔽の一種と考えることができ、通常、実装者が特定の関数を非公開にしたい場合に使用されます。
ライブラリには関数名を適切な序数にリンクする詳細があるため、静的ライブラリにアクセスできる人は、これらの関数を名前で呼び出すことができます。DLL しか持っていない人は、序数によって関数に手動でリンクするか、独自の名前を付けて独自の静的ライブラリを生成する必要があります。