2.8. Creating shared objects

This section explains how to create shared objects on AIX. Once shared objects are created, you can archive them into a library using the ar command (see 2.2.2, “Objects and libraries” on page 45).

In order to create a shared object, the following tasks are required:

1.
Compile the source code files and create object files. Use the appropriate compiler driver to do so, for example, cc -c foo.c.

2.
Create an export file to explicitly control exported symbols from the shared object, which you are going to create.

3.
Re-link the created object file(s) to a shared object file. It is possible to combine multiple object files into a single shared object file. The following linker options are used in this step:

-bE:export_fileSpecifies the export file name created in the previous step.
-bM:SREMarks the resultant object file as a re-entrant shared object module.
-bnoentryIndicates that the output object file has no entry point.

4.
Create an import file to explicitly control how exported symbols in the shared object will be imported by other modules. This is an optional step.

Note

Steps 2 and 4 can be skipped if the -bexpall linker option is used. However, if this option is used, you cannot precisely control the symbol exporting (see “The -bexpall linker option” on page 94).


2.8.1. Import and export files

An export file is a text file containing a list of symbols. It is used to control which symbols are visible outside the shared object. The symbols not specified in the export file are only visible to other routines within the shared object. The use of export files allows a developer to create a shared object that has a well defined interface. Only the symbols listed in the export file can be referenced by executables and other shared objects that are linked with the object. Export files are normally identified by the file name extension .exp.

An import file is a text file that lists the names of symbols that the shared object may reference. It allows the object to be created without the source of those symbols being available. Once an export file is created, an import file can be created from the export file by adding several directive lines starting from “#!”. Table 2-7 shows the supported directives for import files.

Table 2-7. Directive lines for import files
DirectiveDescription
#!(Nothing after the #!) Use null path, null file, and null number. This is treated as a deferred import by the system loader.
#!()Use -bipath, -bifile, and -bimember. This line can be used if the import file is specified as an InputFile parameter on the command line. The file must begin with #! in this case. This line can also be used to restore the default name if it was changed by another #! line.
#!path/file(member)Use the specified path, file, and archive member.
#!path/fileUse the specified path and file, and a null member.
#!fileUse a null path, the specified file, and a null member. At run time, a list of directories is searched to find the shared object.
#!(member)Use -bipath, -bifile, and the specified member. At run time, a list of directories is searched to find the shared object.
#!file (member)Use a null path and the specified file and member. At run time, a list of directories is searched to find the shared object.
#!.[1](A single dot) This name refers to the main executable. Use this file name when you are creating a shared object that imports symbols from multiple main programs with different names. The main program must export symbols imported by other modules, or loading will fail. This import file name can be used with or without the run-time linker.
#!..[1](Two dots) Use this name to list symbols that will be resolved by the run-time linker. Use this file name to create shared objects that will be used by programs making use of the run-time linker. If you use a module that imports symbols from .. in a program that was not linked with the -brtllib option, symbols will be unresolved, and references to such symbols will result in undefined behavior.

[1] These directive lines have been supported on AIX since Version 4.2.

The creation and use of import files are not mandatory except for the following situation:

  • Limiting accessible symbols exported from shared objects so that only specified symbols listed in the import file will be imported.

  • Creating multiple shared objects that have dependencies on each other (see 2.8.3, “Interdependent shared objects” on page 96).

  • Accessing special symbols such as symbols in the main program. In this case, use the #!. directive line in the import file before the symbols that will be imported from the main program.

If run-time linking (explained in 2.5, “Run-time linking” on page 68) or dynamic loading (explained in 2.6, “Dynamic loading” on page 82) is not used, all referenced external symbols must be accessible and resolvable when the module is linked.

If symbols are listed after the #! directive in an import file, the linker considers those symbols to be created declared differently, so they must be programatically resolved and handled by a set of library calls provided by dynamic loading. If symbols are listed after the #!.. directive in an import file, the linker considers those symbols to be declared to be handled by the run-time linker.

The -bexpall linker option

If you are creating a shared object and want all symbols to be exported, then you do not need to create an export file. You can use the -bexpall linker option, which will automatically export all global symbols (except imported symbols, unreferenced symbols defined in archive members, and symbols beginning with an underscore). Additional symbols may be exported by listing them in an export list. Any symbol with a leading underscore will not be exported by this option. These symbols must be listed in an exports list to be exported.

The -bexpfull option

The new -bexpfull[18] link option operates like -bexpall, except that it actually exports all symbols and does not skip symbols that begin with an underscore.

[18] The -bexpfull linker option is supported on AIX 5L Version 5.2 and later and on AIX 5L Version 5.1 with 5100-02 Recommended Maintenance Level and later.

Creating an export list using CreateExportList

You can use the /usr/vac/bin/CreateExportList shell script supplied with the C for AIX Version 6 compiler to automatically generate the symbols that should be included in an export list. It can save a considerable amount of time if you need to create shared objects.

To use this command, do the following:

1.
Compile all of the source files that will be included in the shared object.

2.
Create a single file that lists the names of all of the object files that will be included in the shared object. For example, create a file called object.list that contains the following lines:

source1.o
source2.o

3.
Invoke CreateExportList as follows:

/usr/vac/bin/CreateExportList file.exp -f object.list

where file.exp is the name of the export file you want to create, and object.list is the file that contains the list of object file names.

4.
Edit the resulting export file to remove the symbol names you wish to keep private within the shared object.

5.
Edit the export file to include the #!pathname(member) lines at the appropriate positions in order to use the file as an import file.

2.8.2. A self-contained shared object

The scenario described in this section for creating a self-contained shared object uses the following source code files:

The file source1.c is as follows:

/* source1.c : First shared library source */
void private(void)
{
    printf("private
");
}
int addtot(int a, int b)
{
    int c;
    c = a + b;
    return c;
}

The file source2.c is as follows:

/* source2.c : Second shared library source */
#include <stdio.h>
int disptot(int a)
{
    printf("The total is : %d 
",a);
}

To create a self-contained shared object, do the following:

1.
Create the object files that will be combined together to create the shared object. This is achieved by using the -c compiler option. For example:

cc -c source1.c source2.c

2.
Create an export file that lists the symbol names that should be visible outside the shared object. In this example, the symbols addtotal and displaytotal are the names of the functions that will be called by the main application. The symbol names can also include variable names in addition to function names. Create the libadd.exp export file as follows:

addtot
disptot

3.
Create the shared object shrobj.so with the following command:

cc -o shrobj.o source1.o source2.o -bE:libadd.exp -bM:SRE -bnoentry

Use the dump command to examine the exported symbols from this shared object, as shown in the following example:

$ dump -Tv shrobj.o

shrobj.o:


                       ***Loader Section***

                       ***Loader Symbol Table Information***
[Index]      Value     Scn     IMEX Sclass   Type           IMPid Name

[0]     0x00000000   undef      IMP     DS EXTref   libc.a(shr.o) printf
[1]     0x2000020c   .data      EXP     DS SECdef        [noIMid] addtot
[2]     0x20000218   .data      EXP     DS SECdef        [noIMid] disptot

Two symbols, addtot and disptot, are exported from the created shared object shrobj.o as we expected (see “Displaying symbol definition with dump -Tv” on page 87 how to interpret the command output).

Note

If you do not have to precisely control which symbols will be exported, you can skip the step 2 and use the following command line in step 3:

$ cc -o shrobj.o source1.o source2.o -bexpall -bM:SRE -bnoentry


2.8.3. Interdependent shared objects

The process for creating interdependent shared objects is similar to the process of creating a self-contained shared object but requires the use of an import file. Suppose there are two shared objects, shr1.o and shr2.o, and each references symbols in the other. When creating the first shared object (shr1.o), the second shared object may not exist. This means that when the command to create the first shared object is executed, there will be unresolved symbols since, at this point, the second shared object does not exist. To avoid this paradox, create and use import files for these object files.

Consider the following files for use in this example scenario:

The file source1.c is as follows:

/* source1.c : First shared library source */
int function1(int a)
{
    int c;
    c = a + function2(a);
    return c;
}

int function3(int a)
{
    int c;
    c = a / 2;
    return c;
}

The file source2.c is as follows:

/* source2.c : Second shared library source */
int function2(int a)
{
    int c;
    c = function3(a + 5);
    return c;
}

In this example, each source file needs to be made into a separate, shared object. Note that the resulting shared objects are interdependent, since:

  • Subroutine function1 in source1.c calls function2 in source2.c.

  • Subroutine function2 in source2.c calls function3 in source1.c.

Create the export file libone.exp to define exporting symbols of the shared object shr1.o, which we are going to create from source1.c:

function1
function3

then insert the following directive at the beginning of libone.exp in order to use the file as an import file:

#!libone.a(shr1.o)

Create the export file libtwo.exp to define exporting symbols of the shared object shr2.o, which we are going to create from source2.c:

function2

then insert the following directive at the beginning of libtwo.exp in order to use the file as an import file:

#!libtwo.a(shr2.o)

To create two libraries, libone.a and libtwo.a, which contain only one archive member, shr1.o and shr2.o respectively, do the following:

cc -c source1.c source2.c
cc -o shr1.o source1.o -bE:libone.exp -bI:libtwo.exp -bM:SRE -bnoentry
ar rv libone.a shr1.o
cc -o shr2.o source2.o -bE:libtwo.exp -bI:libone.exp -bM:SRE -bnoentry
ar rv libtwo.a shr2.o

Note the use of the file libone.exp as an export file when creating the first shared object and as an import file when creating the second. If the file was not used when creating the second shared library, the creation of the second shared object would have failed with an error message complaining of unresolved symbols as follows:

cc -o shr2.o source2.o -bE:libtwo.exp -bM:SRE -bnoentry
ld: 0711-317 ERROR: Undefined symbol: .function3
ld: 0711-345 Use the -bloadmap or -bnoquiet option to obtain more information.

Figure 2-8 depicts the function calling relationship between these two shared objects.

Figure 2-8. Function calling relationship for an interdependent shared object


A single import file can be used to list all symbols that are imported from different modules. In this case, the import file is just a concatenation of the individual export files for each of the shared objects. For example, Example 2-23 on page 99 could have been used for the combined import file when linking two shared objects in the previous example.

Example 2-23. Combined import file
#!libone.a(shr1.o)
function1
function3
* a comment line starts with an asterix character. blank lines are ignored.

#!libtwo.a(shr2.o)
function2

Note

It is recommended to create a single self-contained shared object instead of multiple interdependent shared objects whenever possible, in order to avoid the complexity and the possible application performance degrade.


2.8.4. Initialization and termination routines

Optional shared object initialization and termination routines can be specified when creating the shared object. You can use one or the other, or both. The routines may be useful for initializing dynamic data structures or reading configuration information. The initialization routines are called by the program startup code and are performed before the application main routine is started. Termination routines are called when the program makes a graceful exit. They will not be called if the program exits due to receipt of a signal.

The -binitfini linker option is used to specify the names of the routines along with a priority number. The priority is used to indicate the order that the routines should be called in when multiple shared objects with initialization or termination routines are used.

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

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