Dlclose actually does not unload the shared object, no matter how many times it is called

My program uses dlopen to load a shared object, and then dlclose to unload it. Sometimes this shared object is loaded again. I noticed that the static variables were not reinitialized (which is very important for my program), so I added a test ( dlopen with RTLD_NOLOAD ) after dlclose to see if the library was really unloaded. Of course, he was still in memory.

Then I tried calling dlclose several times until the library was unloaded, but I got an infinite loop. This is the code I use to check if the library has been unloaded:

 dlclose(handles[name]); do { void *handle = dlopen(filenames[name], RTLD_NOW | RTLD_NOLOAD); if (!handle) break; dlclose(handle); } while (true); 

My question is: what are the possible reasons why my shared object is not unloaded after dlclose , given that my dlopen calls are the only places where it loads. Can you suggest a course of action to track the source of the problem? In addition, why dlclose do not affect, they each decrease the reference count, right?

EDIT: It just turned out that this only happens when compiling with gcc . With clang, everything is fine.

+12
c ++ c gcc linux dlopen
source share
4 answers

The POSIX standard does not actually require dlclose to unload a library from the address space:

Although the dlclose () operation from the address space is not required to remove structures, none of the implementations are allowed to do this.

Source: Open Group Issue 6 Core Specifications

This means that an invalid dlclose descriptor descriptor is not required at all.

Sometimes unloading is also delayed by the system, it simply puts the library as β€œdeleted” and actually performs this operation at a later time (for efficiency or because it simply will not be possible to perform this operation right now). However, if you call dlopen again before it has ever been executed, the flag is cleared and the reloaded library is reused.

In some cases, the system knows for sure that some library symbols are still in use, in which case it will not unload it from the address space in order to avoid dangling pointers. In some cases, the system does not know for sure that they are used, but it also cannot say for sure that it is not, it is better to be safe than sorry, it simply will never delete this library from memory in this case.

There are other more obscure cases depending on the type of operating system, and also often on the version. For example. A common problem with Linux is that if you created a library that uses the STB_GNU_UNIQUE characters, this library is marked as "not unloaded" and therefore simply will not be unloaded. See here , here ( DF_1_NODELETE means not DF_1_NODELETE ) and. Thus, it may also depend on what symbols or type of symbol the compiler generates. Try running readelf -Ws in your library and find objects with UNIQUE tags.

In general, you cannot rely on dlclose to work as you might expect. In practice, I saw that he "failed" more often than "succeeded" in the last ten years (well, he never lost, he just did not unload the library from memory, but he worked in accordance with the standards).

+14
source share

This is not the answer to all your questions, but it is a solution that can help you avoid problems with dlclose . This question tells you how to influence behavior when loading shared libraries again: you can use the -fno-gnu-unique compiler flag.

From g++ pages for gcc / g++ :

-fno-wildebeest-unique

On systems with the recent GNU assembler and C library, the C ++ compiler uses the "STB_GNU_UNIQUE" binding to make sure that the definitions of the static data elements of the template and static local variables in the built-in functions are unique even with "RTLD_LOCAL"; this is necessary to avoid problems with the library used by two different RTLD_LOCAL plugins, depending on the definition in one of them and, therefore, not agreeing with the other regarding symbol binding. But this leads to the fact that "dlclose" is ignored for the affected DSO; if your program uses DSO reinitialization with "dlclose" and "dlopen", you can use -fno-gnu-unique.

-fno-gnu-unique or -fno-gnu-unique by default or not depends on how GCC is configured: --disable-gnu-unique-object enables this flag by default, --enable-gnu-unique-object disables it.

+4
source share

There are many quirks in dynamically loading a library. Reliance on the OS to initialize static variables is fraught with problems. You are much better off avoiding it at all or using a downloader plugin that handles all special cases for you.

I recommend you check out the glib modules . Glib provides a platform-independent way to load dynamic libraries. You can use these callbacks:

They can handle the allocation and release of any resources. Instead of relying on the OS to distribute statics reliably, you can dynamically distribute what you need.

All you have to do is define these functions in a dynamic library and then load and unload them with:

0
source share

On Windows, use the equivalent using ifdef with WIN or LINUX:

  • LoadLibrary() = dlopen()
  • FreeLibrary() = dlclose()
  • GetProcAddress() = dlsym()

 void *handle; double (*cosine)(double); char *error; handle = dlopen ("/lib/libm.so.6", RTLD_LAZY); if (!handle) { fputs (dlerror(), stderr); exit(1); } cosine = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } printf ("%f\n", (*cosine)(2.0)); dlclose(handle); 
0
source share

All Articles