dlwrap 0.3.9

dlopen wrapper code generator
Documentation
/*
 * SPDX-License-Identifier: Apache-2.0 OR FSFAP
 * SPDX-FileCopyrightText: 2025 Daiki Ueno
 *
 * You can redistribute and/or modify this file under the terms of either
 * Apache License 2.0 (https://www.apache.org/licenses/LICENSE-2.0.html), or
 * FSF All Permissive License
 * (https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html),
 * or both in parallel, as here.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "@LOADER_H@"

#if defined(@ENABLE_DLOPEN@) && @ENABLE_DLOPEN@

#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>

/* If @LIBRARY_SONAME@ is defined, dlopen handle can be automatically
 * set; otherwise, the caller needs to call
 * @LIBRARY_PREFIX@_ensure_library with soname determined at run time.
 */
#ifdef @LIBRARY_SONAME@

static void
ensure_library (void)
{
  if (@LIBRARY_PREFIX@_ensure_library (@LIBRARY_SONAME@, RTLD_LAZY | RTLD_LOCAL) < 0)
    abort ();
}

#if defined(@ENABLE_PTHREAD@) && @ENABLE_PTHREAD@
#include <pthread.h>

static pthread_once_t dlopen_once = PTHREAD_ONCE_INIT;

#define ENSURE_LIBRARY pthread_once(&dlopen_once, ensure_library)

#else /* @ENABLE_PTHREAD@ */

#define ENSURE_LIBRARY do {	    \
    if (!@LIBRARY_PREFIX@_dlhandle) \
      ensure_library();		    \
  } while (0)

#endif /* !@ENABLE_PTHREAD@ */

#else /* @LIBRARY_SONAME@ */

#define ENSURE_LIBRARY do {} while (0)

#endif /* !@LIBRARY_SONAME@ */

static void *@LIBRARY_PREFIX@_dlhandle;

/* Define redirection symbols */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-macros"

#if (2 <= __GNUC__ || (4 <= __clang_major__))
#define FUNC(ret, name, args, cargs)			\
  static __typeof__(name)(*@SYMBOL_PREFIX@_##name);
#else
#define FUNC(ret, name, args, cargs)		\
  static ret(*@SYMBOL_PREFIX@_##name)args;
#endif
#define VOID_FUNC FUNC
#include "@FUNCTIONS_H@"
#undef VOID_FUNC
#undef FUNC

#pragma GCC diagnostic pop

/* Define redirection wrapper functions */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-macros"

#define FUNC(ret, name, args, cargs)        \
ret @FUNCTION_PREFIX@_##name args           \
{					    \
  ENSURE_LIBRARY;			    \
  assert (@SYMBOL_PREFIX@_##name);	    \
  return @SYMBOL_PREFIX@_##name cargs;	    \
}
#define VOID_FUNC(ret, name, args, cargs)   \
ret @FUNCTION_PREFIX@_##name args           \
{					    \
  ENSURE_LIBRARY;			    \
  assert (@SYMBOL_PREFIX@_##name);	    \
  @SYMBOL_PREFIX@_##name cargs;		    \
}
#include "@FUNCTIONS_H@"
#undef VOID_FUNC
#undef FUNC

#pragma GCC diagnostic pop

static int
ensure_symbol (const char *name, void **symp)
{
  if (!*symp)
    {
      void *sym = dlsym (@LIBRARY_PREFIX@_dlhandle, name);
      if (!sym)
	return -EINVAL;
      *symp = sym;
    }
  return 0;
}

int
@LIBRARY_PREFIX@_ensure_library (const char *soname, int flags)
{
  int err;

  if (!@LIBRARY_PREFIX@_dlhandle)
    {
      @LIBRARY_PREFIX@_dlhandle = dlopen (soname, flags);
      if (!@LIBRARY_PREFIX@_dlhandle)
	return -EINVAL;
    }

#define ENSURE_SYMBOL(name)					\
  ensure_symbol(#name, (void **)&@SYMBOL_PREFIX@_##name)

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-macros"

#define FUNC(ret, name, args, cargs)		\
  err = ENSURE_SYMBOL(name);			\
  if (err < 0)					\
    {						\
      dlclose (@LIBRARY_PREFIX@_dlhandle);	\
      @LIBRARY_PREFIX@_dlhandle = NULL;		\
      return err;				\
    }
#define VOID_FUNC FUNC
#include "@FUNCTIONS_H@"
#undef VOID_FUNC
#undef FUNC

#pragma GCC diagnostic pop

#undef ENSURE_SYMBOL
  return 0;
}

void
@LIBRARY_PREFIX@_unload_library (void)
{
  if (@LIBRARY_PREFIX@_dlhandle)
    {
      dlclose (@LIBRARY_PREFIX@_dlhandle);
      @LIBRARY_PREFIX@_dlhandle = NULL;
    }

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-macros"

#define FUNC(ret, name, args, cargs)		\
  @SYMBOL_PREFIX@_##name = NULL;
#define VOID_FUNC FUNC
#include "@FUNCTIONS_H@"
#undef VOID_FUNC
#undef FUNC

#pragma GCC diagnostic pop
}

unsigned
@LIBRARY_PREFIX@_is_usable (void)
{
  return @LIBRARY_PREFIX@_dlhandle != NULL;
}

#else /* @ENABLE_DLOPEN@ */

int
@LIBRARY_PREFIX@_ensure_library (const char *soname, int flags)
{
  (void) soname;
  (void) flags;
  return 0;
}

void
@LIBRARY_PREFIX@_unload_library (void)
{
}

unsigned
@LIBRARY_PREFIX@_is_usable (void)
{
  /* The library is linked at build time, thus always usable */
  return 1;
}

#endif /* !@ENABLE_DLOPEN@ */