From: Daan De Meyer Date: Sun, 26 Oct 2025 18:41:27 +0000 (+0100) Subject: analyze: Add dlopen-metadata verb X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F39457%2Fhead;p=thirdparty%2Fsystemd.git analyze: Add dlopen-metadata verb systemd-analyze dlopen-metadata will show dlopen metadata in the ELF binary. --- diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index dc4ab642a81..04b8d8d41f0 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -915,6 +915,16 @@ alias.service:7: Unknown key name 'MysteryKey' in section 'Service', ignoring. + + <command>systemd-analyze dlopen-metadata <replaceable>FILE</replaceable></command> + + 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 + + dlopen() Metadata for ELF Files document for more information. + + <command>systemd-analyze fdstore <replaceable>UNIT</replaceable>...</command> diff --git a/src/analyze/analyze-dlopen-metadata.c b/src/analyze/analyze-dlopen-metadata.c new file mode 100644 index 00000000000..618c104d175 --- /dev/null +++ b/src/analyze/analyze-dlopen-metadata.c @@ -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 index 00000000000..3f7355d96bd --- /dev/null +++ b/src/analyze/analyze-dlopen-metadata.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_dlopen_metadata(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index fc85388f309..857f2bb3f5f 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -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 }, diff --git a/src/analyze/meson.build b/src/analyze/meson.build index 5b247a70888..e26426fc6c8 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -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', diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index aaf8ddc42d9..d71b76e92ad 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -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