carlosms
carlosms

Reputation: 51

dlclose crashes when copying dynamic libraries

I have an interesting problem that seems to be unresolved by my research on the internet. I'm trying to load libraries dynamically in my c++ project with the functions from dlfcn.h. The problem is that when I try to reload the plugins at running time (because I made a change on any of them), the main program crashes (Segmentation fault (core dumped)) when dlclose() is called. Here is my example that reproduces the error:

main.cpp:

#include    <iostream>
#include    <dlfcn.h>
#include    <time.h>
#include    "IPlugin.h"

int main( )
{
    void * lib_handle;
    char * error;

    while( true )
    {
        std::cout << "Updating the .so" << std::endl;

        lib_handle = dlopen( "./test1.so", RTLD_LAZY );
        if ( ! lib_handle ) 
        {
            std::cerr << dlerror( ) << std::endl;
            return 1;
        }

        create_t fn_create = ( create_t ) dlsym( lib_handle, "create" );

        if ( ( error = dlerror( ) ) != NULL )  
        {
            std::cerr << error << std::endl;
            return 1;
        }

        IPlugin * ik = fn_create( );
        ik->exec( );

        destroy_t fn_destroy = ( destroy_t ) dlsym( lib_handle, "destroy" );

        fn_destroy( ik );

        std::cout << "Waiting 5 seconds before unloading..." << std::endl;

        sleep( 5 );
        dlclose( lib_handle );
    }

    return 0;
}

IPlugin.h:

class IPlugin
{
public:
    IPlugin( ) { } 
    virtual ~IPlugin( ) { }
    virtual void exec( ) = 0;
};

typedef IPlugin * ( * create_t  )( );
typedef void ( * destroy_t  )( IPlugin * );

Test1.h:

#include    <iostream>
#include    "IPlugin.h"

class Test1 : public IPlugin
{
public:
    Test1( );
    virtual ~Test1( );

    void exec( );
};

Test1.cpp:

#include "Test1.h"

Test1::Test1( ) { }

Test1::~Test1( ) { }

void Test1::exec( )
{
    std::cout << "void Test1::exec( )" << std::endl;
}

extern "C"
IPlugin * create( )
{
    return new Test1( );
}

extern "C"
void destroy( IPlugin * plugin )
{
    if( plugin != NULL )
    {
        delete plugin;
    }
}

To compile:

g++ main.cpp -o main -ldl
g++ -shared -fPIC Test1.cpp -o plugin/test1.so

The problem occurs when for example I change something on the Test1::exec method (changing the string to be printed or commenting the line) and while the main program sleeps I copy the new test1.so to main running directory (cp). If I use the move command (mv), no error occurs. What makes the difference between using cp or mv? Is there any way to solve this problem or to do that using cp?

I'm using Fedora 14 with g++ (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4).

Thanks in advance.

Upvotes: 4

Views: 1194

Answers (2)

Archie
Archie

Reputation: 1

Try this:

extern "C"
void destroy( IPlugin * plugin )
{
    if( plugin != NULL && dynamic_cast<Test1*>(plugin))
    {
        delete static_cast<Test1*>(plugin);
    }
}

Upvotes: -1

NPE
NPE

Reputation: 500157

The difference between cp and mv that is pertinent to this question is as follows:

  1. cp opens the destination file and writes the new contents into it. It therefore replaces the old contents with the new contents.
  2. mv doesn't touch the contents of the original file. Instead, it makes the directory entry point to the new file.

This turns out to be important. While the application is running, the OS keeps open handles to the executable and the shared objects. When it needs to consult one of the these files, it uses the relevant handle to access the file's contents.

  1. If you've used cp, the contents has now been corrupted, so anything can happen (a segfault is a pretty likely outcome).
  2. If you've used mv, the open file handle still refers to the original file, which continues to exist on disk even though there's no longer a directory entry for it.

If you've used mv to replace the shared object, you should be able to dlclose the old one and dlopen the new one. However, this is not something that I've done or would recommend.

Upvotes: 5

Related Questions