From: http://www.ibm.com/developerworks/cn/linux/sdk/dll/index.html
Why write an entire application when you just write a plugin?
Plugins and DLLs are often a great way to add functionality without having to write an entire new application. In Linux, plugins and DLLs are implemented in the form of dynamic libraries. E-commerce consultant and designer Allen Wilson introduced the dynamic library and showed you how to use a dynamic library to change the application after an application is running.
Allen Wilson ([email protected]), e-commerce designer, IBM October 01, 2001
Internet Browser users are very familiar with the concept of plug-ins. Plug-ins are downloaded from the Web, and often these plug-ins provide enhanced support for browser audio, video, and special effects. In general, the plug-in provides new functionality for existing applications without changing the original application.
DLLs are program functions that are known to the program when designing and building the application. When you design your application's main program, you use the program framework or the backplane, which selectively loads the required DLLs at run time, which are located in some files that are detached from the main program on disk. This package and dynamic onboarding provides flexible upgrades, maintenance, and licensing policies.
There are also thousands of commands and applications shipped with Linux, which require at least libc library functions. If the LIBC function is packaged with each application, a copy of the thousands of identical functions will appear on the disk. Linux builds these applications to use a single system-level copy of the system library that is typically required, without wasting disk space. Linux is even better, and every process that requires a common system library function uses a single system-level copy to load the copy into memory and share it for each process at once.
In Linux, plugins and DLLs are implemented in the form of dynamic libraries. The remainder of this article is an example of using a dynamic library to change the application after the application has run.
Linux dynamic Link
Applications in Linux are linked to external functions in one of two ways: they are statically linked to the static library ( lib*.a
) at build time, and the library code is included in the application's executable file, or lib*.so
dynamically linked to the shared library () at run time . The dynamic library is mapped into the application's executable memory through the dynamic Link loader. Before starting the application, the dynamic link loader maps the desired shared target library to the memory of the application, or uses the system-shared target and resolves the required external references for the application. Now the application is ready to run.
As an example, here is a small program that demonstrates the default use of dynamic-link libraries in Linux:
Main () { printf ("Hello World");}
When you compile hello.c with GCC, you create an a.out
executable file named. By using the Linux command ldd a.out
(which prints out the interdependencies of shared libraries), you can see that the shared libraries you need are:
libc.so.6 =/lib/libc.so.6 (0x4001d000) /lib/ld-linux.so.2 =/lib/ld-linux.so.2 (0x40000000)
Use the same dynamic link loader to map the DLLs into the application's memory after the application has run. By using the Linux dynamic loader routine, the application controls which dynamic library is loaded and which function in the library is called to perform mount and link and return the address of the required entry point.
Back to top of page
Linux DLL Functions
Linux provides 4 library functions ( dlopen
, dlerror
, dlsym
and dlclose
), an include file (), and dlfcn.h
two shared libraries (static libraries libdl.a
and dynamic libraries libdl.so
) to support dynamic Link loader. These library functions are:
- Dlopen Opens the shared target file and maps it to memory, and returns a handle
- dlsym Returns a pointer to the requested entry point
- dlerror returns NULL or a pointer to an ASCII string that describes the most recent error
- Dlclose closes the handle and cancels the mapping of the shared destination file
The dynamic Link loader routine Dlopen needs to locate the shared destination file in the file system to open the file and create a handle. There are 4 ways to specify the location of the file:
dlopen call
The absolute file path in
- In the directory specified in the LD_LIBRARY_PATH environment variable
- In the list of libraries specified in/etc/ld.so.cache
- First in/usr/lib, then in/lib.
Back to top of page
Examples of DLLs: Small C programs and dltest
The Dynamic Link Loader sample program is a small C program that is designed to practice the DL routines. The program is based on a C program that everyone has written, and it prints "Hello world" to the console. The message that was originally printed was "HeLlO world". The test program links to two functions that print the message again: the first time you use uppercase characters, and the second time with lowercase characters.
The following is a summary of the program:
- Define the DLL include file
dlfcn.h
and the required variables. At least these variables are required:
- Handle to the shared library file
- Pointer to the mapped function entry point
- Pointer to error string
- Print the initial message, "HeLlO World".
- Using the absolute path "/home/dltest/uppercase.so" and the option Rtld_lazy,
dlopen
Open the shared destination file for the uppercase DLL and return the handle.
- Option Rtld_lazy Postpone parsing the external references of the DLL until the DLL is executed.
- The rtld_now option
dlopen
resolves all external references before returning.
dlsym
Returns the address of the entry point printuppercase.
- Call Printuppercase and print the modified message "HELLO World".
dlclose
Closes the handle to the uppercase.so and cancels the DLL mapping from memory.
dlopen
Finds the shared destination path using a relative path based on the environment variable Ld_library_path, opens the shared destination file lowercase.so for the lowercase DLL, and returns a handle.
dlsym
Returns the address of the entry point printlowercase.
- Call Printlowercase and print the modified message "Hello World".
dlclose
Closes the handle to the lowercase.so and cancels the DLL mapping from memory.
Note that each call dlopen
, dlsym
or dlclose
after, is dlerror
called to get the last error message, and the error message string is printed. The following is a test run of Dltest:
Dltest 2-original message HeLlO World dltest 3-open Library with absolute path return-(null)- dltest 4-find symbol printuppercase return-(NULL)-HELLO world dltest 5-printuppercase return-(null)- Dltest 6-close handle return-(NULL)- dltest 7-open Library with relative path return-(null)- Dltest 8-find symbol printlowercase return-(null)-Hello world dltest 9-printlowercase return-(NULL)- dltest 10-close handle return-(NULL)-
The complete list of dltest.c, uppercase.c, and lowercase.c source codes is listed later in this article.
Back to top of page
Build Dltest
Enabling run-time dynamic linking requires three steps:
- To compile a DLL into a location-independent code
- Create a DLL share destination file
- Compile the main program and link with the DL Library
The GCC commands that compile uppercase.c and lowercase.c include the-fpic option. The options-fpic and-fpic cause the generated code to be location-independent, and rebuilding the shared target library requires location-independent. The-FPIC option produces positional-independent code that supports large offsets. The second GCC command for UPPERCASE.O and LOWERCASE.O with a-shared option that produces a shared target file a*.so that is appropriate for dynamic linking.
The Ksh script for compiling and executing dltest is as follows:
#!/bin/ksh# Build shared library# #set-xclear## shared library for Dlopen absolute path test#if [-F UPPERCASE.O] ; Then RM uppercase.ofigcc -c-fpic Uppercase.cif [-F uppercase.so]; then rm uppercase.sofigcc-shared-lc- o UPPE rcase.so UPPERCASE.O # Shared library for Dlopen relative path test#export ld_library_path= ' pwd ' if [-f Lowercas E.O]; Then RM lowercase.ofigcc -c-fpic Lowercase.cif [-F lowercase.so]; then rm lowercase.sofigcc-shared-lc- o Lowe rcase.so lowercase.o## Rebuild test program#if [-f dltest]; then rm dltestfigcc-o dltest Dltest.c-ldlecho Curr ENT ld_library_path= $LD _library_pathdltest
Back to top of page
Conclusion
It is a very simple exercise to create shared target code that can be dynamically linked to an application on a Linux system at run time. The application obtains access to the shared destination file by using the Dlopen, dlsym, and dlclose function calls to the dynamic link loader. Dlerror returns any error as a string that describes the last error that the DL function encountered. At run time, the main application finds the shared target library with an absolute path or relative path relative to Ld_library_path, and requests the address of the required DLL entry point. When needed, indirect function calls can also be made to the DLL, and finally, the handle to the shared destination file is closed and the target file mapping is canceled from memory to make it unusable.
Use the additional options-fpic or-fpic to compile the shared target code to produce location-independent code, and use the-shared option to place the target code in the shared target library.
The shared target code base and dynamic Link loader in Linux provide additional functionality to the application. Reduces the size of executable files on disk and in memory. Optional application functionality can be loaded when needed, and defects can be remediated without rebuilding the entire application, and the application can contain third-party plug-ins.
Back to top of page
Checklist (Applications and DLLs) Dltest.c:
/*************************************************************//* Test Linux Dynamic Function Loading *// * *//* void *dlopen (const char *filename, int flag) *//* Opens Dynamic library and return handle *//* *//* const char *dlerror (void) *//* Returns string describing the last error. *//* *//* void *dlsym (void *handle, char *symbol) *//* Return pointer to symbol ' s load point. *//* If symbol is undefined, and NULL is returned. *//* *//* int dlclose (void *handle) *//* Clos E The dynamic library handle. *//* *//* *//* *//******************/#include <stdio.h> #include <stdlib.h>/* *//* 1-dll include file and variables *//* */#include <dlfcn.h>void *function Lib; /* Handle to gkfx lib file */int (*function) (); /* Pointer to loaded routine */const char *dlerror; /* Pointer to Error string */main (argc, argv) {int rc; /* Return codes */char hellomessage[] = "HeLlO world\n"; /* *//* 2-print the original message *//* */PR intf ("dltest 2-original message \ n"); printf ("%s", hellomessage);/* *//* 3-open Dynamic Loadable libary with ABSO lute path *//* */functionlib = Dlopen ("/home/dltest/uppercase.so", Rtld _lazy); Dlerror = Dlerror (); printf ("Dltest 3-open Library WitH Absolute path return-%s-\ n ", dlerror); if (Dlerror) exit (1);/* *//* 4-find the first loaded function *//* */Function = Dlsym (Functionlib, "printuppercase"); Dlerror = Dlerror (); printf ("Dltest 4-find symbol printuppercase return-%s-\ n", dlerror); if (Dlerror) exit (1);/* *//* 5-execute the first loaded function *//* */rc = (*function) (hellomessage); printf ("Dltest 5-printuppercase return-%s-\ n", dlerror);/* *//* 6-close the shared Librar Y handle *//* Note:after the Dlclose, "Printuppercase" is not loaded *//* */rc = Dlclose (functionlib); Dlerror = Dlerror (); printf ("Dltest 6-close handle return-%s-\n", dlerror); if (RC) exit (1);/* *//* 7-open Dynamic Loadable libary using ld_library path *//* */Functionlib = Dlopen ("lowercase.so", Rtld_lazy); Dlerror = Dlerror (); printf ("Dltest 7-open Library with relative path return-%s-\ n", dlerror); if (Dlerror) exit (1);/* *//* 8-find the second loaded function *//* */Function = Dlsym (Functionlib, "printlowercase"); Dlerror = Dlerror (); printf ("Dltest 8-find symbol printlowercase return-%s-\ n", dlerror); if (Dlerror) exit (1);/* *//* 8-execute the second loaded function *//* */rc = (*function) (hellomessage); printf ("Dltest 9-printlowercase return-%s-\ n", dlerror);/* *//* 10-close the shared Libra RY Handle *//* */rc = Dlclose (functionlib); Dlerror = Dlerror (); printf ("Dltest 10-close handle return-%s-\n", dlerror); if (RC) exit (1); return (0);}
UPPERCASE.C:
/************************************************//* Function to print input string as UPPER case. *//* Returns 1. *//*********************************************** */int printuppercase (inLine) char inline[]; { char upstring[256]; Char *inptr, *outptr; Inptr = InLine; Outptr = upstring; while (*inptr! = ') ') *outptr++ = ToUpper (*inptr++); *outptr++ = ' + '; printf (upstring); return (1);}
Lowercase.c
/********************************************//* Function to print input string as lower case. *//* Returns 2. *//******************************************* */int printlowercase (inLine) char inline[]; { char lowstring[256]; Char *inptr, *outptr; Inptr = InLine; Outptr = lowstring; while (*inptr! = ") *outptr++ = ToLower (*inptr++); *outptr++ = "; printf (lowstring); return (2);}
Write dll[to Linux applications]