Proposal: Direct binding and "as needed" linking as default

Background

The problem

Handling a complete soname upgrade and transition can currently be a challenging task in Opencsw in the case where the library is linked by all lot of executables and libraries. There are two main reasons for that:

1. First: the way dynamic linking works by default doesn't ensure that a binary will be linked against the library it was compiled against.
Indeed, the linker ld.so.1 works like this by default:

  • it loads recursively all library dependencies (in a depth-first way),
  • it iterates through the binary and the library loaded to resolve each dynamic symbol needed,
  • for each of these symbols, it iterates through the libraries loaded, in the order they were loaded, and binds the symbol to the first library which export a symbol with the same name.

This can lead to tricky situations. Let's imagine the following situation that can occurs during a library soname upgrade:

  • B is an executable that has been compiled against library LibA.so.1 and library LibB.so.1
  • LibB.so.1 has been compiled against LibA.so.2, an newer version of the LibA library with a different soname.
  • LibA.so.1 and LibA.so.2 are ABI incompatible but still have symbols in common, for exemple they both export the same symbol SYM.
  • B and LibB.so.1 both use reference to symbol SYM and hence need to resolve it at runtime.

When B will be executed, the dynamic loader will bind SYM of both B and LibB against the first LibA library loaded.
Let's say LibA.so.1 was the first library loaded, it means that LibB.so.1 SYM reference will be bound to LibA.so.1 and not to LibA.so.2.

This can lead to core dump or subtle data corruption.

Let's imagine symbol SYM is "char data;" in LibA.so.1 but has been changed in "int data;" in LibA.so.2.
When LibB.so.1 will try to write something in data, it will expect and int and hence write 4 (or 8 bytes) in a memory location reserved to store a char, 1 byte…

That means that B and LibB should be recompiled against LibA.so.2 and released at the same time to avoid any problem.

Add all the other libraries and binaries same and you end up with a big set of packages which should be updated at the same time, requiring a lot of coordination and synchronous works with a lot of maintainers (among which a few one will probably be missing in action).

2. Second: there are a currently number of useless library/packages dependencies within OpenCSW packages

You will have this is caused by various reasons:

  • some build systems incorrectly want to try to link a binary against a library that is required by one of their dependencies, e.g. B is linked against LibB, because it is also linked against LibA which requires LibB,
  • some pkg-config like script incorrectly output , for exemple they output the set of libraries needed for a static linking and not for a dynamic linking,
  • several different binaries are included the package and they are linked against the same libraries, even if some of these binaries don't need it.

It is easy to miss these problems as the the resulting binaries are perfectly working.
For the library which has been uselessly linked against, this add the new set of reverse dependencies to handle during the next soname upgrade.

Fortunately, there are some way to overcome these problems, which are the heart of this proposal :)

The solutions

1. The first problem can be solved by enabling direct binding during the link phase of the package build

This feature is explained in the Oracle Linker and Libraries Guide explanation.This is is enabled by adding the "-Bdirect" option to the SUN linker ld. This will tell the linker to mark every external symbol as needed to be directly bound at runtime.
The effect is that, at runtime, the linker will now bind the symbol against the exact soname that was identified to provide the symbol during compilation time.

This option needs to be enabled for each binary linked against libraries to effectively solve the problem.

2. The second problem can be solved by enabling "as needed" linking during the phase of the package build.

This is done by adding the "-z ignore" option to the SUN linker ld. The linker at compilation time will then check that a symbol of a library is really used before marking it as required by the binary compiled. In other words the linker will then ignore any -llibrary option that it doesn't identify as necessary.

Goals

  • Easy handling of library soname upgrade
  • Reduction of the number of reverse-dependancies to rebuild

Non-goals

  • Reduction the size of the Opencsw software stack

Overview

This proposal

Require binary to use direct binding in the OpenCSW build policy

As explained above, we need every binary to use direct binding to feel the benefits, so the idea is to require every binary and library in OpenCSW stack to use direct binding for their external symbol resolution unless there is a very good reason not to do it.

Advantages:

  • seamless library soname upgrade without breakage risks and high coordination workload,
  • probably a speed up of the runtime dynamic linking phase, it is probably significant only no number has been gathered here as it is not a goal here.

Disadvantages:

  • sometimes, it might be that seems some programs really need the original linker behaviour but that could be fixed by some other ld options,
  • we never enabled it so maybe we will uncover some nice new problems in at least some of the numerous OpenCSW packages,
  • a slight increase in the size of some binaries as the linker will add a new section.

For the last point, for exemple, the vsftpd binary size increased by 2% but that increase depends on the number of symbols compared to the size of the code.

Require binaries not to be linked against libraries they don't use

Similarly, every binary should never be linked against a library it doesn't really need unless there is a really good reason.

Advantages:

  • less reverse dependencies to handle during a soname upgrade,
  • less dependencies to install for the end user.

Disadvantages:

  • we never enabled it so it might be that we will uncover some problems but that seems less likely that for direct binding.

Libraries and binaries that will benefit from proposal

No number has been gathered about this. Libraries that will benefits are libraries that are widely linked by both libraries and binaries, for exemple: libssl, libcrypto, libjpeg, libpng…
Of course, all packages linked against these libraries will also benefit from this proposal as they will not be required to be upgraded at the same time during a soname upgrade.

Implementation details

To implement this proposal, two modifications will be made:

The implementation and documentation of two new checkpkg checks to enforce packages to follow the new requirements

This checks will we called soname-unused and no-direct-binding and will allow to warn the maintainers about

The modification of the default linker flags in the mgar build system to enable direct binding and as needed linking by default

That modification would allow to transparently and globally enable direct binding and as needed linking for new packages built, without requiring maintainers intervention.

This would simply be done by adding or modifying the following lines in gar.conf.mk:
to define

This will work for most packages except those who don't have a standard build system and don't take into account the LDFLAGS environment variable.
These packages will be caught by the checkpkg new checks and would need some specific build configuration modification or patching.

Implementation plan

As we can't know all the side effects of the modifications, we have to enable it gradually.
The idea is to follow the given steps:

  • Step 1:
    • write and validate the checkpkg tests without globally enabling them.
  • Step 2:
    • enable "direct binding" and "as needed" linking manually for a reduced set of packages,
    • wait sometimes (1 month), to see if something unexpected happens with these packages. :)

Enabling "direct binding" and "as needed" linking manually is easily done by adding the following line in the package Makefile:

EXTRA_LD_OPTIONS = -z ignore -Bdirect
  • Step 3: if everything worked fine:
    • enable the option globally by adding them to the default linker flags, and enable the checkpkg test in informative-only mode,
    • wait again sometimes to be able to catch potential problems.
  • Step 4: if everything worked fine,
    • enable the checkpkg tests in real-check mode so we are able to catch even packages that don't use LDFLAGS and need patching.

Caveats

It is likely that some corner case will be uncovered altought it is not yet clear what kind of situation will trigger them.
In any case, it is always possible to override the checkpkg checks or disable the additionnal ld options when there is a good reason to do it.

Disabling direct binding and as needed linking for a package

This would simply done by redefining COMMON_LDFLAGS before including category.mk.
For exemple, the following lines would remove the direct binding and as needed linking flags.

COMMON_LDFLAGS = 
[...]
include gar/category.mk

It would be wise to check if COMMON_LDFLAGS contains other interesting flags before doing this modification.

Disabling Direct binding for only one library

This is more tricky as it requires to have precise control on the ld command line.
To disable it for library library, the ld command line in the following way.

... {{-z nodirect -l//library// -z direct}} ...

That will disable direct binding for library library and reenable it for any other subsequent library defined on the command line.

It might be wise to see if the -z interpose -llibrary linker option is a better fit to solve the problem. With that option, each symbol will be looked first in library library even if direct binding was enabled for the symbol.

Links

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License