/* GPLv2 or OpenIB.org BSD (MIT) See COPYING file

   These definitions help using the ELF symbol version feature, and must be
   used in conjunction with the library's map file.
 */

#ifndef __UTIL_SYMVER_H
#define __UTIL_SYMVER_H

#include <config.h>
#include <ccan/str.h>

/*
  These macros should only be used if the library is defining compatibility
  symbols, eg:

    213: 000000000000a650   315 FUNC    GLOBAL DEFAULT   13 ibv_get_device_list@IBVERBS_1.0
    214: 000000000000b020   304 FUNC    GLOBAL DEFAULT   13 ibv_get_device_list@@IBVERBS_1.1

  Symbols which have only a single implementation should use a normal extern
  function and be placed in the correct stanza in the linker map file.

  Follow this pattern to use this feature:
    public.h:
      struct ibv_device **ibv_get_device_list(int *num_devices);
    foo.c:
      // Implement the latest version
      LATEST_SYMVER_FUNC(ibv_get_device_list, 1_1, "IBVERBS_1.1",
			 struct ibv_device **,
			 int *num_devices)
      {
       ...
      }

      // Implement the compat version
      COMPAT_SYMVER_FUNC(ibv_get_device_list, 1_0, "IBVERBS_1.0",
			 struct ibv_device_1_0 **,
			 int *num_devices)
      {
       ...
      }

  As well as matching information in the map file.

  These macros deal with the various uglyness in gcc surrounding symbol
  versions

    - The internal name __public_1_x is synthesized by the macro
    - A prototype for the internal name is created by the macro
    - If statically linking the latest symbol expands into a normal function
      definition
    - If statically linking the compat symbols expand into unused static
      functions are are discarded by the compiler.
    - The prototype of the latest symbol is checked against the public
      prototype (only when compiling statically)

  The extra prototypes are included only to avoid -Wmissing-prototypes
  warnings.  See also Documentation/versioning.md
*/

#if HAVE_FUNC_ATTRIBUTE_SYMVER
#define _MAKE_SYMVER(_local_sym, _public_sym, _ver_str)                        \
	__attribute__((__symver__(#_public_sym "@" _ver_str)))
#else
#define _MAKE_SYMVER(_local_sym, _public_sym, _ver_str)                        \
	asm(".symver " #_local_sym "," #_public_sym "@" _ver_str);
#endif
#define _MAKE_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, ...)             \
	_ret __##_public_sym##_##_uniq(__VA_ARGS__);                           \
	_MAKE_SYMVER(__##_public_sym##_##_uniq, _public_sym, _ver_str)         \
	_ret __##_public_sym##_##_uniq(__VA_ARGS__)

#if defined(HAVE_FULL_SYMBOL_VERSIONS) && !defined(_STATIC_LIBRARY_BUILD_)

    // Produce all symbol versions for dynamic linking

#   define COMPAT_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, ...)         \
	_MAKE_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, __VA_ARGS__)
#   define LATEST_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, ...)         \
	_MAKE_SYMVER_FUNC(_public_sym, _uniq, "@" _ver_str, _ret, __VA_ARGS__)

#elif defined(HAVE_LIMITED_SYMBOL_VERSIONS) && !defined(_STATIC_LIBRARY_BUILD_)

    /* Produce only implemenations for the latest symbol and tag it with the
     * correct symbol versions. This supports dynamic linkers that do not
     * understand symbol versions
     */
#    define COMPAT_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, ...)        \
	static inline _ret __##_public_sym##_##_uniq(__VA_ARGS__)
#    define LATEST_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, ...)        \
	_MAKE_SYMVER_FUNC(_public_sym, _uniq, "@" _ver_str, _ret, __VA_ARGS__)

#else

    // Static linking, or linker does not support symbol versions
#define COMPAT_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, ...)            \
	static inline __attribute__((unused))                                  \
		_ret __##_public_sym##_##_uniq(__VA_ARGS__)
#define LATEST_SYMVER_FUNC(_public_sym, _uniq, _ver_str, _ret, ...)            \
	static __attribute__((unused))                                         \
		_ret __##_public_sym##_##_uniq(__VA_ARGS__)                    \
			__attribute__((alias(stringify(_public_sym))));        \
	extern _ret _public_sym(__VA_ARGS__)

#endif

#endif
