编译和链接C ++文件时关于-ldl标志

时间:2016-02-10 19:34:35

标签: c++ linux c++11 dynamic-linking

参考以下代码

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()函数的函数指针保持为静态变量也是错误的吗?我不知道动态链接是如何工作的,我担心函数地址可能会在运行时在内存中移动。手册页说用dlsym()句柄的RTLD_NEXT函数是一个昂贵的计算,所以我只想懒洋洋地评估一次。

注意:我正在Linux发行版上编译它,并且涉及Linux动态链接器,因此我将继续使用Linux进行标记。

2 个答案:

答案 0 :(得分:5)

-ldl是链接器的库名称。它告诉链接器查找并链接名为libdl.so(或有时libdl.a)的文件。它与在命令行的相同位置放置相关库的完整路径具有相同的效果。

命令行上的库和对象顺序很重要。通常,如果库A调用库B,则应将B放在命令行中的A之后。所有库通常应该遍历所有目标文件。在this one等几个问题和答案中广泛涉及这一问题。

关于第二个问题,不,函数的地址在运行时不会改变,除非你dlopen共享库,然后卸载它,然后再次dlopen它。在您的情况下,由于您没有dlopen库,因此将函数地址保存在静态变量中是安全的。当然,如果你运行多个线程,你需要以某种方式确保线程安全(互斥它,或使用线程本地存储)。

答案 1 :(得分:1)

从第二个问题开始,动态链接在运行时工作,无论是在C / C ++中我们称之为后来与此绑定 - &gt;操作。当然,当您调用稍后声明为接口或类实例的绑定时,应该指向该对象的指定对象和内存位置。

第一个问题是我认为特定于编译器。我猜你是不是在Visual Studio中编译(不在Windows操作系统中)如果我是对的你应该要求编译器的供应商配置调试属性。 :)