Search code examples
c++linuxdlldlopen

How to handle a changed Library opened with dlopen()


I'm using a shared object in my program which is loaded via dlopen(). When I overwrite the library with mv debug/newLibrary.so plugin/usedLibrary.so my program crashes as soon as it tries to interact with the loaded library. I cant even use dlclose(), this gets me a SIGSEV.

What is the best way to handle with this situations?

OS is Linux

Edit: actual code

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);
}

Edit 2: The Library (UA_String is from open62541) Its simply build with eclipse and copied in [...]/plugins. Execution works fine until I overwrite it

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
}

Solution

  • Your question is unclear.

    If you have some /tmp/plugin.so and you do

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

    and later (in the same process) some

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

    (or even unlink("/tmp/plugin.so"); ...) you should be able to dlclose(dl);

    However, if your build process is making a new one, e.g. you have some make /tmp/plugin.so target, then you really should do a

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

    or even

     rm /tmp/plugin.so
    

    before linking the shared library, e.g. before

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

    In other words, be sure that your build procedure is not overwriting bytes in the same inode (of the original /tmp/plugin.so)

    So if you overwrite your old /tmp/plugin.so with some mv /tmp/newplugin.so /tmp/plugin.so command in your build process you'll better do a mv /tmp/plugin.so /tmp/plugin.so~ or a rm /tmp/plugin.so just before.

    Notice that mmap(2) (internally invoked by dlopen(3)) is actually working on opened inodes. See path_resolution(7). So you could unlink(2) your shared library while still having it dlopen-ed.

    So never overwrite bytes in an existing shared library inode; do whatever is necessary to be sure to create a fresh shared library inode in your plugin build procedure.

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

    BTW, the real issue is not related to dlopen but to the nature of file descriptors (that is, of opened inodes) on POSIX systems (on which several processes can read and write the same file; the user or sysadmin -or tool developer- is supposed to avoid breaking havoc.).

    Use also 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).

    In practice, the user or sysadmin installing a plugin should ensure that a pristine inode is created for it, or that no process is using that plugin (before installation). It is his responsibility (and is a whole system issue). So you really need to educate your user or sysadmin, and document the issue, e.g. by suggesting the use of install(1) and/or locking utilities like package managers when installing plugins.

    PS. Copying in a private copy the shared object before dlopen might improve the situation, but does not solve the issue (what if the shared object source gets updated during the copy?). The real bug is in the build process which overwrites a shared object instead of writing & creating a pristine new inode.