From: Karel Zak Date: Mon, 13 Jul 2026 11:32:55 +0000 (+0200) Subject: lib/dl-utils: add shared dlopen/dlsym infrastructure X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c0e553ed5d31d73e30e9571de00d31532e77b948;p=thirdparty%2Futil-linux.git lib/dl-utils: add shared dlopen/dlsym infrastructure 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-.c. Signed-off-by: Karel Zak --- diff --git a/include/Makemodule.am b/include/Makemodule.am index 27dd4bb1c..70a6cca21 100644 --- a/include/Makemodule.am +++ b/include/Makemodule.am @@ -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 index 000000000..2dc7277d7 --- /dev/null +++ b/include/dl-utils.h @@ -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 +#include + +/* + * 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-.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 index 000000000..ed686a5ad --- /dev/null +++ b/lib/dl-utils.c @@ -0,0 +1,50 @@ +/* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + */ +#include +#include + +#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; +}