The symbols foo1 and foo2 are both defined to be part of the shared object's public interface. However, each of these symbols is assigned to a different version definition; foo1 is assigned to SUNW_1.1, and foo2 is assigned to SUNW_1.2.
These version definitions, their inheritance, and their symbol association can be displayed using pvs(1) together with the -d, -v and -s options:
$ 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 |
The version definition SUNW_1.2 has a dependency on the version definition SUNW_1.1.
The inheritance of one version definition by another is a useful technique that reduces the version information that will eventually be recorded by any object that binds to a version dependency. Version inheritance is covered in more detail in the section "Binding to a Version Definition".
Any internal version definition has an associated version definition symbol created. As shown in the previous pvs(1) example, these symbols are displayed when using the -v option.
Creating a Weak Version Definition
Internal changes to an object that do not require the introduction of a new interface definition can be defined by creating a weak version definition. Examples of such changes are bug fixes or performance improvements.
Such a version definition is empty, in that it has no global interface symbols associated with it.
For example, suppose the data file data.c, used in the previous examples, is updated to provide more detailed string definitions:
$ cat data.c const char * _foo1 = "string used by function foo1()\n"; const char * _foo2 = "string used by function foo2()\n"; |
A weak version definition can be introduced to identify this change:
$ cat mapfile SUNW_1.1 { # Release X global: foo1; local: *; }; SUNW_1.2 { # Release X+1 global: foo2; } SUNW_1.1; SUNW_1.2.1 { } SUNW_1.2; # Release X+2 $ cc -o libfoo.so.1 -M mapfile -G foo.o data.o $ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}; SUNW_1.2.1 [WEAK]: {SUNW_1.2}; |
The empty version definition is signified by the weak label. These weak version definitions enable applications to verify the existence of a particular implementation by binding to the version definition associated with that functionality. The section "Binding to a Version Definition" illustrates how these definitions can be used in more detail.
Defining Unrelated Interfaces
The previous examples show how new version definitions added to an object inherit any existing version definitions. You can also create version definitions that are unique and independent. In the following example, two new files, bar1.c and bar2.c, are added to the object libfoo.so.1. These files contribute two new symbols, bar1 and bar2, respectively:
$ cat bar1.c extern void foo1(); void bar1() { foo1(); } $ cat bar2.c extern void foo2(); void bar2() { foo2(); } |
These two symbols are intended to define two new public interfaces. Neither of these new interfaces are related to each other. However, each expresses a dependency on the original SUNW_1.2 interface.
The following mapfile definition creates this required association:
$ cat mapfile SUNW_1.1 { # Release X global: foo1; local: *; }; SUNW_1.2 { # Release X+1 global: foo2; } SUNW_1.1; SUNW_1.2.1 { } SUNW_1.2; # Release X+2 SUNW_1.3a { # Release X+3 global: bar1; } SUNW_1.2; SUNW_1.3b { # Release X+3 global: bar2; } SUNW_1.2; |
Again, the version definitions created in libfoo.so.1 using this mapfile, and their related dependencies, can be inspected using pvs(1):
$ cc -o libfoo.so.1 -M mapfile -G foo.o bar1.o bar2.o data.o $ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}; SUNW_1.2.1 [WEAK]: {SUNW_1.2}; SUNW_1.3a: {SUNW_1.2}; SUNW_1.3b: {SUNW_1.2}; |
The following sections explore how these version definition recordings can be used to verify runtime binding requirements and control the binding of an object during its creation.