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

How to handle a changed Library opened with dlopen()

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

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

OS 是 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
}

你的问题不清楚。

如果你有一些 /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

before 链接共享库,例如之前

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

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

因此,如果您在构建过程中用一些 mv /tmp/newplugin.so /tmp/plugin.so 命令覆盖旧的 /tmp/plugin.so,您最好在之前执行 mv /tmp/plugin.so /tmp/plugin.so~rm /tmp/plugin.so

请注意,mmap(2) (internally invoked by dlopen(3)) is actually working on opened inodes. See path_resolution(7). So you could unlink(2) 您的共享库仍然在 dlopen 版。

所以永远不要覆盖现有共享库中的字节 inode;做任何必要的事情以确保在您的插件构建过程中创建一个 fresh 共享库 inode

阅读Advanced Linux Programming & Drepper's How to Write a Shared Library

顺便说一句,真正的问题与 dlopen 无关,而是与 POSIX 系统(多个进程可以在其上读取和写入同一个文件;用户或系统管理员 - 或工具开发人员 - 应该避免造成破坏。)。

也使用 pmap(1) (as pmap 1234) and/or cat /proc/1234/maps to understand the memory mapping of process of pid 1234 (i.e. its virtual address space).

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

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