The Static Library

A static library is a collection of object modules that are placed together in an archive file. Think of it as a repository of code, which is linked with your object code at link time, rather than at runtime. In this section, you will examine how to create, use, and maintain a static library.

Examining the Process Memory Image

Figure 13.1 shows how a small program memory image is allocated in FreeBSD and Linux. Other UNIX platforms will use similar arrangements, but their addresses will be different.

The addresses indicated in Figure 13.1 are only approximate. In the uppermost region of memory are the environment variables. Below them is the top of the stack, which grows downward for most UNIX platforms. At the bottom of the stack is a slack area of unallocated memory.

At the left side of the figure is a series of boxes that represent static library modules and program object modules that are used as input to the linking process. The arrows show how the linker brings them together to form a memory image, which begins at 0x80000000 and works its way up to 0x80049F18. This collection of regions forms what is stored in the executable file.

Figure 13.1. The composition of a process memory image.


The region below 0x80000000 is reserved for any dynamic (shared) libraries that may need to be brought into memory when the program begins its execution. This area is also used for attaching to shared data regions of memory.

The library code, which is linked to the executable image and resides beneath the main program in Figure 13.1, is called static library code. It is static because once it is linked to the program, it never changes. This is in contrast to shared library modules, which are loaded at execution time beneath the address 0x80000000. If you change the shared libraries, it is the changed libraries that are loaded and executed with your program. The participating static library code never changes once the executable file has been written.

Implementing a Static Library

To demonstrate the use of a static library, a small project that implements a Passwd class is used. This project reinforces the concepts that were covered in Chapter 12, "User ID, Password, and Group Management." Listing 13.1 shows the class definition.

Code Listing 13.1. passwd.h—The Include File for the Passwd Class Example
1:   // passwd.h
2:
3:   #include <sys/types.h>
4:   #include <pwd.h>
5:
6:   class Passwd : public passwd {
7:       enum {
8:           undefined,                  // object has no content
9:           defined                     // object has content
10:      }        state;                 // This object's state
11:      int     e;                      // Last errno
12:  protected:
13:      void _dispose();                // Dispose of current content
14:      void _import(struct passwd *p); // Import new contents
15:  public:
16:      Passwd()                        // Constructor
17:          {  state = undefined; e = 0; }
18:      ~Passwd()                       // Destructor
19:          {  _dispose(); }
20:      inline int isValid()
21:          {  return state == defined ? 1 : 0; }
22:      inline int getError()           // Get errno value
23:          {  return e; }
24:      char *getuid(uid_t uid);        // Lookup uid, return name
25:      int getnam(const char *name);   // Lookup name, return Boolean
26:  } ;
27:
28:  // End passwd.h

The code in Listing 13.2 implements the methods for Passwd_getuid() and Passwd::getnam().

Code Listing 13.2. getuid.cc—The Implementation of Passwd:getuid() and Passwd::getnam() Methods
1:   // getuid.cc
2:
3:   #include <errno.h>
4:   #include "passwd.h"
5:
6:   ////////////////////////////////////////////////////////////
7:   // LOOKUP UID VALUE:
8:   //      Returns ptr to this->pw_name
9:   //      Throws errno if call fails
10:  ////////////////////////////////////////////////////////////
11:
12:  char *
13:  Passwd::getuid(uid_t uid) {
14:      passwd *p = 0;
15:
16:      if ( state == defined )
17:          _dispose();             // Dispose of content
18:
19:      e = errno = 0;              // Clear errno
20:      p = ::getpwuid(uid);        // Look up uid
21:
22:      if ( !p ) {
23:          if ( !errno )
24:              e = ENOENT;         // Use ENOENT for "not found"
25:          else
26:              e = errno;          // Capture errno
27:          throw e;                // throw the error
28:      }
29:
30:      _import(p);                 // Copy to this object
31:      return this->pw_name;       // Return login name
32:  }
33:
34:  ////////////////////////////////////////////////////////////
35:  // LOOKUP LOGIN NAME :
36:  //      Returns uid_t value
37:  //      Throws errno if call fails
38:  ////////////////////////////////////////////////////////////
39:
40:  int
41:  Passwd::getnam(const char *name) {
42:      passwd *p = 0;
43:
44:      if ( state == defined )
45:          _dispose();             // Dispose of content
46:
47:      e = errno = 0;              // Clear errno
48:      p = ::getpwnam(name);       // Look up uid
49:
50:      if ( !p ) {
51:          if ( !errno )
52:              e = ENOENT;         // Use ENOENT for "not found"
53:          else
54:              e = errno;          // Else capture errno
55:          throw e;                // Throw the error
56:      }
57:
58:      _import(p);                 // Copy to this object
59:      return p->pw_uid;           // Return uid #
60:  }
61:
62:  // End getuid.cc

Listing 13.3 shows code that implements the protected methods Passwd::_import() and Passwd::_dispose(). These methods manage dynamic string memory allocation and destruction.

Code Listing 13.3. import.cc—The Implementation of the Protected Passwd::_import() and Passwd::_dispose() Methods
1:   // import.cc
2:
3:   #include "passwd.h"
4:   #include <string.h>
5:
6:   extern "C" char *strdup(const char *str);
7:
8:   ////////////////////////////////////////////////////////////
9:   // DISPOSE OF OBJECT'S CONTENTS (IF ANY):
10:  //      1. Check state (if defined)
11:  //      2. Delete all allocated strings
12:  //      3. Set state to "undefined"
13:  ////////////////////////////////////////////////////////////
14:
15:  void
16:  Passwd::_dispose() {
17:      if ( state == defined ) {
18:          delete pw_name;     pw_name = 0;
19:          delete pw_passwd;   pw_passwd = 0;
20:          delete pw_gecos;    pw_gecos = 0;
21:          delete pw_dir;      pw_dir = 0;
22:          delete pw_shell;    pw_shell = 0;
23:      }
24:      state = undefined;
25:  }
26:
27:  ////////////////////////////////////////////////////////////
28:  // IMPORT A STRUCT PW INTO THIS OBJECT :
29:  //      1. Dispose of current contents
30:  //      2. Copy and strdup(3) member components
31:  //      3. Set state to "defined"
32:  ////////////////////////////////////////////////////////////
33:
34:  void
35:  Passwd::_import(passwd *pw) {
36:
37:      if ( state == defined )
38:          _dispose();                 // Dispose of present content
39:
40:      pw_name = strdup(pw->pw_name);
41:      pw_passwd = strdup(pw->pw_passwd);
42:      pw_uid = pw->pw_uid;
43:      pw_gid = pw->pw_gid;
44:      pw_gecos = strdup(pw->pw_gecos);
45:      pw_dir = strdup(pw->pw_dir);
46:      pw_shell = strdup(pw->pw_shell);
47:
48:      state = defined;                // Set into defined state
49:  }
50:
51:  // End import.cc

In order to test the Passwd class that is implemented in Listings 13.1 to 13.3, a main() program is provided in Listing 13.4.

Code Listing 13.4. main.cc—The main() Test Program for the Passwd Class
1:   // main.cc
2:
3:   #include <iostream.h>
4:   #include <string.h>
5:   #include "passwd.h"
6:
7:   int
8:   main(int argc,char **argv) {
9:       unsigned ux;
10:      Passwd pw;
11:      const char *accts[] = {  "uucp", "xyzzy", "games" } ;
12:
13:      (void) argc;
14:      (void) argv;
15:
16:      // Report root's home directory :
17:
18:      try {
19:          pw.getuid(0);           // Lookup root
20:          cout << "Root's home dir is " << pw.pw_dir << ".
";
21:      } catch ( int e ) {
22:          cerr << strerror(e) << ": looking up uid(0)
";
23:      }
24:
25:      // Try a few accounts :
26:
27:      for ( ux=0; ux<sizeof accts/sizeof accts[0]; ++ux )
28:          try {
29:              pw.getnam(accts[ux]);   // Lookup account
30:              cout << "Account " << accts[ux]
31:                  << " uses the shell " << pw.pw_shell << ".
";
32:          } catch ( int e ) {
33:              cerr << strerror(e) << ": looking up account "
34:                  << accts[ux] << ".
";
35:          }
36:
37:      return 0;
38:  }

The main() program instantiates the Passwd class in line 10 of Listing 13.4. The first test (lines 18–23) simply looks up root's home directory and reports it (line 20).

The second group of tests are performed in the for loop of lines 27–35. This loop looks up the account names uucp, xyxxy, and games. The shell program for each is listed if the account exists. Account xyzzy is not expected to exist on most systems and is provided as a test of the error exception raised by the object pw.

The result of compiling and running this test should be something like this:

$ make getuid
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall -fPIC -fhandle-exceptions -g import.cc
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall -fPIC -fhandle-exceptions -g getuid.cc
ar -r libpasswd.a import.o getuid.o
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall -fPIC -fhandle-exceptions -g main.cc
cc -o getuid main.o -L/home/wwg/book3-code/13 -lpasswd -lstdc++
$ ./getuid
Root's home dir is /root.
Account uucp uses the shell /usr/libexec/uucp/uucico.
    No such file or directory: looking up account xyzzy.
Account games uses the shell /sbin/nologin.
$

To aid you with following the upcoming text, please remove the archive file, which the make file above has produced:

$ rm libpasswd.a
						

The project, when compiled, consists of the following object files, which form input to the linker:

The main program main.o
Some protected methods import.o
Methods getuid and getnam getuid.o

The object module main.o is not a reusable piece of code, but the import.o and getuid.o modules implement a class that can be used by other projects. These two object modules will be placed into a static library for general use.

Using the ar(1) Command to Create an Archive

The ar(1) command is used to create and maintain archive files. Since a static library is a special form of an archive, then the ar(1) command can be used to create a static library.

If you have the object modules import.o and getuid.o, the static library libpasswd.a can be created as follows:

$ ar r libpasswd.a import.o getuid.o

The ar(1) command is one of those UNIX commands that break from the traditional getopt(3) processing standard. However, most UNIX platforms today now support a leading hyphen character for this command, allowing it to be given as follows:

$ ar -r libpasswd.a import.o getuid.o

The -r (or simply r) that follows the command name is an option letter that causes the archive libpasswd.a to be created if necessary and replaces the listed object modules if they already exist. If they do not exist in the archive, the listed object modules are added to it.

The normal convention for a library is that it begins with the three letters lib. Archives use the suffix .a. Following these conventions, you end up with a static library named libpasswd.a.

Archives can be updated after their initial creation. If you discover that the getuid.o module has bugs in it, you can replace it with the fixed and recompiled version of the object module, as follows:

$ ar -r libpasswd.a getuid.o

This type of update is generally performed only for large libraries. Smaller archives are usually re-created from scratch by the make file. The following example shows how a make file creates the static library libpasswd.a:

$ make libpasswd.a
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall -fhandle-exceptions import.cc
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall -fhandle-exceptions getuid.cc
ar -r libpasswd.a import.o getuid.o
$

At the completion of this session, the static library libpasswd.a is ready to be used by other projects.

Listing the Contents of an Archive

You can list the contents of an existing archive by performing the following:

$ ar -t libpasswd.a
import.o
getuid.o
$

The option -t (or simply t) causes ar(1) to list the table of contents for the archive named.

Obtaining a Verbose Listing of an Archive

More information can be displayed by adding the option letter v for a verbose table of contents:

$ ar -tv libpasswd.a
rw-r----- 1001/2010   2536 May 11 12:18 2000 import.o
rw-r----- 1001/2010   2948 May 11 12:18 2000 getuid.o
$

The leftmost column shows the permission bits that were present when the module was added to the archive. These are displayed in the same form as the ls(1) command. The numbers 1001 and 2010 in the example represent the user ID and group ID numbers, respectively. The date and time are also shown, just left of the module filenames.

Linking with Static Libraries

The link step for shared libraries is easy to accomplish. The filename of the static library can be placed on the link command line like any object module. Alternatively, you can place the library in a certain directory and link with it using the -l option. The following example shows the former method of specifying the filename:

$ cc -o getuid main.o libpasswd.a -lstdc++
						

In the command shown, the file libpasswd.a is simply specified on the command line, where any *.o object file could have been given. In larger projects, it's often desirable to place the shared library in a central directory, /usr/local/lib, for example. When this is done, you need to tell the linker where this special directory is, using the -L option. One or more library can then be specified using the -l option. The following is a simple example:

$ make getuid
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall -fhandle-exceptions main.cc
cc -o getuid main.o -L/home/me/myproject -lpasswd -lstdc++
$

In this example, the link step specified -L/home/me/myproject to indicate that libraries will be found there. The option -lpasswd caused the linker to look for the library libpasswd.a, in the indicated directory (in addition to system standard directories).

The highlights of the linking process are shown in Figure 13.2.

Figure 13.2. The static library linking process.


The steps used in the linking process can be summarized as follows:

  1. The linking process begins with the loading of the main.o module (in this example).

  2. Then, the linker notes that there are undefined references to symbols Passwd:getuid() and Passwd::getnam() referenced by the main() function.

  3. Since a library has been provided (libpasswd.a), this archive file is searched for an object module that defines the symbol Passwd::getuid. The linker locates a function named Passwd:getnam() in the object module getuid.o, which is contained within the archive file. The linker then extracts module getuid.o from the archive file and loads it. In the process of doing this, the symbol Passwd::getnam() is resolved as well, since it is also contained in the same object module.

  4. The linker reviews its list of unresolved symbols. The symbol Passwd::_import() is now unresolved. This new reference is from the object module getuid.o that the linker just loaded.

  5. Working from the top of the list, the linker searches the archive libpasswd.a again and determines that it must extract and load module import.o. This satisfies the symbol Passwd::_import().

This is an oversimplification of the linking process, since references to the C library functions such as free(3) were ignored (for the delete C++ keyword). However, this illustrates what happens when a static library is involved in the linking process.

From this process, it has been demonstrated that object modules are brought in by the linker only as required. This bodes well for those who wish to use only a few functions in a large collection of functions. After all, you do not want to link with every object module if you need only a small part of the library.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.89.24