web-dev-qa-db-ja.com

gccの「-l」オプションの順序が重要なのはなぜですか?

dis86 ライブラリを使用するプログラムをコンパイルしようとしています。実際には、ライブラリの ser-manual で指定されているプログラム例を使用しています。しかし、コンパイル中にエラーが発生します。私が得るエラーは次のとおりです。

example.c:(.text+0x7): undefined reference to 'ud_init'
example.c:(.text+0x7): undefined reference to 'ud_set_input_file'
.
.
example.c:(.text+0x7): undefined reference to 'ud_insn_asm'

私が使用しているコマンドは次のとおりです。

$ gcc -ludis86 example.c -o example 

ユーザーマニュアルの指示どおり。

明らかに、リンカーはlibudisライブラリをリンクできません。しかし、コマンドを次のように変更すると:

$ gcc example.c -ludis86 -o example 

動作を開始します。だから、誰かが最初のコマンドの問題を説明してください。

69
user1129237

それは、GNUリンカが使用するリンクアルゴリズムの仕組みです(少なくとも静的ライブラリのリンクに関しては)。リンカはシングルパスリンカであり、一度ライブラリを再訪することはありません。 。

ライブラリは、オブジェクトファイルのコレクション(アーカイブ)です。 -lオプションを使用してライブラリを追加すると、リンカはライブラリから無条件にallオブジェクトファイルを取得しません。 現在必要なであるオブジェクトファイル、つまり現在未解決の(保留中の)シンボルを解決するファイルのみを取ります。その後、リンカはそのライブラリを完全に忘れます。

保留中のシンボルのリストは、リンカーが入力オブジェクトファイルを左から右に次々と処理するときに、リンカーによって継続的に維持されます。各オブジェクトファイルを処理すると、一部のシンボルが解決されてリストから削除され、新しく検出された他の未解決のシンボルがリストに追加されます。

そのため、-lを使用してライブラリを含めると、リンカーはそのライブラリを使用して、現在保留中のシンボルをできるだけ多く解決し、そのライブラリを完全に忘れます。 後でがそのライブラリから追加のオブジェクトファイルを必要とすることが突然検出された場合、リンカーはそのライブラリに「戻り」、それらの追加オブジェクトファイルを取得しません。もう手遅れです。

このため、リンカーのコマンドラインで-lオプションlateを使用することを常にお勧めします。これにより、リンカーが-lに到達するまでに、必要なオブジェクトファイルと必要ありません。 -lオプションをリンカーの最初のパラメーターとして配置することは、一般的にまったく意味がありません。最初の時点では、保留中のシンボルのリストは空です(より正確には、単一シンボルmainで構成されます)リンカーはライブラリから何も取得しません。

あなたの場合、オブジェクトファイルexample.oには、シンボルud_initud_set_input_fileなどへの参照が含まれています。リンカーは、最初にそのオブジェクトファイルを受け取る必要があります。これらのシンボルを保留中のシンボルのリストに追加します。その後、-lオプションを使用してライブラリを追加できます:-ludis86。リンカーはライブラリを検索し、それらの保留中のシンボルを解決するライブラリからすべてを取得します。

-ludis86オプションを最初にコマンドラインに配置すると、リンカはライブラリを効果的にignoreします。最初はud_initud_set_input_fileなどを必要とすることを知らないためです。後で、example.oを処理する場合これらのシンボルを検出し、保留中のシンボルリストに追加します。ただし、-ludis86はすでに処理されているため(実質的に無視されるため)、これらのシンボルは最後まで未解決のままになります。

時には、2つ(またはそれ以上)のライブラリーが循環形式で相互に参照する場合、同じライブラリーで-lオプションを2回使用して、リンカーから必要なオブジェクトファイルをそのライブラリーから2回取得できるようにする必要があります。

101
AnT

これと同じ問題 しばらく前にヒットしました。一番下の行は、gnuツールは、不足しているシンボルを解決するために、ライブラリリストで常に「検索」するわけではないということです。簡単な修正は次のいずれかです。

  1. Libsとobjを依存関係の順序で指定するだけです(上記で発見したように)

  2. または、循環依存関係がある場合(libAはlibBの関数を参照しますが、libBはlibAの関数を参照します)、コマンドラインでlibsを2回指定します。これは、マニュアルページにも示されています。例えば。

    gcc foo.c -lfoo -lbar -lfoo
    
  3. 使用 -(および-) paramsを使用して、このような循環依存関係を持つアーカイブのグループを指定します。 GNU --start-groupおよび--end-group。詳細については、 here を参照してください。

オプション2または3を使用すると、リンクのパフォーマンスコストが発生する可能性があります。リンクする量がそれほど多くない場合は、問題ではありません。

8
selbie

またはrescanを使用します

Oracle Solaris 11.1 Linkers and Libraries Guide の41ページから:

アーカイブ間の相互依存関係が存在する可能性があるため、あるアーカイブからのメンバーの抽出は、別のアーカイブからのメンバーの抽出によって解決する必要があります。これらの依存関係が周期的である場合、以前の参照を満たすために、コマンドラインでアーカイブを繰り返し指定する必要があります。

$ cc -o prog .... -lA -lB -lC -lA -lB -lC -lA 

繰り返されるアーカイブ仕様の決定と保守は、面倒な場合があります。

-z rescan-nowオプションを使用すると、このプロセスが簡単になります。 -z rescan-nowオプションは、コマンド行でオプションが検出されるとすぐにリンカーによって処理されます。このオプションの前にコマンドラインから処理されたすべてのアーカイブは、すぐに再処理されます。この処理は、シンボル参照を解決する追加のアーカイブメンバーを見つけようとします。このアーカイブの再スキャンは、新しいメンバーが抽出されないアーカイブリストのパスが発生するまで続きます。前の例は、次のように簡略化できます。

$ cc -o prog .... -lA -lB -lC -z rescan-now 

または、-z rescan-startおよび-z rescan-endオプションを使用して、相互に依存するアーカイブをアーカイブグループにグループ化できます。これらのグループは、コマンド行で終了区切り文字が検出されるとすぐにリンカーによって再処理されます。グループ内で見つかったアーカイブは、シンボル参照を解決する追加のアーカイブメンバーを見つけるために再処理されます。このアーカイブの再スキャンは、新しいメンバーが抽出されないアーカイブグループのパスが発生するまで続きます。アーカイブグループを使用すると、前の例は次のように記述できます。

$ cc -o prog .... -z rescan-start -lA -lB -lC -z rescan-end
4
flerb