使用RTLD_DEEPBIND动态加载共享库

时间:2016-05-18 10:33:04

标签: c++ dynamic-linking

背景: 我有应用程序哪个部分用作其他独立应用程序的库。它们在链接时间链接到该库(比如lib.so)。这种方法的问题是我们必须使用相同的外部库,如boost,ace等,否则我们将有重复的符号,最终会导致崩溃。我们想解决这个问题。

我知道两种技术 - 一种是隐藏所有符号(不确定共享库的全局/本地范围的顺序),另一种是使用动态链接。我们选择了第二个选项(动态链接),因为它为客户提供了使用存根lib.so进行简单测试的机会。我们有非常简单的api。

我在下面写了一个加载示例共享库的应用程序的小例子,它崩溃了(我想了解它崩溃的原因以及它应该如何编写)。 崩溃是在dlopen中,恰好在分配给std :: string(Aclass类型的构造函数)的全局变量的初始化中。从我们的测试中可以看出,在继续初始化库时对std库的任何访问都会导致崩溃。

我们设法通过向EXECUTABLE添加-fPIC标志来删除崩溃(为什么这解决了我们的问题,我认为它应该设置为共享库,任何人都可以更准确地解释我)?不必理解这个标志是有问题的,因为它减慢了应用程序的速度,在我的情况下(低延迟应用程序)它很成问题。

总结: 1.为什么会发生这种崩溃? 2.为什么-fPIC标志足以解决此崩溃问题? 3.为什么将-fPIC标志设置为可执行文件就足够了? 4.是否有可能以其他方式解决我的问题,因此共享库和客户端应用程序可以使用不同版本的库(如boost,ace等,编译器,linux版本和std库保证相同)? 5.删除标志RTLD_DEEPBIND也会修复崩溃但是从gcc man看起来我应该使用这个标志,因为它会改变共享库的符号范围顺序 - 首先它会在本地范围内搜索符号然后在全局 - 看起来对我来说必须有因为共享库将使用与可执行文件不同的外部库(动态加载将保护可执行文件并污染其符号范围)。为什么在这个简单的情况下删除此标志会修复崩溃?

共享库dynLib.cpp:

#include <string>
class Aclass
{
    std::string s;
    s = "123";
}

Aclass a;

Exacutable main.cpp:

#include <stdlib.h>
#include <dlfcn.h>
#include <string>
#include <unistd.h>
#include <iostream>

int main()
{
    std::string dummyCrasher;
    dlerror();
    void* handle = dlopen("./libdynLib.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);
    if(!handle)
    {
        std::cout << "handle is null" << dlerror();
    }

    usleep(1000 * 1000 * 10);
}  

Makefile:makefile

CXXFLAGS=-m32 -march=x86-64 -Wl,v -g -O3 -Wformat -Werror=format -c
CLINKFLAGS=-Wl,-Bstatic -Wl,Bdynamic -ldl -m32 -march=x86-64

all: dynLib.so dynamiclinking

dynLib.so: dynLib.o
    g++44 $(CLINKFLAGS) -shared -o libdynLib.so dynLib.o

dynLib.o: dynLib.cpp
    g++44 $(CXXFLAGS) dynLib.cpp

dynamiclinking: main.o
    g++44 $(CLINKFLAGS) -o dynamiclinking main.o -ldl

main.o: main.cpp
    g++44 (CXXFLAGS) main.cpp

.PHONY: clean
clean:
    rm dynLib.o main.o dynamiclinking libdynLib.so

PS。我手工编写代码(可以做一些拼写错误) PS 2.带-fPIC标志它可以工作:

main.o: main.cpp
    g++44 (CXXFLAGS) main.cpp -fPIC

更新 可以通过libstdc ++的静态链接来解决这个问题。但我的问题仍然没有得到解答:(也许有人有时间看看它?

UPDATE2 GCC 4.4.6和4.8.1也出现同样的问题。

1 个答案:

答案 0 :(得分:0)

我认为您遇到的问题与When we are supposed to use RTLD_DEEPBIND?中的问题相同,可执行文件获取全局变量的副本:

  

这是您构建主要应用程序而不是 -fPIC选项的一个很棒的功能。   [...]   这意味着当在libdep.so中找到符号时,它会被复制到该地址的主可执行文件的初始数据段中。然后查找duplicate中对libdep.so的引用,它指向主可执行文件中符号的副本。

由于RTLD_DEEPBIND,dynLib.so在初始化std::string时会从原始libstdc ++中看到错误的全局变量集,从而崩溃。

至于为什么链接器有这种行为,this article有一个详细的解释(强调我的):

  

回想一下,程序/可执行文件不可重定位,因此其数据地址必须在链接时绑定。因此,链接器必须在程序的地址空间中创建变量的副本,动态加载程序将使用它作为重定位地址。这类似于上一节中的讨论 - 在某种意义上,主程序中的 myglob会覆盖共享库中的那个,并且根据全局符号查找规则,它将被替代使用。 / strong>

最后一点说明:此行为是特定于平台的,至少在PowerPC上,主可执行文件中没有此类额外的全局变量副本。