Skip Navigation Links | |
Exit Print View | |
Oracle Solaris 11.1 Linkers and Libraries Guide Oracle Solaris 11.1 Information Library |
Part I Using the Link-Editor and Runtime Linker
1. Introduction to the Oracle Solaris Link Editors
Specifying the Link-Editor Options
Linking With Additional Libraries
Linking With a Mix of Shared Objects and Archives
Position of an Archive on the Command Line
Directories Searched by the Link-Editor
Directories Searched by the Runtime Linker
Initialization and Termination Sections
Generating an Executable Output File
Generating a Shared Object Output File
Tentative Symbol Order Within the Output File
Defining Additional Symbols with the -u option
Augmenting a Symbol Definition
Identifying Capability Requirements
Identifying a Platform Capability
Identifying a Machine Capability
Identifying Hardware Capabilities
Identifying Software Capabilities
Creating a Family of Symbol Capabilities Functions
Creating a Family of Symbol Capabilities Data Items
Converting Object Capabilities to Symbol Capabilities
Exercising a Capability Family
Debugger Access and Use of Ancillary Objects
5. Link-Editor Quick Reference
7. Building Objects to Optimize System Performance
10. Establishing Dependencies with Dynamic String Tokens
Part IV ELF Application Binary Interface
13. Program Loading and Dynamic Linking
A. Linker and Libraries Updates and New Features
A stub object is a shared object, built entirely from mapfiles, that supplies the same linking interface as the real object, while containing no code or data. Stub objects cannot be used at runtime. However, an application can be built against a stub object, where the stub object provides the real object name to be used at runtime.
When building a stub object, the link-editor ignores any object or library files specified on the command line, and these files need not exist in order to build a stub. Since the compilation step can be omitted, and because the link-editor has relatively little work to do, stub objects can be built very quickly.
Stub objects can be used to solve a variety of build problems.
Speed
Modern machines, using a version of the make utility with the ability to parallelize operations, are capable of compiling and linking many objects simultaneously, and doing so offers significant speedups. However, it is typical that a given object will depend on other objects, and that there will be a core set of objects that nearly everything else depends on. It is necessary to order the builds so that all objects are built ahead of their use by other objects. This ordering creates bottlenecks that reduce the amount of parallelization that is possible and limits the overall speed at which the code can be built.
Complexity/Correctness
In a large body of code, there can be a large number of dependencies between the various objects. The makefiles or other build descriptions for these objects can become very complex and difficult to understand or maintain. The dependencies can change as the system evolves. This can cause a given set of makefiles to become slightly incorrect over time, leading to race conditions and mysterious rare build failures.
Dependency Cycles
It might be desirable to organize code as cooperating shared objects, each of which draw on the resources provided by the other. Such cycles cannot be supported in an environment where objects must be built before the objects that use them, even though the runtime linker is fully capable of loading and using such objects if they could be built.
Stub shared objects offer an alternative method for building code that sidesteps the above issues. Stub objects can be quickly built for all the shared objects produced by the build. Then, all the real shared objects and executables can be built in parallel, in any order, using the stub objects to stand in for the real objects at link-time. Afterwards, the executables and real shared objects are kept, and the stub shared objects are discarded.
Stub objects are built from one or more mapfiles, which must collectively satisfy the following requirements.
At least one mapfile must specify the STUB_OBJECT directive. See STUB_OBJECT Directive.
All function and data symbols that make up the external interface to the object must be explicitly listed in the mapfile.
The mapfile must use symbol scope reduction ('*'), to remove any symbols not explicitly listed from the external interface. See SYMBOL_SCOPE / SYMBOL_VERSION Directives.
All global data exported from the object must have an ASSERT symbol attribute in the mapfile to specify the symbol type and size. In the case where there are multiple symbols that reference the same data, the ASSERT for one of these symbols must specify the TYPE and SIZE attributes, while the others must use the ALIAS attribute to reference this primary symbol. See ASSERT Attribute.
Given such a mapfile, the stub and real versions of the shared object can be built using the same command line for each. The -z stub option is added to the link-edit of the stub object, and is omitted from the link-edit of the real object.
To demonstrate these ideas, the following code implements a shared object named idx5, which exports data from a 5 element array of integers. Each element is initialized to contain its zero-based array index. This data is made available as a global array, as an alternative alias data symbol with weak binding, and through a functional interface.
$ cat idx5.c int _idx5[5] = { 0, 1, 2, 3, 4 }; #pragma weak idx5 = _idx5 int idx5_func(int index) { if ((index < 0) || (index > 4)) return (-1); return (_idx5[index]); }
A mapfile is required to describe the interface provided by this shared object.
$ cat mapfile $mapfile_version 2 STUB_OBJECT; SYMBOL_SCOPE { _idx5 { ASSERT { TYPE=data; SIZE=4[5] }; }; idx5 { ASSERT { BINDING=weak; ALIAS=_idx5 }; }; idx5_func; local: *; };
The following main program is used to print all the index values available from the idx5 shared object.
$ cat main.c #include <stdio.h> extern int _idx5[5], idx5[5], idx5_func(int); int main(int argc, char **argv) { int i; for (i = 0; i < 5; i++) (void) printf("[%d] %d %d %d\n", i, _idx5[i], idx5[i], idx5_func(i)); return (0); }
The following commands create a stub version of this shared object in a subdirectory named stublib. The elfdump command is used to verify that the resulting object is a stub. The command used to build the stub differs from that of the real object only in the addition of the -z stub option, and the use of a different output file name. This demonstrates the ease with which stub generation can be added to existing code.
$ cc -Kpic -G -M mapfile -h libidx5.so.1 idx5.c -o stublib/libidx5.so.1 -zstub $ ln -s libidx5.so.1 stublib/libidx5.so $ elfdump -d stublib/libidx5.so | grep STUB [11] FLAGS_1 0x4000000 [ STUB ]
The main program can now be built, using the stub object to stand in for the real shared object, and setting a runpath that will find the real object at runtime. However, as the real object has not been built, this program cannot yet be run. Attempts to cause the system to load the stub object are rejected, as the runtime linker knows that stub objects lack the actual code and data found in the real object, and cannot execute.
$ cc main.c -L stublib -R '$ORIGIN/lib' -lidx5 -lc $ ./a.out ld.so.1: a.out: fatal: libidx5.so.1: open failed: \ No such file or directory Killed $ LD_PRELOAD=stublib/libidx5.so.1 ./a.out ld.so.1: a.out: fatal: stublib/libidx5.so.1: stub shared object \ cannot be used at runtime Killed
The real object is built using the same command used to build the stub object. The -z stub option is omitted, and the path for the real output file is specified.
$ cc -Kpic -G -M mapfile -h libidx5.so.1 idx5.c -o lib/libidx5.so.1
Once the real object has been built in the lib subdirectory, the program can be run.
$ ./a.out [0] 0 0 0 [1] 1 1 1 [2] 2 2 2 [3] 3 3 3 [4] 4 4 4