リンカーとローダーとは何ですか?それらはどのように機能しますか?


8

リンカーやローダーなどをよりよく理解しようとしています。

コンピュータサイエンスのどの分野に属していますか?コンパイラ、オペレーティングシステム、コンピュータアーキテクチャ?

開発中にリンカーとローダーはどこで機能しますか?


私はこの質問を編集して、リンカとローダーが他の場所について学ぶよりも、このサイトがそのようなことをするのに適した場所であるため、それらについて詳しく説明しました。そこに優れたリソースがある場合は、外部リソースのリストを作成しようとするのではなく、回答を通じて自然にそれらを浮かび上がらせます。
アダムリア

9
問題はありませんが、ウィキペディアの記事を必ず読んでください。en.wikipedia.org / wiki
Ericウィルソン

なぜ投票したのかわからない-この質問は、これを知りたいと思っている他の初心者にも役立つかもしれないと思っていました。これは非プログラミングの質問ですか?
Nishant 2011

2
この質問を考えると、反対票はかなり広く、自分で答えを理解するために行われた多くの作業を示していません。
JBキング

回答:


17

正確な関係は多少変化します。最初に、MS-DOSのようなものによって使用される(ほぼ)最も単純なモデルを検討します。このモデルでは、実行可能ファイルは常に静的にリンクされます。例として、正規の「Hello、World!」を考えてみましょう。私たちが仮定するプログラムはCで書かれています。

コンパイラ

コンパイラーはこれをいくつかの部分にコンパイルします。文字列リテラル「Hello、World!」を受け取り、定数データとしてマークされた1つのセクションに入れ、その特定の文字列の名前を合成します(たとえば、「$ L1」)。呼び出しをprintfコードとしてマークされた別のセクションにコンパイルします。この場合、名前はmain(または、頻繁に_main)と表示されます。また、このコードのチャンクはNバイト長であり、(重要なことに)printfそのコードのオフセットMでの呼び出しが含まれていると言うこともできます。

リンカ

コンパイラが生成を完了すると、リンカが実行されます。これは通常、開発ツールチェーンの一部と見なされます(例外はありますが、MS-DOSにはリンカーが含まれていましたが、使用されることはまれでした)。通常は外部からは見えませんが、通常はいくつかのコマンドライン引数が渡されます。1つは起動コードを含むオブジェクトファイルを指定し、もう1つはC標準ライブラリを含むファイルを指定します。

次に、リンカはスタートアップコードを含むオブジェクトファイルを調べて、たとえば1112バイトの長さであり_main、その中にオフセット784での呼び出しがあることを見つけます。

これに基づいて、シンボルテーブルの作成を開始します。".startup"(または任意の名前)が1112バイト長であるというエントリが1つあり、(これまでのところ)その名前を参照するものはありません。「printf」は現在不明な長さであるという別のエントリがありますが、「。startup + 784」から参照されます。

次に、指定された1つまたは複数のライブラリをスキャンして、現在定義されていない(この場合は)シンボルテーブル内の名前の定義を見つけようとしますprintf。それは長さが4087バイトであると言うprintfのオブジェクトファイルを見つけ、intを文字列に変換するような他のルーチンへの参照と、結果の文字列を出力に書き込むようなputchar(または多分fputc)ファイル。

リンカは、2つの結論の1つに達するまで、再スキャンしてそれらのシンボルの定義を再試行します。すべてのシンボルの定義が見つかったか、定義が見つからないシンボルがあります。

参照が見つかったが定義が見つからない場合は、停止して通常は「未定義の外部XXX」について何かを示すエラーメッセージが表示され、リンクする必要のある他のライブラリまたはオブジェクトファイルを特定するのはあなた次第です。 。

すべてのシンボルの定義が見つかると、次のフェーズに進みます。各シンボルを参照する場所のリストをウォークスルーし、そのシンボルがメモリに配置されたアドレスを入力します(たとえば、 )起動コードがを呼び出す場所で、mainのmainアドレス1112としてアドレスを入力します。それがすべて完了すると、すべてのコードとデータが実行可能ファイルに書き出されます。

おそらく言及すべき他のいくつかのマイナーな詳細があります:通常はコードとデータを別々に保持し、それぞれが完了した後、それらを(多かれ少なかれ)連続したアドレス(たとえば、すべてのピース)にまとめますコード、次にすべてのデータ)。通常、セクション/セグメントの定義を組み合わせる方法に関するいくつかのルールもあります。たとえば、異なるオブジェクトファイルにすべてコードセグメントがある場合、コードの断片を次々に配置するだけです。2つ以上の同一の文字列リテラル(または他の定数)が定義されている場合、それらは通常それらをマージして、すべてが同じ場所を参照するようにします。また、同じシンボルの定義が重複している場合に、何をすべきかについてのルールもいくつかあります。典型的なケースでは、これは単にエラーになります。場合によっては、「あれば他の誰かもそれを定義し、それを誤差を考慮していない-この1つだけの代わりに、その定義を使用します。

すべてのシンボルのエントリを取得すると、リンカは「ピース」を配置してアドレスを割り当てる必要があります。ピースを配置する順序は多少異なります。通常、ピースのタイプに関するいくつかのフラグがあるため、(たとえば)すべての定数データが​​互いに隣り合って、すべてのコードのピースがお互いなど。私たちの単純なMS-DOSのようなシステムでは、これのほとんどはそれほど重要ではありません。

ローダ

それが次のフェーズ、つまりローダーです。ローダーは通常、実行可能ファイルをロードするオペレーティングシステムの一部です。旧バージョン(CP / M、MS_DOS .comファイルなど)では、ローダーは実行可能ファイルからメモリにデータを読み取ってから、あるアドレスで実行を開始しました。少し最近のローダー(MS-DOS .exeファイルなど)は、同じ方法で開始します(ファイルをメモリに読み込みます)。ただし、この場合、リンカによってそこに入力されたエントリに基づいて、実行可能ファイル内の絶対参照を「修正」して、正しいアドレスです。上の例では、起動コードはmain アドレス1112で実行可能ファイルが(たとえば)4000のベースアドレスにロードされています。この場合、ローダーはそのアドレスを修正して5112を参照します。ただし、この単純なシステムでは、ローダーはまだかなり単純なコード-基本的には、再配置のリストをたどって、それぞれにベースアドレスを追加します。

次に、共有オブジェクトファイルやDLLなどをサポートするもう少し新しいOSについて考えてみましょう。これは基本的に、一部の作業をリンカーからローダーにシフトします。特に、.so / DLLで定義されているシンボルの場合、リンカーはアドレス自体を割り当てようとしません

代わりに、基本的に「.so / DLLファイルXXXで定義されています」と書かれたシンボルテーブルエントリを作成します。リンカが実行可能ファイルを書き込むとき、これらのシンボルテーブルエントリのほとんどは基本的に、「シンボルXXXはファイルYYYで定義されています」と言って実行可能ファイルにコピーされます。次に、ローダーがファイルYYYとそのファイル内のシンボルXXXのアドレスを見つけ、実行可能ファイルで使用されている場所に正しいアドレスを入力します。リンカーの場合と同様に、これは再帰的であるため、DLL AはDLL B内のシンボルを参照し、DLL CはDLL Cを参照します。実行可能ファイルからすべての定義へのチェーンは長い場合がありますが、プロセスの基本的な考え方はかなり単純です。外部参照のリストをスキャンして、それぞれの定義を見つけます。また、ほとんどの場合、

繰り返しますが、考慮すべき雑多な部分があります。たとえば、共有は通常、ファイルごとではなく、セクションごとにのみ行われます。たとえば、ファイルにいくつかのコードといくつかの(一定でない)データがある場合、すべてのプロセスは同じコードセクションを共有しますが、それぞれが独自のデータのコピーを取得します。


最も単純なローダーには修正機能すらありません。古いTRS-80を考えています。実行可能ファイルは、アドレス/サイズ/データであり、必要に応じて繰り返されました(サイズは1バイトだったため、通常は複数のブロックが必要でした)。ビデオメモリにロードアドレスを指定して、スプラッシュスクリーンを作成することもできます。
Loren Pechtel、2015年

1

リンカーの詳細については、コンパイラーと組み合わせて説明するのが一般的だと思います。これらは、さまざまなモジュールを1つのまとまりのあるユニットにまとめ、そのコード内でアドレスを確定するためのものです。最適化を実行しようとする人さえいます。

ローダーの詳細については、ローダーをリンカーの同義語として意味しない限り、特定のアーキテクチャー向けのコンパイラーを作成することと組み合わせて説明するのが一般的だと思います。ローダーは、コンパイルされたソフトウェアを開いて実行する方法をオペレーティングシステムに通知する実行可能ファイルヘッダーの一部と考えています。

ウィキペディアの記事を読んだ方が、あなたが探しているよりも多くの情報を提供できることに同意します。それらがどこで開発に参加するかについては、一般的にプロジェクトの制御を超えており、使用するオペレーティングシステムと開発パッケージの選択の一部です。(例えば)MSVCを使用することは非常にまれですが、GCCベースのリンカーを実行したいのですが...不可能かもしれません。非標準のリンカーを使用した唯一の場所は、開発コピーを使用していたときのIBMでした。

これらのトピックについてより具体的で具体的な質問がある場合は、はるかに良い回答が見つかると思います。


1

リンカーとローダーは、関連しているが別々の2つの概念です。

リンカーはコンパイラ理論の一部です。複数のモジュール(ソースコードファイル)で構成されるプロジェクトをコンパイルする場合、コンパイラは、各ソースモジュールに対して単一の中間ファイルを出力するのが一般的です。これにはいくつかの利点があります。その1つは、1つのファイルに変更を加えてから再コンパイルする必要がある場合、ローカルで1つの変更を行っただけでプロジェクト全体を再ビルドする必要がないことです。

しかし、これは、あるモジュールに別のモジュールの関数を呼び出すコードがある場合CALL、他の関数の場所がないため、コンパイラがその命令を生成できないことを意味します。それは別の中間ファイルにあり、その中間のソースファイルをローカルで変更して再コンパイルすると、関数の正確な場所が変わる可能性があります。したがって、代わりに、「正確なアドレスがわからないこの関数が必要です」という「外部参照トークン」を挿入します(正確には、それが何であるか、またはどのように見えるかは関係ありません。抽象的な概念と考えてください)。現時点では。"

すべてが中間ファイルにコンパイルされると、リンカーがジョブを終了します。それはすべての中間ファイルを通過し、最終的なバイナリにそれらを一緒にリンクします。物事をまとめているので、すべての関数の実際のアドレスを知っているので、外部参照トークンをCALLバイナリ内の正しい場所への実際の命令に置き換えることができます。

一方、ローダーはコンパイラではなくオペレーティングシステムに属します。リンカは既知のコードしか解決できないため、その仕事は、バイナリをメモリにロードして実行できるようにすることと、リンクプロセスを完了することです。プログラムがDLLを使用している場合、それらはコンパイル済みバイナリの外部にあるため、リンカーはそれらのアドレスを知りません。OSのローダーが認識している形式で最終的なバイナリに外部参照トークンを残します。すべてがメモリに読み込まれると、ローダーが通過し、これらのトークンをDLL内の実際の関数アドレスと照合します。


-1

コンピュータは基本的に2進数で動作します。
人々は母国語を話します。

プログラミング言語は、人とコンピュータの間の通信用です。
あなたが言う場合:2と3を足し、それから1を引くと、コンピューターが何かを理解することはないでしょう(おそらく一部のプログラミング言語ではそうでしょう)。

したがって、ソースコードをコンピューターが理解できる形式に変換する必要があるため、プログラミング言語をcoと呼ばれるオブジェクトコードに変換するコンパイラーが必要です。しかし、オブジェクトコードはまだコンピュータが理解して直接実行する言語ではありません。そのため、いわゆる機械語で命令を含む実行可能ファイルを作成するリンカーが必要です。機械語は、プロセッサが理解できる2進数にコード化された一連の操作です。すべてのバイナリ命令は、その構造と、プロセッサメーカーによって公開されています。インテルのサイトなどで探して、どのように見えるかを確認できます。


AMDの開発者向けガイドは、次のとおりです
。developer.amd.com/documentation/guides/Pages/default.aspx
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.