From e10302b6b65c34d134ff7d55898e4016a3bf18dd Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 11 Mar 2026 15:54:54 +0100 Subject: [PATCH] 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 c3d2c1befb..d7335cee55 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 0000000000..b3af9b71dc --- /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