次のコードを参照して
test_linker.cpp
_int main() {
srand(time(0));
for (int i = 0; i < 10; ++i) {
cout << Rand() % 10 << endl;
}
return 0;
}
_
random.cpp
_#include <iostream>
using std::cout;
using std::endl;
#include <dlfcn.h>
int Rand() throw() {
// get the original Rand() function
static auto original_Rand = (decltype(&Rand)) dlsym(RTLD_NEXT,"Rand");
cout << "Call made to Rand()" << endl;
return original_Rand();
}
_
次のコマンドでコードをコンパイルしようとすると
_g++ -std=c++11 -Wall -Werror -Wextra -Wvla -pedantic -O3 urandom.cpp -c
g++ -std=c++11 -Wall -O3 test_linker.cpp urandom.o -ldl
_
すべて正常に動作しますが、_-ldl
_フラグをファイルの前に移動すると、リンカーは次のエラーをスローします。
_urandom.cpp:(.text+0xaf): undefined reference to `dlsym'
_
質問1なぜこれが起こるのか誰かが説明できますか?私は通常、コンパイルコマンドのフラグの順序を気にしません。
質問2また、元のRand()
関数への関数ポインタを静的変数のままにしておくのは間違っていますか?ダイナミックリンクがどのように機能するのか正確にはわかりません。実行時に関数アドレスがメモリ内で移動されるのではないかと心配しています。マニュアルページには、_RTLD_NEXT
_ハンドルを持つdlsym()
関数はコストのかかる計算であると書かれているので、これを一度怠惰に評価したかっただけです。
注:これをLinuxディストリビューションでコンパイルしていて、Linuxダイナミックリンカーが関係しているので、先に進んでこれにLinuxのタグを付けます。
-ldl
は、リンカーのライブラリ指定です。 libdl.so
(または場合によってはlibdl.a
)という名前のファイルを見つけてリンクするようにリンカーに指示します。これは、問題のライブラリへのフルパスをコマンドラインの同じ位置に配置するのと同じ効果があります。
コマンドラインでのライブラリとオブジェクトの順序は重要です。通常、ライブラリAがライブラリBを呼び出す場合、コマンドラインでBをAの後に配置する必要があります。通常、すべてのライブラリはすべてのオブジェクトファイルの後に配置する必要があります。これは、いくつかのSO質問と回答 これ のように)で広範囲にカバーされています。
2番目の質問については、いいえ、共有ライブラリをdlopen
してからアンロードしてから、もう一度dlopen
しない限り、関数のアドレスは実行時に変更されません。あなたの場合、ライブラリをdlopen
しないので、関数アドレスを静的変数に保持するのが安全です。もちろん、複数のスレッドを実行する場合は、何らかの方法でスレッドの安全性を確保する必要があります(ミューテックスするか、スレッドローカルストレージを使用します)。
2番目の質問から始めます。動的リンクは、C/C++のいずれかの方法で実行時に機能し、後でこの->操作でバインディングを呼び出します。もちろん、後でインターフェイスやクラスインスタンスのように宣言されたバインディングを呼び出すときは、指定されたオブジェクトとそのオブジェクトのメモリ位置を指している必要があります。
最初の1つの質問は、コンパイラに固有だと思います。私が正しければ、Visual Studioではなく(Windows OSではなく)コンパイルしていると思います。コンパイラのベンダーにデバッグプロパティの構成を依頼する必要があります。 :)