]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
analyze: Add dlopen-metadata verb 39457/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Sun, 26 Oct 2025 18:41:27 +0000 (19:41 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 30 Oct 2025 10:58:23 +0000 (11:58 +0100)
systemd-analyze dlopen-metadata will show dlopen metadata
in the ELF binary.

man/systemd-analyze.xml
src/analyze/analyze-dlopen-metadata.c [new file with mode: 0644]
src/analyze/analyze-dlopen-metadata.h [new file with mode: 0644]
src/analyze/analyze.c
src/analyze/meson.build
test/units/TEST-65-ANALYZE.sh

index dc4ab642a819bf1ea72dffee6430059b38b07660..04b8d8d41f08209c2293c7e48f4478c2a90ae95f 100644 (file)
@@ -915,6 +915,16 @@ alias.service:7: Unknown key name 'MysteryKey' in section 'Service', ignoring.
       </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>
 
diff --git a/src/analyze/analyze-dlopen-metadata.c b/src/analyze/analyze-dlopen-metadata.c
new file mode 100644 (file)
index 0000000..618c104
--- /dev/null
@@ -0,0 +1,72 @@
+/* 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;
+}
diff --git a/src/analyze/analyze-dlopen-metadata.h b/src/analyze/analyze-dlopen-metadata.h
new file mode 100644 (file)
index 0000000..3f7355d
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_dlopen_metadata(int argc, char *argv[], void *userdata);
index fc85388f309f59db7138fded89119def2e3485e9..857f2bb3f5f6dfd38cd58229a7d1ead332c4ee19 100644 (file)
@@ -22,6 +22,7 @@
 #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"
@@ -798,6 +799,7 @@ static int run(int argc, char *argv[]) {
                 { "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       },
index 5b247a70888fe6e31706096ed450fe174a6dc316..e26426fc6c84ee491f436451f7f9c6e1a60076d7 100644 (file)
@@ -10,6 +10,7 @@ systemd_analyze_sources = files(
         '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',
index aaf8ddc42d9046674c05555cad9d8ed2d1dded02..d71b76e92ad69ee37d6b9bfaf89d977338865992 100755 (executable)
@@ -1006,7 +1006,16 @@ systemd-analyze security --threshold=25 --offline=true \
 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