/*
* 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@ */