Alternatives to dlsym() and dlopen() in C++
Asked Answered
B

5

20

I have an application a part of which uses shared libraries. These libraries are linked at compile time.
At Runtime the loader expects the shared object to be in the LD_LIBRARY_PATH , if not found the entire application crashes with error "unable to load shared libraries".Note that there is no guarantee that client would be having the library, in that case I want the application to leave a suitable error message also the independent part should work correctly.

For this purpose I am using dlsym() and dlopen() to use the API in the shared library. The problem with this is if I have a lot of functions in the API, i have to access them Individually using dlsym() and ptrs which in my case are leading to memory corruption and code crashes.

Are there any alternatives for this?

Begum answered 1/7, 2009 at 5:5 Comment(0)
A
33

The common solution to your problem is to declare a table of function pointers, to do a single dlsym() to find it, and then call all the other functions through a pointer to that table. Example (untested):

// libfoo.h
struct APIs {
   void  (*api1)(void);
   void *(*api2)(int);
   long  (*api3)(int, void *);
};

// libfoo.cc
void fn1(void) { ... }
void *fn2(int) { ... }
long fn3(int, void *) { ... }

APIs api_table = { fn1, fn2, fn3 };


// client.cc
#include "libfoo.h"
...
  void *foo_handle = dlopen("libfoo.so", RTLD_LAZY);
  if (!foo_handle) {
     return false;            // library not present
  }
  APIs *table = dlsym(foo_handle, "api_table");
  table->api1();              // calls fn1
  void *p = table->api2(42);  // calls fn2
  long x = table->api3(1, p); // calls fn3

P.S. Accessing your API functions individually using dlsym and pointers does not in itself lead to memory corruption and crashes. Most likely you just have bugs.

EDIT:
You can use this exact same technique with a 3rd-party library. Create a libdrmaa_wrapper.so and put the api_table into it. Link the wrapper directly against libdrmaa.so.

In the main executable, dlopen("libdrmaa_wrapper.so", RTLD_NOW). This dlopen will succeed if (and only if) libdrmaa.so is present at runtime and provides all API functions you used in the api_table. If it does succeed, a single dlsym call will give you access to the entire API.

Andesine answered 1/7, 2009 at 7:11 Comment(5)
and how do I call dlopen() to get the foo_handle? I mean will it load the API shared object say libAPI.so automatically??Begum
It is dlopen() actually for opening the library... :).. nyways I got ur point.. but this works only if there is a APIs structure in the library.. which u can't expect with a 3rd party library..I am trying to use sungrid api libdrmaa.so..Begum
Instead of getting a pointer to a table, you could instead get a pointer to a function which when called returns the pointer to the table. This allows the plugin to initialize itself before any other function is called, and even create the table dynamically.Syncope
For example, PKCS#11 defines C_GetFunctionList for this purpose. Here's an example of its use: https://mcmap.net/q/341060/-pkcs-11-c_getfunctionlist-in-a-dll (It's common practice to load PKCS#11 libraries with dlopen, because the application writer won't know which cryptographic tokens the user wants to use. They might even use several different PKCS#11 libraries at once - they each have a C_GetFunctionList function, and the dlopen handle passed to dlsym says which library you want the function for.)Sassoon
It's possible to hide function pointer table in a stub static library, similarly to what Windows does. This makes usage more transparent. Such libraries can be autogenerated (see Implib.so for complete example).Tankard
A
2

You can wrap your application with another one which first checks for all the required libraries, and if something is missing it errors out nicely, but if everything is allright it execs the real application.

Adames answered 1/7, 2009 at 5:17 Comment(2)
It is quite common for an application to have a script that set up the LD_LIBRARY_PATH before starting the application.Wilton
The problem is not that.. the problem is if the library is not present with client that module shouldn't work, the rest of the code should work correctly.. but in this case if the library is not found the code fails to execute.Begum
C
2

Use below type of code

Class DynLib
{
    /* All your functions */
    void fun1() {};
    void fun2() {};
    .
    .
    .
}

DynLib* getDynLibPointer()
{
    DynLib* x = new Dynlib;
    return x;
}

use dlopen() for loading this library at runtime. and use dlsym() and call getDynLibPointer() which returns DynLib object. from this object you can access all your functions jst as obj.fun1().....

This is ofcource a C++ style of struct method proposed earlier.

Cryptography answered 4/5, 2013 at 6:58 Comment(0)
T
1

You are probably looking for some form of delay library load on Linux. It's not available out-of-the-box but you can easily mimic it by creating a small static stub library that would try to dlopen needed library on first call to any of it's functions (emitting diagnostic message and terminating if dlopen failed) and then forwarding all calls to it.

Such stub libraries can be written by hand, generated by project/library-specific script or generated by universal tool Implib.so:

$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...
Tankard answered 16/2, 2018 at 9:55 Comment(0)
S
-1

Your problem is that the resolution of unresolved symbols is done very early on - on Linux I believe the data symbols are resolved at process startup, and the function symbols are done lazily. Therefore depending on what symbols you have unresolved, and on what sort of static initialization you have going on - you may not get a chance to get in with your code.

My suggestion would be to have a wrapper application that traps the return code/error string "unable to load shared libraries", and then converts this into something more meaningful. If this is generic, it will not need to be updated every time you add a new shared library.

Alternatively you could have your wrapper script run ldd and then parse the output, ldd will report all libraries that are not found for your particular application.

Symbolics answered 1/7, 2009 at 6:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.