静态与动态链接冲突和重复

时间:2014-05-09 08:52:03

标签: linux ld static-linking dynamic-linking

我有一个静态链接到一个版本的mpich的代码A.现在来到库B,由A via dlopen()使用。 B依赖于mpich,但是它与动态链接。

现在的问题是,为了让B利用mpi分配,需要访问当前由A处理的通信器。这个通信器是由静态版本的mpich创建的,当B调用MPI例程时,它会使用动态版本的MPI,不保证与附加到A的静态版本兼容。

这是整体情况。我认为唯一的解决方案是为A和B动态链接mpich。然而我不完全理解的是:

  • 在倾斜时链接器如何处理共享对象依赖项?我是否在VM中有两个mpich实例,还有动态链接,或者链接器是否足够聪明,以便认识到斜坡B所需的符号已经在地址空间中并将解决这些问题。
  • 是否可以告诉链接器:当你打开这个库时,不要去获取动态依赖关系,而是用已经提供的静态符号解决它

2 个答案:

答案 0 :(得分:2)

简而言之:它取决于dlopen选项。默认情况下,如果请求的库所需的符号已存在于全局范围中,则将重用该符号(这是您想要的)。但是您可以使用RTLD_DEEPBIND绕过此行为,使用此标记,依赖项不会从全局范围中重用,并将再次加载。

以下是一些代码,用于重现您的情况并演示此标志的效果。

让我们创建一个将由lib A和程序B使用的公共库。该库将以两个版本存在。

$ cat libcommon_v1.c 
int common_func(int a)
{
    return a+1;
}
$ cat libcommon_v2.c 
int common_func(int a)
{
    return a+2;
}

现在让我们编写使用libcommon_v2的lib A:

$ cat liba.c 
int common_func(int a);

int a_func(int a)
{
    return common_func(a)+1;
}

最后程序B动态链接到libcommon_v1和dlopens lib A:

$ cat progb.c
#include <stdio.h>
#include <dlfcn.h>

int common_func(int a);
int a_func(int a);

int main(int argc, char *argv[])
{
    void *dl_handle;
    int (*a_ptr)(int);
    char c;

    /* just make sure common_func is registered in our global scope */
    common_func(42);

    printf("press 1 for global scope lookup, 2 for deep bind\n");
    c = getchar();
    if(c == '1')
    {
        dl_handle = dlopen("./liba.so", RTLD_NOW);
    }
    else if(c == '2')
    {
        dl_handle = dlopen("./liba.so", RTLD_NOW | RTLD_DEEPBIND);
    }
    else
    {
        printf("wrong choice\n");
        return 1;
    }
    if( ! dl_handle)
    {
        printf("dlopen failed: %s\n", dlerror());
        return 2;
    }
    a_ptr = dlsym(dl_handle, "a_func");
    if( ! a_ptr)
    {
        printf("dlsym failed: %s\n", dlerror());
        return 3;
    }

    printf("calling a_func(42): %d\n", (*a_ptr)(42));

    return 0;
}

让我们构建并运行所有的东西:

$ export LD_LIBRARY_PATH=.
$ gcc -o libcommon_v1.so -fPIC -shared libcommon_v1.c
$ gcc -o libcommon_v2.so -fPIC -shared libcommon_v2.c
$ gcc -Wall -g -o progb progb.c -L. -lcommon_v1 -ldl
$ gcc -o liba.so -fPIC -shared liba.c -L. -lcommon_v2
$ ./progb 
press 1 for global scope lookup, 2 for deep bind
1
calling a_func(42): 44
$ ./progb 
press 1 for global scope lookup, 2 for deep bind
2
calling a_func(42): 45

我们可以清楚地看到,使用默认选项,dlopen重用程序B中存在的符号common_funcRTLD_DEEPBIND的符号,再次加载了libcommon,库A得到了自己的版本common_func

答案 1 :(得分:0)

您没有说明您使用的是哪种工具链(GCC,LLVM,MSC等),最有用的答案将取决于此信息。

我建议你看看&#34; GCC Exception Frames&#34; http://www.airs.com/blog/archives/166

如果这有用,那么可用于GCC和LLVM的Gold Linker支持&#39;链接时间优化&#39;并且可以在&#34; Make&#34;使用DLLTool http://sourceware.org/binutils/docs/binutils/dlltool.html

确实可以让静态和动态代码互相调用,计算机不关心;它将会运行&#39;它被送入的任何东西 - 是否最终以您想要的方式工作,或者HCF依赖于正确的代码和正确的链接器命令。

使用调试器不会很有趣。最好在链接之前修改名称,以便在调试时可以看到代码来自哪个模块。一旦启动并运行,您可以取消断言并具有相同名称的功能链接(以确保它仍然起作用)。

编译器/链接器错误不会是你的朋友。

这种情况(静态和动态链接)更常发生在MinGW和Cygwin中,其中一些库是静态的,但是从Internet下载的库只能以动态形式(没有源)提供。

如果库来自两个不同的编译器工具链,则会出现其他问题,请参阅此StackOverflow线程:&#34; linking dilemma (undefined reference) between MinGW and MSVC. MinGW fails MSVC works&#34;。

最好从源代码中简单地获取最新版本的库并自己编译整个文件,而不是依赖于尝试拼凑不同来源的碎片(尽管可以这样做)。

您甚至可以加载动态库并静态调用它,然后再重新加载部分动态库。

你对内存有多紧张以及你想要多快运行函数,如果一切都在内存中你的程序可以立即将执行转移到被调用函数,如果你将一部分代码交换到VM你的执行时间将会真的很受欢迎。

在您的代码上运行Profiler将有助于确定要加载的库的哪些部分,如果您想要动态动态链接&#39; (通过加载动态库完全控制你的dyna链接,以便静态使用它)。这是令人头疼和噩梦的东西。 GL。

相关问题