Binding to a Version Definition
When a dynamic object is built against other shared objects, these dependencies are recorded in the resulting object. See Shared Object Processing and Recording a Shared Object Name for more details. If a dependency also contain version definitions, then an associated version dependency is recorded in the object being built.
The following example uses the data files from the previous section to
generate a shared object, libfoo.so.1
, which is
suitable for a compile time environment.
$ cc -o libfoo.so.1 -h libfoo.so.1 -M mapfile -G foo.o bar.o data.o $ ln -s libfoo.so.1 libfoo.so $ pvs -dsv libfoo.so.1 libfoo.so.1: _end; _GLOBAL_OFFSET_TABLE_; _DYNAMIC; _edata; _PROCEDURE_LINKAGE_TABLE_; _etext; SUNW_1.1: foo1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}: foo2; SUNW_1.2; SUNW_1.2.1 [WEAK]: {SUNW_1.2}: SUNW_1.2.1; SUNW_1.3a: {SUNW_1.2}: bar1; SUNW_1.3a; SUNW_1.3b: {SUNW_1.2}: bar2; SUNW_1.3b
Six public interfaces are offered by the shared object
libfoo.so.1
. Four of these interfaces,
SUNW_1.1
, SUNW_1.2
,
SUNW_1.3a
, and
SUNW_1.3b
, define exported symbol names.
One interface, SUNW_1.2.1
, describes an internal
implementation change to the object. One interface,
libfoo.so.1
, defines several reserved
labels. Dynamic objects created with
libfoo.so.1
as a dependency, record the
version names of the interfaces the dynamic object binds to.
The following example creates an application that
references symbols foo1
and
foo2
. The versioning dependency
information that is recorded in the application can be examined
using
pvs
(1) with the -r
option.
$ cat prog.c extern void foo1(); extern void foo2(); main() { foo1(); foo2(); } $ cc -o prog prog.c -L. -R. -lfoo $ pvs -r prog libfoo.so.1 (SUNW_1.2, SUNW_1.2.1);
In this example, the application prog
has bound
to the two interfaces SUNW_1.1
and
SUNW_1.2
. These interfaces provided the
global symbols foo1
and foo2
respectively.
Because version definition SUNW_1.1
is defined within libfoo.so.1
as being
inherited by the version definition SUNW_1.2
, you
only need to record the one dependency. This inheritance provides
for the normalization of version definition dependencies. This
normalization reduces the amount of version information that is
maintained within an object. This normalization also reduces the
version verification processing that is required at runtime.
Because the application prog
was built against
the shared object's implementation containing the weak version
definition SUNW_1.2.1
, this dependency is also
recorded. Even though this version definition is defined to inherit
the version definition SUNW_1.2
, the version's
weak nature precludes its normalization with
SUNW_1.1
. A weak version definition
results in a separate dependency recording.
Had there been multiple weak version definitions that inherited from each other, then these definitions are normalized in the same manner as non-weak version definitions are.
Note:
The recording of a version dependency can be suppressed by the link-editor's-z noversion
option.
The runtime linker validates the existence of any
recorded version definitions from the objects that are bound to when
the application is executed. This validation can be displayed using
ldd
(1) with the -v
option.
For example, by running
ldd
(1) on the application
prog
, the version definition
dependencies are shown to be found correctly in the dependency
libfoo.so.1
.
$ ldd -v prog
find object=libfoo.so.1; required by prog
libfoo.so.1 => ./libfoo.so.1
find version=libfoo.so.1;
libfoo.so.1 (SUNW_1.2) => ./libfoo.so.1
libfoo.so.1 (SUNW_1.2.1) => ./libfoo.so.1
....
Note:
ldd
(1) with the -v
option implies verbose output. A
recursive list of all dependencies, together with all
versioning requirements, is generated.
If a non-weak version definition dependency cannot be
found, a fatal error occurs during application initialization. Any
weak version definition dependency that cannot be found is silently
ignored. For example, if the application prog
is run in an environment in which libfoo.so.1
only contains the version definition SUNW_1.1
,
then the following fatal error occurs.
$ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; $ prog ld.so.1: prog: fatal: libfoo.so.1: version 'SUNW_1.2' not \ found (required by file prog)
If prog
had not recorded any
version definition dependencies, the nonexistence of the symbol
foo2
could result in a fatal relocation
error a runtime. This relocation error might occur at process
initialization, or during process execution. An error condition
might not occur at all if the execution path of the application did
not call the function foo2
. See Relocation Errors.
A version definition dependency provides an alternative and immediate indication of the availability of the interfaces required by the application.
For example, prog
might run in an environment in
which libfoo.so.1
only contains the version
definitions SUNW_1.1
and
SUNW_1.2
. In this event, all non-weak
version definition requirements are satisfied. The absence of the
weak version definition SUNW_1.2.1
is deemed
nonfatal. In this case, no runtime error condition is
generated.
$ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}; $ prog string used by foo1() string used by foo2()
ldd
(1) can be used to display all version
definitions that cannot be found.
$ ldd prog
libfoo.so.1 => ./libfoo.so.1
libfoo.so.1 (SUNW_1.2.1) => (version not found)
....
At runtime, if an implementation of a dependency contains no version
definition information, then any version verification of the
dependency is silently ignored. This policy provides a level of
backward compatibility as a transition from non-versioned to
versioned shared objects occurs.
ldd
(1) can always be used to display any
version requirement discrepancies.
Note:
The environment variableLD_NOVERSION
can be
used to suppress all runtime versioning verification.