</example>
</refsect2>
+ <refsect2>
+ <title><command>systemd-analyze dlopen-metadata <replaceable>FILE</replaceable></command></title>
+
+ <para>This command will load the specified file, and if it is an ELF object (executables,
+ libraries, core files, etc.) it will parse the embedded dlopen metadata, if any, and print
+ it in a table or json format. See the
+ <ulink url="https://systemd.io/ELF_DLOPEN_METADATA/">
+ dlopen() Metadata for ELF Files</ulink> document for more information.</para>
+ </refsect2>
+
<refsect2>
<title><command>systemd-analyze fdstore <replaceable>UNIT</replaceable>...</command></title>
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-json.h"
+
+#include "alloc-util.h"
+#include "analyze.h"
+#include "analyze-dlopen-metadata.h"
+#include "chase.h"
+#include "elf-util.h"
+#include "fd-util.h"
+#include "format-table.h"
+#include "json-util.h"
+#include "strv.h"
+
+int verb_dlopen_metadata(int argc, char *argv[], void *userdata) {
+ int r;
+
+ _cleanup_free_ char *abspath = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ fd = chase_and_open(argv[1], arg_root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &abspath);
+ if (fd < 0)
+ return log_error_errno(fd, "Could not open \"%s\": %m", argv[1]);
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *dlopen_metadata = NULL;
+ r = parse_elf_object(
+ fd,
+ abspath,
+ arg_root,
+ /* fork_disable_dump= */ false,
+ /* ret= */ NULL,
+ NULL,
+ &dlopen_metadata);
+ if (r < 0)
+ return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);
+
+ if (!dlopen_metadata)
+ return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "%s does not contain any .note.dlopen sections", argv[1]);
+
+ if (sd_json_format_enabled(arg_json_format_flags))
+ return sd_json_variant_dump(dlopen_metadata, arg_json_format_flags, stdout, NULL);
+
+ _cleanup_(table_unrefp) Table *t = NULL;
+ t = table_new("feature", "description", "soname", "priority");
+ if (!t)
+ return log_oom();
+
+ table_set_ersatz_string(t, TABLE_ERSATZ_NA);
+
+ sd_json_variant *z;
+ JSON_VARIANT_ARRAY_FOREACH(z, dlopen_metadata) {
+ _cleanup_strv_free_ char **sonames = NULL;
+
+ r = sd_json_variant_strv(sd_json_variant_by_key(z, "soname"), &sonames);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract sonames from dlopen metadata: %m");
+
+ r = table_add_many(
+ t,
+ TABLE_STRING, sd_json_variant_string(sd_json_variant_by_key(z, "feature")),
+ TABLE_STRING, sd_json_variant_string(sd_json_variant_by_key(z, "description")),
+ TABLE_STRV_WRAPPED, sonames,
+ TABLE_STRING, sd_json_variant_string(sd_json_variant_by_key(z, "priority")));
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ r = table_print(t, NULL);
+ if (r < 0)
+ return table_log_print_error(r);
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_dlopen_metadata(int argc, char *argv[], void *userdata);
#include "analyze-compare-versions.h"
#include "analyze-condition.h"
#include "analyze-critical-chain.h"
+#include "analyze-dlopen-metadata.h"
#include "analyze-dot.h"
#include "analyze-dump.h"
#include "analyze-exit-status.h"
{ "timespan", 2, VERB_ANY, 0, verb_timespan },
{ "security", VERB_ANY, VERB_ANY, 0, verb_security },
{ "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection },
+ { "dlopen-metadata", 2, 2, 0, verb_dlopen_metadata },
{ "malloc", VERB_ANY, VERB_ANY, 0, verb_malloc },
{ "fdstore", 2, VERB_ANY, 0, verb_fdstore },
{ "image-policy", 2, 2, 0, verb_image_policy },
'analyze-compare-versions.c',
'analyze-condition.c',
'analyze-critical-chain.c',
+ 'analyze-dlopen-metadata.c',
'analyze-dot.c',
'analyze-dump.c',
'analyze-exit-status.c',
rm /tmp/img/usr/lib/systemd/system/testfile.service
if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
+ systemd-analyze inspect-elf /lib/systemd/systemd
systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
+
+ # For some unknown reason the .note.dlopen sections are removed when building with sanitizers, so only
+ # run this test if we're not running under sanitizers.
+ if [[ ! -v ASAN_OPTIONS ]]; then
+ shared="$(ldd /lib/systemd/systemd | grep shared | cut -d' ' -f3)"
+ systemd-analyze dlopen-metadata "$shared"
+ systemd-analyze dlopen-metadata --json=short "$shared"
+ fi
fi
systemd-analyze --threshold=90 security systemd-journald.service