hvn-network

My Blog List

Sunday, August 21, 2016

Create static library, shared library

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:
                  $ cc  -c mod1.c mod2.c mod3.c
  • Create library “libtest.a” :
                  $ ar  -cvq libtest.a mod1.c mod2.c mod3.c
  • List file in library:
                  $ ar  -t  libtest.a
  • Linking executable file with library:
                  $ cc –o prog prog.c libtest.a
                  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 do not need to be relinked to see changes in library code(Because object modules are not copied into executable files)
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:
                   $ gcc -fPIC -g -c -Wall mod1.c mod2.c mod3.c
                           -fPIC (Position Independent Code) causes compiler to generate code that can be loaded anywhere in virtual memory at run-time.
  • Create the shared library:
                  $ gcc -shared -o libfoo.so mod1.o mod2.o mod3.o
  •  Link program against library :
                  $ gcc -g -Wall -o prog prog.c libfoo.so
                  or:
                 $ gcc -g -Wall -o prog prog.c -L. –lfoo
  •   Execute the program
                   $ ./prog
                  ./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
       -  Dynamic linker searches these directories before looking in standard library directories.

                          $ 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
The purpose of the soname is to provide a level of indirection: At run-time, executable can use a version of the shared library that is different (but compatible) from that against which it was linked

  • Specify soname when creating shared library
                        $ gcc -fPIC -c -Wall -g mod1.c mod2.c mod3.c
                        $ 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:
                        $ gcc -g -Wall -o prog prog.c libfoo.so
Linker detects that libfoo.so contains the soname libbar.so and embeds the latter name inside the executable.

  •  Run the program:
                         $ LD_LIBRARY_PATH=.  ./prog 
                       ./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
                        $ ln -s libfoo.so libbar.so
                      $ 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
Create symbolic links for the soname and linker name:
               $ 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

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
- /usr/lib/ - directory in which most 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
                $ mv libdemo.so.1.0.1  /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 -fPIC -g -c -Wall mod1.c mod2.c mod3.c
                $ 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 -fPIC -g -c -Wall mod1.c mod2.c mod3.c
                $ 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:
- RTLD_LAZY: symbol referenced by library should be resolved only as code is executed.  - RTLD_NOW: all symbols references should be resolved before dlopen() completes
  • 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