From ThirdMartini

Contents

[edit] Concept


Both Linux(Unix) and Windows have a concept of shared libraries. .dll files under windows and typically .so files under Linux. These libraries allow us to minimize the size of our applications by having them share functions common to multiple applications inside one shared library.

In this set of examples we're actually taking the concept of shared libraries a step further. We can actually write an application that will load a set of functions from a shared library dynamically. Ie the application does not need to be aware of the shared library it will be using the functions from. Normally when an application that uses shared libraries ( such as libc.so, etc ) the compiler resolves the functions at compile time for you. Here we will demonstrate how to do that at runtime.

Dynamically loading shared libraries allows an application to be extended by 3rd party addons. One such use for dynamic libraries is for application plugins. A plugin interface defines what functions a plugin must provide and any other developer can then develop a plugin for your application without having the source code to that application. Or a 3rd party user can compile an additional plugin for such an application without recompiling the whole app.

In the example below we create a application "dlexample" that will load a library specified on the command line at runtime, try to find the symbol myfunction() in that library and execute it.
A windows example of Dynamic Libraries is here.

[edit] Example


The example below compiles two objects. The first object is a shared object (.so) that will be loaded dynamically by your second object,the user application.

[edit] main.cpp:

#include <stdio.h>
#include <dlfcn.h>
#include <assert.h>
#include <unistd.h>

/* This is the definition of the function we will be importing */
typedef int (*myfunction_t)(char *print);

int main(int argc, char *argv[] )
{
   int ret;
   void *handle;
   char *error;
   myfunction_t myfunction = NULL;

   if( argc != 2 )
   {
       printf("Usage: dlexample <library>\n");
       printf("       dlexample ./library-c.so\n");
       printf("       dlexample ./librqary-c++.so\n");
       return -1;
   }

   printf("Trying to load %s\n",argv[1]);

   /* Use RTL_NOW to force immediate symbol resolution */
   handle  = dlopen(argv[1],RTLD_NOW);
   error   = dlerror();
   /* See if we have a valid handle */
   if( handle == NULL )
   {
       printf("LOADER ERROR: %s\n",error);
       return -1;
   }
   /* Retreive the the pointer to our function in the shared library */
   myfunction = (myfunction_t)dlsym(handle,"myfunction");
   error = dlerror();
   if( error ){
       dlclose(handle);
       printf("LOADER ERROR: %s\n",error);
       return -1;
   }

   assert(myfunction);
   ret = myfunction("test");
   dlclose( handle );
   return ret;
}

[edit] library.c

#include <stdio.h>

int myfunction(const char * print )
{
    printf("%s:%s:%d::%s\n",__FILE__,__FUNCTION__,__LINE__,print);
    return 0;
}

[edit] Makefile

This make file will generate both a library compiled using gcc and g++. They key here is that a loadable library must be compiled with the -fPIC flag so that the dlsym() functions can find the function pointer at runtime.
all: dlexample library-c.so library-c++.so

dlexample: main.c
    gcc -o dlexample main.c -ldl

library-c.so: library.c
    gcc -o library-c.so -shared -fPIC library.c -ldl

library-c++.so:
    g++ -o library-c++.so -shared -fPIC library.cpp -ldl

[edit] C++ Version


When writing c++ code we have to declare all of our exported functions as extern "C" so that the compiler does not mangle our function names in the resulting shared object.

[edit] library.cpp

#include <stdio.h>

extern "C" int myfunction(const char *print)
{
    printf("%s:%s:%d::%s\n",__FILE__,__FUNCTION__,__LINE__,print);
    return 0;
}

[edit] Download

File Version Description
dlexample-1.0.tar.gz 1.0 The sourcecode tarball of the example files.
Personal tools