0

I need a help about dynamically loading a library on C++ code.

I have several libraries having same APIs ( ex: libtest_10.so and libtest_20.so).

Also I know that I have to link a shared library on build time even when I load the library with dlopen() on run-time.

But in my case, I linked the libtest_1.so and libtest_2.so to an test executable on CMakeLists.txt.

PROJECT(test_main)
ADD_EXECUTABLE(${PROJECT_NAME} main.cpp)
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wall -Wextra -rdynamic")
FIND_LIBRARY(LIB_TEST1 NAME test_1 HINTS ../prebuilts)
MESSAGE("libtest_1.so :" ${LIB_TEST1})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC ${LIB_TEST1})
FIND_LIBRARY(LIB_TEST2 NAME test_2 HINTS ../prebuilts)
MESSAGE("libtest_2.so :" ${LIB_TEST2})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC ${LIB_TEST2})

and load the library libtest_2.so with dlopen as below.

#include <dlfcn.h>

void* test_lib_handle = dlopen("/usr/lib/libtest_2.so", RTLD_NOW);
(...)

But when I run the executable, I could see the log from not 'libtest_2.so' but 'libtest_1.so'.

How can I load the library I want to pick ? anyone help me about the compile options or any other proper method?

  • 2
    "I know that I have to link a shared library on build time even when I load the library with dlopen() on run-time." - No, you shouldn't link with the library which you want to load with `dlopen`. – Tsyvarev May 30 '22 at 09:31
  • But As fas as I know, C++(=class) library needs build time linking even when dlopen is used. If I don't link the libraries on build time, 'undefined reference' error occurred. – Hyukjoon Jang May 30 '22 at 09:43
  • 4
    If you load the library with `dlopen`, then you should NOT use its functions (and other symbols) **directly** in the executable. Instead, you should use `dlsym` for obtain **address** of the function in the library, and call the function using that address. `man dlopen` has an example of using that approach. Have you checked this example? – Tsyvarev May 30 '22 at 09:50
  • Okay. I understand your meaning. And let me clarify one more thing. If I want to call an API in class. How can I call it by dlsym(). class A { int doTest(); }; With the above example, do I have to redefine the doTest() as extern "C" ? If then, How can I create any class from the dlopened library. – Hyukjoon Jang May 30 '22 at 10:15
  • 1
    @HyukjoonJang: Realistically speaking, you can't `dlsym` every class method. Creating the class isn't hard: `extern "C" A* newA() { return new A{ }; }` - that's a non-class function which returns a class pointer. But you also have to write a wrapper for `delete` and any other C++ class method you want to call. The problem is really GCC; Visual C++ _can_ do this (delay-loaded DLL). – MSalters May 30 '22 at 10:28
  • If you're willing to use external libraries then [`boost.dll`](https://www.boost.org/doc/libs/master/doc/html/boost_dll.html) can do a lot of the 'heavy lifting' for you. – G.M. May 30 '22 at 10:37
  • @HyukjoonJang: [That answer](https://stackoverflow.com/a/8811214/3440745) provides a mini example which deminstrates the method described by MSalters. – Tsyvarev May 30 '22 at 10:39
  • Hmm. In my case, I use linux enviroment instead of window. So dll case doesn't help me. Actually, I already know the create/destroy the class by such extern C factory method. My real question is how to call the every public method from the dlopened class. As your comment, I have to call all the method by dlsym(). It looks a little bit ugly to me. But it seems that there are no other method. Thanks for your comment. – Hyukjoon Jang May 30 '22 at 10:41
  • AFAIK Boost C++ is cross-platform. It would be strange if boost.dll isn't usable on Linux. I think it's a good idea to look at Boost.DLL https://www.boost.org/doc/libs/1_79_0/doc/html/boost_dll.html and not let the name mislead you. You can build Boost.DLL for Linux and your code will cross compile on Linux and Windows. – James Smith Jun 05 '22 at 17:58
  • I've used dlopen() and dlsym() in the past, and would also suggest using the Factory design pattern https://www.geeksforgeeks.org/creational-pattern-in-c there's many other examples online. Using an interface and a factory will make your code much more robust, and code test-ability is another benefit. My suggestions are motivated from using GoogleTest in projects to mock C++ interfaces. – James Smith Jun 05 '22 at 17:59
  • When using dlopen() it's not necessary to specify the library in the project's Makefile. You do that when you want to link after the build process using the GNU linker on Linux. To access the library functions use dlsym() after you load/open the library. – James Smith Jun 05 '22 at 18:00
  • Just a comment on your statement in quotes: "My real question is how to call the every public method from the dlopened class. As your comment, I have to call all the method by dlsym(). It looks a little bit ugly to me." Again with the Factory pattern you can also setup a pure virtual interface functions for your public methods and export one class interface that contains all your public class functions. For this you'll only need one call to dlsym() to get the class interface. In addition to this I believe Boost.DLL simplifies the whole process and has a much cleaner interface. – James Smith Jun 05 '22 at 18:21

0 Answers0