From 004b7f1f7e5378f985dfd99e5bf2997698d6ffac Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sun, 26 Oct 2025 19:33:30 +0100 Subject: [PATCH 1/3] elf-util: Add support for parsing dlopen metadata Then we can add support for showing dlopen metadata to systemd-analyze. --- src/analyze/analyze-inspect-elf.c | 9 +- src/coredump/coredump-submit.c | 3 +- src/shared/elf-util.c | 178 ++++++++++++++++++++++-------- src/shared/elf-util.h | 9 +- 4 files changed, 149 insertions(+), 50 deletions(-) diff --git a/src/analyze/analyze-inspect-elf.c b/src/analyze/analyze-inspect-elf.c index ccb5a98720..d84601dfab 100644 --- a/src/analyze/analyze-inspect-elf.c +++ b/src/analyze/analyze-inspect-elf.c @@ -27,7 +27,14 @@ static int analyze_elf(char **filenames, sd_json_format_flags_t json_flags) { if (fd < 0) return log_error_errno(fd, "Could not open \"%s\": %m", *filename); - r = parse_elf_object(fd, abspath, arg_root, /* fork_disable_dump= */false, &stacktrace, &package_metadata); + r = parse_elf_object( + fd, + abspath, + arg_root, + /* fork_disable_dump= */ false, + &stacktrace, + &package_metadata, + /* ret_dlopen_metadata= */ NULL); if (r < 0) return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath); diff --git a/src/coredump/coredump-submit.c b/src/coredump/coredump-submit.c index a4c9c424cf..1079dfd7b7 100644 --- a/src/coredump/coredump-submit.c +++ b/src/coredump/coredump-submit.c @@ -689,7 +689,8 @@ int coredump_submit( root, /* fork_disable_dump= */ skip, /* avoid loops */ &stacktrace, - &json_metadata); + &json_metadata, + /* ret_dlopen_metadata= */ NULL); } } diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index 9315562bba..25fb217149 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -20,6 +20,7 @@ #include "fileio.h" #include "format-util.h" #include "io-util.h" +#include "json-util.h" #include "log.h" #include "memstream-util.h" #include "path-util.h" @@ -186,6 +187,7 @@ typedef struct StackContext { unsigned n_thread; unsigned n_frame; sd_json_variant **package_metadata; + sd_json_variant **dlopen_metadata; Set **modules; } StackContext; @@ -352,8 +354,8 @@ static void report_module_metadata(StackContext *c, const char *name, sd_json_va fputs("\n", c->m.f); } -static int parse_package_metadata(const char *name, sd_json_variant *id_json, Elf *elf, bool *ret_interpreter_found, StackContext *c) { - bool interpreter_found = false; +static int parse_metadata(const char *name, sd_json_variant *id_json, Elf *elf, bool *ret_interpreter_found, StackContext *c) { + bool package_metadata_found = false, interpreter_found = false; size_t n_program_headers; int r; @@ -408,7 +410,7 @@ static int parse_package_metadata(const char *name, sd_json_variant *id_json, El /* Package metadata might have different owners, but the * magic ID is always the same. */ - if (note_header.n_type != ELF_PACKAGE_METADATA_ID) + if (!IN_SET(note_header.n_type, ELF_PACKAGE_METADATA_ID, ELF_NOTE_DLOPEN_TYPE)) continue; _cleanup_free_ char *payload_0suffixed = NULL; @@ -430,46 +432,59 @@ static int parse_package_metadata(const char *name, sd_json_variant *id_json, El return log_error_errno(r, "json_parse on \"%s\" failed: %m", strnull(esc)); } - /* If we have a build-id, merge it in the same JSON object so that it appears all - * nicely together in the logs/metadata. */ - if (id_json) { - r = sd_json_variant_merge_object(&v, id_json); + if (note_header.n_type == ELF_PACKAGE_METADATA_ID) { + /* If we have a build-id, merge it in the same JSON object so that it appears all + * nicely together in the logs/metadata. */ + if (id_json) { + r = sd_json_variant_merge_object(&v, id_json); + if (r < 0) + return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m"); + } + + /* Pretty-print to the buffer, so that the metadata goes as plaintext in the + * journal. */ + report_module_metadata(c, name, v); + + /* Then we build a new object using the module name as the key, and merge it + * with the previous parses, so that in the end it all fits together in a single + * JSON blob. */ + r = sd_json_buildo(&w, SD_JSON_BUILD_PAIR(name, SD_JSON_BUILD_VARIANT(v))); + if (r < 0) + return log_error_errno(r, "Failed to build JSON object: %m"); + + r = sd_json_variant_merge_object(c->package_metadata, w); if (r < 0) return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m"); + + package_metadata_found = true; + } else if (c->dlopen_metadata) { + sd_json_variant *z; + + JSON_VARIANT_ARRAY_FOREACH(z, v) { + r = sd_json_variant_append_array(c->dlopen_metadata, z); + if (r < 0) + return log_error_errno(r, "Failed to append entry to dlopen metadata: %m"); + } } - /* Pretty-print to the buffer, so that the metadata goes as plaintext in the - * journal. */ - report_module_metadata(c, name, v); - - /* Then we build a new object using the module name as the key, and merge it - * with the previous parses, so that in the end it all fits together in a single - * JSON blob. */ - r = sd_json_buildo(&w, SD_JSON_BUILD_PAIR(name, SD_JSON_BUILD_VARIANT(v))); - if (r < 0) - return log_error_errno(r, "Failed to build JSON object: %m"); - - r = sd_json_variant_merge_object(c->package_metadata, w); - if (r < 0) - return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m"); - /* Finally stash the name, so we avoid double visits. */ r = set_put_strdup(c->modules, name); if (r < 0) return log_error_errno(r, "set_put_strdup failed: %m"); - if (ret_interpreter_found) - *ret_interpreter_found = interpreter_found; + if (!c->dlopen_metadata) { + if (ret_interpreter_found) + *ret_interpreter_found = interpreter_found; - return 1; + return 1; + } } } if (ret_interpreter_found) *ret_interpreter_found = interpreter_found; - /* Didn't find package metadata for this module - that's ok, just go to the next. */ - return 0; + return c->dlopen_metadata ? 0 : package_metadata_found; } /* Get the build-id out of an ELF object or a dwarf core module. */ @@ -535,7 +550,7 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, * to the ELF object first. We might be lucky and just get it from elfutils. */ elf = sym_dwfl_module_getelf(mod, &bias); if (elf) { - r = parse_package_metadata(name, id_json, elf, NULL, c); + r = parse_metadata(name, id_json, elf, NULL, c); if (r < 0) return DWARF_CB_ABORT; if (r > 0) @@ -590,7 +605,8 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, _cleanup_(elf_endp) Elf *memelf = sym_elf_memory(data->d_buf, data->d_size); if (!memelf) continue; - r = parse_package_metadata(name, id_json, memelf, NULL, c); + + r = parse_metadata(name, id_json, memelf, NULL, c); if (r < 0) return DWARF_CB_ABORT; if (r > 0) @@ -600,7 +616,12 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, return DWARF_CB_OK; } -static int parse_core(int fd, const char *root, char **ret, sd_json_variant **ret_package_metadata) { +static int parse_core( + int fd, + const char *root, + char **ret, + sd_json_variant **ret_package_metadata, + sd_json_variant **ret_dlopen_metadata) { const Dwfl_Callbacks callbacks = { .find_elf = sym_dwfl_build_id_find_elf, @@ -608,10 +629,11 @@ static int parse_core(int fd, const char *root, char **ret, sd_json_variant **re .find_debuginfo = sym_dwfl_standard_find_debuginfo, }; - _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *dlopen_metadata = NULL; _cleanup_set_free_ Set *modules = NULL; _cleanup_(stack_context_done) StackContext c = { .package_metadata = &package_metadata, + .dlopen_metadata = ret_dlopen_metadata ? &dlopen_metadata : NULL, .modules = &modules, }; int r; @@ -667,15 +689,24 @@ static int parse_core(int fd, const char *root, char **ret, sd_json_variant **re if (ret_package_metadata) *ret_package_metadata = TAKE_PTR(package_metadata); + if (ret_dlopen_metadata) + *ret_dlopen_metadata = TAKE_PTR(dlopen_metadata); return 0; } -static int parse_elf(int fd, const char *executable, const char *root, char **ret, sd_json_variant **ret_package_metadata) { - _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *elf_metadata = NULL; +static int parse_elf( + int fd, + const char *executable, + const char *root, + char **ret, + sd_json_variant **ret_package_metadata, + sd_json_variant **ret_dlopen_metadata) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *dlopen_metadata = NULL, *elf_metadata = NULL; _cleanup_set_free_ Set *modules = NULL; _cleanup_(stack_context_done) StackContext c = { .package_metadata = &package_metadata, + .dlopen_metadata = ret_dlopen_metadata ? &dlopen_metadata : NULL, .modules = &modules, }; const char *elf_type; @@ -702,7 +733,7 @@ static int parse_elf(int fd, const char *executable, const char *root, char **re if (elf_header.e_type == ET_CORE) { _cleanup_free_ char *out = NULL; - r = parse_core(fd, root, ret ? &out : NULL, &package_metadata); + r = parse_core(fd, root, ret ? &out : NULL, &package_metadata, &dlopen_metadata); if (r < 0) return log_warning_errno(r, "Failed to inspect core file: %m"); @@ -719,7 +750,7 @@ static int parse_elf(int fd, const char *executable, const char *root, char **re if (r < 0) return log_warning_errno(r, "Failed to parse build-id of ELF file: %m"); - r = parse_package_metadata(e, id_json, c.elf, &interpreter_found, &c); + r = parse_metadata(e, id_json, c.elf, &interpreter_found, &c); if (r < 0) return log_warning_errno(r, "Failed to parse package metadata of ELF file: %m"); @@ -769,18 +800,28 @@ static int parse_elf(int fd, const char *executable, const char *root, char **re if (ret_package_metadata) *ret_package_metadata = TAKE_PTR(elf_metadata); + if (ret_dlopen_metadata) + *ret_dlopen_metadata = TAKE_PTR(dlopen_metadata); return 0; } #endif -int parse_elf_object(int fd, const char *executable, const char *root, bool fork_disable_dump, char **ret, sd_json_variant **ret_package_metadata) { +int parse_elf_object( + int fd, + const char *executable, + const char *root, + bool fork_disable_dump, + char **ret, + sd_json_variant **ret_package_metadata, + sd_json_variant **ret_dlopen_metadata) { #if HAVE_ELFUTILS _cleanup_close_pair_ int error_pipe[2] = EBADF_PAIR, return_pipe[2] = EBADF_PAIR, - json_pipe[2] = EBADF_PAIR; - _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL; + package_metadata_pipe[2] = EBADF_PAIR, + dlopen_metadata_pipe[2] = EBADF_PAIR; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *dlopen_metadata = NULL; _cleanup_free_ char *buf = NULL; int r; @@ -805,7 +846,13 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork } if (ret_package_metadata) { - r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC|O_NONBLOCK)); + r = RET_NERRNO(pipe2(package_metadata_pipe, O_CLOEXEC|O_NONBLOCK)); + if (r < 0) + return r; + } + + if (ret_dlopen_metadata) { + r = RET_NERRNO(pipe2(dlopen_metadata_pipe, O_CLOEXEC|O_NONBLOCK)); if (r < 0) return r; } @@ -818,8 +865,8 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork * the file descriptor and writing into these four pipes. */ r = safe_fork_full("(sd-parse-elf)", NULL, - (int[]){ fd, error_pipe[1], return_pipe[1], json_pipe[1] }, - 4, + (int[]){ fd, error_pipe[1], return_pipe[1], package_metadata_pipe[1], dlopen_metadata_pipe[1] }, + 5, FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_WAIT|FORK_REOPEN_LOG, NULL); if (r < 0) { @@ -846,7 +893,13 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork report_errno_and_exit(error_pipe[1], r); } - r = parse_elf(fd, executable, root, ret ? &buf : NULL, ret_package_metadata ? &package_metadata : NULL); + r = parse_elf( + fd, + executable, + root, + ret ? &buf : NULL, + ret_package_metadata ? &package_metadata : NULL, + ret_dlopen_metadata ? &dlopen_metadata : NULL); if (r < 0) report_errno_and_exit(error_pipe[1], r); @@ -879,9 +932,9 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork /* Bump the space for the returned string. We don't know how much space we'll need in * advance, so we'll just try to write as much as possible and maybe fail later. */ - (void) fcntl(json_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX); + (void) fcntl(package_metadata_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX); - json_out = take_fdopen(&json_pipe[1], "w"); + json_out = take_fdopen(&package_metadata_pipe[1], "w"); if (!json_out) report_errno_and_exit(error_pipe[1], -errno); @@ -890,12 +943,29 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m"); } + if (dlopen_metadata) { + _cleanup_fclose_ FILE *json_out = NULL; + + /* Bump the space for the returned string. We don't know how much space we'll need in + * advance, so we'll just try to write as much as possible and maybe fail later. */ + (void) fcntl(dlopen_metadata_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX); + + json_out = take_fdopen(&dlopen_metadata_pipe[1], "w"); + if (!json_out) + report_errno_and_exit(error_pipe[1], -errno); + + r = sd_json_variant_dump(dlopen_metadata, SD_JSON_FORMAT_FLUSH, json_out, NULL); + if (r < 0) + log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m"); + } + _exit(EXIT_SUCCESS); } error_pipe[1] = safe_close(error_pipe[1]); return_pipe[1] = safe_close(return_pipe[1]); - json_pipe[1] = safe_close(json_pipe[1]); + package_metadata_pipe[1] = safe_close(package_metadata_pipe[1]); + dlopen_metadata_pipe[1] = safe_close(dlopen_metadata_pipe[1]); if (ret) { _cleanup_fclose_ FILE *in = NULL; @@ -912,19 +982,33 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork if (ret_package_metadata) { _cleanup_fclose_ FILE *json_in = NULL; - json_in = take_fdopen(&json_pipe[0], "r"); + json_in = take_fdopen(&package_metadata_pipe[0], "r"); if (!json_in) return -errno; r = sd_json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL); if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */ - log_warning_errno(r, "Failed to read or parse json metadata, ignoring: %m"); + log_warning_errno(r, "Failed to read or parse package metadata, ignoring: %m"); + } + + if (ret_dlopen_metadata) { + _cleanup_fclose_ FILE *json_in = NULL; + + json_in = take_fdopen(&dlopen_metadata_pipe[0], "r"); + if (!json_in) + return -errno; + + r = sd_json_parse_file(json_in, NULL, 0, &dlopen_metadata, NULL, NULL); + if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */ + log_warning_errno(r, "Failed to read or parse dlopen metadata, ignoring: %m"); } if (ret) *ret = TAKE_PTR(buf); if (ret_package_metadata) *ret_package_metadata = TAKE_PTR(package_metadata); + if (ret_dlopen_metadata) + *ret_dlopen_metadata = TAKE_PTR(dlopen_metadata); return 0; #else diff --git a/src/shared/elf-util.h b/src/shared/elf-util.h index 19c40c2107..b5c3db80ee 100644 --- a/src/shared/elf-util.h +++ b/src/shared/elf-util.h @@ -9,4 +9,11 @@ int dlopen_elf(void); /* Parse an ELF object in a forked process, so that errors while iterating over * untrusted and potentially malicious data do not propagate to the main caller's process. * If fork_disable_dump, the child process will not dump core if it crashes. */ -int parse_elf_object(int fd, const char *executable, const char *root, bool fork_disable_dump, char **ret, sd_json_variant **ret_package_metadata); +int parse_elf_object( + int fd, + const char *executable, + const char *root, + bool fork_disable_dump, + char **ret, + sd_json_variant **ret_package_metadata, + sd_json_variant **ret_dlopen_metadata); From da30f59f602c61dc3219d4d7bfea1cccfcc7a139 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 28 Oct 2025 11:19:16 +0100 Subject: [PATCH 2/3] TEST-65-ANALYZE: Add missing --no-pager --- test/units/TEST-65-ANALYZE.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index e391b765a3..aaf8ddc42d 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -18,7 +18,7 @@ systemd-analyze time || : systemd-analyze critical-chain || : # blame systemd-analyze blame -systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame +systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame --no-pager (! systemd-analyze blame --global) # plot systemd-analyze plot >/dev/null || : From 0fe29d0672d8c7ad422ca88ad12d10f08f3f2a05 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sun, 26 Oct 2025 19:41:27 +0100 Subject: [PATCH 3/3] analyze: Add dlopen-metadata verb systemd-analyze dlopen-metadata will show dlopen metadata in the ELF binary. --- man/systemd-analyze.xml | 10 ++++ src/analyze/analyze-dlopen-metadata.c | 72 +++++++++++++++++++++++++++ src/analyze/analyze-dlopen-metadata.h | 4 ++ src/analyze/analyze.c | 2 + src/analyze/meson.build | 1 + test/units/TEST-65-ANALYZE.sh | 9 ++++ 6 files changed, 98 insertions(+) create mode 100644 src/analyze/analyze-dlopen-metadata.c create mode 100644 src/analyze/analyze-dlopen-metadata.h diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index dc4ab642a8..04b8d8d41f 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 0000000000..618c104d17 --- /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 0000000000..3f7355d96b --- /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 fc85388f30..857f2bb3f5 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 5b247a7088..e26426fc6c 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 aaf8ddc42d..d71b76e92a 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