[libvirt] PATCH: 8/11: Make better use of linker scripts

Daniel P. Berrange berrange at redhat.com
Thu Oct 30 16:56:30 UTC 2008


On Thu, Oct 30, 2008 at 01:41:14PM +0000, Daniel P. Berrange wrote:
> The libvirt.so file currently whitelists all the symbols in our public
> API. libvirtd and virsh, however, also want to use a bunch of our so
> called 'private' symbols which aren't in the public API. For saferead
> and safewrite() we dealt with this by compiling the code twice with
> some nasty macro tricks to give the function a different name to avoid
> dup symbols when statically linking. For the other APIs, we prefixed
> them with __ and then just added them to the exports.
> 
> Neither option is very good because they both impose a burden on the
> source code - needing to append __ everywhere and write crazy macros.
> Each time we want to export another symbol, we have to make lots of
> manual changes to add __. This was OK if it was just a handful of
> symbols, but we're now upto 30 odd, and its not scaling. When we
> make drivers modular we'll be adding 100's more symbols.
> 
> As an aside, we use ELF library versioning when linking, via the
> libtool -version-info flag. Since we maintain ABI compat going
> forwards though, the version in the eventual .so is always going
> to be 'libvirt.so.0'.  This is sub-optimal because you may build
> a binary that requires 'virDomainBlockPeek' which appeared in
> libvirt 0.4.2, but the ELF version cannot validate this. There is
> nothing stopping your application launching against libvirt 0.3.0
> and then aborting with a linker failure sometime while it is
> running when it eventually referneces the non-existant symbol.
> Likewise RPM automatic versioning hooks off the ELF version too,
> so that's not preventing you installing too old a libvirt for
> your app's needs.
> 
> A far better solution to all these problems has been sitting in
> front of us the whole time. Namely we need to make full use of
> the linkers symbol version scripts.
> 
> Our current libvirt_sym.version script is defining an unversioned
> set of symbols. This only enforces what's exported, and can do
> nothing about version compatability. So instead we need to switch
> to a fully versioned script, where every symbol we export is tagged
> with the libvirt version number at which it was introduced.
> 
> Taking the virDomainBlockPeek example again, this appeared in the
> libvirt 0.4.2 release, so we'd add a section to the linker script
> 
>   LIBVIRT_0.4.2 {
>       global:
>           virDomainBlockPeek;
>           virDomainMemoryPeek;
>   } LIBVIRT_0.4.1;
> 
> Then 0.4.5 added in storage pool discovery so you get another
> section
> 
>   LIBVIRT_0.4.5 {
>       global:
>           virConnectFindStoragePoolSources;
>   } LIBVIRT_0.4.2;
> 
> And so on for every release.
> 
> The resulting libvirt.so file will thus gain metadata listing all
> the ABI versions for all symbols it includes.
> 
> That deals with public APIs. Now we also want to export some internal
> APIs whose semantics/syntax may arbitrarily change between releases.
> Thus they should not be included in any of the formal public version
> sections.
> 
> Instead they should be in a seperate versioned section, whose version
> changes on every release. This will ensure that if libvirtd or virsh
> link to some internal symbols, they are guareteened to only run
> against the exactly same libvirt.so they were linked to. This will
> avoid some nasty bugs our users have hit where they installed a custom
> libvirt version on their OS which already had another version installed,
> and then libvirtd crashed/behave wierdly/etc
> 
> To do this we rename  libvirt_sym.version to libvirt_sym.version.in
> and add a section called
> 
>    LIBVIRT_PRIVATE_ at VERSION@ {
> 
>      global:
>         /* libvirt_internal.h */
>         debugFlag;
>         virStateInitialize;
>         virStateCleanup;
>         virStateReload;
>         virStateActive;
> 
>         .... more private symbols...
>    }
> 
> The @VERSION@ gets subsituted by the version number of the libvirt
> release by configure.
> 
> Do a build with this all active and look at the resulting libvirt.so
> using objdump (or eu-readelf). 
> 
>    # objdump -p  src/.libs/libvirt.so
> Version definitions:
> 1 0x01 0x0e5a1d10 libvirt.so.0
> 2 0x00 0x0af6bd33 LIBVIRT_0.0.3
> 3 0x00 0x0af6bd35 LIBVIRT_0.0.5
>   LIBVIRT_0.0.3 
> 4 0x00 0x0af6be30 LIBVIRT_0.1.0
>   LIBVIRT_0.0.5 
> 5 0x00 0x0af6be31 LIBVIRT_0.1.1
>   LIBVIRT_0.1.0 
> 6 0x00 0x0af6be34 LIBVIRT_0.1.4
>   LIBVIRT_0.1.1 
> 7 0x00 0x0af6be35 LIBVIRT_0.1.5
>   LIBVIRT_0.1.4 
> 8 0x00 0x0af6be39 LIBVIRT_0.1.9
>   LIBVIRT_0.1.5 
> 9 0x00 0x0af6bb30 LIBVIRT_0.2.0
>   LIBVIRT_0.1.9 
> 10 0x00 0x0af6bb31 LIBVIRT_0.2.1
>    LIBVIRT_0.2.0 
> 11 0x00 0x0af6bb33 LIBVIRT_0.2.3
>    LIBVIRT_0.2.1 
> 12 0x00 0x0af6bc30 LIBVIRT_0.3.0
>    LIBVIRT_0.2.3 
> 13 0x00 0x0af6bc32 LIBVIRT_0.3.2
>    LIBVIRT_0.3.0 
> 14 0x00 0x0af6bc33 LIBVIRT_0.3.3
>    LIBVIRT_0.3.2 
> 15 0x00 0x0af6b930 LIBVIRT_0.4.0
>    LIBVIRT_0.3.3 
> 16 0x00 0x0af6b931 LIBVIRT_0.4.1
>    LIBVIRT_0.4.0 
> 17 0x00 0x0af6b932 LIBVIRT_0.4.2
>    LIBVIRT_0.4.1 
> 18 0x00 0x0af6b935 LIBVIRT_0.4.5
>    LIBVIRT_0.4.2 
> 19 0x00 0x0af6ba30 LIBVIRT_0.5.0
>    LIBVIRT_0.4.5 
> 20 0x00 0x09e39b06 LIBVIRT_PRIVATE_0.4.6
> 
> 
> You can see that as well as the main SONAME libvirt.so.0, we've
> got version info for each release which introduced new public
> API symbols, as well as our versioned private symbols.  If you
> look at glibc's  libc.so, you'll see a similar set of versioned
> interfaces.
> 
> Then take a look at the virsh binary, and see that it has references
> to all of the versions corresponding to symbols it uses. You can
> also see that it used some libvirt internal symbols, by presence of
> the LIBVIRT_PRIVATE_0.4.6 reference. This ensures that this virsh
> binary can't accidentally be used against libvirt 0.4.5 or 0.4.7
> where it'd likely crash at runtime
> 
>   # objdump -p src/.libs/virsh
> Version References:
>   required from libpthread.so.0:
>     0x09691a75 0x00 10 GLIBC_2.2.5
>   required from libc.so.6:
>     0x0d696918 0x00 19 GLIBC_2.8
>     0x09691974 0x00 13 GLIBC_2.3.4
>     0x09691a75 0x00 04 GLIBC_2.2.5
>   required from libvirt.so.0:
>     0x0af6ba30 0x00 22 LIBVIRT_0.5.0
>     0x0af6bc30 0x00 21 LIBVIRT_0.3.0
>     0x0af6bc33 0x00 20 LIBVIRT_0.3.3
>     0x0af6be34 0x00 18 LIBVIRT_0.1.4
>     0x0af6be35 0x00 17 LIBVIRT_0.1.5
>     0x0af6b935 0x00 16 LIBVIRT_0.4.5
>     0x0af6be31 0x00 15 LIBVIRT_0.1.1
>     0x0af6bc32 0x00 14 LIBVIRT_0.3.2
>     0x0af6be39 0x00 12 LIBVIRT_0.1.9
>     0x0af6bd33 0x00 11 LIBVIRT_0.0.3
>     0x0af6bb31 0x00 09 LIBVIRT_0.2.1
>     0x09e39b06 0x00 08 LIBVIRT_PRIVATE_0.4.6
>     0x0af6b930 0x00 07 LIBVIRT_0.4.0
>     0x0af6bb30 0x00 06 LIBVIRT_0.2.0
>     0x0af6be30 0x00 05 LIBVIRT_0.1.0
>     0x0af6bb33 0x00 03 LIBVIRT_0.2.3
>     0x0af6b931 0x00 02 LIBVIRT_0.4.1


As a further example, of something using only a subset of the
API, consider a really trivial 3 liner demo program which uses
3 functions:

$ cat demo.c
#include <stdio.h>
#include <libvirt/libvirt.h>

int main()
{
    char buf[10];
    virDomainPtr dom;
    virConnectPtr cb = virConnectOpen(NULL);

    dom = virDomainLookupByName(cb, "foo");
    virDomainBlockPeek(dom, "hda", 0, 10, buf, 0);
}


$ gcc -o demo -lvirt demo.c
$ objdump -p demo

Version References:
  required from libc.so.6:
    0x09691a75 0x00 03 GLIBC_2.2.5
  required from libvirt.so.0:
    0x0af6b932 0x00 04 LIBVIRT_0.4.2
    0x0af6bd33 0x00 02 LIBVIRT_0.0.3

So you can see the linker only encodes the exact versions used
by the 3 vir* methods it calls. 0.0.3 for virConnectOpen and
virDomainLookupByName, and 0.4.2 for virDomainBlockPeek.


Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list