Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

consider the following code for a dynamic loaded library built with g++-4.7 on linux, -fPIC and linked with -rdynamic option:

typedef std::vector< void* > cbRegister_t;

struct Wrapper
{
    cbRegister_t instance;
    Wrapper() : instance() { HDebugLog("Wrapper CTOR!");}
    ~Wrapper() { HDebugLog("Wrapper DESTRUCTOR!"); }
};
inline cbRegister_t& getLibraryUnregisterMap()
{
    static Wrapper unregisterLibraryMap;
    HDebugLog("getLibraryUnregisterMap: we have " <<unregisterLibraryMap.instance.size() << " elements. the address of the map is " << &unregisterLibraryMap.instance);
    return unregisterLibraryMap.instance;
}

void registerLibrary(void* p)
{
  auto& map = getLibraryUnregisterMap();
  map.push_back(p);
}

void unregisterLibrary()
{
  auto& map = getLibraryUnregisterMap();
}

void __attribute__ ((constructor)) library_init()
{
  static SomeData cbContainer;
  HDebugLog("Library constructor: address of static cbContainer is: " << &cbContainer );
  registerLibrary( &cbContainer);
} 
void __attribute__ ((destructor)) library_fini()
{ unregisterLibrary(); }

This code loads fine from a client with dlopen and the RTLD_NOW flag. The library constructor is called. The problem happens when i call dlclose on the handle. It gives status zero, meaning that it was successful. But the library destructor library_fini is not called. dlopen is called on a single place so the reference count should not be a problem, but in order to be absolutely sure that there are really no references dangling i tried doing the dlclose several times:

int result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: closing library: " << libraryPath);
HDebugLog("Library::dynamicLibraryClose: dlclose 1 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 2 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 3 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 4 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 5 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 6 failed with error: " << result << " => ");
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 7 failed with error: " << result << " => ");
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 8 failed with error: " << result << " => " );
HAssertMsg( !libraryPath.empty(), "library path is not set");
HAssertMsg( 0 == dlopen(libraryPath.c_str(), RTLD_NOLOAD) , "library is still loaded");

All those debug logs show that each time i call dlclose the status result is zero. Success!

The last assertion, which doesn't fail, confirms that the library is not resident anymore . But at this point library_fini has not been called yet!

This behavior is definitely a bug, either on gcc or ld.so (or whatever is using linux/ubuntu to dynamically load libraries these days). This behavior clearly is not what the standard says, because library destructors should be guaranteed to be run if the reference count is zero before dlclose returns. library destructors happen to be run after the Wrapper.instance is destroyed, which renders the library destructor completely useless, as is not able to do any finalization of the data

share|improve this question
add comment

1 Answer

up vote 2 down vote accepted

This works with glibc 2.17, gcc 4.8.0 or icc 13.1 or icc 12.1:

icc -std=c99 -nostdlib -shared test.c -o /tmp/test

/* test.c */
#include 
#include 

int __attribute__((constructor)) x_init(void)
{
    puts("init() works");
    return 0;
}

int __attribute__((destructor)) x_fini(void)
{
    puts("fini() works");
    return 0;
}

against:

#include <dlfcn.h>

int
main(void)
{
    void *foo = dlopen("/tmp/test", RTLD_LAZY);

    if (dlclose(foo) < 0) {
        return 1;
    }
    return 0;
}

Also tested with glibc 2.10, glibc 2.12. And all RTLD_* flags.

Edit:
Using an actual Ubuntu system (gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2), GNU C Library (Ubuntu EGLIBC 2.15-0ubuntu20), I must say that above code works there too. So maybe after all it's not about the compiler and/or glibc.

share|improve this answer
    
i suggest you to give a closer look to the documentation, just below the dlclose() section: "manpages.ubuntu.com/manpages/hardy/man3/dlopen.3.html "libraries should export routines using the __attribute__((constructor)) and __attribute__((destructor)) function attributes. See the gcc info pages for information on these, Constructor routines are executed before dlopen() returns, and destructor routines are executed before dlclose() returns." –  lurscher Apr 3 '13 at 4:01
    
That must be a GNU extension, no such thing is mentioned in POSIX. _fini is a recognised special symbol though. –  hroptatyr Apr 3 '13 at 4:04
1  
Also, I can confirm, it must be a bug, I tried your code with a dummy example, and it works (gcc 4.9.0, glibc 2.17) –  hroptatyr Apr 3 '13 at 4:06
    
thanks @hroptatyr, that gives me hope and strength for the road ahead (of building and installing a new gcc version) –  lurscher Apr 3 '13 at 4:07
    
actually, what you mean it works? that the destructor is called before dlclose returns? –  lurscher Apr 3 '13 at 4:08
show 9 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.