Dynamic Link Libraries

Introduction

A Dynamic Link Library (DLL) contains an executable code that can be shared by many processes in a system at any one time. A program can link against a DLL at compile time, meaning that the linker will match the DLL's symbols (such as function names and variables) with references from the program and ascertain the address within the DLL of the functions or variables.

Alternatively a program can dynamically load a DLL into memory during execution, locate the address of a symbol, use it and then unload the DLL. Traditionally and for greater memory efficiency, the Symbian platform links by ordinal, whereas Unix-like operating systems link by name. The following sections provide example code to illustrate how the DLL loader works with each operating system.

The Symbian platform v9.3 kernel supports all functionalities of P.I.P.S. libraries - referred to as "P.I.P.S.- ready". Pre v9.3 the kernel only supports the Symbian platform- standard ordinal lookup in DLLs; it does not support symbolic lookup. Thus the function dlsym() cannot be used with a symbolic name. To overcome this restriction, pass the function's ordinal number as symbol parameter refer the library's list of exports (the DEF file) for the function's ordinal number. For example, instead of: dlsym(&handle, "foo") use dlsym (&handle, "3") where the function foo() has ordinal 3.

Using shared libraries in a Unix-like environment

The following code shows how a shared library is dynamically loaded and used in a Unix-like environment using the dl interface.

int main(int argc, char *argv[])
{
  //Handle
  void* dll_handle;

  //Function prototype
  void (*printSum)(int a, int b);

  //Error message
  const char* errorMsg;
 
  //Filename of dll to load 
  char dllFileName[] = "/root/PortDoc/Example6/dll/shareddll";

  //Load the dll
  dll_handle = dlopen(dllFileName, RTLD_LAZY);
  if (!dll_handle)
  {
     fprintf(stderr, "Error during load of library: %s\n", dlerror()); 
     return EXIT_FAILURE;
  }

  //Find the symbol
  printSum = dlsym(dll_handle, "printSum");   

  //Get the error message 
  errorMsg = dlerror();
  if (errorMsg)
  {
     fprintf(stderr, "Error during symbol lookup: %s\n", errorMsg);
     return EXIT_FAILURE;
  } 

  //Now call the function
  printSum(4, 6);

  //Dispose of the DLL
  dlclose(dll_handle);

  return EXIT_SUCCESS;
}

As an example, the shareddll might have a function such as:

void printSum(int a, int b)
    {
    printf("Result is : %d\n", a + b);
    }

DLLs on the Symbian platform(prior to v9.3)

Symbian platform programs uses the function or variable's ordinal position within a DLL to link with DLLs. For example, implementing a shared DLL on the Symbian platform could look like this:

EXPORT_C void printSum(int a, int b)
    {
    printf("Result is : %d\n", a + b);
    }

Compiling this will generate a .def file, which contains the ordinals of each symbol in the DLL. For instance:

EXPORTS
    printSum @ 1 NONAME

Here, the .def shows that the ordinal for the printSum() function is 1.

The following code shows how the main() function is changed in P.I.P.S..

int main(int argc, char *argv[])
{
  //Handle
  void* dll_handle;

  //Function prototype
  void (*printSum)(int a, int b);

  //Error message
  const char* errorMsg;
 
  //Filename of dll to load 
  char dllFileName[] = "/root/PortDoc/Example6/dll/shareddll";

  //Load the dll
  dll_handle = dlopen(dllFileName, RTLD_LAZY);
  if (!dll_handle)
  {
     fprintf(stderr, "Error during load of library: %s\n", dlerror()); 
     return EXIT_FAILURE;
  }

  //Find the symbol, using the ordinal
  printSum = dlsym(dll_handle, "1");   

  //Get the error message 
  errorMsg = dlerror();
  if (errorMsg)
  {
     fprintf(stderr, "Error during symbol lookup: %s\n", errorMsg);
     return EXIT_FAILURE;
  } 

  //Now call the function
  printSum(4, 6);

  //Dispose of the DLL
  dlclose(dll_handle);

  return EXIT_SUCCESS;
}

Note that for the Symbian library loader, the mode parameter for the dlopen() function is irrelevant and the library will be loaded immediately. So although RTLD_LAZY is the mode parameter in the example, it will behave just as RTLD_NOW. Also note that there is no facility to call any library constructor and destructor functions using __attribute((constructor)) or __attribute((destructor)).

DLLs on the Symbian platform (v9.3 onwards)

P.I.P.S. provides APIs from libdl to support dynamic lookup by name, which is used in Unix-like platforms, instead of the native Symbian platform lookup by ordinal mechanism. Lookup by name is possible thanks to the new libdl, which in turn uses the existing RLibrary functions to load and unload DLLs.

A symbol name lookup version of the dlsym() function is provided, similar to UNIX®, which does not need any special treatment (the example shown in the Using Shared libraries in a Unix-like environment section will suffice). However, it is still necessary to generate the .def file for the DLL. The following sections provides a brief summary of the functionality provided by libdl.

The libdl APIs, which support dynamic lookup by name, are described in the following table:

libdl API

Description

void *dlopen (const char *path, int mode)

Call dlopen() to gain access to symbols of any shared library or DLL. If dlopen() fails for any reason, it will return a NULL. The default search path for the library can be provided in the environment variable LD_LIBRARY_PATH.

void *dlsym(void *handle, const char *symbol)

The dlsym() function returns the address binding of the symbol described in the null-terminated character string 'symbol', as it occurs in the shared object identified by 'handle'.

int dlclose(void *handle)

Call dlclose() with a valid dlopen-ed handle to remove it from the cache list maintained by dlopen(). If it fails, dlclose() will return -1; otherwise, it will return 0.

char *dlerror(void)

Call dlerror() to obtain the most recent error that occurred due to any of the dl routines, that is, dlopen(), dlsym(), or dlclose().

Related information