目次のタイトル
シェルコマンドコラム: Linux シェルコマンドの完全な分析
説明する
ld
Linux 環境のリンカー ツールで、主な機能は複数のターゲット ファイル (.o
または.obj
ファイル) を実行可能ファイルまたはライブラリ ファイルにリンクすることです。ld
このコマンドの主な機能と効果は次のとおりです。
-
オブジェクト ファイルのリンク:
ld
複数のオブジェクト ファイルを 1 つの出力ファイルにリンクできます。これらのオブジェクト ファイルは通常、コンパイラによって生成され、プログラムのマシン コードが含まれています。 -
シンボルの解決: リンク プロセス中に、
ld
オブジェクト ファイル内のシンボルが解決され、すべての関数と変数の参照が正しく解決されます。 -
実行可能ファイルの生成:
ld
実行可能ファイルを生成できるだけでなく、共有ライブラリや静的ライブラリも生成できます。 -
再配置のハンドル: ターゲット ファイル内のコードは任意の場所で実行される可能性があるため、
ld
コードが正しいメモリ アドレスで実行されるように、これらのコードの再配置を処理する必要があります。 -
セグメントのマージ: (コード セグメント) や(データ セグメント)
ld
など、ターゲット ファイル内の類似したセグメントがマージされます。.text
.data
-
リンク時の最適化: 場合によっては、
ld
生成された実行可能ファイルのパフォーマンスを向上させるために、リンク時の最適化も実行できます。 -
スタートアップ コードの処理:
ld
プログラムの実行開始時に実行される最初のコードであるスタートアップ コードのリンクも担当します。
全体として、ld
コマンドは Linux プログラミングに不可欠なツールであり、プログラムのすべての部分が正しく組み合わされて、実行可能な実行可能ファイルが生成されるようにします。
構文形式
ld [options] object_files [--] [library_files]
パラメータの説明
-b <input-format>
: オブジェクトコード入力ファイルの形式を指定します。-Bstatic
: 静的ライブラリのみを使用してください。-Bdynamic
: 動的ライブラリのみを使用します。-Bsymbolic
: 共有ライブラリ内のグローバル シンボルへの参照をバンドルします。-c <MRI-commandfile>, --mri-script=<MRI-commandfile>
: MRI リンカーとの互換性のため、ld は MRI コマンド言語で書かれたスクリプト ファイルを受け入れます。--cref
: 相互参照テーブルを作成します。-d,-dc,-dp
: 再配置可能な出力ファイルが指定されている場合でも (-r を使用して)、パブリック シンボルにスペースが割り当てられます。スクリプトコマンド「FORCE_COMMON_ALLOCATION」も同じ効果があります。-defsym
: 指定されたグローバル シンボルを出力ファイルに作成します。-demangle
: エラー メッセージ内のシンボル名を復元します。-e <entry>
: 指定されたシンボルをプログラムの最初の実行ポイントとして使用します。-E,--export-dynamic
: ELF 形式のファイルの場合、動的にリンクされた実行可能ファイルを作成するときに、すべてのシンボルを動的シンボル テーブルに追加します。-f <name>, --auxiliary=<name>
: ELF 形式の共有オブジェクトの場合、DT_AUXILIARY 名を設定します。-F <name>, --filter=<name>
: ELF 形式の共有オブジェクトの場合、DT_FILTER 名を設定します。これは、作成される共有オブジェクトのシンボル テーブルを共有オブジェクト名のシンボル テーブルのフィルターとして使用する必要があることをダイナミック リンカーに伝えます。-g
: 無視されます。他のツールとの互換性を提供するために使用されます。-h
: ELF 形式の共有オブジェクトの場合、DT_SONAME 名を設定します。-I<file>, -dynamic-linker <file>, --dynamic-linker=<file>
: ダイナミックリンカを指定します。これは、ダイナミック リンク ライブラリに依存する ELF 実行可能ファイルをビルドする場合にのみ意味があります。通常、デフォルトの動的リンカーは正しいので、何をしているのかわからない場合は、このオプションを使用しないでください。-l <namespec>, --library=<namespec>
: 指定したライブラリ ファイルをリンクするファイルのリストに追加します。-L <searchdir>, --library-path=searchdir
: 指定されたパスを検索ライブラリのディレクトリ リストに追加します。-M, --print-map
: 診断目的でリンク マップを表示します。-Map=<mapfile>
: リンクマップを指定したファイルに出力します。-m <emulation>
: 指定されたリンカをエミュレートします。-N,--omagic
: テキストおよびデータセグメントの読み取り/書き込みを指定します。-n,--nmagic
: セクションのページ配置をオフにし、共有ライブラリへのリンクを無効にします。出力形式が Unix スタイルのマジック ナンバーをサポートしている場合は、出力を「NMAGIC」としてマークします。-noinhibit-exec
: 致命的ではないリンク エラーが発生した場合でも、出力ファイルを生成します。通常、リンカーはリンク プロセス中にエラーが発生した場合、出力ファイルを生成しません。-no-keep-memory
: ld は通常、メモリ使用速度を最適化するために、入力ファイルのシンボル テーブルをメモリにキャッシュします。このオプションは、シンボル テーブルをキャッシュしないように ld に指示します。大きな実行可能ファイルをリンクするとき、ld のメモリ領域が不足した場合は、このオプションの使用が必要になる場合があります。-O <level>
: ゼロ以外の最適化レベルの場合、ld は出力を最適化します。この操作は時間がかかるため、最終結果を生成する場合にのみ使用してください。-o <output>, --output=<output>
: 出力ファイル名を指定します。-oformat=<output-format>
: 出力ファイルのバイナリ形式を指定します。-R <filename>,--just-symbols=<filename>
: 指定されたファイルからシンボル名とアドレスを読み取ります。-r,--relocatable
: 再配置可能な出力を生成します (部分結合と呼ばれます)。-rpath=<dir>
: 指定されたディレクトリをランタイム ライブラリの検索パスに追加します。-rpath-link=<dir>
: ランタイム共有ライブラリを検索するディレクトリを指定します。-S,--strip-debug
: 出力ファイルからのデバッガ シンボル情報を無視します。-s,--strip-all
: 出力ファイルからのシンボル情報をすべて無視します。-shared, -Bshareable
: 共有ライブラリを作成します。-split-by-file[=size]
: 各オブジェクト ファイルの出力ファイルに最大サイズの追加セグメントを作成します。サイズのデフォルトは 1 です。-split-by-reloc[=count]
: 指定された長さの追加セグメントを出力ファイルに作成します。--section-start=<sectionname>=<org>
: 出力ファイル内の指定されたアドレスにある指定されたセクションを見つけます。-T <scriptfile>, --script=<scriptfile>
: scriptfile をリンカー スクリプトとして使用します。このスクリプトは ld のデフォルトのリンカー スクリプトを置き換える (追加ではない) ため、スクリプトでは出力ファイルに必要なすべてを指定する必要があります。スクリプト ファイルが現在のディレクトリに存在しない場合、ld は -L オプションで指定されたディレクトリを検索します。-Ttext=<org>
: 指定されたアドレスをテキストセグメントの開始点として使用します。-Tdata=<org>
: 指定したアドレスをデータセグメントの開始点として使用します。-Tbss=<org>
: 指定されたアドレスを bss セグメントの開始点として使用します。-t,--trace
: 処理中の入力ファイルの名前を表示します。-u <symbol>, --undefined=<symbol>
: 指定されたシンボルを出力ファイル内で強制的に未定義にします。-v, -V, --version
:ldのバージョン番号を表示します。-warn-common
: ユニバーサル シンボルが別のユニバーサル シンボルと結合されている場合に警告します。-warn-constructors
: グローバル コンストラクターが使用されていない場合に警告します。-warn-once
: 未定義のシンボルごとに 1 回だけ警告します。-warn-section-align
: 出力セクションのアドレスが調整のために変更された場合に警告します。--whole-archive
: 指定されたアーカイブ ファイルについて、アーカイブ内のすべてのファイルを含めます。-X, --discard-locals
: すべてのローカル一時シンボルを削除します。-x, --discard-all
: すべてのローカル シンボルを削除します。
エラー状態
- 未解決のシンボル:
ld
リンク プロセス中にシンボルの定義が見つからない場合は、エラーが報告され、未解決のシンボルの入力を求められます。 - 複数定義: 複数の対象ファイルに同じシンボルが定義されている場合、
ld
エラーが報告されます。 - ファイル形式の不一致: ELF や a.out など、異なる形式でターゲット ファイルをリンクしようとすると、
ld
エラーが報告されます。 - ライブラリが見つかりません:
-l
オプションで指定したライブラリがライブラリ検索パスに見つからない場合、ld
エラーが報告されます。 - 入出力エラー: 入力ファイルの読み取りまたは出力ファイルの書き込み中にエラーが発生した場合、エラーが
ld
報告されます。
予防
Linux でコマンドを使用する場合ld
、リンク プロセスがスムーズに行われ、正しい出力ファイルが生成されるようにするために、いくつかの点を考慮する必要があります。
-
ライブラリの順序: コマンド ラインでは、ライブラリの順序が重要です。通常、オブジェクト ファイルが最初にリストされ、次にライブラリ ファイルが続きます。これは、
ld
引数が左から右に処理され、オブジェクト ファイル内のシンボル参照を解決するときに、後続のライブラリ ファイル内でそれらのシンボルを検索するためです。 -
システム ライブラリのリンク: システム ライブラリとリンクする場合、
-l
通常、数学ライブラリとのリンクなどにオプションが使用されます-lm
。ただし、ライブラリの検索パスが正しく設定されていることを確認するために、-L
オプションを使用して検索パスを追加できます。 -
静的リンクと動的リンク: デフォルトでは、
ld
動的にリンクされたライブラリが試行されます。静的リンクが必要な場合は、オプションを使用できます-static
。ただし、静的リンクを使用すると出力ファイルのサイズが大きくなることに注意してください。 -
シンボルの競合: 異なるターゲット ファイルに重複したシンボルの定義がないことを確認してください。そうしないと、
ld
複数の定義エラーが報告されます。 -
適切なリンカー スクリプトを使用する:
ld
リンカー スクリプトを使用して、出力ファイルのレイアウトを制御します。特別なニーズがある場合は、-T
オプションを使用してカスタム リンカー スクリプトを指定できます。 -
不要なシンボル情報を削除する: 出力ファイルのサイズを減らすために、
-s
シンボル情報を削除するオプションを使用できます。ただし、これにより、その後のデバッグが困難になる可能性があります。 -
互換性を確保する: リンクされたオブジェクト ファイルが別のシステムで生成された場合、または異なるコンパイラ オプションを使用した場合、互換性の問題が発生する可能性があります。すべてのファイルが同じ環境でコンパイルされていることを確認してください。
-
出力を確認する: リンクが完了したら、
file
コマンドを使用して出力ファイルのタイプをチェックし、期待した形式 (ELF 実行可能ファイルなど) であることを確認します。 -
古いライブラリやオプションの使用を避ける: 時間の経過とともに、一部のライブラリやオプションは非推奨になる可能性があります。現在のバージョンのライブラリと推奨されるリンク オプションを使用していることを確認してください。
-
ドキュメントをお読みください:
ld
機能とオプションはバージョンごとに異なる場合があります。man ld
最新の情報やアドバイスについては、このドキュメントまたはその他の関連ドキュメントを定期的に参照してください。
ld
要約すると、リンク プロセスが正しく行われ、期待どおりの出力ファイルが生成されることを確認するには、コマンドを使用するときに注意する必要があるいくつかの側面があります。
基礎となる実装
ld
コマンドは、GNU Binutils スイートの一部であり、Linux の標準リンカーです。その基盤となる実装には、複数の複雑な手順とアルゴリズムが含まれます。以下は、そのコア実装の概要です。
-
入力処理:
ld
まず、すべての入力オブジェクト ファイルとライブラリ ファイルを読み取ります。- ELF (Executable and Linkable Format) またはその他の形式のファイル ヘッダーを解析して、セグメント情報、シンボル テーブルなどを取得します。
-
シンボル分析:
ld
すべての入力ファイルからのシンボルを含むグローバル シンボル テーブルを構築します。- 未解決のシンボル (たとえば、1 つのオブジェクト ファイルで参照されているが別の場所で定義されている関数) の場合、
ld
すべての入力ライブラリ ファイルでこれらのシンボルの定義が検索されます。 - シンボルの定義が見つからない場合は、
ld
エラーが報告されます。
-
アドレスの割り当て:
ld
すべての入力ファイル セグメント (例.text
:.data
) を 1 つの大きなセグメントに結合し、それにアドレスを割り当てます。- また、リンク時に変更する必要があるコードまたはデータの場所を指定する再配置エントリも処理します。
-
移転:
- オブジェクト ファイルは個別にコンパイルされるため、オブジェクト ファイルに含まれるアドレス参照は、最終的な正しいアドレスではない可能性があります。
ld
再配置エントリを使用してこれらの参照を更新し、正しいアドレスを指すようにします。
- オブジェクト ファイルは個別にコンパイルされるため、オブジェクト ファイルに含まれるアドレス参照は、最終的な正しいアドレスではない可能性があります。
-
生成された出力:
- すべてのシンボルが解決され、すべてのセグメントにアドレスが割り当てられると、
ld
出力ファイルが生成されます。 - 出力ファイルは通常 ELF 形式ですが、ターゲット プラットフォームに応じて他の形式にすることもできます。
- すべてのシンボルが解決され、すべてのセグメントにアドレスが割り当てられると、
-
最適化:
- 場合によっては、
ld
未使用のコードやデータを削除するなど、リンク時の最適化も可能です。
- 場合によっては、
ld
基礎となる実装は主に C 言語で書かれており、オープンソースです。詳細な実装に興味がある場合は、GNU Binutils プロジェクトのソース コードをチェックしてください。
全体として、ld
基盤となる実装には、入力ファイルの解析からアドレスの割り当て、再配置、最終出力の生成に至るまで、複数の複雑な手順が含まれます。これらの手順により、結果の実行可能ファイルまたはライブラリ ファイルが実行時にそのコードとデータに正しくアクセスできるようになります。
例
例1
2 つのオブジェクト ファイルをリンクしfile1.o
、file2.o
実行可能ファイルを生成しますprogram
。
ld -o program file1.o file2.o
例 2
オブジェクト ファイルをリンクしfile.o
、静的ライブラリを使用しますlibstatic.a
。
ld -o program file.o -Bstatic -lstatic
例 3
オブジェクト ファイルをリンクし、ダイナミック リンカーのパスを指定します。
ld -o program file.o -I/usr/local/lib/ld-linux.so.2
例 4
ターゲット ファイルをリンクし、出力ファイルのバイナリ形式を次のように指定しますelf64-x86-64
。
ld -o program file.o -oformat=elf64-x86-64
例 5
オブジェクト ファイルをリンクし、my_symbol
出力ファイル内のシンボルを強制的に未定義シンボルにします。
ld -o program file.o -u my_symbol
例6
オブジェクト ファイルをリンクし、リンカー スクリプトを使用しますlinker_script.ld
。
ld -o program file.o -T linker_script.ld
例 7
ターゲット ファイルをリンクし、入力ファイルの処理時に名前を表示します。
ld -o program file.o -t
ここでは7つのld
使用例を紹介しますので、ぜひ参考にしてください。さらにご質問がある場合、またはさらに詳しい説明が必要な場合は、お知らせください。
C 言語を使用して LD アイデアをシミュレートする
ld
コマンドの機能の実装は、ld
複数のファイル形式の処理、シンボルの解析、再配置の処理などを行う完全なプログラム リンカーであるため、複雑なタスクです。機能を完全に実装するには、ld
多くのコードと深い知識が必要です。
ただし、リンカーの基本的な動作を理解するのに役立つ、リンカーの簡略化された概念的な実装を提供できます。これは非常に基本的な例にすぎず、実際のリンク タスクには使用できません。
#include <stdio.h>
#include <stdlib.h>
// 假设我们有两个简单的目标文件格式:
// file1.o: "HELLO "
// file2.o: "WORLD"
int main(int argc, char *argv[]) {
if (argc < 4) {
printf("Usage: %s output input1 input2\n", argv[0]);
return 1;
}
FILE *input1 = fopen(argv[2], "rb");
FILE *input2 = fopen(argv[3], "rb");
FILE *output = fopen(argv[1], "wb");
if (!input1 || !input2 || !output) {
perror("Error opening files");
return 1;
}
// 简单地将两个输入文件的内容复制到输出文件
char ch;
while ((ch = fgetc(input1)) != EOF) {
fputc(ch, output);
}
while ((ch = fgetc(input2)) != EOF) {
fputc(ch, output);
}
fclose(input1);
fclose(input2);
fclose(output);
printf("Linking completed.\n");
return 0;
}
この簡素化されたバージョンのリンカーは、2 つの入力ファイルの内容を 1 つの出力ファイルにコピーするだけです。実際のld
リンカでは、さまざまなオブジェクト ファイル形式、シンボルの解析、再配置の処理、セクションのマージなどの複雑なタスクを処理します。
リンカーがどのように機能するかを本当に深く理解したい場合は、リンカーとローダーに関する専門書を読むか、GNU ld のようなオープンソース リンカーのソース コードを参照することをお勧めします。
結論
調査の過程で、私たちはシェル コマンドの能力と幅広い用途について深い理解を得ることができました。ただし、これらのテクニックを学ぶことは単なる始まりにすぎません。本当の力は、効率と生産性を高めるためにそれらを日常生活にどのように組み込むかによって生まれます。
心理学では、学習は継続的で積極的なプロセスであると教えています。したがって、これらのコマンドを読んで理解するだけでなく、実践することをお勧めします。独自のコマンドを作成してみて、シェル プログラミングを徐々にマスターして、日常の一部として取り入れてください。
同时,请记住分享是学习过程中非常重要的一环。如果你发现本博客对你有帮助,请不吝点赞并留下评论。分享你自己在使用Shell命令时遇到的问题或者有趣的经验,可以帮助更多人从中学习。
此外,我也欢迎你收藏本博客,并随时回来查阅。因为复习和反复实践也是巩固知识、提高技能的关键。
最后,请记住:每个人都可以通过持续学习和实践成为Shell编程专家。我期待看到你在这个旅途中取得更大进步!
阅读我的CSDN主页,解锁更多精彩内容:泡沫的CSDN主页