Dynamically Loaded Libraries


The final type of library that we ll explore is the dynamically loaded (and linked) library. This library can be loaded at any time during the execution of an application, unlike a shared object that is loaded immediately upon application start-up. We ll build our shared object file as we did before, as:

 $ gcc -fPIC -c initapi.c $ gcc -fPIC -c randapi.c $ gcc -shared initapi.o randapi.o -o libmyrand.so $ su - <provide your root password> $ cp libmyrand.so /usr/local/lib $ exit 

In this example, we ll move our shared library to a common location ( /usr/local/lib ). This is a standard directory for libraries, rather than relying on the image and shared library always being in the same location (as was assumed in the previous example). Note that this library is identical to our original shared library. What is different is how our application deals with the library.

Note  

In order to copy our library to /usr/local/lib , we must first gain root privileges. To do so, we use the su command to create a login shell for the root user .

Now that we have our shared library re-created, how do we access this in a dynamic fashion from our test application? The answer is that we must modify our test application to change the way that we access the API. Let s first look at our updated test app (modified from Listing 6.4). Then we ll investigate how this is built for dynamic loading. Our updated test application is shown in Listing 6.5. We ll walk through this, identifying what changed from our original application, and then look at the dynamically loaded (DL) API in more detail.

Listing 6.5: Updated Test Application for Dynamic Linkage (on the CD-ROM at ./source/ch6/dynamic/test.c )
start example
  1:  /*  2:  * Dynamic rand function test.  3:  *  4:  */  5:   6:  #include <stdlib.h>  7:  #include <stdio.h>  8:  #include <dlfcn.h>  9:  #include "randapi.h"  10:   11:  #define ITERATIONS      1000000L  12:   13:   14:  int main()  15:  {  16:  long  i;  17:  long  isum;  18:  float fsum;  19:  void  *handle;  20:  char  *err;  21:   22:  void (*initRand_d)(void);  23:  float (*getSRand_d)(void);  24:  int (*getRand_d)(int);  25:   26:  /* Open a handle to the dynamic library */  27:  handle =  dlopen  ( "/usr/local/lib/libmyrand.so", RTLD_LAZY );  28:  if (handle == (void *)0) {  29:  fputs(  dlerror  (), stderr );  30:  exit(-1);  31:  }  32:   33:  /* Check access to the initRand() function */  34:  initRand_d =  dlsym  ( handle, "initRand" );  35:  err =  dlerror  ();  36:  if (err != NULL) {  37:  fputs( err, stderr );  38:  exit(-1);  39:  }  40:   41:  /* Check access to the getSRand() function */  42:  getSRand_d =  dlsym  ( handle, "getSRand" );  43:  err =  dlerror  ();  44:  if (err != NULL) {  45:  fputs( err, stderr );  46:  exit(-1);  47:  }  48:   49:  /* Check access to the getRand() function */  50:  getRand_d =  dlsym  ( handle, "getRand" );  51:  err =  dlerror  ();  52:  if (err != NULL) {  53:  fputs( err, stderr );  54:  exit(-1);  55:  }  56:   57:   58:  /* Initialize the random number API */  59:  (*initRand_d)();  60:   61:  /* Find the average of getRand(10) returns (0..9) */  62:  isum = 0L;  63:  for (i = 0 ; i < ITERATIONS ; i++) {  64:   65:  isum += (*getRand_d)(10);  66:   67:  }  68:   69:  printf( "getRand() Average %d\n", (int)(isum / ITERATIONS) );  70:   71:   72:  /* Find the average of getSRand() returns */  73:  fsum = 0.0;  74:  for (i = 0 ; i < ITERATIONS ; i++) {  75:   76:  fsum += (*getSRand_d)();  77:   78:  }  79:   80:  printf( "getSRand() Average %f\n", (fsum / (float)ITERATIONS) );  81:   82:  /* Close our handle to the dynamic library */  83:   dlclose  ( handle );  84:   85:  return;  86:  } 
end example
 

This code may appear a little convoluted given the earlier implementation, but it s actually quite simple once you understand how the DL API works. All that s really going on is that we re opening the shared object file using dlopen , and then assigning a local function pointer to the function within the shared object (using dlsym ). This allows us then to call it from our application. When we re done, we close the shared library using dlclose , and the references are removed (freeing any used memory for the interface).

We make the DL API visible to us by including the dlfcn.h (DL function) header file. The first step in using a dynamic library is opening it with dlopen (line 27). We specify the library we need to use ( /usr/local/lib/libmyrand.so ) and also a single flag. Of the two flags that are possible ( RTLD_LAZY and RTLD_NOW ), we specify RTLD_LAZY to resolve references as we go, rather than immediately upon loading the library, which would be the case with RTLD_NOW . The function dlopen returns an opaque handle representing the opened library. Note that if an error occurs, we can use the dlerror function to provide an error string suitable for emitting to stdout or stderr .

Note  

While not necessary in this example, if we desired to have an initialization function called when our shared library was opened via dlopen , we could create a function called _init in our shared library. The dlopen function will ensure that this _init function is called before dlopen returns to the caller.

Getting the references for the functions that we need to address is the next step. Let s look at one below (taken from lines 34 “39 of Listing 6.5).

  34:  initRand_d =  dlsym  ( handle, "initRand" );  35:  err =  dlerror  ();  36:  if (err != NULL) {  37:  fputs( err, stderr );  38:  exit(-1);  39:  } 

The process, as can be seen from this code snippet, is very simple. The API function dlsym searches our shared library for the function defined (in this case, our initialization function "initRand" ). Upon locating it, a (void *) pointer is returned and stored in a local function pointer. This may then be called (as shown at line 59) to perform the actual initialization. We automatically check our error status (at line 35), and if an error string was returned, we emit it and exit the application.

That s really it for identifying the functions that we desire to call in the shared library. After we grab the initRand function pointer, we grab getSRand (lines 42 “47) and then getRand (lines 49 “55).

Our test application is now fundamentally the same, except that instead of calling functions directly, we call them indirectly using the pointer-to-function interface. That s a small price to pay for the flexibility that the dynamically loaded interface provides.

Our last step in the new test application is to close out the library. This is done with the dlclose API function (line 83). If the API finds that there are no other users of the shared library, then it is unloaded.

Note  

As was provided with dlopen , dlclose provides a mechanism by which the shared object can export a completion routine that is called when the dlclose API function is called. The developer must simply add a function called _fini to the shared library, and dlclose will ensure that _ fini is called prior to dlclose return .

And that s it! For the small amount of pain involved in creating an application that utilizes dynamically loaded shared libraries, it provides a very flexible environment that ultimately can save on memory use. Note also that it s not always necessary to make all dynamic functions visible when your application starts. You can instead make only those that are necessary for normal operation and load other dynamic libraries as they become necessary.

The dynamically loaded library API is very simple and is shown here for completeness:

 void *  dlopen  ( const char *filename, int flag );     const char *  dlerror  ( void );     void *  dlsym  ( void *handle, char *symbol );     int  dlclose  ( void *handle ); 

How a library is made up depends upon what it s representing. The library should contain all functions that are necessary for the given problem domain. Functions that are not specifically associated with the domain should be excluded and potentially included in another library.




GNU/Linux Application Programming
GNU/Linux Application Programming (Programming Series)
ISBN: 1584505680
EAN: 2147483647
Year: 2006
Pages: 203
Authors: M. Tim Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net