1.. SPDX-License-Identifier: BSD-3-Clause 2 Copyright 2018 The DPDK contributors 3 4.. _abi_versioning: 5 6ABI Versioning 7============== 8 9This document details the mechanics of ABI version management in DPDK. 10 11.. _what_is_soname: 12 13What is a library's soname? 14--------------------------- 15 16System libraries usually adopt the familiar major and minor version naming 17convention, where major versions (e.g. ``librte_eal 21.x, 22.x``) are presumed 18to be ABI incompatible with each other and minor versions (e.g. ``librte_eal 1921.1, 21.2``) are presumed to be ABI compatible. A library's `soname 20<https://en.wikipedia.org/wiki/Soname>`_. is typically used to provide backward 21compatibility information about a given library, describing the lowest common 22denominator ABI supported by the library. The soname or logical name for the 23library, is typically comprised of the library's name and major version e.g. 24``librte_eal.so.21``. 25 26During an application's build process, a library's soname is noted as a runtime 27dependency of the application. This information is then used by the `dynamic 28linker <https://en.wikipedia.org/wiki/Dynamic_linker>`_ when resolving the 29applications dependencies at runtime, to load a library supporting the correct 30ABI version. The library loaded at runtime therefore, may be a minor revision 31supporting the same major ABI version (e.g. ``librte_eal.21.2``), as the library 32used to link the application (e.g ``librte_eal.21.0``). 33 34.. _major_abi_versions: 35 36Major ABI versions 37------------------ 38 39An ABI version change to a given library, especially in core libraries such as 40``librte_mbuf``, may cause an implicit ripple effect on the ABI of it's 41consuming libraries, causing ABI breakages. There may however be no explicit 42reason to bump a dependent library's ABI version, as there may have been no 43obvious change to the dependent library's API, even though the library's ABI 44compatibility will have been broken. 45 46This interdependence of DPDK libraries, means that ABI versioning of libraries 47is more manageable at a project level, with all project libraries sharing a 48**single ABI version**. In addition, the need to maintain a stable ABI for some 49number of releases as described in the section :doc:`abi_policy`, means 50that ABI version increments need to carefully planned and managed at a project 51level. 52 53Major ABI versions are therefore declared typically aligned with an LTS release 54and is then supported some number of subsequent releases, shared across all 55libraries. This means that a single project level ABI version, reflected in all 56individual library's soname, library filenames and associated version maps 57persists over multiple releases. 58 59.. code-block:: none 60 61 $ head ./lib/acl/version.map 62 DPDK_21 { 63 global: 64 ... 65 66 $ head ./lib/eal/version.map 67 DPDK_21 { 68 global: 69 ... 70 71When an ABI change is made between major ABI versions to a given library, a new 72section is added to that library's version map describing the impending new ABI 73version, as described in the section :ref:`example_abi_macro_usage`. The 74library's soname and filename however do not change, e.g. ``libacl.so.21``, as 75ABI compatibility with the last major ABI version continues to be preserved for 76that library. 77 78.. code-block:: none 79 80 $ head ./lib/acl/version.map 81 DPDK_21 { 82 global: 83 ... 84 85 DPDK_22 { 86 global: 87 88 } DPDK_21; 89 ... 90 91 $ head ./lib/eal/version.map 92 DPDK_21 { 93 global: 94 ... 95 96However when a new ABI version is declared, for example DPDK ``22``, old 97deprecated functions may be safely removed at this point and the entire old 98major ABI version removed, see the section :ref:`deprecating_entire_abi` on 99how this may be done. 100 101.. code-block:: none 102 103 $ head ./lib/acl/version.map 104 DPDK_22 { 105 global: 106 ... 107 108 $ head ./lib/eal/version.map 109 DPDK_22 { 110 global: 111 ... 112 113At the same time, the major ABI version is changed atomically across all 114libraries by incrementing the major version in the ABI_VERSION file. This is 115done globally for all libraries. 116 117Minor ABI versions 118~~~~~~~~~~~~~~~~~~ 119 120Each non-LTS release will also increment minor ABI version, to permit multiple 121DPDK versions being installed alongside each other. Both stable and 122experimental ABI's are versioned using the global version file that is updated 123at the start of each release cycle, and are managed at the project level. 124 125Versioning Macros 126----------------- 127 128When a symbol is exported from a library to provide an API, it also provides a 129calling convention (ABI) that is embodied in its name, return type and 130arguments. Occasionally that function may need to change to accommodate new 131functionality or behavior. When that occurs, it is may be required to allow for 132backward compatibility for a time with older binaries that are dynamically 133linked to the DPDK. 134 135To support backward compatibility the ``rte_function_versioning.h`` 136header file provides macros to use when updating exported functions. These 137macros are used in conjunction with the ``version.map`` file for 138a given library to allow multiple versions of a symbol to exist in a shared 139library so that older binaries need not be immediately recompiled. 140 141The macros exported are: 142 143* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding 144 versioned symbol ``b@DPDK_n`` to the internal function ``be``. 145 146* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing 147 the linker to bind references to symbol ``b`` to the internal symbol 148 ``be``. 149 150* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the 151 fully qualified function ``p``, so that if a symbol becomes versioned, it 152 can still be mapped back to the public symbol name. 153 154* ``__vsym``: Annotation to be used in a declaration of the internal symbol 155 ``be`` to signal that it is being used as an implementation of a particular 156 version of symbol ``b``. 157 158* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry 159 binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``. 160 The macro is used when a symbol matures to become part of the stable ABI, to 161 provide an alias to experimental until the next major ABI version. 162 163.. _example_abi_macro_usage: 164 165Examples of ABI Macro use 166~~~~~~~~~~~~~~~~~~~~~~~~~ 167 168Updating a public API 169_____________________ 170 171Assume we have a function as follows 172 173.. code-block:: c 174 175 /* 176 * Create an acl context object for apps to 177 * manipulate 178 */ 179 struct rte_acl_ctx * 180 rte_acl_create(const struct rte_acl_param *param) 181 { 182 ... 183 } 184 185 186Assume that struct rte_acl_ctx is a private structure, and that a developer 187wishes to enhance the acl api so that a debugging flag can be enabled on a 188per-context basis. This requires an addition to the structure (which, being 189private, is safe), but it also requires modifying the code as follows 190 191.. code-block:: c 192 193 /* 194 * Create an acl context object for apps to 195 * manipulate 196 */ 197 struct rte_acl_ctx * 198 rte_acl_create(const struct rte_acl_param *param, int debug) 199 { 200 ... 201 } 202 203 204Note also that, being a public function, the header file prototype must also be 205changed, as must all the call sites, to reflect the new ABI footprint. We will 206maintain previous ABI versions that are accessible only to previously compiled 207binaries. 208 209The addition of a parameter to the function is ABI breaking as the function is 210public, and existing application may use it in its current form. However, the 211compatibility macros in DPDK allow a developer to use symbol versioning so that 212multiple functions can be mapped to the same public symbol based on when an 213application was linked to it. To see how this is done, we start with the 214requisite libraries version map file. Initially the version map file for the acl 215library looks like this 216 217.. code-block:: none 218 219 DPDK_21 { 220 global: 221 222 rte_acl_add_rules; 223 rte_acl_build; 224 rte_acl_classify; 225 rte_acl_classify_alg; 226 rte_acl_classify_scalar; 227 rte_acl_create; 228 rte_acl_dump; 229 rte_acl_find_existing; 230 rte_acl_free; 231 rte_acl_ipv4vlan_add_rules; 232 rte_acl_ipv4vlan_build; 233 rte_acl_list_dump; 234 rte_acl_reset; 235 rte_acl_reset_rules; 236 rte_acl_set_ctx_classify; 237 238 local: *; 239 }; 240 241This file needs to be modified as follows 242 243.. code-block:: none 244 245 DPDK_21 { 246 global: 247 248 rte_acl_add_rules; 249 rte_acl_build; 250 rte_acl_classify; 251 rte_acl_classify_alg; 252 rte_acl_classify_scalar; 253 rte_acl_create; 254 rte_acl_dump; 255 rte_acl_find_existing; 256 rte_acl_free; 257 rte_acl_ipv4vlan_add_rules; 258 rte_acl_ipv4vlan_build; 259 rte_acl_list_dump; 260 rte_acl_reset; 261 rte_acl_reset_rules; 262 rte_acl_set_ctx_classify; 263 264 local: *; 265 }; 266 267 DPDK_22 { 268 global: 269 rte_acl_create; 270 271 } DPDK_21; 272 273The addition of the new block tells the linker that a new version node 274``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits 275the symbols from the DPDK_21 node. This list is directly translated into a 276list of exported symbols when DPDK is compiled as a shared library. 277 278Next, we need to specify in the code which function maps to the rte_acl_create 279symbol at which versions. First, at the site of the initial symbol definition, 280we need to update the function so that it is uniquely named, and not in conflict 281with the public symbol name 282 283.. code-block:: c 284 285 -struct rte_acl_ctx * 286 -rte_acl_create(const struct rte_acl_param *param) 287 +struct rte_acl_ctx * __vsym 288 +rte_acl_create_v21(const struct rte_acl_param *param) 289 { 290 size_t sz; 291 struct rte_acl_ctx *ctx; 292 ... 293 294Note that the base name of the symbol was kept intact, as this is conducive to 295the macros used for versioning symbols and we have annotated the function as 296``__vsym``, an implementation of a versioned symbol . That is our next step, 297mapping this new symbol name to the initial symbol name at version node 21. 298Immediately after the function, we add the VERSION_SYMBOL macro. 299 300.. code-block:: c 301 302 #include <rte_function_versioning.h> 303 304 ... 305 VERSION_SYMBOL(rte_acl_create, _v21, 21); 306 307Remembering to also add the rte_function_versioning.h header to the requisite c 308file where these changes are being made. The macro instructs the linker to 309create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created 310in older builds, but now points to the above newly named function. We have now 311mapped the original rte_acl_create symbol to the original function (but with a 312new name). 313 314Please see the section :ref:`Enabling versioning macros 315<enabling_versioning_macros>` to enable this macro in the meson/ninja build. 316Next, we need to create the new ``v22`` version of the symbol. We create a new 317function name, with the ``v22`` suffix, and implement it appropriately. 318 319.. code-block:: c 320 321 struct rte_acl_ctx * __vsym 322 rte_acl_create_v22(const struct rte_acl_param *param, int debug); 323 { 324 struct rte_acl_ctx *ctx = rte_acl_create_v21(param); 325 326 ctx->debug = debug; 327 328 return ctx; 329 } 330 331This code serves as our new API call. Its the same as our old call, but adds the 332new parameter in place. Next we need to map this function to the new default 333symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function, 334we add the BIND_DEFAULT_SYMBOL macro. 335 336.. code-block:: c 337 338 #include <rte_function_versioning.h> 339 340 ... 341 BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22); 342 343The macro instructs the linker to create the new default symbol 344``rte_acl_create@DPDK_22``, which points to the above newly named function. 345 346We finally modify the prototype of the call in the public header file, 347such that it contains both versions of the symbol and the public API. 348 349.. code-block:: c 350 351 struct rte_acl_ctx * 352 rte_acl_create(const struct rte_acl_param *param); 353 354 struct rte_acl_ctx * __vsym 355 rte_acl_create_v21(const struct rte_acl_param *param); 356 357 struct rte_acl_ctx * __vsym 358 rte_acl_create_v22(const struct rte_acl_param *param, int debug); 359 360 361And that's it, on the next shared library rebuild, there will be two versions of 362rte_acl_create, an old DPDK_21 version, used by previously built applications, 363and a new DPDK_22 version, used by future built applications. 364 365.. note:: 366 367 **Before you leave**, please take care reviewing the sections on 368 :ref:`mapping static symbols <mapping_static_symbols>`, 369 :ref:`enabling versioning macros <enabling_versioning_macros>`, 370 and :ref:`ABI deprecation <abi_deprecation>`. 371 372 373.. _mapping_static_symbols: 374 375Mapping static symbols 376______________________ 377 378Now we've taken what was a public symbol, and duplicated it into two uniquely 379and differently named symbols. We've then mapped each of those back to the 380public symbol ``rte_acl_create`` with different version tags. This only applies 381to dynamic linking, as static linking has no notion of versioning. That leaves 382this code in a position of no longer having a symbol simply named 383``rte_acl_create`` and a static build will fail on that missing symbol. 384 385To correct this, we can simply map a function of our choosing back to the public 386symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro. Generally the 387assumption is that the most recent version of the symbol is the one you want to 388map. So, back in the C file where, immediately after ``rte_acl_create_v22`` is 389defined, we add this 390 391 392.. code-block:: c 393 394 struct rte_acl_ctx * __vsym 395 rte_acl_create_v22(const struct rte_acl_param *param, int debug) 396 { 397 ... 398 } 399 MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22); 400 401That tells the compiler that, when building a static library, any calls to the 402symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22`` 403 404 405.. _enabling_versioning_macros: 406 407Enabling versioning macros 408__________________________ 409 410Finally, we need to indicate to the :doc:`meson/ninja build system 411<../prog_guide/build-sdk-meson>` to enable versioning macros when building the 412library or driver. In the libraries or driver where we have added symbol 413versioning, in the ``meson.build`` file we add the following 414 415.. code-block:: none 416 417 use_function_versioning = true 418 419at the start of the head of the file. This will indicate to the tool-chain to 420enable the function version macros when building. 421 422 423.. _aliasing_experimental_symbols: 424 425Aliasing experimental symbols 426_____________________________ 427 428In situations in which an ``experimental`` symbol has been stable for some time, 429and it becomes a candidate for promotion to the stable ABI. At this time, when 430promoting the symbol, the maintainer may choose to provide an alias to the 431``experimental`` symbol version, so as not to break consuming applications. 432This alias is then dropped in the next major ABI version. 433 434The process to provide an alias to ``experimental`` is similar to that, of 435:ref:`symbol versioning <example_abi_macro_usage>` described above. 436Assume we have an experimental function ``rte_acl_create`` as follows: 437 438.. code-block:: c 439 440 #include <rte_compat.h> 441 442 /* 443 * Create an acl context object for apps to 444 * manipulate 445 */ 446 __rte_experimental 447 struct rte_acl_ctx * 448 rte_acl_create(const struct rte_acl_param *param) 449 { 450 ... 451 } 452 453In the map file, experimental symbols are listed as part of the ``EXPERIMENTAL`` 454version node. 455 456.. code-block:: none 457 458 DPDK_21 { 459 global: 460 ... 461 462 local: *; 463 }; 464 465 EXPERIMENTAL { 466 global: 467 468 rte_acl_create; 469 }; 470 471When we promote the symbol to the stable ABI, we simply strip the 472``__rte_experimental`` annotation from the function and move the symbol from the 473``EXPERIMENTAL`` node, to the node of the next major ABI version as follow. 474 475.. code-block:: c 476 477 /* 478 * Create an acl context object for apps to 479 * manipulate 480 */ 481 struct rte_acl_ctx * 482 rte_acl_create(const struct rte_acl_param *param) 483 { 484 ... 485 } 486 487We then update the map file, adding the symbol ``rte_acl_create`` 488to the ``DPDK_22`` version node. 489 490.. code-block:: none 491 492 DPDK_21 { 493 global: 494 ... 495 496 local: *; 497 }; 498 499 DPDK_22 { 500 global: 501 502 rte_acl_create; 503 } DPDK_21; 504 505 506Although there are strictly no guarantees or commitments associated with 507:ref:`experimental symbols <experimental_apis>`, a maintainer may wish to offer 508an alias to experimental. The process to add an alias to experimental, 509is similar to the symbol versioning process. Assuming we have an experimental 510symbol as before, we now add the symbol to both the ``EXPERIMENTAL`` 511and ``DPDK_22`` version nodes. 512 513.. code-block:: c 514 515 #include <rte_compat.h>; 516 #include <rte_function_versioning.h> 517 518 /* 519 * Create an acl context object for apps to 520 * manipulate 521 */ 522 struct rte_acl_ctx * 523 rte_acl_create(const struct rte_acl_param *param) 524 { 525 ... 526 } 527 528 __rte_experimental 529 struct rte_acl_ctx * 530 rte_acl_create_e(const struct rte_acl_param *param) 531 { 532 return rte_acl_create(param); 533 } 534 VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e); 535 536 struct rte_acl_ctx * 537 rte_acl_create_v22(const struct rte_acl_param *param) 538 { 539 return rte_acl_create(param); 540 } 541 BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22); 542 543In the map file, we map the symbol to both the ``EXPERIMENTAL`` 544and ``DPDK_22`` version nodes. 545 546.. code-block:: none 547 548 DPDK_21 { 549 global: 550 ... 551 552 local: *; 553 }; 554 555 DPDK_22 { 556 global: 557 558 rte_acl_create; 559 } DPDK_21; 560 561 EXPERIMENTAL { 562 global: 563 564 rte_acl_create; 565 }; 566 567.. note:: 568 569 Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`, 570 when aliasing to experimental you will also need to take care of 571 :ref:`mapping static symbols <mapping_static_symbols>`. 572 573 574.. _abi_deprecation: 575 576Deprecating part of a public API 577________________________________ 578 579Lets assume that you've done the above updates, and in preparation for the next 580major ABI version you decide you would like to retire the old version of the 581function. After having gone through the ABI deprecation announcement process, 582removal is easy. Start by removing the symbol from the requisite version map 583file: 584 585.. code-block:: none 586 587 DPDK_21 { 588 global: 589 590 rte_acl_add_rules; 591 rte_acl_build; 592 rte_acl_classify; 593 rte_acl_classify_alg; 594 rte_acl_classify_scalar; 595 rte_acl_dump; 596 - rte_acl_create 597 rte_acl_find_existing; 598 rte_acl_free; 599 rte_acl_ipv4vlan_add_rules; 600 rte_acl_ipv4vlan_build; 601 rte_acl_list_dump; 602 rte_acl_reset; 603 rte_acl_reset_rules; 604 rte_acl_set_ctx_classify; 605 606 local: *; 607 }; 608 609 DPDK_22 { 610 global: 611 rte_acl_create; 612 } DPDK_21; 613 614 615Next remove the corresponding versioned export. 616 617.. code-block:: c 618 619 -VERSION_SYMBOL(rte_acl_create, _v21, 21); 620 621 622Note that the internal function definition could also be removed, but its used 623in our example by the newer version ``v22``, so we leave it in place and declare 624it as static. This is a coding style choice. 625 626.. _deprecating_entire_abi: 627 628Deprecating an entire ABI version 629_________________________________ 630 631While removing a symbol from an ABI may be useful, it is more practical to 632remove an entire version node at once, as is typically done at the declaration 633of a major ABI version. If a version node completely specifies an API, then 634removing part of it, typically makes it incomplete. In those cases it is better 635to remove the entire node. 636 637To do this, start by modifying the version map file, such that all symbols from 638the node to be removed are merged into the next node in the map. 639 640In the case of our map above, it would transform to look as follows 641 642.. code-block:: none 643 644 DPDK_22 { 645 global: 646 647 rte_acl_add_rules; 648 rte_acl_build; 649 rte_acl_classify; 650 rte_acl_classify_alg; 651 rte_acl_classify_scalar; 652 rte_acl_dump; 653 rte_acl_create 654 rte_acl_find_existing; 655 rte_acl_free; 656 rte_acl_ipv4vlan_add_rules; 657 rte_acl_ipv4vlan_build; 658 rte_acl_list_dump; 659 rte_acl_reset; 660 rte_acl_reset_rules; 661 rte_acl_set_ctx_classify; 662 663 local: *; 664 }; 665 666Then any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be 667updated to point to the new version node in any header files for all affected 668symbols. 669 670.. code-block:: c 671 672 -BIND_DEFAULT_SYMBOL(rte_acl_create, _v21, 21); 673 +BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22); 674 675Lastly, any VERSION_SYMBOL macros that point to the old version nodes 676should be removed, taking care to preserve any code that is shared 677with the new version node. 678 679 680Running the ABI Validator 681------------------------- 682 683The ``devtools`` directory in the DPDK source tree contains a utility program, 684``check-abi.sh``, for validating the DPDK ABI based on the libabigail 685`abidiff utility <https://sourceware.org/libabigail/manual/abidiff.html>`_. 686 687The syntax of the ``check-abi.sh`` utility is:: 688 689 devtools/check-abi.sh <refdir> <newdir> 690 691Where <refdir> specifies the directory housing the reference build of DPDK, 692and <newdir> specifies the DPDK build directory to check the ABI of. 693 694The ABI compatibility is automatically verified when using a build script 695from ``devtools``, if the variable ``DPDK_ABI_REF_VERSION`` is set with a tag, 696as described in :ref:`ABI check recommendations<integrated_abi_check>`. 697