1

How can I conditionality load and use a shared library if it is installed, but still run without that functionality if it is not there? more specifically, can I do that without using that library as a plugin? I prefer failing during build time rather than runtime if possible.

if I build it with the library flag -lfoo, it builds. But then it fails to run it libfoo.so.2 is not installed in the target system. But if I don't add the library flag it fails in linking.

here is some code snipets for better picture.

myAdapter.cpp

#include "newlib/foo.h" //this is from the shared library
...

bool myAdapter::isAvailable()
{
  handle_ = dlopen("libfoo.so.2", RTLD_LAZY);
  if (!handle_)
  {
      return false;
  }

  return true;
}

...

bool myAdapter::init()
{  
  if (!isAvailable())
  {
    return false;
  }

  isInitilized = false;
  isConnected = false;
  if (!fooInit()) // shared library function
  {
    fooCleanup(); //shared library function
    return false;
  }

  // these are my private functions but they call shared library functions.
  if (!createUserParams_() || !setCallbacks_() || !createContext_() || !connect_())
  {
    return false;
  }
  
  return true;
}
...

myApp.cpp

#include "myAdapter.h"
...
int main()
{
...
foo = new myAdapter();
if (!foo.init())
{
cout << "Foo function is not available;
isFooAvailable = false;
}
}

...

}
asisas07
  • 91
  • 5
  • 1
    `prefer failing during build time rather than runtime if possible. if I build it with the library flag -lfoo, it builds. But then it fails to run it libfoo.so.2 is not installed in the target system. ` how should it fail while building if you don’t know if the library is installed on the target system? – t.niese May 20 '21 at 16:11
  • I already added isAvailable() function in init(). So, it won't go any further at runtime. I just wanted to check as much as possible during the build time. – asisas07 May 20 '21 at 16:26
  • can't edit my answer anymore, so I add here. I would like to check if I am using the library APIs correctly. Also, the shared library has bunch of internal structs and enums such as for error codes etc. I also would like to leverage those. – asisas07 May 20 '21 at 16:36
  • Does this answer your question? [How to make a shared library delay loaded on Linux](https://stackoverflow.com/questions/23403848/how-to-make-a-shared-library-delay-loaded-on-linux). That thread mentions a helper script project to generate stubs for the library functions which will load the library on first access - https://github.com/yugr/Implib.so – dewaffled May 20 '21 at 16:47

1 Answers1

2

First, remove any linkage you have to libfoo.a, libfoo.sa, or any -lfoo parameter in your build. Everything is to be dynamically loaded.

Second, after dlopen succeeds, you need to get the address, via dlsym, of all the functions you need to use - and call through into those.

Third, instead of making a direct call to your functions, you call through to them via the pointer you loaded via dlsym.

Finally, your isAvailable has a side effect (loading the library) and will re-attempt every time. You just need to load the library once.

Here's an improved version of your code. I made some hypothetical assumptions of what your params to those library functions would be. Change it to meet your needs:

class myAdapter
{
    void* handle_;

    typedef bool (*CREATE_USER_PARAMS)(UserParams*);
    typedef int (*CONNECT)(int);

    CREATE_USER_PARAMS createUserParams_;
    CONNECT connect_; 

    myAdapter() : 
        handle_(),
        createUserParams_(),
        connect_()
    {
       handle_ = dlopen("libfoo.so.2", RTLD_NOW);
       if (handle_)
       {
           createUserParams = (CREATE_USER_PARAMS)dlsym(handle_, "createUserParams");
           connect_ = (CONNECT)dlsym(handle_, "connect");
           if (!createUserParams_ || !connect_)
           {
              // error
              dlclose(handle_);
              handle_ = nullptr;
              connect = nullptr_;
              createUserParams_ = nullptr;
           }
       }
    }  

    ~myAdapter()
    {
       if (handle_) 
       {
           dlclose(handle_);
           handle = nullptr;
       }
    }

    bool createUserParams(UserParams* params)
    {
        if (createUserParams_ != nullptr)
        {
            return createUserParams_(params);
        }
        else
        {
           return false;
        }
    }

    int connect(int sock)
    {
        if (connect_ != nullptr)
        {
            return (connect_(sock));
        }
        else
        {
           return -1;
        }
    }
};
selbie
  • 100,020
  • 15
  • 103
  • 173
  • Thank you for the detailed answer, I appreciate it. But this is as mentioned in my question, using the shared library as a plugin. and this will fail on runtime if any of the API signatures changes, for example, if they change the init to return int instead of bool, etc. I would like to catch those errors during build time if possible. Maybe with some linking flags etc. Again, I don't know if this is possible, that is why I am asking. thanks again. – asisas07 May 20 '21 at 17:54