As explained in 2.4.1, “AIX default linking” on page 64, all referenced symbols must be resolved at link-time when building executable files using the default, static, and lazy loading link methods on AIX. Those pre-resolved symbols cannot be rebound after the executable files are created.
The run-time link method, or run-time linking, enables a program to resolve its referenced symbols at the program load-time rather than link-time. It is the ability to resolve undefined and non-deferred symbols in shared modules after the program execution has already began. It is a mechanism for providing run-time definitions (for example, function definitions that are not available at the program link-time) and symbol rebinding capabilities. For example, if main() calls func1() in libfunc1.so, which then calls func2() in libfunc2.so, assuming that libfunc1.so and libfunc2.so were built to enable run-time linking, then the main application could provide an alternate definition of func2() that would override the one originally found in libfunc2.so.
Please note that it is the main application that has to be built to enable run-time linking. Simply linking a module with the run-time link library is not enough. This structure allows a module to be built to support run-time linking, yet continue to function in an application that has not been so enabled.
Note
In AIX, even if the run-time link method is used, all symbols except for the deferred symbols (see “Displaying symbol definition with dump -Tv” on page 87 for the definition of deferred symbols) must be resolved at the program load-time. However, in some other UNIX operating systems, resolution of function symbols is deferred until the function is first called (references to variables must be resolved at load-time). This allows the definition for a function to be loaded after the module referring to that symbol is loaded on those operating systems.
In order to use the run-time link method, the following points must be understood:
The run-time link method is enabled by the run-time link library (/usr/lib/librtl.a) specified by the -brtl option when generating the executable file. The -brtl option is mutually exclusive with the -blazy option.
Only run-time linking shared objects are run-time linked. Objects or archive members other than run-time shared objects are linked in the default link method.
Run-time linking shared objects must be created before linking them with the main program, as explained in “Creating run-time linking shared objects” on page 71.
Run-time linking shared objects should use the file name convention libname.so. If this naming convention is used, it is easy to distinguish run-time linking shared objects from other types of objects and easy to specify the file path name on the command line (see 2.5.5, “Extended search order with the -brtl linker option” on page 81).
An advantage of using run-time linking is that developers do not need to maintain a list of module interdependencies and import/export lists (see 2.8.1, “Import and export files” on page 92). By using the -bexpall linker option, all shared objects can export all symbols, and the run-time linker can be used to resolve the inter-module dependencies.
This section explains how to use run-time linking by providing several simple examples.
Our example program is composed of four C program source files, main.c (shown in Example 2-8), func1.c (Example 2-9), func2.c, and func3.c (Example 2-10). The source file of func2.c is the same as func3.c, except that the function name is func2, not func3.
#include <stdio.h> #include <stdlib.h> extern void func1(void); int main(int argc, char *argv[]) { func1(); } |
#include <stdio.h> #include <stdlib.h> void func1(void) { printf("within function %s at line number: %d in %s " , __FUNCTION__, __LINE__, __FILE__); func3(); } |
#include <stdio.h>
#include <stdlib.h>
void func3(void)
{
printf("within function %s at line number: %d in %s
"
, __FUNCTION__, __LINE__, __FILE__);
}
|
If this simple application is statically built as follows:
$ cc -c main.c func1.c func2.c func3.c main.c: func1.c: func2.c: func3.c: $ ls main.o func1.o func2.o func3.o func1.o func2.o func3.o main.o $ cc main.o func1.o func2.o func3.o
then the generated executable file a.out prints the following output:
$ ./a.out within function func1 at line number: 7 in func1.c within function func3 at line number: 7 in func3.c
Please note the function func2 in func2.c is not called at all in this application. This function is intentionally included in this example in order to show the important aspect of run-time linking in later sections.
In order to use run-time linking, run-time linking shared objects must be created before linking them with the main program. To create a run-time linking shared object, do the following:
1. | Compile the source file and create an object file. |
2. | |
3. | Archive the created run-time linking shared object into a library archive (libname.a). This is an optional step. |
In fact, these steps are very similar to the steps to create regular (run-time linking disabled) shared objects except for the specified linker options (see 2.8, “Creating shared objects” on page 92).
For example, to create three run-time linking shared objects from func1.c, func2.c, and func3.c, do the following:
$ cc -c func1.c func2.c func3.c func1.c: func2.c: func3.c: $ ld -G -o func1.so func1.o -bnoentry -bexpall $ ld -G -o func2.so func2.o -bnoentry -bexpall -lc $ ld -G -o func3.so func3.o -bnoentry -bexpall -lc $ ls *.o *.so func1.o func1.so func2.o func2.so func3.o func3.so
The following linker options are specified in this example:
-bnoentry | Indicates that the output file has no entry point. To retain any needed symbols, specify them with the -u flag or with an export file. You can also use the -r flag or the -bnogc or -bgcbtpass options to keep all external symbols in some or all object files. If neither the -bnoentry nor the -bnox option is used and the entry point is not found, a warning is issued. |
-bexpall | Exports all global symbols, except imported symbols, unreferenced symbols defined in archive members, and symbols beginning with an underscore (_). You may export additional symbols by listing them in an export file. This option does not affect symbols exported by the -bautoexp option. This option only applies to AIX Version 4.2 and later. |
When you use this option, you may be able to avoid using an export file. On the other hand, using an export file provides explicit control over which symbols are exported, and allows you to use other global symbols within your shared object without worrying about conflicting with names exported from other shared objects. The default is -bnoexpall. | |
-lc | Specifies referenced libraries. As for func2.o and func3.o, the standard C library must be specified to resolve the symbol of printf(). In the case of func1.o, there is no need to specify any libraries, since it does not call any functions, except for func3. |
-G | The -G linker option is equivalent to specifying all the options shown in Table 2-5 on page 73. |
For the detailed information about these linker options, please refer to the ld command section in the AIX 5L Version 5.2 Reference Documentation: Commands Reference.
Note
Compiler drivers also have the -G option. Do not use the compiler -G option to create run-time linking shared objects from object files.
In order to create an executable linked with run-time linking shared objects, do the following:
1. | Compile the main program source file, which contains main(), and create an object file. |
2. | Re-link this object file with the -G linker option and create a run-time linking shared object (.so). |
3. | Link the object file compiled from the main program source file with run-time linking shared objects as well as the -brtl linker option specified. |
If the object file created from the main program source file does not have be run-time linking enabled, then the required task is shorten as follows:
Use an appropriate compiler driver, not the linker, to compile the main program source file, which contains main(), then link the object file with run-time linking shared objects by specifying the -brtl option.
As for our program example, we have built the executable a.out as follows using the later simple method:
$ ls *.o *.so func1.o func1.so func2.o func2.so func3.o func3.so $ cc main.c func1.so func2.so func3.so -brtl $ ls a.out a.out
If executed, the program would print the following error message and fail to execute, since the dependent module func1.so could not be found by the system loader:
$ ./a.out exec(): 0509-036 Cannot load program ./a.out because of the following errors: 0509-150 Dependent module func1.so could not be loaded. 0509-022 Cannot load module func1.so. 0509-026 System error: A file or directory in the path name does not exist.
If LIBPATH is set as follows, it executes as expected:
$ LIBPATH=$PWD ./a.out within function func1 at line number: 7 in func1.c within function func3 at line number: 7 in func3.c
If the linker command (ld) was used instead of the compiler driver (cc) as follows:
$ cc -c main.c $ ld main.o func1.so func2.so func3.so -brtl
then the program would print the following error message, since the generated executable would not contain the necessary start up routine:
$ LIBPATH=$PWD ./a.out exec(): 0509-036 Cannot load program ./a.out because of the following errors: 0509-151 The program does not have an entry point or the o_snentry field in the auxiliary header is invalid. 0509-194 Examine file headers with the 'dump -ohv' command.
By using the dump command, you can examine whether the generated executable is linked using run-time linking or not. For example, the dump command with the -H option shows the header information for our example application a.out, as shown in Example 2-11.
Example 2-11 includes two important lines (highlighted):
The run-time linking shared object func2.so is included in the dependent module list, though any symbols in this module are not referenced in the application at all.
Note
If this executable was compiled without -brtl, func2.so would not be included in the dependent module list.
The run-time linker, the archive member shr.o in librtl.a, is included in the dependent module list.
$ dump -H a.out a.out: ***Loader Section*** Loader Header Information VERSION# #SYMtableENT #RELOCent LENidSTR 0x00000001 0x00000009 0x00000011 0x0000005e #IMPfilID OFFidSTR LENstrTBL OFFstrTBL 0x00000006 0x000001c4 0x0000002a 0x00000222 ***Import File Strings*** INDEX PATH BASE MEMBER 0 /usr/lpp/xlopt:/usr/lib:/lib 1 func1.so 2 func2.so 3 func3.so 4 libc.a shr.o 5 librtl.a shr.o |
Note
The order of run-time linking shared objects listed in the executable’s header is same with the order of object files specified in the linker command line when generating the executable file:
$ cc main.c func1.so func2.so func3.so -brtl
The dump command with the -Tv option shows the referenced symbols information for our example application a.out, as shown in Example 2-12.
A very important fact in Example 2-12 is that there is no entry for the function func3, which is called from func1 in func1.c. As explained in 2.3, “Resolving symbols at link-time” on page 53, all symbols must be resolved at the program link-time, and all the resolved symbol information will be shown in the XCOFF loader section of the generated executable on AIX by default. However, in the case of run-time linking, symbols to be resolved by the run-time linker will not be shown in the XCOFF loader section of the generated executable, thus func3 is not shown in Example 2-12.
$ dump -Tv a.out a.out: ***Loader Section*** ***Loader Symbol Table Information*** [Index] Value Scn IMEX Sclass Type IMPid Name [0] 0x20000448 .data RW SECdef [noIMid] __rtinit [1] 0x00000000 undef IMP RW EXTref libc.a(shr.o) errno [2] 0x00000000 undef IMP DS EXTref libc.a(shr.o) exit [3] 0x00000000 undef IMP DS EXTref libc.a(shr.o) __mod_init [4] 0x00000000 undef IMP BS EXTref libc.a(shr.o) __crt0v [5] 0x00000000 undef IMP BS EXTref libc.a(shr.o) __malloc_user_defined_name [6] 0x00000000 undef IMP DS EXTref func1.so func1 [7] 0x00000000 undef IMP DS EXTref librtl.a(shr.o) __rtld [8] 0x20000458 .data ENTpt DS SECdef [noIMid] __start |
Example 2-13 on page 77 shows the header information for the dependent shared object module func1.so. The double dot characters (..) in the last line indicate that at least one run-time linking shared objects are required in order to resolve unresolved symbols in this module.
$ dump -H func1.so func1.so: ***Loader Section*** Loader Header Information VERSION# #SYMtableENT #RELOCent LENidSTR 0x00000001 0x00000003 0x00000006 0x00000015 #IMPfilID OFFidSTR LENstrTBL OFFstrTBL 0x00000002 0x000000b0 0x00000000 0x00000000 ***Import File Strings*** INDEX PATH BASE MEMBER 0 /usr/lib:/lib 1 .. |
Example 2-14 shows the loader section information for the dependent shared object module func1.so. There are two imported symbols, printf and func3, in this module.[16] The double dot characters (..) in the IMPid column indicate that the symbol will be resolved by the run-time linker at the program load-time.
[16] See “Displaying symbol definition with dump -Tv” on page 87 how to interpret columns shown in the dump -Tv output.
$ dump -Tv func1.so func1.so: ***Loader Section*** ***Loader Symbol Table Information*** [Index] Value Scn IMEX Sclass Type IMPid Name [0] 0x00000050 .data EXP DS SECdef [noIMid] func1 [1] 0x00000000 undef IMP DS EXTref .. printf [2] 0x00000000 undef IMP DS EXTref .. func3 |
Figure 2-6 on page 78 depicts the function calling relationship for our application built in this section. The run-time linking shared object func2.so is referenced by the executable file (a.out) and will be loaded into the system memory by the system loader at the program execution time, even if a symbol in this object is not referenced by the executable.
If you closely examine Example 2-12 on page 76, you notice that func1.so was not linked as a run-time linking shared object. The IMPid column of the func1 symbol shows the file name of func1.so, thus it is treated as a regular shared object. The reason is that the object file created from the main program source file, which includes main(), had not been run-time linking enabled, therefore all dependent modules that were required to resolve symbols referenced by main.o had to be loaded at the program load-time by the system loader.
In order to enable the main program object as a run-time linking shared object, we could have done the following (highlighted lines and command options are different):
$ cc -c main.c func1.c func2.c func3.c main.c: func1.c: func2.c: func3.c: $ ld -G -o main.so main.o -bexpall ld: 0711-327 WARNING: Entry point not found: __start $ ld -G -o func1.so func1.o -bnoentry -bexpall $ ld -G -o func2.so func2.o -bnoentry -bexpall -lc $ ld -G -o func3.so func3.o -bnoentry -bexpall -lc $ cc -o a.out main.so func1.so func2.so func3.so -brtl
After specifying LIBPATH, the program prints the following output as expected:
$ LIBPATH=$PWD ./a.out within function func1 at line number: 7 in func1.c within function func3 at line number: 7 in func3.c
Example 2-15 on page 79 shows the referenced symbol information for a.out generated by the compiler driver. Please note that Example 2-15 on page 79 does not contain the symbol information for either func1 or func3.
$ dump -Tv a.out a.out: ***Loader Section*** ***Loader Symbol Table Information*** [Index] Value Scn IMEX Sclass Type IMPid Name [0] 0x200003e8 .data RW SECdef [noIMid] __rtinit [1] 0x00000000 undef IMP RW EXTref libc.a(shr.o) errno [2] 0x00000000 undef IMP DS EXTref libc.a(shr.o) exit [3] 0x00000000 undef IMP DS EXTref libc.a(shr.o) __mod_init [4] 0x00000000 undef IMP BS EXTref libc.a(shr.o) __crt0v [5] 0x00000000 undef IMP BS EXTref libc.a(shr.o) __malloc_user_defined_name [6] 0x00000000 undef IMP DS EXTref main.so main [7] 0x00000000 undef IMP DS EXTref librtl.a(shr.o) __rtld [8] 0x200003f8 .data ENTpt DS SECdef [noIMid] __start |
Although, it is technically possible to link all dependent shared objects in an application using run-time linking, it should be avoided in order for the application performance. In general, to take advantage of the AIX architecture, the shared modules should be as self contained as possible. Run-time linking should be used only when necessary. Application program code ported from other UNIX operating systems often does not have this sort of organization and can therefore require extra effort to enable it on AIX. It is important to emphasize the fact that the performance and efficiency of AIX is best explained by a well organized application structure with a well defined interface between modules.
Example 2-16 on page 80 and Example 2-17 on page 80 respectively show the symbol information for func2.so and func3.so used in 2.5.1, “How to use run-time linking” on page 70. Although these objects themselves are run-time linking enabled, they have no dependent run-time linking shared object.
$dump -Tv func2.so func2.so: ***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] 0x00000050 .data EXP DS SECdef [noIMid] func2 |
$ dump -Tv func3.so func3.so: ***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] 0x00000050 .data EXP DS SECdef [noIMid] func3 |
If func2.so and func3.so are swapped as follows, the new func2.so will contain the function func3 and the new func3.so will contain func2:
$ mv func3.so temp.so $ mv func2.so func3.so $ mv temp.so func2.so
After func2.so and func3.so swapped, the run-time linker looks for the symbol definition for func3 and found in the new func2.so in the order of func1.so, func2.so, and func3.so. The referenced symbol func3 is rebound at the program load-time without re-linking the application, thus the executable prints the following output:
$LIBPATH=$PWD ./a.out within function func1 at line number: 7 in func1.c within function func3 at line number: 7 in func3.c
Figure 2-7 on page 81 depicts the function calling relationship for our application after swapping func2.so and fun3.so. Now, new func2.so, which was originally func3.so, contains func3, and new func3.so, which was originally func2.so, contains func2.
The run-time linking shared object func3.so is required by the executable file (a.out) and will be loaded into the system memory by the system loader at the program execution time, even if no symbol in this object is not referenced by the executable.
When an executable file is linked in the run-time link method by specifying the -brtl linker option, the linker uses the extended search order to look for the shared objects and libraries to resolve symbols. In order to exploit this extended search order, run-time linking shared objects must follow the file name convention libname.so.
If the -brtl linker option is specified, the linker looks for libname.so, as well as libname.a, in the directories specified by the -L linker option in addition to the default directory search path (/usr/lib and /lib). The -lname linker option can be also used to specify libname.so as well as libname.a.
For example, if a run-time linking shared object named libfunc1.so, which is stored in the /project/lib directory, is linked when generating the executable file a.out, there are several methods to specify the object:
cc main.c /project/lib/libfunc1.so -brtl
This method almost invalidates the merit of the run-time link method. Although libfunc1.so is loaded at the program load-time, it must reside in the /project/lib directory when the executable is invoked.
cp /project/lib/libfunc1.so .; cc main.c libfunc1.so -brtl
This method is useful if all the run-time linking shared objects are located in the current directory. However, once a stable version is developed, those objects are most likely deployed in a specific directory. Therefore, it is less useful compared to the third method.
cc main.c -lfunc1 -L/project/lib -brtl
This is the recommended method to generate an executable file using the run-time link method. The linker looks for the specified run-time shared objects in the order explained in 2.3.1, “The -L linker option” on page 55.
Note
This method can be used only if the run-time shared object’s file name follows the libname.so naming convention.
To follow the libname.so naming convention, either rename the run-time linking shared object files or archive them into a library.