|
|
This section contains the process by which a small specialized shared library is created and built. We refer to the guidelines given earlier in this chapter.
The name of the library to be built is libmaux (for
math auxiliary library).
The interface consists of three functions, an external
variable, and a header file.
The three functions are:
The source files before any modifications for inclusion in a shared library
follow.
File log.c
/* log.c */ #include "maux.h" #include <math.h>/* * Return the log of "x" relative to the base "a". * * logd(base, x) := log(x) / log(base); * where "log" is "log to the base E". */
double logd(base, x) double base, x; { extern int stats_logd; extern int total_calls;
double logbase; double logx;
total_calls++; stats_logd++;
logbase = log((double)base); logx = log((double)x); if(logbase == -HUGE || logx == -HUGE) { mauxerr = 1; return(0); } else mauxerr = 0; return(logx/logbase); }
File poly.c
/* poly.c */
#include "maux.h" #include <math.h>
/* Evaluate the polynomial * f(x) := a[0] * (x ^ n) + a[1] * (x ^ (n-1)) + ... + a[n]; * Note that there are N+1 coefficients! * This uses Horner's Method, which is: * f(x) := (((((a[0]*x) + a[1])*x) + a[2]) + ...) + a[n]; * It's equivalent, but uses many fewer operations * and is more precise. */ double polyd(a, n, x) double a[]; int n; double x; { extern int stats_polyd; extern int total_calls; double result; int i;
total_calls++; stats_polyd++; if (n < 0) { mauxerr = 1; return(0); } result = a[0]; for (i = 1; i <= n; i++) { result *= (double)x; result += (double)a[i]; } mauxerr = 0; return(result); }
File stats.c
/* stats.c */ #include "maux.h"int total_calls = 0; int stats_logd = 0; int stats_polyd = 0;
int mauxerr;
/* Return structure with usage stats for functions in library * or 0 if space cannot be allocated for the structure */ struct mstats * maux_stat() { extern char * malloc(); struct mstats * st;
if((st = (struct mstats *) malloc(sizeof(struct mstats))) == 0) return(0); st->st_polyd = stats_polyd; st->st_logd = stats_logd; st->st_total = total_calls; return(st); }
Header File maux.h
/* maux.h */
struct mstats { int st_polyd; int st_logd; int st_total; };
extern double polyd(); extern double logd(); extern struct mstats * maux_stat();
extern int mauxerr;
First, we choose the region addresses for the library's .text and .data sections from the segments reserved for private use on the 80386 Computer. Note that the region addresses must be on a segment boundary (4 MB):
.text 0xB0600000
.data 0xB0a00000
Also we choose the pathname for our target library:
/my/directory/libmaux_s
This example is for illustration purposes, and so we will include everything in the shared library. In a real case, it is unlikely that you would make a shared library with these three small routines, unless you had many programmers using them frequently.
According to the guidelines given earlier in the chapter, you need to first minimize the global data. We realize that total_calls, stats_logd, and stats_polyd do not need to be visible outside the library, but are needed in multiple files within the library. Hence, we will use the #hide directive in our specification file to make these variables static after the shared library is built.
We need to define text and global data in separate source files. The only piece of global data we have left is mauxerr, which we will remove from stats.c and put in a new file maux_defs.c. We will also have to initialize it to zero, since shared libraries cannot have any uninitialized variables.
Next, we notice that there are some references to symbols that we do not define in our shared library (i.e. log and malloc). We can import these symbols. To do so, we create a new header file, import.h, which will be included in each of log.c, poly.c, and stats.c. The header file defines C preprocessor macros for these symbols to make transparent the use of indirection in the actual C source files. Use the _libmaux_ prefixes on the pointers to the symbols because those pointers are made external, and the use of the library name as a prefix helps prevent name conflicts.
/* New header file import.h */ #define malloc (*_libmaux_malloc) #define log (*_libmaux_log)extern char * malloc(); extern double log();
Now, you need to define the imported symbol pointers somewhere. You have already created a file for global data maux_defs.c, so add the definitions to that.
/* Data file maux_defs.c */int mauxerr = 0; double (*_libmaux_log)() = 0; char * (*_libmaux_malloc)() = 0;
Finally, you observe that there are floating-point operations in the
code, and you remember that the routines for these cannot be imported.
(If you tried to write the specification file
and build the shared library without
taking this into account, mkshlib would give you errors about
unresolved references.)
This means you will have to use the #objects noload directive
in our specification file
to search the C host shared library to resolve the references.
This is the specification file for libmaux:
libmaux.sl
1 ## 2 ## libmaux.sl - libmaux specification lfile 3 #address .text 0xB0680000 4 #address .data 0xB06a0000 5 #target /my/directory/libmaux_s 6 #branch 7 polyd 1 8 logd 2 9 maux_stat 3 10 #objects 11 maux_defs.o 12 poly.o 13 log.o 14 stats.o 15 #objects noload 16 -lc_s 17 #hide linker 18 #export linker 19 mauxerr 20 #init maux_defs.o 21 _libmaux_malloc malloc 22 _libmaux_log log
Briefly, this is what the specification file does. Lines 1 and 2 are comment lines. Lines 3 and 4 give the virtual addresses for the shared library text and data regions, respectively. Line 5 gives the pathname of the shared library on the target machine. The target shared library must be installed there for a.out files that use it to work correctly. Line 6 contains the #branch directive. Line 7 through 9 specify the branch table. They assign the functions polyd(), logd(), and maux_stat() to branch table entries 1, 2, and 3. Only external text symbols, such as C functions, should be placed in the branch table.
Line 10 contains the #objects directive. Lines 11 through 14 give the list of object files that will be used to construct the host and target shared libraries. When building the host shared library archive, each file listed here will reside in its own archive member. When building the target library, the order of object files will be preserved. The data files must be first. Otherwise, an addition of static data to poly.o, for example, would move external data symbols and break compatibility.
Line 15 contains the #objects noload directive, and line 16 gives information about where to resolve the references to the floating-point routines.
Lines 17 through 19 contain the #hide linker and #export linker directives, which tell what external symbols are to be left external after the shared library is built. Together, these #hide and #export directives say that only mauxerr will remain external. The symbols in the branch table and those specified in the #init directive will remain external by definition.
Line 20 contains the #init directive. Lines 21 and 22 give imported symbol information for the object file maux_defs.o. You can imagine assignments of the symbol values on the right to the symbols on the left. Thus _libmaux will hold a pointer to malloc, and so on.
Now, you have to compile the .o files as you would for any other library:
cc -c maux_defs.c poly.c log.c stats.cNext, you need to invoke mkshlib to build our host and target libraries:
mkshlib -s libmaux.sl -t libmaux_s -h libmaux_s.aPresuming all of the source files have been compiled appropriately, the mkshlib command line shown above will create both the host library, libmaux_s.a, and the target library, libmaux_s. Before any a.out files built with libmaux_s.a can be executed, the target shared library libmaux_s must be moved to /my/directory/libmaux_s as specified in the specification file.
To use the shared library with a file, x.c, which contains a
reference to one or more of the routines in libmaux,
issue the following command line:
cc x.c libmaux_s.a -lm -lc_s
This command line causes the following:
The most important thing to note from the command line, however, is that you have to specify the C host shared library (in this case with the -lc_s) on the command line, since libmaux was built with direct references to the floating-point routines in that library.