关于编译和链接 C++ 文件时的 -ldl 标志
About the -ldl flag while compiling and linking C++ files
参考以下代码
test_linker.cpp
int main() {
srand(time(0));
for (int i = 0; i < 10; ++i) {
cout << rand() % 10 << endl;
}
return 0;
}
urandom.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 标记它。
从你的第二个问题开始,动态链接在 运行 时间内工作,在 C/C++ 中我们稍后调用这个 -> 操作的绑定。当然,当您稍后调用声明为接口或 class 实例的绑定时,应该指向指定的对象和该对象的内存位置。
第一个问题我认为更具体到编译器。我猜你不是在 Visual Studio 中编译(不是在 Windows OS 中)如果我是对的,你应该要求编译器的供应商配置调试属性。 :)
-ldl
是 linker 的图书馆名称。它告诉 linker 找到并 link 一个名为 libdl.so
(或有时 libdl.a
)的文件。它与在命令行的相同位置放置相关库的完整路径具有相同的效果。
命令行上的库和对象顺序很重要。通常,如果库 A 调用库 B,则在命令行中 B 应放在 A 之后。所有库通常都应该在所有目标文件之后。这在 this one.
等几个 SO 问题和答案中有广泛介绍
至于第二个问题,不,函数的地址在 运行 时间不会改变,除非你 dlopen
一个共享库,然后卸载它,然后 dlopen
再次。在您的情况下,由于您没有 dlopen
库,因此将函数地址保存在静态变量中是安全的。当然,如果你 运行 多个线程,你需要以某种方式确保线程安全(互斥,或使用线程本地存储)。
参考以下代码
test_linker.cpp
int main() {
srand(time(0));
for (int i = 0; i < 10; ++i) {
cout << rand() % 10 << endl;
}
return 0;
}
urandom.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 标记它。
从你的第二个问题开始,动态链接在 运行 时间内工作,在 C/C++ 中我们稍后调用这个 -> 操作的绑定。当然,当您稍后调用声明为接口或 class 实例的绑定时,应该指向指定的对象和该对象的内存位置。
第一个问题我认为更具体到编译器。我猜你不是在 Visual Studio 中编译(不是在 Windows OS 中)如果我是对的,你应该要求编译器的供应商配置调试属性。 :)
-ldl
是 linker 的图书馆名称。它告诉 linker 找到并 link 一个名为 libdl.so
(或有时 libdl.a
)的文件。它与在命令行的相同位置放置相关库的完整路径具有相同的效果。
命令行上的库和对象顺序很重要。通常,如果库 A 调用库 B,则在命令行中 B 应放在 A 之后。所有库通常都应该在所有目标文件之后。这在 this one.
等几个 SO 问题和答案中有广泛介绍至于第二个问题,不,函数的地址在 运行 时间不会改变,除非你 dlopen
一个共享库,然后卸载它,然后 dlopen
再次。在您的情况下,由于您没有 dlopen
库,因此将函数地址保存在静态变量中是安全的。当然,如果你 运行 多个线程,你需要以某种方式确保线程安全(互斥,或使用线程本地存储)。