静态与dynamic链接冲突和重复

我有一个静态链接到一个版本的mpich的代码A. 现在来到图书馆B,由A通过dlopen()使用。 B也取决于mpich,但dynamic地与它链接。

现在的问题是,为了让B利用mpi分发,需要访问当前由A处理的通信器。这个通信器是由mpich的一个静态版本创build的,当B调用MPI例程时,它将使用dynamic版本的MPI,这是不符合附加到A的静态版本兼容。

这是整体画面。 我认为唯一的解决办法是让mpichdynamic链接到A和B.但是我不完全理解的是:

  • 链接器如何处理共享对象的依赖关系? 在虚拟机中有两个mpich实例还有dynamic链接,或者链接器是否足够聪明,可以认识到被转折的B所需要的符号已经在地址空间中,并且将根据这些符号来解决。
  • 是否有可能告诉链接器:当你使用这个库的时候,不要去获取dynamic的依赖关系,而是使用已经由A提供的静态符号来解决它

你没有说你正在使用哪个工具链(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主题:“ MinGW和MSVC之间的链接困境(未定义的引用) ” MinGW失败MSVC工作 “。

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

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

内存有多紧张,你想要多快功能运行,如果一切都在内存中你的程序可以将执行转移到被调用的函数中,如果你将代码的一部分交换到虚拟机,你的执行时间将真正需要击中。

在你的代码上运行一个Profiler可以帮助你决定要加载一个库的哪些部分,如果你想做'动态链接'(通过加载动态链接库来完全控制你的dyna链接,这样可以静态使用)。 这是头痛和噩梦的东西。 GL。

总之:这取决于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版本。