[dpdk-dev] [PATCH 1/4] compat: Add infrastructure to support symbol versioning

Neil Horman nhorman at tuxdriver.com
Tue Sep 23 19:31:14 CEST 2014


On Tue, Sep 23, 2014 at 05:29:48PM +0100, Sergio Gonzalez Monroy wrote:
> On Tue, Sep 23, 2014 at 10:58:29AM -0400, Neil Horman wrote:
> > On Tue, Sep 23, 2014 at 11:39:29AM +0100, Sergio Gonzalez Monroy wrote:
> > > Hi Neil,
> > > 
> > > On Mon, Sep 15, 2014 at 03:23:48PM -0400, Neil Horman wrote:
> > > > Add initial pass header files to support symbol versioning.
> > > > 
> > > > Signed-off-by: Neil Horman <nhorman at tuxdriver.com>
> > > > CC: Thomas Monjalon <thomas.monjalon at 6wind.com>
> > > > CC: "Richardson, Bruce" <bruce.richardson at intel.com>
> > > > ---
> > > >  lib/Makefile                   |  1 +
> > > >  lib/librte_compat/Makefile     | 38 +++++++++++++++++++
> > > >  lib/librte_compat/rte_compat.h | 86 ++++++++++++++++++++++++++++++++++++++++++
> > > >  mk/rte.lib.mk                  |  6 +++
> > > >  4 files changed, 131 insertions(+)
> > > >  create mode 100644 lib/librte_compat/Makefile
> > > >  create mode 100644 lib/librte_compat/rte_compat.h
> > > > 
> > > > diff --git a/lib/Makefile b/lib/Makefile
> > > > index 10c5bb3..a85b55b 100644
> > > > --- a/lib/Makefile
> > > > +++ b/lib/Makefile
> > > > @@ -32,6 +32,7 @@
> > > >  include $(RTE_SDK)/mk/rte.vars.mk
> > > >  
> > > >  DIRS-$(CONFIG_RTE_LIBC) += libc
> > > > +DIRS-y += librte_compat
> > > >  DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
> > > >  DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
> > > >  DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
> > > > diff --git a/lib/librte_compat/Makefile b/lib/librte_compat/Makefile
> > > > new file mode 100644
> > > > index 0000000..a61511a
> > > > --- /dev/null
> > > > +++ b/lib/librte_compat/Makefile
> > > > @@ -0,0 +1,38 @@
> > > > +#   BSD LICENSE
> > > > +#
> > > > +#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> > > > +#   All rights reserved.
> > > > +#
> > > > +#   Redistribution and use in source and binary forms, with or without
> > > > +#   modification, are permitted provided that the following conditions
> > > > +#   are met:
> > > > +#
> > > > +#     * Redistributions of source code must retain the above copyright
> > > > +#       notice, this list of conditions and the following disclaimer.
> > > > +#     * Redistributions in binary form must reproduce the above copyright
> > > > +#       notice, this list of conditions and the following disclaimer in
> > > > +#       the documentation and/or other materials provided with the
> > > > +#       distribution.
> > > > +#     * Neither the name of Intel Corporation nor the names of its
> > > > +#       contributors may be used to endorse or promote products derived
> > > > +#       from this software without specific prior written permission.
> > > > +#
> > > > +#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> > > > +#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> > > > +#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> > > > +#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> > > > +#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> > > > +#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> > > > +#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> > > > +#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> > > > +#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> > > > +#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> > > > +#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> > > > +
> > > > +include $(RTE_SDK)/mk/rte.vars.mk
> > > > +
> > > > +
> > > > +# install includes
> > > > +SYMLINK-y-include := rte_compat.h
> > > > +
> > > > +include $(RTE_SDK)/mk/rte.lib.mk
> > > > diff --git a/lib/librte_compat/rte_compat.h b/lib/librte_compat/rte_compat.h
> > > > new file mode 100644
> > > > index 0000000..6d65a53
> > > > --- /dev/null
> > > > +++ b/lib/librte_compat/rte_compat.h
> > > > @@ -0,0 +1,86 @@
> > > > +/*-
> > > > + *   BSD LICENSE
> > > > + *
> > > > + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> > > > + *   All rights reserved.
> > > > + *
> > > > + *   Redistribution and use in source and binary forms, with or without
> > > > + *   modification, are permitted provided that the following conditions
> > > > + *   are met:
> > > > + *
> > > > + *     * Redistributions of source code must retain the above copyright
> > > > + *       notice, this list of conditions and the following disclaimer.
> > > > + *     * Redistributions in binary form must reproduce the above copyright
> > > > + *       notice, this list of conditions and the following disclaimer in
> > > > + *       the documentation and/or other materials provided with the
> > > > + *       distribution.
> > > > + *     * Neither the name of Intel Corporation nor the names of its
> > > > + *       contributors may be used to endorse or promote products derived
> > > > + *       from this software without specific prior written permission.
> > > > + *
> > > > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> > > > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> > > > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> > > > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> > > > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> > > > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> > > > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> > > > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> > > > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> > > > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> > > > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> > > > + */
> > > > +
> > > > +#ifndef _RTE_COMPAT_H_
> > > > +#define _RTE_COMPAT_H_
> > > > +
> > > > +/*
> > > > + * This is just a stringification macro for use below.
> > > > + */
> > > > +#define SA(x) #x
> > > > +
> > > > +#ifdef RTE_SYMBOL_VERSIONING
> > > > +
> > > > +/*
> > > > + * Provides backwards compatibility when updating exported functions.
> > > > + * When a symol is exported from a library to provide an API, it also provides a
> > > > + * calling convention (ABI) that is embodied in its name, return type,
> > > > + * arguments, etc.  On occasion that function may need to change to accomodate
> > > > + * new functionality, behavior, etc.  When that occurs, it is desireable to
> > > > + * allow for backwards compatibility for a time with older binaries that are
> > > > + * dynamically linked to the dpdk.  to support that the __vsym and
> > > > + * VERSION_SYMBOL macros are created.  They, in conjunction with the
> > > > + * <library>_version.map file for a given library allow for multiple versions of
> > > > + * a symbol to exist in a shared library so that older binaries need not be
> > > > + * immediately recompiled. Their use is outlined in the following example:
> > > > + * Assumptions: DPDK 1.(X) contains a function int foo(char *string)
> > > > + *              DPDK 1.(X+1) needs to change foo to be int foo(int index)
> > > > + *
> > > > + * To accomplish this:
> > > > + * 1) Edit lib/<library>/library_version.map to add a DPDK_1.8 node, in which
> > > > + * foo is exported as a global symbol
> > > > + *
> > > > + * 2) rename the existing function int foo(char *string) to 
> > > > + * 	int __vsym foo_v18(char *string)
> > > > + *
> > > > + * 3) Add this macro immediately below the function
> > > > + * 	VERSION_SYMBOL(foo, _v18, 1.8);
> > > > + *
> > > > + */
> > > > +#define VERSION_SYMBOL(b, e, v) __asm__(".symver " SA(b) SA(e) ", "SA(b)"@DPDK_"SA(v))
> > > > +#define __vsym __attribute__((used))
> > > > +
> > > 
> > > I may be missing something here but would it not be neccessary to define a
> > > default symbol?
> > > Otherwise there would be multiple definitions of a given symbol and the linker
> > > won't know which symbol version to bind to.
> > > 
> > > Following your example, something along these lines:
> > >  4) Edit lib/<library>/library_version.map to add a DPDK_1.9 node that is a
> > >    successor to DPDK_1.8, in which foo is exported as a global symbol 
> > >    DPDK_1.9 {
> > >       global: foo;
> > >    } DPDK_1.8;
> > > 
> > >  5) rename new function int foo(int index) to
> > >    int __vsym foo_v19(int index)
> > > 
> > >  6) Add this macro immediately below the function:
> > >    DEFAULT_SYMBOL(foo, _v19, 1.9);
> > > 
> > > #define DEFAULT_SYMBOL(b, e, v) __asm__(".symver " SA(b) SA(e) ", "SA(b)"@@DPDK_"SA(v))
> > > 
> > 
> > You're spot on (though the macro that I created in rte_compat.h is
> > VERSION_SYMBOL).  
> > 
> > When you use a version script to create a DSO, at link time, the appropriate
> > version is appended to the symbol name (you can see it with objdump -t in a
> > linked binary).  If you want to update the symbol to a new version, you do what
> > I documented in the header file (though now that I re-read it, it could be more
> > clear.  Hows this for a change to the documentation:
> > 
> > To make a new version of a function foo in a DSO:
> > 
> > 1) Edit lib/<library>/library_version.map to add a DPDK_1.8 node, in which
> >    foo is exported as a global symbol
> > 
> > 2) rename the existing function int foo(char *string) to 
> >    int __vsym foo_v18(char *string)
> > 
> > 3) Add this macro immediately below the function
> >    VERSION_SYMBOL(foo, _v18, 1.8);
> > 
> > 4) Implement the new version of the function foo.
> > 
> > 
> > Those steps above will create two symbols in your export table of the DSO:
> > 
> > foo@@DPDK_1.8
> > foo@@DPDK_1.9
> > 
> > Any application linked against this DSO will link against the latest version
> > (DPDK_1.9).  But if you look at the symbols referenced in a binary linked
> > against an older version of the same DSO, you'll note they explicitly look for
> > foo@@DPDK_1.8.  Thats how we provide backwards compatibility
> > 
> > Does that answer your questions?
> > 
> > Neil
> > 
> Correct me if I am wrong but when we define multiple versions of a symbol we
> need to specify a default one.
You are corrected :).  The "Default" symbol is implicitly the latest version of
the symbol (where the ordinality of the symbol versions is defined by the map
file).

> As an example, if we were to have three versions of foo the export table of the
> DSO should look something like this:
> 
> foo at VER_1.0
> foo at VER_1.1
> foo@@VER_1.2
> 
> In the above example, foo VER_1.2 is the default one and is such based on the
fact that it is ordinally the most recent in the version map file.

Its the default symbol because its the latest one according to the map file (and
denoted by the double @'s).  When linking that is the only symbol visibile to
the application being linked.

> Effectively we would need two macros VERSION_SYMBOL and DEFAULT_VERSION_SYMBOL
> (maybe this name is more appropriate).
> 
Nope, we don't, because as you note above, the default is implicit by the fact
that it is ordinally the latest, and the latest version of the symbol is the
only version that the linker "sees" when linking new applications.  The
VERSION_SYMBOL macro exists to tie older binary applications to the older
versions of the symbol at _load_ time.

> #define VERSION_SYMBOL(b, e, v)         \
>     __asm__(".symver " SA(b) SA(e) ", "SA(b)"@DPDK_"SA(v))
> #define DEFAULT_VERSION_SYMBOL(b, e, v) \
>     __asm__(".symver " SA(b) SA(e) ", "SA(b)"@@DPDK_"SA(v))
> 
Nope.  Don't need it.

> Following on the example, we should have something like:
> 
>    int __vsym foo_v18(char *string) {...}
>    VERSION_SYMBOL(foo, _v18, 1.8);
> 
>    int __vsym foo_v19(int index) {...}
>    DEFAULT_VERSION_SYMBOL(foo, _v19, 1.9);
> 

Nope.  Lets start a bit further back.  Assume we have the following map file:

DPDK_1.8 {
	global:
	foo;
};

And we have this in a C file:

void foo(int num) {
	<implementation for version 1.8>	
}

Then we want to update the foo function to something that is binary
incompatible.  We would change the version map file as follows:

DPDK_1.8 {
	global:
	foo;
}

DPDK_1.9 {
	global:
	foo;
} DPDK_1.8;

That construct makes the linker see DPDK_1.9 ad ordinally "newer" than DPDK_1.8,
so symobls that are global in that version are exported rather than their older
counterparts in the DPDK_1.8 export set.  When the linker links a new
application it only links to the latest version

Then in the C file we do the following:

void foo_v18(int num) {
	<implementation for version 1.8>
}
VERSION_SYMBOL(foo, _v18, 1.8);

int foo(char *name) {
	<implementation for version 1.9>
}

With this change, the new foo function is implicitly matched to version 1.9 in
the map file, and thats what gets linked to new application.  The
VERSION_SYMBOL macro exports an additional symbol, foo@@DPDK_1.9, so that
previously built applications, that were linked when the origional version of
foo was the latest, will still find the appropriate symbol as foo@@DPDK_1.8 

We could in fact something like what you are suggesting, in that we could use
the VERSION_SYMBOL macro on every exported function so that we explicitly tied
every version of every exported symbol to a statically defined version of the
function with a variant name (i.e. we could have a foo_v18, a foo_v19, foo_vX,
for every supported API version if you wanted), but that creates alot more work
for us.  for instance, when doing a non DSO build, you still have to map each
symbol to a specfic version, and you have to keep that updated.  By doing it the
way I did above, the actual function name is always the latest version, and you
only have to rename functions if you need to modify the api (I'm working under
the assumption that needing to do this is going to be somewhat rare).

Hope that helps
Neil

> The DSO export table would have the following symbols:
> 
>    foo at DPDK_1.8
>    foo@@DPDK_1.9
> 
> Old binaries linked against DPDK 1.8 would have references to:
> foo@@DPDK_1.8
> 
> and new binaries linked against DPDK 1.9 would have to:
> foo@@DPDK_1.9
> 
> Sergio
> 
> > > > +#else
> > > > +/*
> > > > + * No symbol versioning in use
> > > > + */
> > > > +#define VERSION_SYMBOL(b, e, v)
> > > > +#define __vsym
> > > > +
> > > > +/*
> > > > + * RTE_SYMBOL_VERSIONING
> > > > + */
> > > > +#endif
> > > > +
> > > > +
> > > > +#endif /* _RTE_COMPAT_H_ */
> > > > diff --git a/mk/rte.lib.mk b/mk/rte.lib.mk
> > > > index f458258..82ac309 100644
> > > > --- a/mk/rte.lib.mk
> > > > +++ b/mk/rte.lib.mk
> > > > @@ -40,8 +40,12 @@ VPATH += $(SRCDIR)
> > > >  
> > > >  ifeq ($(RTE_BUILD_SHARED_LIB),y)
> > > >  LIB := $(patsubst %.a,%.so,$(LIB))
> > > > +
> > > > +CPU_LDFLAGS += --version-script=$(EXPORT_MAP)
> > > > +
> > > >  endif
> > > >  
> > > > +
> > > >  _BUILD = $(LIB)
> > > >  _INSTALL = $(INSTALL-FILES-y) $(SYMLINK-FILES-y) $(RTE_OUTPUT)/lib/$(LIB)
> > > >  _CLEAN = doclean
> > > > @@ -160,7 +164,9 @@ endif
> > > >  $(RTE_OUTPUT)/lib/$(LIB): $(LIB)
> > > >  	@echo "  INSTALL-LIB $(LIB)"
> > > >  	@[ -d $(RTE_OUTPUT)/lib ] || mkdir -p $(RTE_OUTPUT)/lib
> > > > +ifneq ($(LIB),)
> > > >  	$(Q)cp -f $(LIB) $(RTE_OUTPUT)/lib
> > > > +endif
> > > >  
> > > >  #
> > > >  # Clean all generated files
> > > > -- 
> > > > 1.9.3
> > > > 
> > > 
> > > Sergio
> > > 
> 


More information about the dev mailing list