【问题标题】:Static vs. dynamic linking conflicts and duplication静态与动态链接冲突和重复
【发布时间】:2014-05-09 08:52:03
【问题描述】:

我有一个静态链接到一个版本的 mpich 的代码 A。现在是库 B,A 通过 dlopen() 使用它。 B 也取决于 mpich,但与它动态链接。

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

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

  • 链接器在 dlopen 时如何处理共享对象依赖关系?我是否会在 VM 中也有两个带有动态链接的 mpich 实例,或者链接器是否足够聪明,可以意识到 dlopened B 所需的符号已经在地址空间中,并将针对这些符号进行解析。
  • 是否可以告诉链接器:当你dlopen这个库时,不要去获取动态依赖,而是用A已经提供的静态符号来解决它

【问题讨论】:

  • 致任何在谷歌上搜索这个问题的人:两个答案都很好。我只是将赏金分配给一个,将复选标记分配给另一个。

标签: linux ld static-linking dynamic-linking


【解决方案1】:

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

这里有一些代码可以重现您的情况并演示此标志的效果。

让我们创建一个库 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 的库 A:

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

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

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

$ 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_func,而使用 RTLD_DEEPBIND,libcommon 被再次加载,库 A 获得了自己的 common_func 版本。

【讨论】:

  • @AndrewMedico 剥离调试信息和动态链接之间没有关系。
  • @AndrewMedico “我认为唯一的解决方案是让 mpich 为 A 和 B 动态链接。”如果你把问题读到最后。
【解决方案2】:

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

我建议你看看“GCC 异常帧”http://www.airs.com/blog/archives/166

如果这有帮助,那么可用于 GCC 和 LLVM 的 Gold Linker 支持“链接时间优化”并且可以使用 DLLTool http://sourceware.org/binutils/docs/binutils/dlltool.html 在“Make”中运行。

确实可以让静态代码和动态代码相互调用,计算机不在乎;它会“运行”它所提供的任何东西——最终是否完全按照您想要的方式工作或 HCF 取决于正确的代码和正确的链接器命令。

使用调试器不会很有趣。最好在链接之前修改名称,以便在调试时可以看到代码来自哪个模块。一旦它启动并运行,您就可以取消 Mangle 的定义并拥有同名的 Functions Link(以确保它仍然有效)。

编译器/链接器错误不会成为您的朋友。

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

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

最好从源代码获取最新版本的库并自己编译整个内容,而不是依赖于尝试将来自不同来源的零碎拼凑在一起(尽管这样做是可能的)。

您甚至可以加载动态库并(静态地)调用它,然后稍后重新加载它的一部分。

您的内存有多紧,您希望函数运行多快,如果所有内容都在内存中,您的程序可以立即将执行转移到调用的函数,如果您将部分代码交换到 VM,您的执行时间将真的很受打击。

如果您想进行“动态动态链接”(通过加载动态库来完全控制您的动态链接,以便可以静态使用),在您的代码上运行探查器将有助于决定加载库的哪些部分。这就是造成头痛和噩梦的东西。总账。

【讨论】:

  • This is the stuff that headaches and nightmare are made of. 哈哈。谢谢你。我会将赏金奖励给其他帖子,但您的帖子也非常有用。谢谢。
猜你喜欢
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 2011-05-14
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
相关资源
最近更新 更多