如何使用dlopen()处理已更改的库

时间:2016-08-22 09:05:51

标签: c++ linux dll dlopen

我在我的程序中使用了一个通过dlopen()加载的共享对象。 当我用mv debug/newLibrary.so plugin/usedLibrary.so覆盖库时,我的程序一旦尝试与加载的库进行交互就会崩溃。我甚至不能使用dlclose(),这让我得到一个SIGSEV。

处理这种情况的最佳方法是什么?

操作系统是Linux

编辑:实际代码

void DynamicallyLoadedLibrary::loadLibrary() {
    // ModificationTime updaten
    lastModificationTime = modificationTime();

    // Library laden
    libraryHandle = dlopen(path.c_str(), RTLD_NOW);

    if (!libraryHandle) { // Library gefunden?
        throw DynamicLibraryException("Dynamic Library not found: " + path + "\n" + dlerror());
    }

    // Funktion laden
    externalFunction = (dll_function) dlsym(libraryHandle, "run");

    char *error;
    if ((error = dlerror()) != NULL) { // Funktion gefunden?
        throw DynamicLibraryException("Dynamic Library not found: run()\n" + string(error));
    }
}

void DynamicallyLoadedLibrary::close() {
    if (libraryHandle != nullptr) {
        cout << "DLL/close(): " << dlclose(libraryHandle) << endl; // DEBUG
        libraryHandle = nullptr;
        externalFunction = nullptr;
    }
}

void DynamicallyLoadedLibrary::operator()(vector<shared_ptr<ServerData>> &data) {
    // Wenn Datei sich geaendert hat, neu laden
    if (fileChanged()) {
        close();
        loadLibrary();
    }

    externalFunction(data);
}

编辑2:库(UA_String来自open62541) 它只是用eclipse构建并在[...] / plugins中复制。执行工作正常,直到我覆盖它

extern "C" void run(vector<shared_ptr<ServerData>> &data) {
    cout << "++++ OPC_WORKING_PACKAGE EXTERN ++++" << endl; // XXX
    for (unsigned int i = 0; i < data.size(); i++){
        UA_String *uaString = (UA_String*) data[i]->dataReference();
        cout << string((char*) uaString->data, uaString->length) << endl;
    }
    cout << "---- OPC_WORKING_PACKAGE EXTERN ----" << endl; // XXX
}

1 个答案:

答案 0 :(得分:3)

你的问题不清楚。

如果您有/tmp/plugin.so,那么

void* dl = dlopen("/tmp/plugin.so", TRL_NOW);

以及稍后(在相同的过程中)一些

rename("/tmp/plugin.so", "/tmp/oldplugin.so")

(甚至unlink("/tmp/plugin.so"); ...)您应该能够dlclose(dl);

但是,如果您的构建过程正在制作新的过程,例如你有一些make /tmp/plugin.so目标,那么你真的应该做一个

 mv /tmp/plugin.so /tmp/plugin.so~ 

甚至

 rm /tmp/plugin.so
在链接共享库之前

,例如前

gcc -shared -Wall -O /tmp/plugin*.pic.o -o /tmp/plugin.so

换句话说,请确保您的构建过程不会覆盖相同 inode(原始/tmp/plugin.so)中的字节

因此,如果您在构建过程中使用/tmp/plugin.so命令覆盖旧的mv /tmp/newplugin.so /tmp/plugin.so,那么您最好在之前执行mv /tmp/plugin.so /tmp/plugin.so~rm /tmp/plugin.so。< / p>

请注意,mmap(2)(由dlopen(3)内部调用)实际上正在处理打开的inode。见path_resolution(7)。因此,您可以unlink(2)使用共享库dlopen - 编辑。

所以永远不要覆盖现有共享库 inode 中的字节;尽一切可能确保在插件构建过程中创建新鲜共享库 inode

阅读Advanced Linux Programming&amp; Drepper的How to Write a Shared Library

BTW,真正的问题与dlopen无关,而与POSIX系统上file descriptors(即打开的inode)的性质无关(多个进程可以读取和写入相同的文件) ;用户或系统管理员 - 或工具开发者 - 应该避免破坏。)。

还可以使用pmap(1)(作为pmap 1234)和/或cat /proc/1234/maps来了解pid 1234 process的内存映射(即其virtual address space)。

实际上,安装插件的用户或系统管理员应确保为其创建原始inode,或者没有进程正在使用该插件(在安装之前)。这是他的责任(并且是一个完整的系统问题)。所以你真的需要教育你的用户或系统管理员,并记录问题,例如建议在安装插件时使用install(1)和/或锁定package managers等实用程序。

PS。在dlopen之前复制私有副本中的共享对象可能会改善这种情况,但不能解决问题(如果共享对象源在复制期间得到更新会怎样?)。真正的错误在于构建过程,它会覆盖共享对象而不是写入&amp;创建一个原始的新inode。