Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

analyze: add inspect-elf verb to parse package metadata #21454

Merged
merged 2 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions man/systemd-analyze.xml
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,39 @@ $ systemd-analyze verify /tmp/source:alias.service
</programlisting>
</example>
</refsect2>

<refsect2>
<title><command>systemd-analyze inspect-elf <replaceable>FILE</replaceable>...</command></title>

<para>This command will load the specified file(s), and if they are ELF objects (executables,
libraries, core files, etc.) it will parse the embedded packaging metadata, if any, and print
it in a table or json format. See the <ulink url="https://systemd.io/COREDUMP_PACKAGE_METADATA/">
Packaging Metadata</ulink> documentation for more information.</para>

<example>
<title>Table output</title>

<programlisting>$ systemd-analyze inspect-elf --json=pretty /tmp/core.fsverity.1000.f77dac5dc161402aa44e15b7dd9dcf97.58561.1637106137000000
{
"elfType" : "coredump",
"elfArchitecture" : "AMD x86-64",
"/home/bluca/git/fsverity-utils/fsverity" : {
"type" : "deb",
"name" : "fsverity-utils",
"version" : "1.3-1",
"buildId" : "7c895ecd2a271f93e96268f479fdc3c64a2ec4ee"
},
"/home/bluca/git/fsverity-utils/libfsverity.so.0" : {
"type" : "deb",
"name" : "fsverity-utils",
"version" : "1.3-1",
"buildId" : "b5e428254abf14237b0ae70ed85fffbb98a78f88"
}
}
</programlisting>
</example>

</refsect2>
</refsect1>

<refsect1>
Expand Down
4 changes: 4 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,10 @@ if want_elfutils != 'false' and not skip_deps
libdw = dependency('libdw',
required : want_elfutils == 'true')
have = libdw.found()

# New in elfutils 0.177
conf.set10('HAVE_DWELF_ELF_E_MACHINE_STRING',
have and cc.has_function('dwelf_elf_e_machine_string', dependencies : libdw))
else
have = false
libdw = []
Expand Down
9 changes: 9 additions & 0 deletions shell-completion/bash/systemd-analyze
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ _systemd_analyze() {
[CAT_CONFIG]='cat-config'
[SECURITY]='security'
[CONDITION]='condition'
[INSPECT_ELF]='inspect-elf'
)

local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
Expand Down Expand Up @@ -169,6 +170,14 @@ _systemd_analyze() {
fi
comps=$( __get_services $mode )
fi

elif __contains_word "$verb" ${VERBS[INSPECT_ELF]}; then
if [[ $cur = -* ]]; then
comps='--help --version --json=off --json=pretty --json=short'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
fi
fi

COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
Expand Down
1 change: 1 addition & 0 deletions shell-completion/zsh/_systemd-analyze
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
'timestamp:Parse a systemd syntax timestamp'
'timespan:Parse a systemd syntax timespan'
'security:Analyze security settings of a service'
'inspect-elf:Parse and print ELF package metadata'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think needs some annotation that an executable file should follow. But I don't know how to do this off the top of my head. Let's leave that for later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, me neither, never really used zsh

# log-level, log-target, service-watchdogs have been deprecated
)

Expand Down
128 changes: 128 additions & 0 deletions src/analyze/analyze-elf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

bluca marked this conversation as resolved.
Show resolved Hide resolved
#include "analyze-elf.h"
#include "elf-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "format-util.h"
#include "json.h"
#include "path-util.h"
#include "strv.h"

int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
char **filename;
int r;

STRV_FOREACH(filename, filenames) {
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *abspath = NULL;
_cleanup_close_ int fd = -1;

r = path_make_absolute_cwd(*filename, &abspath);
if (r < 0)
return log_error_errno(r, "Could not make an absolute path out of \"%s\": %m", *filename);

path_simplify(abspath);

fd = RET_NERRNO(open(abspath, O_RDONLY|O_CLOEXEC));
if (fd < 0)
return log_error_errno(fd, "Could not open \"%s\": %m", abspath);

r = parse_elf_object(fd, abspath, /* fork_disable_dump= */false, NULL, &package_metadata);
if (r < 0)
return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);

t = table_new("", "");
if (!t)
return log_oom();

r = table_set_align_percent(t, TABLE_HEADER_CELL(0), 100);
if (r < 0)
return table_log_add_error(r);

r = table_add_many(
t,
TABLE_STRING, "path:",
TABLE_STRING, abspath);
if (r < 0)
return table_log_add_error(r);

if (package_metadata) {
JsonVariant *module_json;
const char *module_name;

JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, package_metadata) {
const char *field_name;
JsonVariant *field;

/* The ELF type and architecture are added as top-level objects,
* since they are only parsed for the file itself, but the packaging
* metadata is parsed recursively in core files, so there might be
* multiple modules. */
if (STR_IN_SET(module_name, "elfType", "elfArchitecture")) {
_cleanup_free_ char *suffixed = NULL;

suffixed = strjoin(module_name, ":");
if (!suffixed)
return log_oom();

r = table_add_many(
t,
TABLE_STRING, suffixed,
TABLE_STRING, json_variant_string(module_json));
if (r < 0)
return table_log_add_error(r);

continue;
}

/* path/elfType/elfArchitecture come first just once per file,
* then we might have multiple modules, so add a separator between
* them to make the output more readable. */
r = table_add_many(t, TABLE_EMPTY, TABLE_EMPTY);
if (r < 0)
return table_log_add_error(r);

/* In case of core files the module name will be the executable,
* but for binaries/libraries it's just the path, so don't print it
* twice. */
if (!streq(abspath, module_name)) {
r = table_add_many(
t,
TABLE_STRING, "module name:",
TABLE_STRING, module_name);
if (r < 0)
return table_log_add_error(r);
}

JSON_VARIANT_OBJECT_FOREACH(field_name, field, module_json)
if (json_variant_is_string(field)) {
_cleanup_free_ char *suffixed = NULL;

suffixed = strjoin(field_name, ":");
if (!suffixed)
return log_oom();

r = table_add_many(
t,
TABLE_STRING, suffixed,
TABLE_STRING, json_variant_string(field));
if (r < 0)
return table_log_add_error(r);
}
}
}
if (json_flags & JSON_FORMAT_OFF) {
(void) table_set_header(t, true);

r = table_print(t, NULL);
if (r < 0)
return table_log_print_error(r);
} else
json_variant_dump(package_metadata, json_flags, stdout, NULL);
}

return 0;
}
6 changes: 6 additions & 0 deletions src/analyze/analyze-elf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once

#include "json.h"

int analyze_elf(char **filenames, JsonFormatFlags json_flags);
11 changes: 10 additions & 1 deletion src/analyze/analyze.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "alloc-util.h"
#include "analyze-condition.h"
#include "analyze-elf.h"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "bus-error.h"
Expand Down Expand Up @@ -2431,6 +2432,12 @@ static int do_security(int argc, char *argv[], void *userdata) {
/*flags=*/ 0);
}

static int do_elf_inspection(int argc, char *argv[], void *userdata) {
pager_open(arg_pager_flags);

return analyze_elf(strv_skip(argv, 1), arg_json_format_flags);
}

static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL, *dot_link = NULL;
int r;
Expand Down Expand Up @@ -2473,6 +2480,7 @@ static int help(int argc, char *argv[], void *userdata) {
" timestamp TIMESTAMP... Validate a timestamp\n"
" timespan SPAN... Validate a time span\n"
" security [UNIT...] Analyze security of unit\n"
" inspect-elf FILE... Parse and print ELF package metadata\n"
keszybz marked this conversation as resolved.
Show resolved Hide resolved
"\nOptions:\n"
" --recursive-errors=MODE Control which units are verified\n"
" --offline=BOOL Perform a security review on unit file(s)\n"
Expand Down Expand Up @@ -2759,7 +2767,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --offline= is only supported for security right now.");

if (arg_json_format_flags != JSON_FORMAT_OFF && !streq_ptr(argv[optind], "security"))
if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --json= is only supported for security right now.");

Expand Down Expand Up @@ -2835,6 +2843,7 @@ static int run(int argc, char *argv[]) {
{ "timestamp", 2, VERB_ANY, 0, test_timestamp },
{ "timespan", 2, VERB_ANY, 0, dump_timespan },
{ "security", VERB_ANY, VERB_ANY, 0, do_security },
{ "inspect-elf", 2, VERB_ANY, 0, do_elf_inspection },
{}
};

Expand Down
2 changes: 2 additions & 0 deletions src/analyze/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ systemd_analyze_sources = files('''
analyze.c
analyze-condition.c
analyze-condition.h
analyze-elf.c
analyze-elf.h
analyze-verify.c
analyze-verify.h
analyze-security.c
Expand Down
Loading