Loading and Linking Shared Libraries form Applications
- We use Static Linking to link at compile time, load time dynamic linking to link during load time and finally here we do linking during run time.
- the idea is to basically open a .so module while program's running, calling the function by name and finally closing the file when the usage ends.
- Here are some functions to look out for:
- dlopen()
#include<dlfcn.h> void *dlopen(const char *filename, int flag); Returns pointer to handle if OK NULL on error-
just to open the .so file, flag explanation:
- Two types, one for visibility and one for resolution time.
- For visibility if want other .so file that might rely on this .so file to see it's internal functions then use RTLD_GLOBAL flag, else just go with default nothing or u can do explicit RTLD_LOCAL flag.
- For resolution you can either use RTLD_NOW which tell the linker to resolve the references to external symbols immediately or RTLD_LAZY which instructs the linker to defer the symbol resolution until the code from library is executed.
-
dlsym()
#include <dlfcn.h> void *dlsym(void *handle, char *symbol); Returns pointer to symbol if OK, NULL on error-
takes the handle of the previously opened shared library and a symbol name and returns address of the symbol
-
dlclose()
int dlclose(void *handle); Returns 0 if OK, -1 on error-
just closes the shared library being pointed by the handler provided
-
dlerror()
const char *dlerror(void); Returns error msg if previous class to dlopen, dlsym or dlclose failed NULL if prev call was OK- does what it said just above!
Example of usage
- first create a shared library
/*addvec.c*/
int addcnt = 0;
void addvec(int *x, int *y, int *z, int n){
int i;
addcnt++;
for(i = 0; i < n; i++)
z[i] = x[i] + y[i];
}
/*multvec.c*/
int multcnt = 0;
int multvec(int *x, int *y, int *z, int n){
int i;
multcnt++;
for(i = 0; i < n; i++)
z[i] = x[i] * y[i];
}
linux> gcc -shared -fpic -o libvector.so addvec.c mulvec.c
- Again
-fpicmakes the linker makes this a Position Independent Code, more about it next lesson. - Now to use for function from object shared module we can do the following:
/*dil.c*/
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int x[2] = {1, 2};
int y[2] = {3 ,4};
int z[2];
int main(){
void *handle;
void (*addvec)(int*, int*, int);
char *error;
handle = dlopen("./libvector.so", RTDL_LAZY);
if(!handle){
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
addvec = dlsym(handle, "addvec");
if((error = dlerror()) != NULL){
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
addvec(x, y, z, 2);
printf("z = [%d %d]\n", z[0], z[1]);
if(dlclose(handle) < 0){
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
return 0;
}
- And we execute this using the following:
linux> gcc -rdynamic -o prog2e dill.c -ldl
NOTE: the shared object module, .so file might itself have some symbol resolutions left to do. It can do it in 2 ways, first from other dynamic libararies opened with dlopen() with flag RTLD_GLOBAL and another is if the current executable is compiled with -rdynamic flag then it's global symbols are also avaiable for it's symbol resolution.