sd-dlopen: make macros to generate .notes.dlopen sections public API (#41047)

If this new scheme of adding dependencies is supposed to be used more
widely we need to start making it easy to add them. So add a new
self-contained header that projects can simply include without the need
to link against libsystemd itself. This will allow them to generate
`.notes.dlopen` sections:

```
> readelf -p .note.dlopen ./l2md

String dump of section '.note.dlopen':
  [     a]  |@FDO
  [    10]  [{"feature":"manifest-json","description":"Manifest-based change detection via gzip and JSON parsing","priority":"suggested","soname":["libz.so.1","libsystemd.so.0"]}]
  [    c2]  |@FDO
  [    c8]  [{"feature":"manifest-http","description":"HTTP transport for lore.kernel.org manifest fetch","priority":"suggested","soname":["libcurl.so.4"]}]
```
This commit is contained in:
Luca Boccassi
2026-03-18 12:33:58 +00:00
committed by GitHub
32 changed files with 468 additions and 101 deletions

View File

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

143
man/SD_ELF_NOTE_DLOPEN.xml Normal file
View File

@@ -0,0 +1,143 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="SD_ELF_NOTE_DLOPEN" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>SD_ELF_NOTE_DLOPEN</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>SD_ELF_NOTE_DLOPEN</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>SD_ELF_NOTE_DLOPEN</refname>
<refname>SD_ELF_NOTE_DLOPEN_VENDOR</refname>
<refname>SD_ELF_NOTE_DLOPEN_TYPE</refname>
<refname>SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED</refname>
<refname>SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED</refname>
<refname>SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED</refname>
<refpurpose>Embed ELF .note.dlopen metadata for shared library dependencies</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-dlopen.h&gt;</funcsynopsisinfo>
<funcsynopsisinfo><function>SD_ELF_NOTE_DLOPEN(<replaceable>feature</replaceable>, <replaceable>description</replaceable>, <replaceable>priority</replaceable>, <replaceable>soname...</replaceable>)</function></funcsynopsisinfo>
<funcsynopsisinfo>#define SD_ELF_NOTE_DLOPEN_VENDOR "FDO"</funcsynopsisinfo>
<funcsynopsisinfo>#define SD_ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a)</funcsynopsisinfo>
<funcsynopsisinfo>#define SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required"</funcsynopsisinfo>
<funcsynopsisinfo>#define SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended"</funcsynopsisinfo>
<funcsynopsisinfo>#define SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested"</funcsynopsisinfo>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>SD_ELF_NOTE_DLOPEN()</function> is a macro that embeds a
<filename>.note.dlopen</filename> ELF note section in the compiled binary, declaring a weak dependency
on a shared library loaded via <function>dlopen()</function>. This implements the
<ulink url="https://uapi-group.org/specifications/specs/elf_dlopen_metadata/">ELF dlopen
metadata</ulink> specification, allowing package managers and build systems to discover runtime
dependencies that are not visible through regular ELF <constant>DT_NEEDED</constant> entries.</para>
<para>The macro takes the following parameters:</para>
<variablelist>
<varlistentry>
<term><parameter>feature</parameter></term>
<listitem><para>A short string identifying the feature this library provides (e.g.
<literal>XKB</literal>, <literal>PCRE2</literal>).</para>
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
</varlistentry>
<varlistentry>
<term><parameter>description</parameter></term>
<listitem><para>A human-readable description of what the library is used for.</para>
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
</varlistentry>
<varlistentry>
<term><parameter>priority</parameter></term>
<listitem><para>One of <constant>SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED</constant>,
<constant>SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED</constant>, or
<constant>SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED</constant>, indicating how important the
dependency is.</para>
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
</varlistentry>
<varlistentry>
<term><parameter>soname...</parameter></term>
<listitem><para>One or more shared object names (sonames) for the library, e.g.
<literal>libfoo.so.1</literal>. Multiple sonames may be specified as separate arguments (up to 5)
for libraries that have changed soname across versions.</para>
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
</varlistentry>
</variablelist>
<para>The embedded metadata can be read from a compiled ELF binary using:</para>
<programlisting>systemd-analyze dlopen-metadata <replaceable>binary</replaceable></programlisting>
</refsect1>
<refsect1>
<title>Examples</title>
<example>
<title>Single soname</title>
<programlisting>#include &lt;systemd/sd-dlopen.h&gt;
SD_ELF_NOTE_DLOPEN("XKB", "Keyboard layout support",
SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
"libxkbcommon.so.0");</programlisting>
</example>
<example>
<title>Multiple sonames for different library versions</title>
<programlisting>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");</programlisting>
</example>
</refsect1>
<refsect1>
<title>Notes</title>
<para>The macros described here are header-only and do not require runtime linkage against
<citerefentry><refentrytitle>libsystemd</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
Only the installed header and include path (as provided by
<command>pkg-config --cflags libsystemd</command>) are needed.</para>
</refsect1>
<refsect1>
<title>History</title>
<para><function>SD_ELF_NOTE_DLOPEN()</function>,
<function>SD_ELF_NOTE_DLOPEN_VENDOR</function>,
<function>SD_ELF_NOTE_DLOPEN_TYPE</function>,
<function>SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED</function>,
<function>SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED</function>, and
<function>SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED</function> were added in version 261.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd-dlopen</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>dlopen</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@@ -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',

80
man/sd-dlopen.xml Normal file
View File

@@ -0,0 +1,80 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="sd-dlopen" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd-dlopen</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sd-dlopen</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd-dlopen</refname>
<refpurpose>ELF dlopen metadata annotation macros</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-dlopen.h&gt;</funcsynopsisinfo>
</funcsynopsis>
<cmdsynopsis>
<command>pkg-config --cflags libsystemd</command>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>sd-dlopen.h</filename> provides macros for embedding
<filename>.note.dlopen</filename> metadata in ELF binaries, implementing the
<ulink url="https://uapi-group.org/specifications/specs/elf_dlopen_metadata/">ELF dlopen
metadata</ulink> specification for declaring optional shared library dependencies that are loaded via
<citerefentry project='man-pages'><refentrytitle>dlopen</refentrytitle><manvolnum>3</manvolnum></citerefentry>
at runtime.</para>
<para>The header is self-contained and does not require runtime linkage against
<citerefentry><refentrytitle>libsystemd</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
Projects only need the installed header to use the macros.</para>
<para>Package managers and build systems can read the embedded ELF notes to discover runtime
dependencies that are not visible in ELF <constant>DT_NEEDED</constant> entries.</para>
<para>See
<citerefentry><refentrytitle>SD_ELF_NOTE_DLOPEN</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for details on the available macros and constants.</para>
</refsect1>
<refsect1>
<title>Notes</title>
<para>The macros described here are header-only and do not require runtime linkage against
<citerefentry><refentrytitle>libsystemd</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
Only the installed header and include path (as provided by
<command>pkg-config --cflags libsystemd</command>) are needed.</para>
</refsect1>
<refsect1>
<title>History</title>
<para><function>SD_ELF_NOTE_DLOPEN()</function> and associated macros and constants were added in
version 261.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>SD_ELF_NOTE_DLOPEN</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>dlopen</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@@ -20,6 +20,8 @@
#include <zstd_errors.h>
#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");

View File

@@ -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<X+1> 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

View File

@@ -2,6 +2,8 @@
#include <sys/syslog.h>
#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(

View File

@@ -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,

View File

@@ -3,6 +3,8 @@
#include <sys/stat.h>
#include <sys/syslog.h>
#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(

View File

@@ -2,6 +2,8 @@
#include <syslog.h>
#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(

View File

@@ -2,6 +2,7 @@
#include <sys/syslog.h>
#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(

View File

@@ -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;

View File

@@ -2,6 +2,7 @@
#include <stdlib.h>
#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(

View File

@@ -8,6 +8,7 @@
#endif
#include <unistd.h>
#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;

View File

@@ -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(

View File

@@ -2,6 +2,8 @@
#include <syslog.h>
#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(

View File

@@ -4,6 +4,8 @@
#include <linux/netlink.h>
#include <sys/socket.h>
#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(

View File

@@ -4,6 +4,8 @@
# include <crypt.h>
#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;

View File

@@ -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(

View File

@@ -2,6 +2,8 @@
#include <stdio.h>
#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(

View File

@@ -2,6 +2,8 @@
#include <syslog.h>
#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(

View File

@@ -4,6 +4,7 @@
#include <syslog.h>
#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(

View File

@@ -6,6 +6,8 @@
#include <passwdqc.h>
#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(

View File

@@ -8,6 +8,8 @@
#include <stdio.h>
#include <unistd.h>
#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(

View File

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

View File

@@ -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(

View File

@@ -7,6 +7,8 @@
#endif
#include <stdio.h>
#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") {

View File

@@ -16,6 +16,8 @@
#include <asm/sgidefs.h>
#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(

View File

@@ -15,6 +15,8 @@
#include <selinux/selinux.h>
#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(

View File

@@ -4,6 +4,7 @@
#include <unistd.h>
#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(

View File

@@ -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',

98
src/systemd/sd-dlopen.h Normal file
View File

@@ -0,0 +1,98 @@
/* SPDX-License-Identifier: MIT-0 */
#ifndef foosddlopenhfoo
#define foosddlopenhfoo
/***
Copyright © 2026 The systemd Project
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.
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 <stdint.h>
#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