0
votes

I am writing a function to check if a .so is loaded under linux with the following code:

#include <iostream>
#include <dlfcn.h>
#include <unistd.h>
using namespace std;

bool isLibraryLoaded(const string& libPath)
{
   return (nullptr != dlopen(libPath.c_str(), RTLD_NOW | RTLD_NOLOAD));
}

int main(int argc, char** argv)
{
   const string libPath = "/path/to/library.so";

   cout << "loaded: " << isLibraryLoaded(libPath) << endl;

   sleep(8); // first execution of lsof

   // load lib
   void* handle = dlopen(libPath.c_str(), RTLD_NOW);
   cout << "handle: " << handle << endl;
   if (nullptr != handle)
   {
      cout << "loaded: " << isLibraryLoaded(libPath) << endl;
   }
   else
   {
      cout << "error: " << dlerror() << endl;
   }

   sleep(8); // second execution of lsof

   // unload lib
   if (0 == dlclose(handle))
   {
      cout << "loaded: " << isLibraryLoaded(libPath) << endl;
   }
   else
   {
      cout << "error: " << dlerror() << endl;
   }

   sleep(8); // third execution of lsof

   return 0;
}

output of the program:

loaded: 0
handle: 0x6420b0
loaded: 1
loaded: 1

first execution of lsof:

# lsof /path/to/library.so
(return -1)

second execution of lsof:

# lsof /path/to/library.so
COMMAND  PID   USER  FD   TYPE DEVICE  SIZE/OFF    NODE NAME
test    3240     me mem    REG   8,33 333493040 5242954 /path/to/library.so

third execution of lsof:

# lsof /path/to/library.so
COMMAND  PID   USER  FD   TYPE DEVICE  SIZE/OFF    NODE NAME
test    3240     me mem    REG   8,33 333493040 5242954 /path/to/library.so

The program is very simple, the library should be loaded and unloaded again. But it is NOT. I am very confusing and have the following questions:

  1. Is there something wrong with my code?
  2. Why does dlclose() not really unload the library?
  3. What could be the potential reasons?
  4. Is there any way for C/C++ to detect if a library is loaded/unloaded?

My environment:

g++ (Ubuntu 6.4.0-17ubuntu1~16.04) 6.4.0 20180424
1
Perhaps RTLD_NOLOAD is not being honoured. It's documented as needing glibc 2.2 - do you have that? - Paul Sanders

1 Answers

4
votes

This code:

bool isLibraryLoaded(const string& libPath)
{
   return (nullptr != dlopen(libPath.c_str(), RTLD_NOW | RTLD_NOLOAD));
}

is wrong because IFF the library is already loaded (i.e. if the function returns true), then the handle reference count is incremented by dlopen. You need a matching dlclose to undo that increment.

Here is the fixed version:

bool isLibraryLoaded(const string& libPath)
{
   void *h = dlopen(libPath.c_str(), RTLD_NOW | RTLD_NOLOAD);
   if (h != nullptr) {
     dlclose(h);
     return true;
   }
   return false;
}

With that fix:

$ ./a.out
loaded: 0
handle: 0x19382a0
loaded: 1
loaded: 0