共有ライブラリ内の未解決のシンボルを簡単にチェックしますか?


83

私はかなり大きなC ++共有オブジェクトライブラリを書いていますが、デバッグを面倒にする小さな問題に遭遇しました。

ヘッダーファイルで関数/メソッドを定義し、そのスタブを作成するのを忘れた場合(開発中)、実行可能ファイルではなく共有オブジェクトライブラリとしてビルドしているため、コンパイル時にエラーが表示されず、その機能を実装するのを忘れました。私が何かが間違っていることを見つける唯一の方法は、実行時に、このライブラリに対してリンクしているアプリケーションが「未定義のシンボル」エラーでフォールオーバーするときです。

コンパイル時に必要なすべてのシンボルがあるかどうかを確認する簡単な方法を探しています。おそらく、Makefileに追加できるものです。

私が思いついた解決策の1つは、コンパイルされたライブラリを実行してnm -C -U、すべての未定義の参照のデマングルされたリストを取得することです。問題は、GLibCなどの他のライブラリにあるすべての参照のリストも表示されることです。もちろん、最終的なアプリケーションがまとめられると、このライブラリと一緒にリンクされます。の出力を使用することも可能であろうnmとするgrepすべての私のヘッダファイルを通って、対応する名前のいずれかどうかを確認..しかし、これは非常識と思われます。確かにこれは珍しい問題ではなく、それを解決するためのより良い方法がありますか?


3
nm -C -u何度も私を救ってくれました!(-u私のシステムでは小文字に注意してください。)このコメントをここに残して、次に必要になったときに見つけられるようにします。
dpritch 2017

回答:


94

リンカオプション-z defs/を確認してください--no-undefined。共有オブジェクトを作成するときに、未解決のシンボルがあるとリンクが失敗します。

gccを使用してリンカーを呼び出す場合は、コンパイラ-Wlオプションを使用してオプションをリンカに渡します。

gcc -shared ... -Wl,-z,defs

例として、次のファイルについて考えてみます。

#include <stdio.h>

void forgot_to_define(FILE *fp);

void doit(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp != NULL)
    {
        forgot_to_define(fp);
        fclose(fp);
    }
}

これを共有オブジェクトに組み込むと、成功します。

> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed
succeeded

ただし、を追加する-z defsと、リンクが失敗し、欠落しているシンボルについて通知されます。

> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed
/tmp/cccIwwbn.o: In function `doit':
silly.c:(.text+0x2c): undefined reference to `forgot_to_define'
collect2: ld returned 1 exit status
failed

3
+1この回答は、コンパイル時にDLL外部シンボルを解決する必要があるWindowsのバックグラウンドから来た人を支援する可能性があります。素敵なコードスニペットを探す方法!
Shmil The Cat 2014

@ShmilTheCat:こんにちは、makeファイルに-Wl、-z、defsを入れようとしましたが、実行中に未定義のシンボルエラーが発生しますが、コンパイル中には発生しません。私に何ができる?。私のシナリオでは、2つのフォルダーが互いに内部にあり(たとえば、A / B、つまりBはAの内部にあります)、「libA.so」にいくつかの記号またはBが表示されます。Bのすべてのシンボルが「libA.so」で使用できるかどうかはわかりません。どうすればそれを確認できますか?
Vinay 2016

@ShmilTheCat Windows DLLのようにする-Bsymbolicために、リンカーコマンドラインに追加できます。チェックのforgot_to_defineおかげで現在ライブラリに存在している場合でも-z、実行可能ファイルはそれを独自の定義でオーバーライドでき、ライブラリ自体の定義はそのオーバーライドに移動します。-Bsymbolicライブラリ自体の関数に対する定義がその関数に移動するように強制します。
kaz 2016年

実際に使用した機能でのみ動作します。私がそう// forgot_to_define(fp);してエラー報告されませ
エージェントスミス2017

19

Linux(あなたが使用しているように見える)ではldd -r a.out、あなたが探している答えを正確に与えるはずです。

更新:a.outチェックする対象を作成する簡単な方法:

 echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
 ldd -r ./a.out

4
これは、元の投稿で提案したnm -C-Uとほぼ同じことを行います。問題は、話す「a.out」アプリケーションがないことです。これは共有ライブラリであるため、ldd -r mylibrary.soは、他のさまざまなダイナミックライブラリのシンボルを使用するため、出力のヒープ全体を提供します。特に興味があります。外部ライブラリではなくヘッダーファイルで定義されているシンボルが欠落しています。
デビッドクラリッジ

2
これは受け入れられるべきです。問題は、未定義のシンボルを回避する方法ではなく、未定義のシンボルをチェックする簡単な方法は何
ですか

9

テストスイートはどうですか?必要なシンボルにリンクするモック実行可能ファイルを作成します。リンクが失敗した場合は、ライブラリインターフェイスが不完全であることを意味します。


4

私は一度同じ問題を抱えていました。私はC ++でコンポーネントモデルを開発していましたが、もちろん、コンポーネントは実行時に動的にロードされるはずです。私が適用した解決策は3つあります。

  1. 静的にコンパイルできるビルドシステムを定義するには、少し時間がかかります。エンジニアリングに時間を費やすことはありませんが、これらの厄介なランタイムエラーをキャッチする時間を大幅に節約できます。
  2. 関数をよく知られたよく理解されているセクションにグループ化して、関数/スタブをグループ化して、対応する各関数にスタブがあることを確認できるようにします。それをうまく文書化することに時間をかけるなら、おそらく定義をチェックするスクリプトを書いて(たとえば、そのdoxygenコメントを介して)、対応する.cppファイルでそれをチェックすることができます。
  3. 同じライブラリのセットをロードし、RTLD_NOWフラグをdlopenに指定するいくつかのテスト実行可能ファイルを実行します(* NIXの下にいる場合)。それらは欠落しているシンボルを通知します。

お役に立てば幸いです。


RTLD_NOWの行に、即時(遅延ではない)バインディングを強制するenv varが存在しますか?
ステファノボリーニ

1
うん。ここではLD_BIND_NOW(libc5; 2.1.1以降のglibc)です。空でない文字列に設定すると、動的リンカーは、関数呼び出しの解決を最初に参照される時点まで延期するのではなく、プログラムの起動時にすべてのシンボルを解決します。これは、デバッガーを使用するときに役立ちます。
ステファノボリーニ

これは、OPの目的にも役立ちます。LD_WARN(ELFのみ)(2.1.3以降のglibc)空でない文字列に設定されている場合、未解決のシンボルについて警告します。
ステファノボリーニ

ステファノ:はい、そうです。それらの変数は存在します。ただし、動的ロードを後で(たとえば、ユーザーがモジュールをロードするとき)まで延期する場合、目的の(将来の)ライブラリをロードするテストプログラムを実際に作成しない限り、これらの変数は役に立ちません。
ディエゴセビージャ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.