1. Static library
1.1. Example code files
/* mod1.c */ #include stdio.h #include unistd.h int test1[250000] = { 1, 2, 3 }; #ifndef VERSION #define VERSION "" #endif void x1(void) { printf("Called mod1-x1 " VERSION "\n"); sleep(10); printf("exiting x1\n"); } /* mod2.c */ #include stdio.h #include unistd.h int test2[100000] = { 1, 2, 3 }; #ifndef VERSION #define VERSION "" #endif void x2(void) { printf("Called mod2-x2 " VERSION "\n"); sleep(10); printf("exiting x2\n"); } /* mod3.c */ #include stdio.h #include unistd.h int test3[10000] = { 1, 2, 3 }; #ifndef VERSION #define VERSION "" #endif void x3(void) { printf("Called mod3-x3 " VERSION "\n"); sleep(10); printf("exiting x3\n"); } /*#* prog.c */ #include stdio.h #include stdlib.h void x1(void); void x2(void); int main(int argc, char *argv[]) { x1(); x2(); exit(EXIT_SUCCESS); } /* main */
1.2. Simple way of building a program:
$ cc -c -g prog.c mod1.c mod2.c mod3.c
$ cc -o prog_nolib prog.o mod1.o mod2.o mod3.o
1.3. Use static library
- Compile object file:
- Create library “libtest.a” :
- List file in library:
- Linking executable file with library:
Or
$ cc –o prog prog.c –L/path/to/library-directory –lctest
1.4. Disvantages:
- Disk space is wasted wasted storing multiple copies of object modules.
- Memory is wasted when several different programs using the same modules execute at the same time, because each program holds hold separate copies of the object modules in virtual memory.
- Programs must be relinked in order to see changes to object modules.
2. Shared library
2.1. Concept and Advantages
- Create single copy of all object modules, which is shared by all applications at run-time
- Advantages:
- Programs are smaller: disk space and virtual memory requirements are reduced.
2.2. Create and using a shared library
- Create the object modules using -fPIC:
-fPIC (Position
Independent Code) causes compiler to generate code that can be loaded
anywhere in virtual memory at run-time.
- Create the shared library:
- Link program against library :
or:
$ gcc -g -Wall -o prog prog.c -L. –lfoo
- Execute the program
./prog:
error in loading shared libraries: libfoo.so: cannot open shared object file: No
such file or directory - We
need to give the
dynamic linker information on how to find our shared library at run-time
$ LD_LIBRARY_PATH=. ./prog
Called mod1-x1
exiting x1
Called mod2-x2
exiting x2
2.3. The shared library soname
In the earlier example, we embedded the actual name (the real name) of the shared library in an executable file.It is possible to create an alias, called the soname, which will be embedded in an executable file instead of the real name.
At run-time, the dynamic linker will use the soname when searching for the library
- Specify soname when creating shared library
$ gcc -shared -Wl,-soname,libbar.so -o libfoo.so mod1.o mod2.o mod3.o
-Wl,-soname,libbar.so instructs linker to mark the shared library libfoo.so with the soname libbar.so.
- Create executable:
Linker detects that libfoo.so contains the soname libbar.so and embeds the latter name inside the executable.
- Run the program:
./prog: error in loading shared libraries: libbar.so: cannot open shared object file: No such file or directory
Dynamic linker cannot find anything named libbar.so.
- Create a symbolic link from the soname to the real name of the library
$ LD_LIBRARY_PATH=. ./prog
Called mod1-x1
Called mod2-x2
At run-time this link can point to a version of the library which is different from the version against which linking was performed.
fig 1. Steps
required in building a shared library, linking a program against it, and
creating the required soname symbolic link.
fig 2. The steps that occur as the program is executed
2.4. Shared library versions and naming
- If a new version of a shared library is compatible with an existing library, we can make a new minor version of the library.
- If a new version of a shared library is incompatible with an existing library, we must make a new major version of the library.
2.5. Creating shared libraries using standard naming conventions
Create the shared library with real name libdemo.so.1.0.1 and soname libdemo.so.1.
$ gcc
-fPIC -g -c -Wall mod1.c mod2.c mod3.c
$ gcc -shared
-Wl,-soname,libdemo.so.1 -o libdemo.so.1.0.1 mod1.o mod2.o mod3.o
$ ln
-s libdemo.so.1.0.1 libdemo.so.1
$ ln -s libdemo.so.1
libdemo.so
Build executable using the linker
name:
$ gcc
-g -Wall -o ./prog prog.c -L. -ldemo
Run
the program as usual:
$ LD_LIBRARY_PATH=. ./prog
Called mod1-x1
Called mod2-x2
- /lib - directory containing libraries
- /usr/local/lib - non-standard or experimental libraries should be install here
cd /usr/lib
ln -s libdemo.so.1.0.1 libdemo.so.1
ln -s lindemo.so.1 libdemo.so
$ gcc -shared -Wl,-soname,libdemo.so.1 -o libdemo.so.1.0.2 mod1.o mod2.o mod3.o
$ mv libdemo.so.1.0.2 /usr/lib
$ ldconfig -v | grep libdemo
libdemo.so.1 -> libdemo.so.1.0.2 (changed)
$ gcc -shared -Wl,-soname,libdemo.so.2 -o libdemo.so.2.0.1 mod1.o mod2.o mod3.o
2.6. Installing shared libraries
- Production applications should not require the user to set LD_LIBRARY_PATH
- We can install a shared library in one of the standard library directories
- /lib - directory containing libraries
- /usr/local/lib - non-standard or experimental libraries should be install here
- After installation, symbolic links for soname and linker name must be created, as relative links in the same directory.
- Example install in /usr/lib
cd /usr/lib
ln -s libdemo.so.1.0.1 libdemo.so.1
ln -s lindemo.so.1 libdemo.so
2.7. Upgrading Shared libraries
- Example - upgrading a shared library with a new minor version (1.0.2)
$ gcc -shared -Wl,-soname,libdemo.so.1 -o libdemo.so.1.0.2 mod1.o mod2.o mod3.o
$ mv libdemo.so.1.0.2 /usr/lib
$ ldconfig -v | grep libdemo
libdemo.so.1 -> libdemo.so.1.0.2 (changed)
- Assuming the linker name was already correctly set up, we do not need to modify it.
- Then upgrading to a new major version (2.0.1):
$ gcc -shared -Wl,-soname,libdemo.so.2 -o libdemo.so.2.0.1 mod1.o mod2.o mod3.o
$ mv libdemo.so.2.0.1 /usr/lib
$ ldconfig -v | grep libdemo
libdemo.so.1 -> libdemo.so.1.0.2
libdemo.so.2 -> libdemo.so.2.0.1 (changed)
$ cd /usr/lib
$ ln -sf libdemo.so.2 libdemo.so
- ldconfig automatically creates a soname symbolic link, but we must manually update the linker name symbolic link.
2.8. Dynamically Loaded Libraries
The dlopen API allows a program to :
- Open a shared library at run time
- search for function( or variable ) by name in lobrary
- and the call the function ( or access the variable)
dlopen API:
Four key functions: dlopen(), dlerror(), dlsym(), and dlclose().
void *dlopen(const char *filename, int flag);
dlopen() opens library, and increments the library's count of open references.
- If filename contains a slash (/), dlopen() interprets it as an (absolute or relative) pathname...
- otherwise it employs the usual library search rules.
- The flag argument is a bit mask that must include one of the following:
- dlopen() returns a handle that is used to refer to the library in later calls, or NULL if an error occurred (e.g., library couldn't be found).
const char *dlerror(void);
dlerror() returns a pointer to a message string after an error return from dlopen(), dlsym(), or dlclose().
Returns NULL if no error has occurred since last call to dlerror().
void *dlsym(void *handle, char *symbol);
dlsym() searches a library to find a symbol (e.g., a function) and return its address.
- For a function, we can dereference this pointer to call the function.
- Returns NULL if symbol could not be found.
- Complication: the value of a symbol may be NULL (0), which is indistinguishable from "symbol not found". To differentiate the two possibilities, call dlerror() beforehand.
int dlclose(void *handle);
dlclose() decrements the count of open references to the library.
If reference count falls to zero, library is unloaded.
Example- dynload.c
#define _GNU_SOURCE
/*#}*/
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h
int
main(int argc, char *argv[])
{
void *libHandle; /* Handle for shared library */
void (*funcp)(void); /* Pointer to function with no args */
char *err;
if (argc != 3) { /* Check command line arguments */
fprintf(stderr, "Usage: %s lib-path func-name\n", argv[0]);
exit(EXIT_FAILURE);
} /* if */
/* Load the shared library and get a handle for later use */
libHandle = dlopen(argv[1], RTLD_LAZY);
if (libHandle == NULL) {
fprintf(stderr, "Error on dlopen: %s\n", dlerror());
exit(EXIT_FAILURE);
} /* if */
/* Get a pointer to named function inside library */
(void) dlerror(); /* Clear dlerror() */
funcp = dlsym(libHandle, argv[2]);
err = dlerror();
if (err != NULL) { /* Non-NULL from dlerror() means we got error */
fprintf(stderr, "Error on dlsym: %s\n", err);
exit(EXIT_FAILURE);
} /* if */
/*#{{*/
{
Dl_info dli;
dladdr(funcp, &dli);
printf("Lib name = %s\n", dli.dli_fname);
printf("Load address = 0x%lx\n", (long) dli.dli_fbase);
printf("Name of nearest sym = %s\n", dli.dli_sname);
printf("Load address = 0x%lx\n", (long) dli.dli_saddr);
}
/*#}*/
/* If the function address is non-NULL try calling it */
if (funcp == NULL)
printf("%s is NULL\n", argv[2]);
else
(*funcp)();
/* And close the library */
dlclose(libHandle);
exit(EXIT_SUCCESS);
} /* main */
Compile and run this program:
Reference:
No comments:
Post a Comment