11.5 How to create and register new module loaders

Sometimes libltdl’s many ways of gaining access to modules are not sufficient for the purposes of a project. You can write your own loader, and register it with libltdl so that lt_dlopen will be able to use it.

Writing a loader involves writing at least three functions that can be called by lt_dlopen, lt_dlsym and lt_dlclose. Optionally, you can provide a finalisation function to perform any cleanup operations when lt_dlexit executes, and a symbol prefix string that will be prepended to any symbols passed to lt_dlsym. These functions must match the function pointer types below, after which they can be allocated to an instance of lt_user_dlloader and registered.

Registering the loader requires that you choose a name for it, so that it can be recognised by lt_dlloader_find and removed with lt_dlloader_remove. The name you choose must be unique, and not already in use by libltdl’s builtin loaders:

"dlopen"

The system dynamic library loader, if one exists.

"dld"

The GNU dld loader, if libdld was installed when libltdl was built.

"dlpreload"

The loader for lt_dlopening of preloaded static modules.

The prefix "dl" is reserved for loaders supplied with future versions of libltdl, so you should not use that for your own loader names.

The following types are defined in ltdl.h:

Type: lt_module

lt_module is a dlloader dependent module. The dynamic module loader extensions communicate using these low level types.

Type: lt_dlloader

lt_dlloader is a handle for module loader types.

Type: lt_user_data

lt_user_data is used for specifying loader instance data.

Type: struct lt_user_dlloader {const char *sym_prefix; lt_module_open *module_open; lt_module_close *module_close; lt_find_sym *find_sym; lt_dlloader_exit *dlloader_exit; }

If you want to define a new way to open dynamic modules, and have the lt_dlopen API use it, you need to instantiate one of these structures and pass it to lt_dlloader_add. You can pass whatever you like in the dlloader_data field, and it will be passed back as the value of the first parameter to each of the functions specified in the function pointer fields.

Type: lt_module lt_module_open (const char *filename)

The type of the loader function for an lt_dlloader module loader. The value set in the dlloader_data field of the struct lt_user_dlloader structure will be passed into this function in the loader_data parameter. Implementation of such a function should attempt to load the named module, and return an lt_module suitable for passing in to the associated lt_module_close and lt_sym_find function pointers. If the function fails it should return NULL, and set the error message with lt_dlseterror.

Type: int lt_module_close (lt_user_data loader_data, lt_module module)

The type of the unloader function for a user defined module loader. Implementation of such a function should attempt to release any resources tied up by the module module, and then unload it from memory. If the function fails for some reason, set the error message with lt_dlseterror and return non-zero.

Type: void * lt_find_sym (lt_module module, const char *symbol)

The type of the symbol lookup function for a user defined module loader. Implementation of such a function should return the address of the named symbol in the module module, or else set the error message with lt_dlseterror and return NULL if lookup fails.

Type: int lt_dlloader_exit (lt_user_data loader_data)

The type of the finalisation function for a user defined module loader. Implementation of such a function should free any resources associated with the loader, including any user specified data in the dlloader_data field of the lt_user_dlloader. If non-NULL, the function will be called by lt_dlexit, and lt_dlloader_remove.

For example:

int
register_myloader (void)
{
  lt_user_dlloader dlloader;

  /* User modules are responsible for their own initialisation. */
  if (myloader_init () != 0)
    return MYLOADER_INIT_ERROR;

  dlloader.sym_prefix    = NULL;
  dlloader.module_open   = myloader_open;
  dlloader.module_close  = myloader_close;
  dlloader.find_sym      = myloader_find_sym;
  dlloader.dlloader_exit = myloader_exit;
  dlloader.dlloader_data = (lt_user_data)myloader_function;

  /* Add my loader as the default module loader. */
  if (lt_dlloader_add (lt_dlloader_next (NULL), &dlloader,
                       "myloader") != 0)
    return ERROR;

  return OK;
}

Note that if there is any initialisation required for the loader, it must be performed manually before the loader is registered – libltdl doesn’t handle user loader initialisation.

Finalisation is handled by libltdl however, and it is important to ensure the dlloader_exit callback releases any resources claimed during the initialisation phase.

libltdl provides the following functions for writing your own module loaders:

Function: int lt_dlloader_add (lt_dlloader *place, lt_user_dlloader *dlloader, const char *loader_name)

Add a new module loader to the list of all loaders, either as the last loader (if place is NULL), else immediately before the loader passed as place. loader_name will be returned by lt_dlloader_name if it is subsequently passed a newly registered loader. These loader_names must be unique, or lt_dlloader_remove and lt_dlloader_find cannot work. Returns 0 for success.

/* Make myloader be the last one. */
if (lt_dlloader_add (NULL, myloader) != 0)
  perror (lt_dlerror ());
Function: int lt_dlloader_remove (const char *loader_name)

Remove the loader identified by the unique name, loader_name. Before this can succeed, all modules opened by the named loader must have been closed. Returns 0 for success, otherwise an error message can be obtained from lt_dlerror.

/* Remove myloader. */
if (lt_dlloader_remove ("myloader") != 0)
  perror (lt_dlerror ());
Function: lt_dlloader * lt_dlloader_next (lt_dlloader *place)

Iterate over the module loaders, returning the first loader if place is NULL, and the next one on subsequent calls. The handle is for use with lt_dlloader_add.

/* Make myloader be the first one. */
if (lt_dlloader_add (lt_dlloader_next (NULL), myloader) != 0)
  return ERROR;
Function: lt_dlloader * lt_dlloader_find (const char *loader_name)

Return the first loader with a matching loader_name identifier, or else NULL, if the identifier is not found.

The identifiers that may be used by libltdl itself, if the host architecture supports them are dlopen12, dld and dlpreload.

/* Add a user loader as the next module loader to be tried if
   the standard dlopen loader were to fail when lt_dlopening. */
if (lt_dlloader_add (lt_dlloader_find ("dlopen"), myloader) != 0)
  return ERROR;
Function: const char * lt_dlloader_name (lt_dlloader *place)

Return the identifying name of place, as obtained from lt_dlloader_next or lt_dlloader_find. If this function fails, it will return NULL and set an error for retrieval with lt_dlerror.

Function: lt_user_data * lt_dlloader_data (lt_dlloader *place)

Return the address of the dlloader_data of place, as obtained from lt_dlloader_next or lt_dlloader_find. If this function fails, it will return NULL and set an error for retrieval with lt_dlerror.

11.5.1 Error handling within user module loaders

Function: int lt_dladderror (const char *diagnostic)

This function allows you to integrate your own error messages into lt_dlerror. Pass in a suitable diagnostic message for return by lt_dlerror, and an error identifier for use with lt_dlseterror is returned.

If the allocation of an identifier fails, this function returns -1.

int myerror = lt_dladderror ("doh!");
if (myerror < 0)
  perror (lt_dlerror ());
Function: int lt_dlseterror (int errorcode)

When writing your own module loaders, you should use this function to raise errors so that they are propagated through the lt_dlerror interface. All of the standard errors used by libltdl are declared in ltdl.h, or you can add more of your own with lt_dladderror. This function returns 0 on success.

if (lt_dlseterror (LTDL_ERROR_NO_MEMORY) != 0)
  perror (lt_dlerror ());

Footnotes

(12)

This is used for the host dependent module loading API – shl_load and LoadLibrary for example