Next: Expressions, Previous: PHDRS, Up: Scripts [Contents][Index]
The linker supports symbol versions when using ELF. Symbol versions areonly useful when using shared libraries. The dynamic linker can usesymbol versions to select a specific version of a function when it runsa program that may have been linked against an earlier version of theshared library.
You can include a version script directly in the main linker script, oryou can supply the version script as an implicit linker script. You canalso use the ‘--version-script’ linker option.
The syntax of the VERSION
command is simply
VERSION { version-script-commands }
The format of the version script commands is identical to that used bySun’s linker in Solaris 2.5. The version script defines a tree ofversion nodes. You specify the node names and interdependencies in theversion script. You can specify which symbols are bound to whichversion nodes, and you can reduce a specified set of symbols to localscope so that they are not globally visible outside of the sharedlibrary.
The easiest way to demonstrate the version script language is with a fewexamples.
VERS_1.1 { global: foo1; local: old*; original*; new*; }; VERS_1.2 { foo2; } VERS_1.1; VERS_2.0 { bar1; bar2; extern "C++" { ns::*; "f(int, double)"; }; } VERS_1.2;
This example version script defines three version nodes. The firstversion node defined is ‘VERS_1.1’; it has no other dependencies.The script binds the symbol ‘foo1’ to ‘VERS_1.1’. It reducesa number of symbols to local scope so that they are not visible outsideof the shared library; this is done using wildcard patterns, so that anysymbol whose name begins with ‘old’, ‘original’, or ‘new’is matched. The wildcard patterns available are the same as those usedin the shell when matching filenames (also known as “globbing”).However, if you specify the symbol name inside double quotes, then thename is treated as literal, rather than as a glob pattern.
Next, the version script defines node ‘VERS_1.2’. This nodedepends upon ‘VERS_1.1’. The script binds the symbol ‘foo2’to the version node ‘VERS_1.2’.
Finally, the version script defines node ‘VERS_2.0’. This nodedepends upon ‘VERS_1.2’. The scripts binds the symbols ‘bar1’and ‘bar2’ are bound to the version node ‘VERS_2.0’.
When the linker finds a symbol defined in a library which is notspecifically bound to a version node, it will effectively bind it to anunspecified base version of the library. You can bind all otherwiseunspecified symbols to a given version node by using ‘global: *;’somewhere in the version script. Note that it’s slightly crazy to usewildcards in a global spec except on the last version node. Globalwildcards elsewhere run the risk of accidentally adding symbols to theset exported for an old version. That’s wrong since older versionsought to have a fixed set of symbols.
The names of the version nodes have no specific meaning other than whatthey might suggest to the person reading them. The ‘2.0’ versioncould just as well have appeared in between ‘1.1’ and ‘1.2’.However, this would be a confusing way to write a version script.
Node name can be omitted, provided it is the only version nodein the version script. Such version script doesn’t assign any versions tosymbols, only selects which symbols will be globally visible out and whichwon’t.
{ global: foo; bar; local: *; };
When you link an application against a shared library that has versionedsymbols, the application itself knows which version of each symbol itrequires, and it also knows which version nodes it needs from eachshared library it is linked against. Thus at runtime, the dynamicloader can make a quick check to make sure that the libraries you havelinked against do in fact supply all of the version nodes that theapplication will need to resolve all of the dynamic symbols. In thisway it is possible for the dynamic linker to know with certainty thatall external symbols that it needs will be resolvable without having tosearch for each symbol reference.
The symbol versioning is in effect a much more sophisticated way ofdoing minor version checking that SunOS does. The fundamental problemthat is being addressed here is that typically references to externalfunctions are bound on an as-needed basis, and are not all bound whenthe application starts up. If a shared library is out of date, arequired interface may be missing; when the application tries to usethat interface, it may suddenly and unexpectedly fail. With symbolversioning, the user will get a warning when they start their program ifthe libraries being used with the application are too old.
There are several GNU extensions to Sun’s versioning approach. Thefirst of these is the ability to bind a symbol to a version node in thesource file where the symbol is defined instead of in the versioningscript. This was done mainly to reduce the burden on the librarymaintainer. You can do this by putting something like:
__asm__(".symver original_foo,foo@VERS_1.1");
in the C source file. This renames the function ‘original_foo’ tobe an alias for ‘foo’ bound to the version node ‘VERS_1.1’.The ‘local:’ directive can be used to prevent the symbol‘original_foo’ from being exported. A ‘.symver’ directivetakes precedence over a version script.
The second GNU extension is to allow multiple versions of the samefunction to appear in a given shared library. In this way you can makean incompatible change to an interface without increasing the majorversion number of the shared library, while still allowing applicationslinked against the old interface to continue to function.
To do this, you must use multiple ‘.symver’ directives in thesource file. Here is an example:
__asm__(".symver original_foo,foo@"); __asm__(".symver old_foo,foo@VERS_1.1"); __asm__(".symver old_foo1,foo@VERS_1.2"); __asm__(".symver new_foo,foo@@VERS_2.0");
In this example, ‘foo@’ represents the symbol ‘foo’ bound to theunspecified base version of the symbol. The source file that contains thisexample would define 4 C functions: ‘original_foo’, ‘old_foo’,‘old_foo1’, and ‘new_foo’.
When you have multiple definitions of a given symbol, there needs to besome way to specify a default version to which external references tothis symbol will be bound. You can do this with the‘foo@@VERS_2.0’ type of ‘.symver’ directive. You can onlydeclare one version of a symbol as the default in this manner; otherwiseyou would effectively have multiple definitions of the same symbol.
If you wish to bind a reference to a specific version of the symbolwithin the shared library, you can use the aliases of convenience(i.e., ‘old_foo’), or you can use the ‘.symver’ directive tospecifically bind to an external version of the function in question.
You can also specify the language in the version script:
VERSION extern "lang" { version-script-commands }
The supported ‘lang’s are ‘C’, ‘C++’, and ‘Java’.The linker will iterate over the list of symbols at the link time anddemangle them according to ‘lang’ before matching them to thepatterns specified in ‘version-script-commands’. The default‘lang’ is ‘C’.
Demangled names may contains spaces and other special characters. Asdescribed above, you can use a glob pattern to match demangled names,or you can use a double-quoted string to match the string exactly. Inthe latter case, be aware that minor differences (such as differingwhitespace) between the version script and the demangler output willcause a mismatch. As the exact string generated by the demanglermight change in the future, even if the mangled name does not, youshould check that all of your version directives are behaving as youexpect when you upgrade.
Next: Expressions, Previous: PHDRS, Up: Scripts [Contents][Index]