2

I have dynamic library created as follows

cat myfile.cc

struct Tcl_Interp;
extern "C" int My_Init(Tcl_Interp *) { return 0; }

1) complile the cc file

g++ -fPIC -c myfile.cc

2) Creating a shared library

g++ -static-libstdc++ -static-libgcc -shared -o libmy.so myfile.o -L/tools/linux64/qt-4.6.0/lib -lQtCore -lQtGui

3) load the library from a TCL proc then I give command

tclsh and given command % load libmy.so

is there any C++ function/ Qt equivalent to load that can load the shared library on demand from another C++ function.

My requirement is to load the dynamic library on run time inside the function and then use the qt functions directly

1) load the qt shared libraries (for lib1.so) 2) call directly the functions without any call for resolve

For example we have dopen, but for that for each function call we have to call dsym. My requirement is only call for shared library then directly call those functions.

TechEnthusiast
  • 273
  • 4
  • 18
  • 1
    Did you take a look at [QLibrary](http://doc.qt.io/qt-5/qlibrary.html)? – rocambille Sep 02 '16 at 11:28
  • As I understand I need to resolve all the calls to QT methods that does not seem to be cleaner . Can u suggest some cleaner approach where I have only one few lines of code to load the shared library at runtime – TechEnthusiast Sep 02 '16 at 13:30
  • Ok I didn't get that point when reading your question. If you're loading the library at runtime using dlopen, AFAIK there is no way to go without dllsym. The other way is to link to the library at compile time. – rocambille Sep 02 '16 at 13:37
  • This question is incomplete without mentioning that you're trying to demand-load C++ libraries with a naked C++ API, namely qtcore. – Kuba hasn't forgotten Monica Sep 06 '16 at 20:51

2 Answers2

3

You want boilerplate-less delay loading. On Windows, MSVC implements delay loading by emitting a stub that resolves the function through a function pointer. You can do the same. First, let's observe that function pointers and functions are interchangeable if all you do is call them. The syntax for invoking a function or a function pointer is the same:

void foo_impl() {}
void (*foo)() = foo_impl;

int main() {
  foo_impl();
  foo();
}

The idea is to set the function pointer initially to a thunk that will resolve the real function at runtime:

extern void (*foo)();
void foo_thunk() {
  foo = QLibrary::resolve("libmylib", "foo");
  if (!foo) abort();
  return foo();
}
void (*foo)() = foo_thunk;

int main() {
  foo(); // calls foo_thunk to resolve foo and calls foo from libmylib
  foo(); // calls foo from libmylib
}

When you first call foo, it will really call foo_thunk, resolve the function address, and call real foo implementation.

To do this, you can split the library into two libraries:

  1. The library implementation. It is unaware of demand-loading.
  2. A demand-load stub.

The executable will link to the demand-load stub library; that is either static or dynamic. The demand-load stub will automatically resolve the symbols at runtime and call into the implementation.

If you're clever, you can design the header for the implementation such that the header itself can be used to generate all the stubs without having to enter their details twice.

Complete Example

Everything follows, it's also available from https://github.com/KubaO/stackoverflown/tree/master/questions/demand-load-39291032

The top-level project consists of:

  • lib1 - the dynamic library
  • lib1_demand - the static demand-load thunk for lib1
  • main - the application that uses lib1_demand

demand-load-39291032.pro

TEMPLATE = subdirs
SUBDIRS = lib1 lib1_demand main
main.depends = lib1_demand
lib1_demand.depends = lib1

We can factor out the cleverness into a separate header. This header allows us to define the library interface so that the thunks can be automatically generated.

The heavy use of preprocessor and a somewhat redundant syntax is needed due to limitations of C. If you wanted to implement this for C++ only, there'd be no need to repeat the argument list.

demand_load.h

// Configuration macros:
// DEMAND_NAME - must be set to a unique identifier of the library
// DEMAND_LOAD - if defined, the functions are declared as function pointers, **or**
// DEMAND_BUILD - if defined, the thunks and function pointers are defined

#if defined(DEMAND_FUN)
#error Multiple inclusion of demand_load.h without undefining DEMAND_FUN first.
#endif

#if !defined(DEMAND_NAME)
#error DEMAND_NAME must be defined
#endif

#if defined(DEMAND_LOAD)
// Interface via a function pointer
#define DEMAND_FUN(ret,name,args,arg_call) \
    extern ret (*name)args;

#elif defined(DEMAND_BUILD)
// Implementation of the demand loader stub
#ifndef DEMAND_CAT
#define DEMAND_CAT_(x,y) x##y
#define DEMAND_CAT(x,y) DEMAND_CAT_(x,y)
#endif
void (* DEMAND_CAT(resolve_,DEMAND_NAME)(const char *))();
#if defined(__cplusplus)
#define DEMAND_FUN(ret,name,args,arg_call) \
    extern ret (*name)args; \
    ret name##_thunk args { \
        name = reinterpret_cast<decltype(name)>(DEMAND_CAT(resolve_,DEMAND_NAME)(#name)); \
        return name arg_call; \
    }\
    ret (*name)args = name##_thunk;
#else
#define DEMAND_FUN(ret,name,args,arg_call) \
    extern ret (*name)args; \
    ret name##_impl args { \
        name = (void*)DEMAND_CAT(resolve_,DEMAND_NAME)(#name); \
        name arg_call; \
    }\
    ret (*name)args = name##_impl;
#endif // __cplusplus

#else
// Interface via a function
#define DEMAND_FUN(ret,name,args,arg_call) \
    ret name args;
#endif

Then, the dynamic library itself:

lib1/lib1.pro

TEMPLATE = lib
SOURCES = lib1.c
HEADERS = lib1.h
INCLUDEPATH += ..
DEPENDPATH += ..

Instead of declaring the functions directly, we'll use DEMAND_FUN from demand_load.h. If DEMAND_LOAD_LIB1 is defined when the header is included, it will offer a demand-load interface to the library. If DEMAND_BUILD is defined, it'll define the demand-load thunks. If neither is defined, it will offer a normal interface.

We take care to undefine the implementation-specific macros so that the global namespace is not polluted. We can then include multiple libraries the project, each one individually selectable between demand- and non-demand loading.

lib1/lib1.h

#ifndef LIB_H
#define LIB_H

#ifdef __cplusplus
extern "C" {
#endif

#define DEMAND_NAME LIB1
#ifdef DEMAND_LOAD_LIB1
#define DEMAND_LOAD
#endif
#include "demand_load.h"
#undef DEMAND_LOAD

DEMAND_FUN(int, My_Add, (int i, int j), (i,j))
DEMAND_FUN(int, My_Subtract, (int i, int j), (i,j))

#undef DEMAND_FUN
#undef DEMAND_NAME

#ifdef __cplusplus
}
#endif

#endif

The implementation is uncontroversial:

lib1/lib1.c

#include "lib1.h"

int My_Add(int i, int j) {
    return i+j;
}

int My_Subtract(int i, int j) {
    return i-j;
}

For the user of such a library, demand loading is reduced to defining one macro and using the thunk library lib1_demand instead of the dynamic library lib1.

main/main.pro

if (true) {
   # Use demand-loaded lib1
   DEFINES += DEMAND_LOAD_LIB1
   LIBS += -L../lib1_demand -llib1_demand
} else {
   # Use direct-loaded lib1
   LIBS += -L../lib1 -llib1
}
QT = core
CONFIG += console c++11
CONFIG -= app_bundle
TARGET = demand-load-39291032
TEMPLATE = app
INCLUDEPATH += ..
DEPENDPATH += ..
SOURCES = main.cpp

main/main.cpp

#include "lib1/lib1.h"
#include <QtCore>

int main() {
    auto a = My_Add(1, 2);
    Q_ASSERT(a == 3);
    auto b = My_Add(3, 4);
    Q_ASSERT(b == 7);
    auto c = My_Subtract(5, 7);
    Q_ASSERT(c == -2);
}

Finally, the implementation of the thunk. Here we have a choice between using dlopen+dlsym or QLibrary. For simplicity, I opted for the latter:

lib1_demand/lib1_demand.pro

QT = core
TEMPLATE = lib
CONFIG += staticlib
INCLUDEPATH += ..
DEPENDPATH += ..

SOURCES = lib1_demand.cpp
HEADERS = ../demand_load.h

lib1_demand/lib1_demand.cpp

#define DEMAND_BUILD
#include "lib1/lib1.h"
#include <QLibrary>

void (* resolve_LIB1(const char * name))() {
    auto f = QLibrary::resolve("../lib1/liblib1", name);
    return f;
}
Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • The shared library is my case m which I have to load on demand on runtime is libQtCore.so and libQtGui.so In my case the functions calls for Qt library code will be around 80000 , so as I understand I need to have DEMAND_FUNC for each every call of QT functions , this these to me not practically feasible. If there is any cleaner way , for my problem – TechEnthusiast Sep 03 '16 at 06:53
  • Using C++ libraries with naked C++ APIs using `dlsym`/`QLibrary` is not possible at all, because you can't have method pointers to constructors/destructors. Why do you think you need to demand-load Qt? You need to revise your design. If you really think you *do* need it, then you'll have to do some hard work. You'll have to generate thunks in assembly, automatically, based on the object files you're linking with. You'll also have to deal with non-callable symbols - I'm sure Qt has a global or two, and these can only be thunked by leveraging PIC. You've got some real work ahead of you. – Kuba hasn't forgotten Monica Sep 06 '16 at 20:51
2

Quite apart from the process of loading a library into your C++ code (which Kuber Ober's answer covers just fine) the code that you are loading is wrong; even if you manage to load it, your code will crash! This is because you have a variable of type Tcl_Interp at file scope; that's wrong use of the Tcl library. Instead, the library provides only one way to obtain a handle to an interpreter context, Tcl_CreateInterp() (and a few other functions that are wrappers round it), and that returns a Tcl_Interp* that has already been initialised correctly. (Strictly, it actually returns a handle to what is effectively an internal subclass of Tcl_Interp, so you really can't usefully allocate one yourself.)

The correct usage of the library is this:

Tcl_FindExecutable(NULL); // Or argv[0] if you have it
Tcl_Interp *interp = Tcl_CreateInterp();
// And now, you can use the rest of the API as you see fit

That's for putting a Tcl interpreter inside your code. To do it the other way round, you create an int My_Init(Tcl_Interp*) function as you describe and it is used to tell you where the interpreter is, but then you wouldn't be asking how to load the code, as Tcl has reasonable support for that already.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215