59 lines
3.2 KiB
Markdown
59 lines
3.2 KiB
Markdown
# Combining versions
|
||
|
||
Sun introduced a symbol versioning scheme to use for the linker. Their
|
||
implementation is relatively simple: symbol versions are defined in a version
|
||
script provided when a shared library was created. The dynamic linker can
|
||
verify that all required versions are present. This is useful for ensuring that
|
||
an application can run with a specific version of the library.
|
||
|
||
In the Sun versioning scheme, when a symbol is changed to have an incompatible
|
||
interface, the library file name must change. This then produces a new
|
||
`DT_SONAME` entry, which leads to new `DT_NEEDED` entries, and thus manages
|
||
incompatibility at that level.
|
||
|
||
Ulrich Drepper and Eric Youngdale introduced a much more sophisticated symbol
|
||
versioning scheme, which is used by the glibc, the GNU linker, and gold. The
|
||
key differences are that versions may be specified in object files and that
|
||
shared libraries may contain multiple independent versions of the same symbol.
|
||
Versions are specified in object files by naming the symbol `NAME@VERSION` or
|
||
`NAME@@VERSION`. In the former case the symbol is a hidden version, available
|
||
only by specific request. In the latter case the symbol is a default version,
|
||
and references to `NAME` will be linked to `NAME@@VERSION`. Versions may also
|
||
be specified in version scripts.
|
||
|
||
This facility means that in principle it is never necessary to change the
|
||
library file name. The versioning scheme lets the dynamic linker direct each
|
||
symbol reference to the appropriate version. This in turn means that in a
|
||
complicated program with many shared libraries compiled against different
|
||
versions of the base library, only one instance of the base library needs to be
|
||
loaded.
|
||
|
||
However, this additional complexity leads to additional ambiguity. There are
|
||
now two possible sources of a symbol version: the name in the object file and
|
||
an entry in the version script. There is the possibility that two instances of
|
||
the same name will disagree on whether the name should be globally visible or
|
||
not–in fact, this is normal, as undefined references will always use
|
||
`NAME@VERSION`, not `NAME@@VERSION`. Symbol overriding can be confusing: if the
|
||
main executable defines `NAME` without a version, which versions should it
|
||
override in the shared library? Which version should be used in the program?
|
||
Symbol visibility adds an additional wrinkle to this.
|
||
|
||
The most important issue for the linker arises when it sees both NAME and
|
||
`NAME@VERSION`, and then sees `NAME@@VERSION`. At that time the linker has seen
|
||
two separate symbols and has to decide whether to merge them. The rules that
|
||
gold currently follows are these:
|
||
|
||
* If `NAME` is hidden, and `NAME@@VERSION` is in a shared object, they are two
|
||
independent symbols, and we do not change `NAME` or its version.
|
||
* If `NAME` already has a version, because we earlier saw `NAME@@VERSION2`,
|
||
then we produce two separate symbols, and leave `NAME@@VERSION2` as the
|
||
default symbol.
|
||
* Otherwise, we change the version of `NAME` to `VERSION`, and do normal symbol
|
||
resolution.
|
||
|
||
I recently fixed a bug in this code in gold, which was breaking symbol
|
||
overriding in a specific case. I wouldn’t be surprised if there are more bugs.
|
||
As far as I know nobody has worked through all the symbol combining issues and
|
||
defined what should happen.
|
||
|