From e10302b6b65c34d134ff7d55898e4016a3bf18dd Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 11 Mar 2026 15:54:54 +0100 Subject: [PATCH 1/4] sd-dlopen: add header-only public API for FDO .note.dlopen ELF metadata Expose ELF note dlopen annotation macros as a public header-only API in sd-dlopen.h. This allows any project to embed .note.dlopen metadata in their ELF binaries by simply including the header - no runtime linkage against libsystemd is required. The header provides SD_ELF_NOTE_DLOPEN() and associated macros/constants implementing the ELF dlopen metadata specification for declaring optional shared library dependencies loaded via dlopen() at runtime. Signed-off-by: Christian Brauner --- src/systemd/meson.build | 1 + src/systemd/sd-dlopen.h | 94 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/systemd/sd-dlopen.h diff --git a/src/systemd/meson.build b/src/systemd/meson.build index c3d2c1befb7..d7335cee558 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -6,6 +6,7 @@ _systemd_headers = [ 'sd-bus-vtable.h', 'sd-daemon.h', 'sd-device.h', + 'sd-dlopen.h', 'sd-event.h', 'sd-gpt.h', 'sd-hwdb.h', diff --git a/src/systemd/sd-dlopen.h b/src/systemd/sd-dlopen.h new file mode 100644 index 00000000000..b3af9b71dca --- /dev/null +++ b/src/systemd/sd-dlopen.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddlopenhfoo +#define foosddlopenhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _SD_PASTE +# define _SD_PASTE_INNER(a, b) a##b +# define _SD_PASTE(a, b) _SD_PASTE_INNER(a, b) +#endif + +#ifndef _SD_UNIQ +# ifdef __COUNTER__ +# define _SD_UNIQ _SD_PASTE(_sd_uniq_, __COUNTER__) +# else +# define _SD_UNIQ _SD_PASTE(_sd_uniq_, __LINE__) +# endif +#endif + +/* ELF note macros implementing the FDO .note.dlopen standard. + * + * These macros embed metadata in an ELF binary's .note.dlopen section, + * declaring optional shared library dependencies that are loaded via + * dlopen() at runtime. Package managers and build systems can read + * these notes to discover runtime dependencies not visible in ELF + * DT_NEEDED entries. + * + * Usage: + * + * SD_ELF_NOTE_DLOPEN("myfeature", "Feature description", + * SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + * "libfoo.so.1"); + * + * See SD_ELF_NOTE_DLOPEN(3) for details. + */ + +#define SD_ELF_NOTE_DLOPEN_VENDOR "FDO" +#define SD_ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a) +#define SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required" +#define SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended" +#define SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested" + +#define _SD_ELF_NOTE_DLOPEN(json, variable_name) \ + __attribute__((used, section(".note.dlopen"))) _Alignas(sizeof(uint32_t)) static const struct { \ + struct { \ + uint32_t n_namesz, n_descsz, n_type; \ + } nhdr; \ + char name[sizeof(SD_ELF_NOTE_DLOPEN_VENDOR)]; \ + _Alignas(sizeof(uint32_t)) char dlopen_json[sizeof(json)]; \ + } variable_name = { \ + .nhdr = { \ + .n_namesz = sizeof(SD_ELF_NOTE_DLOPEN_VENDOR), \ + .n_descsz = sizeof(json), \ + .n_type = SD_ELF_NOTE_DLOPEN_TYPE, \ + }, \ + .name = SD_ELF_NOTE_DLOPEN_VENDOR, \ + .dlopen_json = json, \ + } + +#define _SD_SONAME_ARRAY1(a) "[\"" a "\"]" +#define _SD_SONAME_ARRAY2(a, b) "[\"" a "\",\"" b "\"]" +#define _SD_SONAME_ARRAY3(a, b, c) "[\"" a "\",\"" b "\",\"" c "\"]" +#define _SD_SONAME_ARRAY4(a, b, c, d) "[\"" a "\",\"" b "\",\"" c "\",\"" d "\"]" +#define _SD_SONAME_ARRAY5(a, b, c, d, e) "[\"" a "\",\"" b "\",\"" c "\",\"" d "\",\"" e "\"]" +#define _SD_SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME +#define _SD_SONAME_ARRAY(...) _SD_SONAME_ARRAY_GET(__VA_ARGS__, _SD_SONAME_ARRAY5, _SD_SONAME_ARRAY4, _SD_SONAME_ARRAY3, _SD_SONAME_ARRAY2, _SD_SONAME_ARRAY1)(__VA_ARGS__) + +#define SD_ELF_NOTE_DLOPEN(feature, description, priority, ...) \ + _SD_ELF_NOTE_DLOPEN("[{\"feature\":\"" feature "\",\"description\":\"" description "\",\"priority\":\"" priority "\",\"soname\":" _SD_SONAME_ARRAY(__VA_ARGS__) "}]", _SD_PASTE(_sd_elf_note_dlopen_, _SD_UNIQ)) + +#ifdef __cplusplus +} +#endif + +#endif From 8b12350b75c60468ce832942fd55ff5bbdd7c9e8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 11 Mar 2026 15:55:02 +0100 Subject: [PATCH 2/4] dlfcn-util: migrate to public SD_ELF_NOTE_DLOPEN() API Switch all internal callers from the private ELF_NOTE_DLOPEN() macro to the new public SD_ELF_NOTE_DLOPEN() API from sd-dlopen.h, and remove the now-redundant macro definitions from dlfcn-util.h. Signed-off-by: Christian Brauner --- src/basic/compress.c | 11 ++++-- src/basic/dlfcn-util.h | 41 -------------------- src/basic/gcrypt-util.c | 7 +++- src/locale/xkbcommon-util.c | 7 +++- src/shared/acl-util.c | 7 +++- src/shared/apparmor-util.c | 7 +++- src/shared/blkid-util.c | 6 ++- src/shared/bpf-dlopen.c | 7 +++- src/shared/cryptsetup-util.c | 6 ++- src/shared/elf-util.c | 13 ++++--- src/shared/idn-util.c | 7 +++- src/shared/libarchive-util.c | 7 +++- src/shared/libaudit-util.c | 7 +++- src/shared/libcrypt-util.c | 7 +++- src/shared/libfido2-util.c | 7 +++- src/shared/libmount-util.c | 7 +++- src/shared/module-util.c | 7 +++- src/shared/pam-util.c | 6 ++- src/shared/password-quality-util-passwdqc.c | 7 +++- src/shared/password-quality-util-pwquality.c | 7 +++- src/shared/pcre2-util.c | 7 +++- src/shared/pkcs11-util.c | 7 +++- src/shared/qrcode-util.c | 7 +++- src/shared/seccomp-util.c | 7 +++- src/shared/selinux-util.c | 7 +++- src/shared/tpm2-util.c | 21 ++++++---- 26 files changed, 136 insertions(+), 101 deletions(-) diff --git a/src/basic/compress.c b/src/basic/compress.c index c10448938e0..5c9ca829dfe 100644 --- a/src/basic/compress.c +++ b/src/basic/compress.c @@ -20,6 +20,8 @@ #include #endif +#include "sd-dlopen.h" + #include "alloc-util.h" #include "bitfield.h" #include "compress.h" @@ -148,7 +150,8 @@ bool compression_supported(Compression c) { int dlopen_lzma(void) { #if HAVE_XZ - ELF_NOTE_DLOPEN("lzma", + SD_ELF_NOTE_DLOPEN( + "lzma", "Support lzma compression in journal and coredump files", COMPRESSION_PRIORITY_XZ, "liblzma.so.5"); @@ -219,7 +222,8 @@ int compress_blob_xz(const void *src, uint64_t src_size, int dlopen_lz4(void) { #if HAVE_LZ4 - ELF_NOTE_DLOPEN("lz4", + SD_ELF_NOTE_DLOPEN( + "lz4", "Support lz4 compression in journal and coredump files", COMPRESSION_PRIORITY_LZ4, "liblz4.so.1"); @@ -286,7 +290,8 @@ int compress_blob_lz4(const void *src, uint64_t src_size, int dlopen_zstd(void) { #if HAVE_ZSTD - ELF_NOTE_DLOPEN("zstd", + SD_ELF_NOTE_DLOPEN( + "zstd", "Support zstd compression in journal and coredump files", COMPRESSION_PRIORITY_ZSTD, "libzstd.so.1"); diff --git a/src/basic/dlfcn-util.h b/src/basic/dlfcn-util.h index 9760b31da4d..40de1379055 100644 --- a/src/basic/dlfcn-util.h +++ b/src/basic/dlfcn-util.h @@ -33,47 +33,6 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l #define DLSYM_ARG_FORCE(arg) \ &sym_##arg, STRINGIFY(arg) -#define ELF_NOTE_DLOPEN_VENDOR "FDO" -#define ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a) -#define ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required" -#define ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended" -#define ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested" - -/* Add an ".note.dlopen" ELF note to our binary that declares our weak dlopen() dependency. This - * information can be read from an ELF file via "readelf -p .note.dlopen" or an equivalent command. */ -#define _ELF_NOTE_DLOPEN(json, variable_name) \ - __attribute__((used, section(".note.dlopen"))) _Alignas(sizeof(uint32_t)) static const struct { \ - struct { \ - uint32_t n_namesz, n_descsz, n_type; \ - } nhdr; \ - char name[sizeof(ELF_NOTE_DLOPEN_VENDOR)]; \ - _Alignas(sizeof(uint32_t)) char dlopen_json[sizeof(json)]; \ - } variable_name = { \ - .nhdr = { \ - .n_namesz = sizeof(ELF_NOTE_DLOPEN_VENDOR), \ - .n_descsz = sizeof(json), \ - .n_type = ELF_NOTE_DLOPEN_TYPE, \ - }, \ - .name = ELF_NOTE_DLOPEN_VENDOR, \ - .dlopen_json = json, \ - } - -#define _SONAME_ARRAY1(a) "[\""a"\"]" -#define _SONAME_ARRAY2(a, b) "[\""a"\",\""b"\"]" -#define _SONAME_ARRAY3(a, b, c) "[\""a"\",\""b"\",\""c"\"]" -#define _SONAME_ARRAY4(a, b, c, d) "[\""a"\",\""b"\",\""c"\"",\""d"\"]" -#define _SONAME_ARRAY5(a, b, c, d, e) "[\""a"\",\""b"\",\""c"\"",\""d"\",\""e"\"]" -#define _SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME -#define _SONAME_ARRAY(...) _SONAME_ARRAY_GET(__VA_ARGS__, _SONAME_ARRAY5, _SONAME_ARRAY4, _SONAME_ARRAY3, _SONAME_ARRAY2, _SONAME_ARRAY1)(__VA_ARGS__) - -/* The 'priority' must be one of 'required', 'recommended' or 'suggested' as per specification, use the - * macro defined above to specify it. - * Multiple sonames can be passed and they will be automatically constructed into a json array (but note that - * due to preprocessor language limitations if more than the limit defined above is used, a new - * _SONAME_ARRAY will need to be added). */ -#define ELF_NOTE_DLOPEN(feature, description, priority, ...) \ - _ELF_NOTE_DLOPEN("[{\"feature\":\"" feature "\",\"description\":\"" description "\",\"priority\":\"" priority "\",\"soname\":" _SONAME_ARRAY(__VA_ARGS__) "}]", UNIQ_T(s, UNIQ)) - /* If called dlopen_many_sym_or_warn() will fail with EPERM. This can be used to block lazy loading of shared * libs, if we transfer a process into a different namespace. Note that this does not work for all calls of * dlopen(), just those through our dlopen_safe() wrapper (which we use comprehensively in our diff --git a/src/basic/gcrypt-util.c b/src/basic/gcrypt-util.c index ebbc3e33bc7..79cab18822f 100644 --- a/src/basic/gcrypt-util.c +++ b/src/basic/gcrypt-util.c @@ -2,6 +2,8 @@ #include +#include "sd-dlopen.h" + #include "gcrypt-util.h" #if HAVE_GCRYPT @@ -44,9 +46,10 @@ DLSYM_PROTOTYPE(gcry_strerror) = NULL; int dlopen_gcrypt(void) { #if HAVE_GCRYPT - ELF_NOTE_DLOPEN("gcrypt", + SD_ELF_NOTE_DLOPEN( + "gcrypt", "Support for journald forward-sealing", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libgcrypt.so.20"); return dlopen_many_sym_or_warn( diff --git a/src/locale/xkbcommon-util.c b/src/locale/xkbcommon-util.c index 2334587e88c..c0810331a9e 100644 --- a/src/locale/xkbcommon-util.c +++ b/src/locale/xkbcommon-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-dlopen.h" + #include "dlfcn-util.h" #include "log.h" #include "string-util.h" @@ -15,9 +17,10 @@ DLSYM_PROTOTYPE(xkb_keymap_new_from_names) = NULL; DLSYM_PROTOTYPE(xkb_keymap_unref) = NULL; static int dlopen_xkbcommon(void) { - ELF_NOTE_DLOPEN("xkbcommon", + SD_ELF_NOTE_DLOPEN( + "xkbcommon", "Support for keyboard locale descriptions", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libxkbcommon.so.0"); + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libxkbcommon.so.0"); return dlopen_many_sym_or_warn( &xkbcommon_dl, "libxkbcommon.so.0", LOG_DEBUG, diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index ae4684414ca..07206bdb5f6 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -3,6 +3,8 @@ #include #include +#include "sd-dlopen.h" + #include "acl-util.h" #include "alloc-util.h" #include "errno-util.h" @@ -44,9 +46,10 @@ DLSYM_PROTOTYPE(acl_set_tag_type); DLSYM_PROTOTYPE(acl_to_any_text); int dlopen_libacl(void) { - ELF_NOTE_DLOPEN("acl", + SD_ELF_NOTE_DLOPEN( + "acl", "Support for file Access Control Lists (ACLs)", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libacl.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c index 10b57c60901..e24f4315a02 100644 --- a/src/shared/apparmor-util.c +++ b/src/shared/apparmor-util.c @@ -2,6 +2,8 @@ #include +#include "sd-dlopen.h" + #include "alloc-util.h" #include "apparmor-util.h" #include "fileio.h" @@ -20,9 +22,10 @@ DLSYM_PROTOTYPE(aa_policy_cache_replace_all) = NULL; DLSYM_PROTOTYPE(aa_policy_cache_unref) = NULL; int dlopen_libapparmor(void) { - ELF_NOTE_DLOPEN("apparmor", + SD_ELF_NOTE_DLOPEN( + "apparmor", "Support for AppArmor policies", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libapparmor.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/blkid-util.c b/src/shared/blkid-util.c index ae20b47d9ef..f1b7ccdfeb9 100644 --- a/src/shared/blkid-util.c +++ b/src/shared/blkid-util.c @@ -2,6 +2,7 @@ #include +#include "sd-dlopen.h" #include "sd-id128.h" #include "blkid-util.h" @@ -49,9 +50,10 @@ DLSYM_PROTOTYPE(blkid_probe_set_superblocks_flags) = NULL; DLSYM_PROTOTYPE(blkid_safe_string) = NULL; int dlopen_libblkid(void) { - ELF_NOTE_DLOPEN("blkid", + SD_ELF_NOTE_DLOPEN( + "blkid", "Support for block device identification", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libblkid.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c index 940c25a7260..c8e2e7ce4d8 100644 --- a/src/shared/bpf-dlopen.c +++ b/src/shared/bpf-dlopen.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-dlopen.h" + #include "bpf-dlopen.h" #include "dlfcn-util.h" #include "errno-util.h" @@ -81,9 +83,10 @@ int dlopen_bpf_full(int log_level) { if (cached != 0) return cached; - ELF_NOTE_DLOPEN("bpf", + SD_ELF_NOTE_DLOPEN( + "bpf", "Support firewalling and sandboxing with BPF", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libbpf.so.1", "libbpf.so.0"); DISABLE_WARNING_DEPRECATED_DECLARATIONS; diff --git a/src/shared/cryptsetup-util.c b/src/shared/cryptsetup-util.c index a766a92b203..5058bca3fd8 100644 --- a/src/shared/cryptsetup-util.c +++ b/src/shared/cryptsetup-util.c @@ -2,6 +2,7 @@ #include +#include "sd-dlopen.h" #include "sd-json.h" #include "alloc-util.h" @@ -270,9 +271,10 @@ int dlopen_cryptsetup(void) { * still available though, and given we want to support 2.2.0 for a while longer, we'll use the old * symbol if the new one is not available. */ - ELF_NOTE_DLOPEN("cryptsetup", + SD_ELF_NOTE_DLOPEN( + "cryptsetup", "Support for disk encryption, integrity, and authentication", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libcryptsetup.so.12"); r = dlopen_many_sym_or_warn( diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index e199b42c82a..be9e673a511 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -8,6 +8,7 @@ #endif #include +#include "sd-dlopen.h" #include "sd-json.h" #include "alloc-util.h" @@ -94,9 +95,10 @@ int dlopen_dw(void) { #if HAVE_ELFUTILS int r; - ELF_NOTE_DLOPEN("dw", + SD_ELF_NOTE_DLOPEN( + "dw", "Support for backtrace and ELF package metadata decoding from core files", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libdw.so.1"); r = dlopen_many_sym_or_warn( @@ -147,9 +149,10 @@ int dlopen_elf(void) { #if HAVE_ELFUTILS int r; - ELF_NOTE_DLOPEN("elf", + SD_ELF_NOTE_DLOPEN( + "elf", "Support for backtraces and reading ELF package metadata from core files", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libelf.so.1"); r = dlopen_many_sym_or_warn( @@ -406,7 +409,7 @@ static int parse_metadata(const char *name, sd_json_variant *id_json, Elf *elf, /* Package metadata might have different owners, but the * magic ID is always the same. */ - if (!IN_SET(note_header.n_type, ELF_PACKAGE_METADATA_ID, ELF_NOTE_DLOPEN_TYPE)) + if (!IN_SET(note_header.n_type, ELF_PACKAGE_METADATA_ID, SD_ELF_NOTE_DLOPEN_TYPE)) continue; _cleanup_free_ char *payload_0suffixed = NULL; diff --git a/src/shared/idn-util.c b/src/shared/idn-util.c index aad0db1426c..a1b4a6c4987 100644 --- a/src/shared/idn-util.c +++ b/src/shared/idn-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-dlopen.h" + #include "idn-util.h" #include "log.h" /* IWYU pragma: keep */ @@ -10,9 +12,10 @@ const char *(*sym_idn2_strerror)(int rc) _const_ = NULL; DLSYM_PROTOTYPE(idn2_to_unicode_8z8z) = NULL; int dlopen_idn(void) { - ELF_NOTE_DLOPEN("idn", + SD_ELF_NOTE_DLOPEN( + "idn", "Support for internationalized domain names", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libidn2.so.0"); return dlopen_many_sym_or_warn( diff --git a/src/shared/libarchive-util.c b/src/shared/libarchive-util.c index d2714bd81c1..02b6b36b3c2 100644 --- a/src/shared/libarchive-util.c +++ b/src/shared/libarchive-util.c @@ -2,6 +2,8 @@ #include +#include "sd-dlopen.h" + #include "libarchive-util.h" #include "user-util.h" /* IWYU pragma: keep */ @@ -79,9 +81,10 @@ DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext) = NULL; DLSYM_PROTOTYPE(archive_write_set_format_pax) = NULL; int dlopen_libarchive(void) { - ELF_NOTE_DLOPEN("archive", + SD_ELF_NOTE_DLOPEN( + "archive", "Support for decompressing archive files", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libarchive.so.13"); return dlopen_many_sym_or_warn( diff --git a/src/shared/libaudit-util.c b/src/shared/libaudit-util.c index 4bdd4c1317e..bf5791955bb 100644 --- a/src/shared/libaudit-util.c +++ b/src/shared/libaudit-util.c @@ -4,6 +4,8 @@ #include #include +#include "sd-dlopen.h" + #include "errno-util.h" #include "fd-util.h" #include "iovec-util.h" @@ -23,9 +25,10 @@ static DLSYM_PROTOTYPE(audit_open) = NULL; int dlopen_libaudit(void) { #if HAVE_AUDIT - ELF_NOTE_DLOPEN("audit", + SD_ELF_NOTE_DLOPEN( + "audit", "Support for Audit logging", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libaudit.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/libcrypt-util.c b/src/shared/libcrypt-util.c index 4760d66c92a..df3ba146f9e 100644 --- a/src/shared/libcrypt-util.c +++ b/src/shared/libcrypt-util.c @@ -4,6 +4,8 @@ # include #endif +#include "sd-dlopen.h" + #include "alloc-util.h" #include "dlfcn-util.h" #include "errno-util.h" @@ -33,9 +35,10 @@ int dlopen_libcrypt(void) { /* Several distributions like Debian/Ubuntu and OpenSUSE provide libxcrypt as libcrypt.so.1 * (libcrypt.so.1.1 on some architectures), while others like Fedora/CentOS and Arch provide it as * libcrypt.so.2. */ - ELF_NOTE_DLOPEN("crypt", + SD_ELF_NOTE_DLOPEN( + "crypt", "Support for hashing passwords", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libcrypt.so.2", "libcrypt.so.1", "libcrypt.so.1.1"); _cleanup_(dlclosep) void *dl = NULL; diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index b4aee235a99..f4c8ad5c861 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-dlopen.h" + #include "libfido2-util.h" #include "log.h" @@ -82,9 +84,10 @@ int dlopen_libfido2(void) { #if HAVE_LIBFIDO2 int r; - ELF_NOTE_DLOPEN("fido2", + SD_ELF_NOTE_DLOPEN( + "fido2", "Support fido2 for encryption and authentication", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libfido2.so.1"); r = dlopen_many_sym_or_warn( diff --git a/src/shared/libmount-util.c b/src/shared/libmount-util.c index c6c6074c259..be4dd0712ee 100644 --- a/src/shared/libmount-util.c +++ b/src/shared/libmount-util.c @@ -2,6 +2,8 @@ #include +#include "sd-dlopen.h" + #include "fstab-util.h" #include "libmount-util.h" #include "log.h" @@ -41,9 +43,10 @@ DLSYM_PROTOTYPE(mnt_table_parse_swaps) = NULL; DLSYM_PROTOTYPE(mnt_unref_monitor) = NULL; int dlopen_libmount(void) { - ELF_NOTE_DLOPEN("mount", + SD_ELF_NOTE_DLOPEN( + "mount", "Support for mount enumeration", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libmount.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/module-util.c b/src/shared/module-util.c index a7f9e178e3c..8ad7dab180a 100644 --- a/src/shared/module-util.c +++ b/src/shared/module-util.c @@ -2,6 +2,8 @@ #include +#include "sd-dlopen.h" + #include "log.h" #include "module-util.h" #include "proc-cmdline.h" @@ -26,9 +28,10 @@ DLSYM_PROTOTYPE(kmod_unref) = NULL; DLSYM_PROTOTYPE(kmod_validate_resources) = NULL; int dlopen_libkmod(void) { - ELF_NOTE_DLOPEN("kmod", + SD_ELF_NOTE_DLOPEN( + "kmod", "Support for loading kernel modules", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libkmod.so.2"); return dlopen_many_sym_or_warn( diff --git a/src/shared/pam-util.c b/src/shared/pam-util.c index d6158ec8cae..0f04e97377b 100644 --- a/src/shared/pam-util.c +++ b/src/shared/pam-util.c @@ -4,6 +4,7 @@ #include #include "sd-bus.h" +#include "sd-dlopen.h" #include "alloc-util.h" #include "bus-internal.h" @@ -35,9 +36,10 @@ DLSYM_PROTOTYPE(pam_syslog) = NULL; DLSYM_PROTOTYPE(pam_vsyslog) = NULL; int dlopen_libpam(void) { - ELF_NOTE_DLOPEN("pam", + SD_ELF_NOTE_DLOPEN( + "pam", "Support for LinuxPAM", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libpam.so.0"); return dlopen_many_sym_or_warn( diff --git a/src/shared/password-quality-util-passwdqc.c b/src/shared/password-quality-util-passwdqc.c index 5b0b22458bf..33c77b01a0c 100644 --- a/src/shared/password-quality-util-passwdqc.c +++ b/src/shared/password-quality-util-passwdqc.c @@ -6,6 +6,8 @@ #include +#include "sd-dlopen.h" + #include "alloc-util.h" #include "dlfcn-util.h" #include "errno-util.h" @@ -137,9 +139,10 @@ int check_password_quality( int dlopen_passwdqc(void) { #if HAVE_PASSWDQC - ELF_NOTE_DLOPEN("passwdqc", + SD_ELF_NOTE_DLOPEN( + "passwdqc", "Support for password quality checks", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libpasswdqc.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/password-quality-util-pwquality.c b/src/shared/password-quality-util-pwquality.c index 05d85bd6965..3eba9495b64 100644 --- a/src/shared/password-quality-util-pwquality.c +++ b/src/shared/password-quality-util-pwquality.c @@ -8,6 +8,8 @@ #include #include +#include "sd-dlopen.h" + #include "alloc-util.h" #include "dlfcn-util.h" #include "errno-util.h" @@ -153,9 +155,10 @@ int check_password_quality(const char *password, const char *old, const char *us int dlopen_pwquality(void) { #if HAVE_PWQUALITY - ELF_NOTE_DLOPEN("pwquality", + SD_ELF_NOTE_DLOPEN( + "pwquality", "Support for password quality checks", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libpwquality.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/pcre2-util.c b/src/shared/pcre2-util.c index 10a767442a4..22a7635170b 100644 --- a/src/shared/pcre2-util.c +++ b/src/shared/pcre2-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-dlopen.h" + #include "dlfcn-util.h" #include "hash-funcs.h" #include "log.h" @@ -28,9 +30,10 @@ const struct hash_ops pcre2_code_hash_ops_free = {}; int dlopen_pcre2(void) { #if HAVE_PCRE2 - ELF_NOTE_DLOPEN("pcre2", + SD_ELF_NOTE_DLOPEN( + "pcre2", "Support for regular expressions", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libpcre2-8.so.0"); /* So here's something weird: PCRE2 actually renames the symbols exported by the library via C diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index 3062bcc5541..1fa0d77d6d0 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-dlopen.h" + #include "alloc-util.h" #include "ask-password-api.h" #include "dlfcn-util.h" @@ -1766,9 +1768,10 @@ static int list_callback( int dlopen_p11kit(void) { #if HAVE_P11KIT - ELF_NOTE_DLOPEN("p11-kit", + SD_ELF_NOTE_DLOPEN( + "p11-kit", "Support for PKCS11 hardware tokens", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libp11-kit.so.0"); return dlopen_many_sym_or_warn( diff --git a/src/shared/qrcode-util.c b/src/shared/qrcode-util.c index 320b2353ff9..ee6d3c40f93 100644 --- a/src/shared/qrcode-util.c +++ b/src/shared/qrcode-util.c @@ -7,6 +7,8 @@ #endif #include +#include "sd-dlopen.h" + #include "ansi-color.h" #include "dlfcn-util.h" #include "locale-util.h" @@ -30,9 +32,10 @@ int dlopen_qrencode(void) { #if HAVE_QRENCODE int r; - ELF_NOTE_DLOPEN("qrencode", + SD_ELF_NOTE_DLOPEN( + "qrencode", "Support for generating QR codes", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libqrencode.so.4", "libqrencode.so.3"); FOREACH_STRING(s, "libqrencode.so.4", "libqrencode.so.3") { diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 55e3c14e443..d2f7612a53d 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -16,6 +16,8 @@ #include #endif +#include "sd-dlopen.h" + #include "af-list.h" #include "alloc-util.h" #include "env-util.h" @@ -49,9 +51,10 @@ DLSYM_PROTOTYPE(seccomp_syscall_resolve_name) = NULL; DLSYM_PROTOTYPE(seccomp_syscall_resolve_num_arch) = NULL; int dlopen_libseccomp(void) { - ELF_NOTE_DLOPEN("seccomp", + SD_ELF_NOTE_DLOPEN( + "seccomp", "Support for Seccomp Sandboxes", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libseccomp.so.2"); return dlopen_many_sym_or_warn( diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c index 4fca62b66d3..f980ec83acb 100644 --- a/src/shared/selinux-util.c +++ b/src/shared/selinux-util.c @@ -15,6 +15,8 @@ #include #endif +#include "sd-dlopen.h" + #include "alloc-util.h" #include "errno-util.h" #include "fd-util.h" @@ -88,9 +90,10 @@ DLSYM_PROTOTYPE(setsockcreatecon_raw) = NULL; DLSYM_PROTOTYPE(string_to_security_class) = NULL; int dlopen_libselinux(void) { - ELF_NOTE_DLOPEN("selinux", + SD_ELF_NOTE_DLOPEN( + "selinux", "Support for SELinux", - ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libselinux.so.1"); return dlopen_many_sym_or_warn( diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 9e05c847edf..c12ba2d28c7 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -4,6 +4,7 @@ #include #include "sd-device.h" +#include "sd-dlopen.h" #include "alloc-util.h" #include "ansi-color.h" @@ -130,9 +131,10 @@ static DLSYM_PROTOTYPE(Tss2_RC_Decode) = NULL; static int dlopen_tpm2_esys(void) { int r; - ELF_NOTE_DLOPEN("tpm", + SD_ELF_NOTE_DLOPEN( + "tpm", "Support for TPM", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libtss2-esys.so.0"); r = dlopen_many_sym_or_warn( @@ -193,9 +195,10 @@ static int dlopen_tpm2_esys(void) { } static int dlopen_tpm2_rc(void) { - ELF_NOTE_DLOPEN("tpm", + SD_ELF_NOTE_DLOPEN( + "tpm", "Support for TPM", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libtss2-rc.so.0"); return dlopen_many_sym_or_warn( @@ -204,9 +207,10 @@ static int dlopen_tpm2_rc(void) { } static int dlopen_tpm2_mu(void) { - ELF_NOTE_DLOPEN("tpm", + SD_ELF_NOTE_DLOPEN( + "tpm", "Support for TPM", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libtss2-mu.so.0"); return dlopen_many_sym_or_warn( @@ -236,9 +240,10 @@ static int dlopen_tpm2_tcti_device(void) { /* The "device" TCTI is the most relevant one, let's also load it explicitly on dlopen_tpm2(), even * if we don't resolve any symbols here. */ - ELF_NOTE_DLOPEN("tpm", + SD_ELF_NOTE_DLOPEN( + "tpm", "Support for TPM", - ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libtss2-tcti-device.so.0"); return dlopen_verbose( From f90ee22bcf8465edea38de70cd64875d9b0db343 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 11 Mar 2026 15:55:08 +0100 Subject: [PATCH 3/4] man: add sd-dlopen(3) and SD_ELF_NOTE_DLOPEN(3) man pages Document the new public sd-dlopen.h header and SD_ELF_NOTE_DLOPEN() macro with associated constants. Includes usage examples for single and multiple soname annotations. Signed-off-by: Christian Brauner --- man/SD_ELF_NOTE_DLOPEN.xml | 143 +++++++++++++++++++++++++++++++++++++ man/rules/meson.build | 9 +++ man/sd-dlopen.xml | 80 +++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 man/SD_ELF_NOTE_DLOPEN.xml create mode 100644 man/sd-dlopen.xml diff --git a/man/SD_ELF_NOTE_DLOPEN.xml b/man/SD_ELF_NOTE_DLOPEN.xml new file mode 100644 index 00000000000..0e3eed84cfd --- /dev/null +++ b/man/SD_ELF_NOTE_DLOPEN.xml @@ -0,0 +1,143 @@ + + + + + + + + SD_ELF_NOTE_DLOPEN + systemd + + + + SD_ELF_NOTE_DLOPEN + 3 + + + + SD_ELF_NOTE_DLOPEN + SD_ELF_NOTE_DLOPEN_VENDOR + SD_ELF_NOTE_DLOPEN_TYPE + SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED + + Embed ELF .note.dlopen metadata for shared library dependencies + + + + + #include <systemd/sd-dlopen.h> + + SD_ELF_NOTE_DLOPEN(feature, description, priority, soname...) + + #define SD_ELF_NOTE_DLOPEN_VENDOR "FDO" + #define SD_ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a) + #define SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required" + #define SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended" + #define SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested" + + + + + + Description + + SD_ELF_NOTE_DLOPEN() is a macro that embeds a + .note.dlopen ELF note section in the compiled binary, declaring a weak dependency + on a shared library loaded via dlopen(). This implements the + ELF dlopen + metadata specification, allowing package managers and build systems to discover runtime + dependencies that are not visible through regular ELF DT_NEEDED entries. + + The macro takes the following parameters: + + + + feature + A short string identifying the feature this library provides (e.g. + XKB, PCRE2). + + + + + description + A human-readable description of what the library is used for. + + + + + priority + One of SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, or + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, indicating how important the + dependency is. + + + + + soname... + One or more shared object names (sonames) for the library, e.g. + libfoo.so.1. Multiple sonames may be specified as separate arguments (up to 5) + for libraries that have changed soname across versions. + + + + + The embedded metadata can be read from a compiled ELF binary using: + systemd-analyze dlopen-metadata binary + + + + + Examples + + + Single soname + #include <systemd/sd-dlopen.h> + +SD_ELF_NOTE_DLOPEN("XKB", "Keyboard layout support", + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + "libxkbcommon.so.0"); + + + + Multiple sonames for different library versions + SD_ELF_NOTE_DLOPEN("crypt", "Support for hashing passwords", + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + "libcrypt.so.2", "libcrypt.so.1", "libcrypt.so.1.1"); + + + + + + Notes + + The macros described here are header-only and do not require runtime linkage against + libsystemd3. + Only the installed header and include path (as provided by + pkg-config --cflags libsystemd) are needed. + + + + History + SD_ELF_NOTE_DLOPEN(), + SD_ELF_NOTE_DLOPEN_VENDOR, + SD_ELF_NOTE_DLOPEN_TYPE, + SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED, + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, and + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED were added in version 261. + + + + See Also + + + systemd1 + sd-dlopen3 + dlopen3 + + + + diff --git a/man/rules/meson.build b/man/rules/meson.build index d69793150be..d7cbd5b6520 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -127,6 +127,7 @@ manpages = [ 'SD_WARNING'], ''], ['sd-device', '3', [], ''], + ['sd-dlopen', '3', [], ''], ['sd-event', '3', [], ''], ['sd-hwdb', '3', [], ''], ['sd-id128', @@ -154,6 +155,14 @@ manpages = [ ['sd-login', '3', [], 'HAVE_PAM'], ['sd-path', '3', [], ''], ['sd-varlink', '3', [], ''], + ['SD_ELF_NOTE_DLOPEN', + '3', + ['SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED', + 'SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED', + 'SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED', + 'SD_ELF_NOTE_DLOPEN_TYPE', + 'SD_ELF_NOTE_DLOPEN_VENDOR'], + ''], ['sd_booted', '3', [], ''], ['sd_bus_add_match', '3', diff --git a/man/sd-dlopen.xml b/man/sd-dlopen.xml new file mode 100644 index 00000000000..ed43e396d69 --- /dev/null +++ b/man/sd-dlopen.xml @@ -0,0 +1,80 @@ + + + + + + + + sd-dlopen + systemd + + + + sd-dlopen + 3 + + + + sd-dlopen + ELF dlopen metadata annotation macros + + + + + #include <systemd/sd-dlopen.h> + + + + pkg-config --cflags libsystemd + + + + + + Description + + sd-dlopen.h provides macros for embedding + .note.dlopen metadata in ELF binaries, implementing the + ELF dlopen + metadata specification for declaring optional shared library dependencies that are loaded via + dlopen3 + at runtime. + + The header is self-contained and does not require runtime linkage against + libsystemd3. + Projects only need the installed header to use the macros. + + Package managers and build systems can read the embedded ELF notes to discover runtime + dependencies that are not visible in ELF DT_NEEDED entries. + + See + SD_ELF_NOTE_DLOPEN3 + for details on the available macros and constants. + + + + Notes + + The macros described here are header-only and do not require runtime linkage against + libsystemd3. + Only the installed header and include path (as provided by + pkg-config --cflags libsystemd) are needed. + + + + History + SD_ELF_NOTE_DLOPEN() and associated macros and constants were added in + version 261. + + + + See Also + + systemd1 + SD_ELF_NOTE_DLOPEN3 + dlopen3 + + + + From 919b8c7c92b120ba986a28882c151777661e97d6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 12 Mar 2026 13:51:16 +0100 Subject: [PATCH 4/4] sd-dlopen: relicense header to MIT-0 Relicense sd-dlopen.h from LGPL-2.1-or-later to MIT-0 so that downstream projects can copy/paste the macros directly without introducing a build dependency on the systemd headers. Acked-by: Lennart Poettering Acked-by: Luca Boccassi Signed-off-by: Christian Brauner --- LICENSES/README.md | 1 + src/systemd/sd-dlopen.h | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/LICENSES/README.md b/LICENSES/README.md index 5522a08e109..9bd26baa0dc 100644 --- a/LICENSES/README.md +++ b/LICENSES/README.md @@ -57,6 +57,7 @@ The following exceptions apply: * the following sources are licensed under the **MIT-0** license: - all examples under man/ - config files and examples under /network + - src/systemd/sd-dlopen.h * the following sources are under **Public Domain** (LicenseRef-murmurhash2-public-domain): - src/basic/MurmurHash2.c - src/basic/MurmurHash2.h diff --git a/src/systemd/sd-dlopen.h b/src/systemd/sd-dlopen.h index b3af9b71dca..a58b90e0ec0 100644 --- a/src/systemd/sd-dlopen.h +++ b/src/systemd/sd-dlopen.h @@ -1,20 +1,24 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-License-Identifier: MIT-0 */ #ifndef foosddlopenhfoo #define foosddlopenhfoo /*** - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. + Copyright © 2026 The systemd Project - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so. - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. ***/ #include