]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/dl-utils: add shared dlopen/dlsym infrastructure
authorKarel Zak <kzak@redhat.com>
Mon, 13 Jul 2026 11:32:55 +0000 (13:32 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 14 Jul 2026 10:58:30 +0000 (12:58 +0200)
Add generic helpers for optional runtime dependencies loaded via dlopen().
Each optional library gets its own struct of function pointers and a symbol
table mapping names to offsets in that struct.  The shared ul_dlopen_symbols()
resolves all entries in one call.

This also includes a copy of the ELF .note.dlopen metadata macro (from
systemd's sd-dlopen.h, MIT-0 licensed) so that packaging tools can
automatically derive optional dlopen() dependencies.

Library-specific wrappers will live in lib/dl-<name>.c.

Signed-off-by: Karel Zak <kzak@redhat.com>
include/Makemodule.am
include/dl-utils.h [new file with mode: 0644]
lib/dl-utils.c [new file with mode: 0644]

index 27dd4bb1c8853f85ad7b75c2bf8ddab372b2eff4..70a6cca21c80e8fd5126472b85d4593f449b217f 100644 (file)
@@ -22,6 +22,7 @@ dist_noinst_HEADERS += \
        include/c_strtod.h \
        include/debug.h \
        include/default_shell.h \
+       include/dl-utils.h \
        include/encode.h \
        include/env.h \
        include/exec_shell.h \
diff --git a/include/dl-utils.h b/include/dl-utils.h
new file mode 100644 (file)
index 0000000..2dc7277
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ */
+#ifndef UTIL_LINUX_DL_UTILS_H
+#define UTIL_LINUX_DL_UTILS_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Generic dlopen/dlsym helpers for optional runtime dependencies.
+ *
+ * Each optional library gets its own struct of function pointers and a
+ * symbol table mapping names to offsets in that struct.  The generic
+ * ul_dlopen_symbols() resolves all entries in one call.
+ *
+ * Library-specific wrappers live in lib/dl-<name>.c and expose a load
+ * function (e.g. ul_load_libcryptsetup()) and a call macro.
+ */
+
+/* Symbol descriptor for the dlsym resolution loop */
+struct ul_dlsym {
+       const char *name;
+       size_t offset;
+};
+
+/* Populate a symbol table entry; _type is the function-pointer struct */
+#define UL_DLSYM(_type, _name) \
+       { \
+               .name = # _name, \
+               .offset = offsetof(_type, _name), \
+       }
+
+extern int ul_dlopen_symbols(const char *libname, int flags,
+                            const struct ul_dlsym *syms, size_t nsyms,
+                            void *opers, void **dl_handle);
+
+/*
+ * ELF .note.dlopen metadata — declares an optional dlopen() dependency so
+ * that packaging tools (rpm, dpkg) can automatically derive it.
+ *
+ * See https://uapi-group.org/specifications/specs/elf_dlopen_metadata/
+ *
+ * Adapted from systemd's sd-dlopen.h (MIT-0, no copyright claimed).
+ */
+#define UL_ELF_NOTE_DLOPEN_VENDOR "FDO"
+#define UL_ELF_NOTE_DLOPEN_TYPE   UINT32_C(0x407c0c0a)
+
+#define UL_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED    "required"
+#define UL_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended"
+#define UL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED   "suggested"
+
+#ifndef _UL_ELF_NOTE_DLOPEN_SECTION_FLAGS
+# define _UL_ELF_NOTE_DLOPEN_SECTION_FLAGS "aGR"
+#endif
+
+#define _UL_ELF_NOTE_DLOPEN(json)                                                                                            \
+        __asm__ (                                                                                                             \
+                ".ifndef \"ul_dlopen:" json "\"\n"                                                                            \
+                ".set \"ul_dlopen:" json "\", 1\n"                                                                            \
+                ".pushsection .note.dlopen, \"" _UL_ELF_NOTE_DLOPEN_SECTION_FLAGS "\", %note, \"ul_dlopen:" json "\", comdat\n" \
+                ".balign 4\n"                                                                                                 \
+                ".long 884f - 883f\n"                                                                                         \
+                ".long 882f - 881f\n"                                                                                         \
+                ".long 0x407c0c0a\n"                                                                                          \
+                "883:\n"                                                                                                      \
+                ".asciz \"" UL_ELF_NOTE_DLOPEN_VENDOR "\"\n"                                                                  \
+                "884:\n"                                                                                                      \
+                ".balign 4\n"                                                                                                 \
+                "881:\n"                                                                                                      \
+                ".asciz \"" json "\"\n"                                                                                       \
+                "882:\n"                                                                                                      \
+                ".balign 4\n"                                                                                                 \
+                ".popsection\n"                                                                                               \
+                ".endif\n")
+
+#define _UL_SONAME_ARRAY1(a)          "[\\\"" a "\\\"]"
+#define _UL_SONAME_ARRAY2(a, b)       "[\\\"" a "\\\",\\\"" b "\\\"]"
+#define _UL_SONAME_ARRAY3(a, b, c)    "[\\\"" a "\\\",\\\"" b "\\\",\\\"" c "\\\"]"
+#define _UL_SONAME_ARRAY4(a, b, c, d) "[\\\"" a "\\\",\\\"" b "\\\",\\\"" c "\\\",\\\"" d "\\\"]"
+#define _UL_SONAME_ARRAY_GET(_1,_2,_3,_4,NAME,...) NAME
+#define _UL_SONAME_ARRAY(...) _UL_SONAME_ARRAY_GET(__VA_ARGS__, _UL_SONAME_ARRAY4, _UL_SONAME_ARRAY3, _UL_SONAME_ARRAY2, _UL_SONAME_ARRAY1)(__VA_ARGS__)
+
+#define UL_ELF_NOTE_DLOPEN(feature, description, priority, ...) \
+        _UL_ELF_NOTE_DLOPEN("[{\\\"feature\\\":\\\"" feature "\\\",\\\"description\\\":\\\"" description "\\\",\\\"priority\\\":\\\"" priority "\\\",\\\"soname\\\":" _UL_SONAME_ARRAY(__VA_ARGS__) "}]")
+
+#endif /* UTIL_LINUX_DL_UTILS_H */
diff --git a/lib/dl-utils.c b/lib/dl-utils.c
new file mode 100644 (file)
index 0000000..ed686a5
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ */
+#include <dlfcn.h>
+#include <errno.h>
+
+#include "dl-utils.h"
+
+/*
+ * Open @libname with dlopen() and resolve all symbols listed in @syms
+ * into the caller-provided @opers struct.  On success the handle is
+ * stored in *@dl_handle and 0 is returned.  On failure *@dl_handle is
+ * set to NULL and a negative errno value is returned (usually -ENOTSUP).
+ */
+int ul_dlopen_symbols(const char *libname, int flags,
+                     const struct ul_dlsym *syms, size_t nsyms,
+                     void *opers, void **dl_handle)
+{
+       size_t i;
+
+       errno = 0;
+
+       *dl_handle = dlopen(libname, flags);
+       if (!*dl_handle)
+               goto failed;
+
+       dlerror();
+
+       for (i = 0; i < nsyms; i++) {
+               const struct ul_dlsym *def = &syms[i];
+               void **sym;
+
+               sym = (void **) ((char *) opers + def->offset);
+               *sym = dlsym(*dl_handle, def->name);
+
+               if (dlerror())
+                       goto failed;
+       }
+
+       return 0;
+failed:
+       if (*dl_handle) {
+               dlclose(*dl_handle);
+               *dl_handle = NULL;
+       }
+       if (!errno)
+               errno = ENOTSUP;
+       return -errno;
+}