|
|
0. IntroductionThe days of a program living as a single entity are all but gone. Today’s programs need to be more versatile and expandable. The simplest way to provide flexibility and expandability to your program is through the use of modules otherwise known as plugins. Web browsers and music players are two good examples of programs that allow plugins. Browsers use plugins to add support to web pages such as Java, Flash and QuickTime so that you can have a more enriched surfing experience. Music players such as XMMS use plugins to support different encodings as well as have visual plugins to watch your music dance on the screen. This article shows how to provide plugin support to your programs. Note: I use module and plugin interchangeably, for purposes of this article they are the same.1. How To Work With PluginsThere are only four functions needed to work with plugins. They are part of the dl (Dynamic Loader) library. I will give just a brief introduction to them here. You can view the info pages for each of these to get a more in-depth description.
2. A Simple Loader Program for PluginsHere is the code for a simple loader program that takes the plugin name as a command line argument.
The code is pretty simple. After the loader loads the plugin it looks inside the plugins symbol table using the dlsym command to get the address of the function `entry.’ Once I have the address of this function I can call the function, I assign it to the function pointer that I created. Then the plugin is unloaded. The function pointer line may need some explaining. int (*my_entry)() is used as a pointer to a function that takes no arguments and returns an int. Which I can use to point to the function `entry’ in the plugin. int entry() The following command is used to compile the loader program:
3. Two Simple PluginsNow that we have a loader we need some plugins for it to load. There is no defined prototype for a modules entry point; you may use whatever you like. In my examples I have the entry point return an int and take no arguments. You can set up your entry points to take whatever arguments they need and return whatever you want. It does not need to be called `entry' either. I simply use this to make it easier to understand the purpose of the function. In addition, you may have more than one entry point into a plugin. Below are two samples of a modules, each with the same entry point:module1.c
text version of this listing
module2.c
text version of this listing
To compile the plugins:
4. Using the LoaderHere are the commands for using the two different plugins and there output:
5. Adding Bookkeeping Functions for PluginsThis section assumes you are using the gcc compiler do to the fact that the commands used are specific to gcc, other compilers may have similar features, you may check you documentation for compatibility. Gcc provides an `__attribute__' flag to be used with functions. This flag offers many useful features to functions; however, I will only discuss two of them here, see the info page on gcc for other descriptions of the other attributes. The two I wish to discuss are `constructor' and `destructor'. The ELF (Executable and Linkable Format) binary provides two sections .init and .fini which can contain code that is executed before and after a module is loaded (in a regular program these would be run before and after main() is executed.) Placing code in these sections can allow you to initialize variables or do other bookkeeping responsibilities your module may require. For example you could have the module read variables from the main program that it will need to get started or have the plugin set variables inside the main program such as the interface type of the plugin. The interface type of a plugin is the set of commands that the plugin in question provides. In my example it provided only one function 'entry'; yours may provide others. Below is a sample of using these attributes:
The names init() and fini() are not necessary, I use them to clarify where these functions to be placed for easier reading. There are several function names that you must avoid because gcc uses these names. Some of which are _init, _fini, _start and _end. To see a full listing of functions and variables that gcc creates you can run `nm’ on the binary file. The `constructor' and `destructor' attributes are what tell the compiler where to place the code inside the binary file. Simply put, `constructor' tells the compiler that the corresponding function goes in the .init section of the and likewise the `destructor' attribute tells the compiler the place the corresponding function in the .fini section. 6. ConclusionWith the use of the dl library it a simple task to provide plugin support to your program. Allowing for easy expandability and flexibility. Although this example only demonstrates grabbing one function from a plugin it is easy to grab multiple functions from a plugin and use them as if they were part of the original program.Published in Issue 84 of Linux Gazette, November 2002 |