[RFC] eal: add new function versioning macros

Message ID 20250305212349.2036410-1-david.marchand@redhat.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series [RFC] eal: add new function versioning macros |

Checks

Context Check Description
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Functional success Functional PASS
ci/github-robot: build success github build: passed
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-marvell-Functional success Functional Testing PASS RETEST #1
ci/iol-mellanox-Performance success Performance Testing PASS RETEST #1
ci/iol-intel-Functional success Functional Testing PASS RETEST #1
ci/iol-intel-Performance success Performance Testing PASS RETEST #1
ci/intel-Testing success Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS RETEST #1
ci/iol-sample-apps-testing success Testing PASS RETEST #1

Commit Message

David Marchand March 5, 2025, 9:23 p.m. UTC
For versioning symbols:
- MSVC uses pragmas on the function symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
versioning functions that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a function is just a call to a single macro:
- VERSION_FUNCTION (resp. VERSION_FUNCTION_EXPERIMENTAL), for keeping an
  old implementation code under a versioned function,
- DEFAULT_FUNCTION, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

Documentation has been updated though it needs some polishing.
The experimental macro has not been tested.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 doc/guides/contributing/abi_versioning.rst | 133 ++++-----------------
 lib/eal/include/rte_function_versioning.h  |  27 +++++
 lib/net/rte_net_crc.c                      |  41 +++----
 3 files changed, 69 insertions(+), 132 deletions(-)
  

Comments

Patrick Robb March 6, 2025, 2:57 a.m. UTC | #1
Recheck-request: iol-intel-Performance

There was an infra failure with the Arm Grace server about 12 hours ago
which caused this failure - sending a retest request.
  
Bruce Richardson March 6, 2025, 10:23 a.m. UTC | #2
On Wed, Mar 05, 2025 at 10:23:49PM +0100, David Marchand wrote:
> For versioning symbols:
> - MSVC uses pragmas on the function symbol,
> - GNU linker uses special asm directives,
> 
> To accommodate both GNU linker and MSVC linker, introduce new macros for
> versioning functions that will surround the whole function.
> 
> This has the advantage of hiding all the ugly details in the macros.
> Now versioning a function is just a call to a single macro:
> - VERSION_FUNCTION (resp. VERSION_FUNCTION_EXPERIMENTAL), for keeping an
>   old implementation code under a versioned function,
> - DEFAULT_FUNCTION, for declaring the new default versioned function,
>   and handling the static link special case, instead of
>   BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,
> 
> Documentation has been updated though it needs some polishing.
> The experimental macro has not been tested.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---
This looks a nice improvement to me, thanks.
  
Morten Brørup March 11, 2025, 10:18 a.m. UTC | #3
> From: David Marchand [mailto:david.marchand@redhat.com]
> Sent: Tuesday, 11 March 2025 10.56
> 
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
> 
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to
> the
> build framework to produce a version script adapted to the linker in
> use
> (think GNU linker vs MSVC linker).
> 
> This greatly simplifies versioning symbols: a developer does not need
> to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
> 
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
> 
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
> 
> 
> Comments please.

Excellent. I'm all for automating this!

Feature creep:

Have you thought about how this (or related automation) can possibly also benefit the CI, e.g. for ABI breakage testing?

Or possible benefits to (automated) documentation of versioned functions?
Or possible benefits to remembering all versioned ABIs when writing the release notes?
  
David Marchand March 11, 2025, 1:43 p.m. UTC | #4
On Tue, Mar 11, 2025 at 11:18 AM Morten Brørup <mb@smartsharesystems.com> wrote:
>
> > From: David Marchand [mailto:david.marchand@redhat.com]
> > Sent: Tuesday, 11 March 2025 10.56
> >
> > So far, each DPDK library (or driver) exposing symbols in an ABI had to
> > maintain a version.map and use some macros for symbol versioning,
> > specially crafted with the GNU linker in mind.
> >
> > This series proposes to rework the whole principle, and instead rely on
> > marking the symbol exports in the source code itself, then let it to
> > the
> > build framework to produce a version script adapted to the linker in
> > use
> > (think GNU linker vs MSVC linker).
> >
> > This greatly simplifies versioning symbols: a developer does not need
> > to
> > know anything about version.map, or that a versioned symbol must be
> > renamed with _v26, annotated with __vsym, exported in a header etc...
> >
> > Checking symbol maps becomes unnecessary since generated by the build
> > framework.
> >
> > Updating to a new ABI is just a matter of bumping the value in
> > ABI_VERSION.
> >
> >
> > Comments please.
>
> Excellent. I'm all for automating this!
>
> Feature creep:
>
> Have you thought about how this (or related automation) can possibly also benefit the CI, e.g. for ABI breakage testing?
>
> Or possible benefits to (automated) documentation of versioned functions?
> Or possible benefits to remembering all versioned ABIs when writing the release notes?

Not really, this series is already touching enough code and needs in
detail reviews.
A simple ack is pointless.

I prefer focusing on just making this part right.
  
David Marchand March 18, 2025, 8:19 a.m. UTC | #5
On Mon, Mar 17, 2025 at 4:43 PM David Marchand
<david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
>
>
> Comments please.
>
>
> --
> David Marchand
>
> Depends-on: series-34869 ("remove driver-specific logic for AVX builds")

Erm, looks like the CI did not catch this dependency (maybe I should
have not put it after the signature.. ?).

Anyway... this is 25.07 material, I'll repost once dependencies (Bruce
series) have been merged in next release.
  
David Marchand March 26, 2025, 12:02 p.m. UTC | #6
On Mon, Mar 17, 2025 at 4:43 PM David Marchand
<david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
>
>
> Comments please.

- I am considering making rte_function_versioning.h a non exported
header (precisely, moving it to buildtools/ and maybe renaming it).

This header contains macros not prefixed with RTE_.
Using it requires some build trick (see use_function_versioning).
And I don't see symbol versioning as a MUST infrastructure that DPDK
needs to provide to datapath applications.

Yet technically, this change would be an API breakage if some
applications indeed relied on it.

Cc: techboard for info.


- On a similar note, this RFC series adds the rte_exports.h header
(defining RTE_EXPORT*_SYMBOL()) in config/, though its job is for
extracting a symbol list during the build.
So a better location is probably buildtools/.
  
Morten Brørup March 26, 2025, 12:26 p.m. UTC | #7
> From: David Marchand [mailto:david.marchand@redhat.com]
> Sent: Wednesday, 26 March 2025 13.03
> 

[...]

> And I don't see symbol versioning as a MUST infrastructure that DPDK
> needs to provide to datapath applications.

Agree.
Getting rid of the exotic stuff like symbol versioning would make the DPDK API easier understandable for DPDK application developers.
Also, it's my impression that symbol versioning does not really play a major role in keeping or breaking ABI compatibility.
Conclusion: Not worth the complexity.
  
Bruce Richardson March 26, 2025, 1:07 p.m. UTC | #8
On Wed, Mar 26, 2025 at 01:02:32PM +0100, David Marchand wrote:
> On Mon, Mar 17, 2025 at 4:43 PM David Marchand
> <david.marchand@redhat.com> wrote:
> >
> > So far, each DPDK library (or driver) exposing symbols in an ABI had to
> > maintain a version.map and use some macros for symbol versioning,
> > specially crafted with the GNU linker in mind.
> >
> > This series proposes to rework the whole principle, and instead rely on
> > marking the symbol exports in the source code itself, then let it to the
> > build framework to produce a version script adapted to the linker in use
> > (think GNU linker vs MSVC linker).
> >
> > This greatly simplifies versioning symbols: a developer does not need to
> > know anything about version.map, or that a versioned symbol must be
> > renamed with _v26, annotated with __vsym, exported in a header etc...
> >
> > Checking symbol maps becomes unnecessary since generated by the build
> > framework.
> >
> > Updating to a new ABI is just a matter of bumping the value in
> > ABI_VERSION.
> >
> >
> > Comments please.
> 
> - I am considering making rte_function_versioning.h a non exported
> header (precisely, moving it to buildtools/ and maybe renaming it).
> 

+1 for not exporting it.
-1 for moving to buildtools. I don't see the need to introduce yet another
header path in DPDK. Let's just keep it where it is, or moved slightly in
the EAL folder, and then not export it.

> This header contains macros not prefixed with RTE_.
> Using it requires some build trick (see use_function_versioning).
> And I don't see symbol versioning as a MUST infrastructure that DPDK
> needs to provide to datapath applications.
> 
> Yet technically, this change would be an API breakage if some
> applications indeed relied on it.
> 
> Cc: techboard for info.
> 
> 
> - On a similar note, this RFC series adds the rte_exports.h header
> (defining RTE_EXPORT*_SYMBOL()) in config/, though its job is for
> extracting a symbol list during the build.
> So a better location is probably buildtools/.
> 

Again, don't particularly like buildtools as a path, as it's not really a
tool, just a header file. I'd rather keep the tools folders for scripts and
the like.

/Bruce
  
Bruce Richardson March 26, 2025, 1:36 p.m. UTC | #9
On Wed, Mar 26, 2025 at 01:02:32PM +0100, David Marchand wrote:
> On Mon, Mar 17, 2025 at 4:43 PM David Marchand
> <david.marchand@redhat.com> wrote:
> >
> > So far, each DPDK library (or driver) exposing symbols in an ABI had to
> > maintain a version.map and use some macros for symbol versioning,
> > specially crafted with the GNU linker in mind.
> >
> > This series proposes to rework the whole principle, and instead rely on
> > marking the symbol exports in the source code itself, then let it to the
> > build framework to produce a version script adapted to the linker in use
> > (think GNU linker vs MSVC linker).
> >
> > This greatly simplifies versioning symbols: a developer does not need to
> > know anything about version.map, or that a versioned symbol must be
> > renamed with _v26, annotated with __vsym, exported in a header etc...
> >
> > Checking symbol maps becomes unnecessary since generated by the build
> > framework.
> >
> > Updating to a new ABI is just a matter of bumping the value in
> > ABI_VERSION.
> >
> >
> > Comments please.
> 
> - I am considering making rte_function_versioning.h a non exported
> header (precisely, moving it to buildtools/ and maybe renaming it).
> 
> This header contains macros not prefixed with RTE_.
> Using it requires some build trick (see use_function_versioning).
> And I don't see symbol versioning as a MUST infrastructure that DPDK
> needs to provide to datapath applications.
> 
> Yet technically, this change would be an API breakage if some
> applications indeed relied on it.
> 

Is it not needed for exporting if an exported library header had versioned
symbols is in?

/Bruce
  
David Marchand March 26, 2025, 1:54 p.m. UTC | #10
On Wed, Mar 26, 2025 2:37 PM Bruce Richardson
<bruce.richardson@intel.com> wrote:
> > - I am considering making rte_function_versioning.h a non exported
> > header (precisely, moving it to buildtools/ and maybe renaming it).
> >
> > This header contains macros not prefixed with RTE_.
> > Using it requires some build trick (see use_function_versioning).
> > And I don't see symbol versioning as a MUST infrastructure that DPDK
> > needs to provide to datapath applications.
> >
> > Yet technically, this change would be an API breakage if some
> > applications indeed relied on it.
> >
>
> Is it not needed for exporting if an exported library header had versioned
> symbols is in?

That could be, though we don't expose such versionned symbols atm (and
I don't think we ever did in DPDK).
The only public symbol is always the symbol implemented for the latest
ABI for newly compiled applications.

Wrt to your other comment on the header(s) location, I don't mind
moving to lib/eal/common.
  
Bruce Richardson March 26, 2025, 2:16 p.m. UTC | #11
On Wed, Mar 26, 2025 at 02:54:32PM +0100, David Marchand wrote:
> On Wed, Mar 26, 2025 2:37 PM Bruce Richardson
> <bruce.richardson@intel.com> wrote:
> > > - I am considering making rte_function_versioning.h a non exported
> > > header (precisely, moving it to buildtools/ and maybe renaming it).
> > >
> > > This header contains macros not prefixed with RTE_.
> > > Using it requires some build trick (see use_function_versioning).
> > > And I don't see symbol versioning as a MUST infrastructure that DPDK
> > > needs to provide to datapath applications.
> > >
> > > Yet technically, this change would be an API breakage if some
> > > applications indeed relied on it.
> > >
> >
> > Is it not needed for exporting if an exported library header had versioned
> > symbols is in?
> 
> That could be, though we don't expose such versionned symbols atm (and
> I don't think we ever did in DPDK).
> The only public symbol is always the symbol implemented for the latest
> ABI for newly compiled applications.
>

Ok, that's good. Looking through code and code history I do indeed see that
the use of function versioning in the past seems to have all been done
through the C file. Therefore, no issues with making the header
internal-only and not exported.

/Bruce
  
David Marchand March 27, 2025, 6:22 p.m. UTC | #12
On Thu, Mar 27, 2025 at 2:37 PM David Marchand
<david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.

Intel CI keeps on spewing non sense failures on doc generation, as it
filters out doc/ updates from patches...
  
David Marchand April 7, 2025, 1:46 p.m. UTC | #13
On Thu, Apr 3, 2025 at 6:59 PM David Marchand <david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
>
>
>
> --
> David Marchand
>
> Changes since v6:
> - used argparse in python scripts,
> - renamed eal_symbol_exports.h as eal_export.h,
> - renamed some base symbols export files,
>
> Changes since v5:
> - fixed Windows symbol exports for net/mlx5,
>
> Changes since RFC v4:
> - rebased on main, now that Bruce series is merged,
> - the export macros header has been moved to lib/eal/common/
>   and its inclusion is now mandatory (rather than an implicit -include),
> - reordered patches: symbol versioning is touched last and merged
>   in the export header (replacing the legacy rte_function_versioning.h),
>
> Changes since RFC v3:
> - fixed/simplified documentation,
> - rebased on top of Bruce series for common handling of AVX sources,
>
> Changes since RFC v2:
> - updated RTE_VERSION_SYMBOL() (and friends) so that only the fonction
>   signature is enclosed in the macro,
> - dropped invalid exports for some dead symbols or inline helpers,
> - updated documentation and tooling,
> - converted the whole tree (via a local script of mine),
>
> David Marchand (8):
>   lib: remove incorrect exported symbols
>   drivers: remove incorrect exported symbols
>   buildtools: display symbols version from map
>   build: generate symbol maps
>   build: mark exported symbols
>   build: use dynamically generated version maps
>   build: remove static version maps
>   eal: rework function versioning macros

Series applied.

Heads up to subtree maintainers.
TL;DR: please rebase your trees.

If you had changes on version.map in your trees, you'll need to
convert the new symbols and use RTE_EXPORT_SYMBOL* macros.
If you have some doubt, ping me and I'll help.
  

Patch

diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..b83383fd0b 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -277,86 +277,52 @@  list of exported symbols when DPDK is compiled as a shared library.
 
 Next, we need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``VERSION_FUNCTION``, passing the current ABI version,
+the function return type, and the function name, then the full implementation of the
+function.
 
 .. code-block:: c
 
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ +VERSION_FUNCTION(21,
+ +struct rte_acl_ctx *,
+ +rte_acl_create, (const struct rte_acl_param *param)
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
-
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -}
+ +})
 
 Remembering to also add the rte_function_versioning.h header to the requisite c
 file where these changes are being made. The macro instructs the linker to
 create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+in older builds, but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``DEFAULT_FUNCTION``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   DEFAULT_FUNCTION(22,
+   struct rte_acl_ctx *,
+   rte_acl_create, (const struct rte_acl_param *param, int debug);
    {
         struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
 
         ctx->debug = debug;
 
         return ctx;
-   }
-
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+   })
 
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it, on the next shared library rebuild, there will be two versions of
 rte_acl_create, an old DPDK_21 version, used by previously built applications,
@@ -365,43 +331,10 @@  and a new DPDK_22 version, used by future built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -519,26 +452,19 @@  and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   DEFAULT_FUNCTION(22,
+   struct rte_acl_ctx *,
+   rte_acl_create, (const struct rte_acl_param *param)
    {
    ...
-   }
-
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
+   })
 
+   VERSION_FUNCTION_EXPERIMENTAL(
    struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   rte_acl_create, (const struct rte_acl_param *param)
    {
       return rte_acl_create(param);
-   }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+   })
 
 In the map file, we map the symbol to both the ``EXPERIMENTAL``
 and ``DPDK_22`` version nodes.
@@ -564,13 +490,6 @@  and ``DPDK_22`` version nodes.
         rte_acl_create;
    };
 
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
index eb6dd2bc17..7a33a45928 100644
--- a/lib/eal/include/rte_function_versioning.h
+++ b/lib/eal/include/rte_function_versioning.h
@@ -96,4 +96,31 @@ 
  */
 #endif
 
+#ifdef RTE_BUILD_SHARED_LIB
+
+#define VERSION_FUNCTION(ver, type, name, ...) \
+__rte_used type name ## _v ## ver __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver));
+
+#define VERSION_FUNCTION_EXPERIMENTAL(type, name, ...) \
+__rte_used type name ## _exp __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL")
+
+#define DEFAULT_FUNCTION(ver, type, name, ...) \
+__rte_used type name ## _v ## ver __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver));
+
+#else /* !RTE_BUILD_SHARED_LIB */
+
+#define VERSION_FUNCTION(ver, type, name, ...) \
+type name ## _v ## ver __VA_ARGS__
+
+#define VERSION_FUNCTION_EXPERIMENTAL(type, name, ...) \
+type name ## _exp __VA_ARGS__
+
+#define DEFAULT_FUNCTION(ver, type, name, ...) \
+type name __VA_ARGS__
+
+#endif /* RTE_BUILD_SHARED_LIB */
+
 #endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 2fb3eec231..a1c17e0735 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -345,8 +345,9 @@  handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+VERSION_FUNCTION(25,
+void,
+rte_net_crc_set_alg, (enum rte_net_crc_alg alg)
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -372,11 +373,11 @@  rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 
 	if (handlers == NULL)
 		handlers = handlers_scalar;
-}
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
+})
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type)
+DEFAULT_FUNCTION(26,
+struct rte_net_crc *,
+rte_net_crc_set_alg, (enum rte_net_crc_alg alg, enum rte_net_crc_type type)
 {
 	uint16_t max_simd_bitwidth;
 	struct rte_net_crc *crc;
@@ -413,21 +414,16 @@  struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 		break;
 	}
 	return crc;
-}
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
+})
 
 void rte_net_crc_free(struct rte_net_crc *crc)
 {
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type)
+VERSION_FUNCTION(25,
+uint32_t,
+rte_net_crc_calc, (const void *data, uint32_t data_len, enum rte_net_crc_type type)
 {
 	uint32_t ret;
 	rte_net_crc_handler f_handle;
@@ -436,19 +432,14 @@  rte_net_crc_calc_v25(const void *data,
 	ret = f_handle(data, data_len);
 
 	return ret;
-}
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
+})
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len)
+DEFAULT_FUNCTION(26,
+uint32_t,
+rte_net_crc_calc, (const struct rte_net_crc *ctx, const void *data, const uint32_t data_len)
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
-}
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
+})
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)