diff --git a/docs/COREDUMP_PACKAGE_METADATA.md b/docs/COREDUMP_PACKAGE_METADATA.md
new file mode 100644
index 00000000000..54ff31ee78f
--- /dev/null
+++ b/docs/COREDUMP_PACKAGE_METADATA.md
@@ -0,0 +1,110 @@
+---
+title: Package Metadata for Core Files
+category: Interfaces
+layout: default
+---
+
+# Package Metadata for Core Files
+
+*Intended audience: hackers working on userspace subsystems that create ELF binaries
+or parse ELF core files.*
+
+## Motivation
+
+ELF binaries get stamped with a unique, build-time generated hex string identifier called
+`build-id`, [which gets embedded as an ELF note called `.note.gnu.build-id`](https://fedoraproject.org/wiki/Releases/FeatureBuildId).
+In most cases, this allows to associate a stripped binary with its debugging information.
+It is used, for example, to dynamically fetch DWARF symbols from a debuginfo server, or
+to query the local package manager and find out the package metadata or, again, the DWARF
+symbols or program sources.
+
+However, this usage of the `build-id` requires either local metadata, usually set up by
+the package manager, or access to a remote server over the network. Both of those might
+be unavailable or forbidden.
+
+Thus it becomes desirable to add additional metadata to a binary at build time, so that
+`systemd-coredump` and other services analyzing core files are able to extract said
+metadata simply from the core file itself, without external dependencies.
+
+## Implementation
+
+This document will attempt to define a common metadata format specification, so that
+multiple implementers might use it when building packages, or core file analyzers, and
+so on.
+
+The metadata will be embedded in a single, new ELF header section, in a key-value JSON
+format. Implementers working on parsing core files should not assume a specific list of
+keys, but parse anything that is included in the section.
+Implementers working on build tools should strive to use the same key names, for
+consistency. The most common will be listed here. When corresponding to the content of
+os-release, the values should match, again for consistency.
+
+* Section header
+
+```
+SECTION: `.note.package`
+node-id: `0xcafe1a7e`
+Owner: `FDO` (FreeDesktop.org)
+Value: a JSON string with the structure described below
+```
+
+* JSON payload
+
+```json
+{
+ "packageType":"rpm", # this provides a namespace for the package+package-version fields
+ "packageDistro":"fedora",
+ "packageDistroVersion":"33",
+ "package":"coreutils",
+ "packageVersion": "4711.0815.fc13.arm32",
+ "cpe": # A CPE name for the operating system, `CPE_NAME` from os-release is a good default
+}
+```
+
+A reference implementations of a [build-time tool is provided](https://github.com/keszybz/rpm-version-note/)
+and can be used to generate a linker script, which can then be used at build time via
+```LDFLAGS="-Wl,-T,/path/to/generated/script"``` to include the note in the binary.
+
+Generator:
+```console
+$ ./generate-package-notes.py --rpm systemd-248~rc2-1.fc34
+SECTIONS
+{
+ .note.package : ALIGN(4) {
+ BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */
+ BYTE(0x73) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */
+ BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */
+ BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
+ BYTE(0x7b) BYTE(0x22) BYTE(0x70) BYTE(0x61) /* Value: '{"packageType":"rpm","package":"systemd","packageVersion":"248~rc2-1.fc34","cpe":"cpe:/o:fedoraproject:fedora:33"}\x00\x00' */
+ BYTE(0x63) BYTE(0x6b) BYTE(0x61) BYTE(0x67)
+ BYTE(0x65) BYTE(0x54) BYTE(0x79) BYTE(0x70)
+ BYTE(0x65) BYTE(0x22) BYTE(0x3a) BYTE(0x22)
+ BYTE(0x72) BYTE(0x70) BYTE(0x6d) BYTE(0x22)
+ BYTE(0x2c) BYTE(0x22) BYTE(0x70) BYTE(0x61)
+ BYTE(0x63) BYTE(0x6b) BYTE(0x61) BYTE(0x67)
+ BYTE(0x65) BYTE(0x22) BYTE(0x3a) BYTE(0x22)
+ BYTE(0x73) BYTE(0x79) BYTE(0x73) BYTE(0x74)
+ BYTE(0x65) BYTE(0x6d) BYTE(0x64) BYTE(0x22)
+ BYTE(0x2c) BYTE(0x22) BYTE(0x70) BYTE(0x61)
+ BYTE(0x63) BYTE(0x6b) BYTE(0x61) BYTE(0x67)
+ BYTE(0x65) BYTE(0x56) BYTE(0x65) BYTE(0x72)
+ BYTE(0x73) BYTE(0x69) BYTE(0x6f) BYTE(0x6e)
+ BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x32)
+ BYTE(0x34) BYTE(0x38) BYTE(0x7e) BYTE(0x72)
+ BYTE(0x63) BYTE(0x32) BYTE(0x2d) BYTE(0x31)
+ BYTE(0x2e) BYTE(0x66) BYTE(0x63) BYTE(0x33)
+ BYTE(0x34) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
+ BYTE(0x63) BYTE(0x70) BYTE(0x65) BYTE(0x22)
+ BYTE(0x3a) BYTE(0x22) BYTE(0x63) BYTE(0x70)
+ BYTE(0x65) BYTE(0x3a) BYTE(0x2f) BYTE(0x6f)
+ BYTE(0x3a) BYTE(0x66) BYTE(0x65) BYTE(0x64)
+ BYTE(0x6f) BYTE(0x72) BYTE(0x61) BYTE(0x70)
+ BYTE(0x72) BYTE(0x6f) BYTE(0x6a) BYTE(0x65)
+ BYTE(0x63) BYTE(0x74) BYTE(0x3a) BYTE(0x66)
+ BYTE(0x65) BYTE(0x64) BYTE(0x6f) BYTE(0x72)
+ BYTE(0x61) BYTE(0x3a) BYTE(0x33) BYTE(0x33)
+ BYTE(0x22) BYTE(0x7d) BYTE(0x00) BYTE(0x00)
+ }
+}
+INSERT AFTER .note.gnu.build-id;
+```
diff --git a/man/systemd-coredump.xml b/man/systemd-coredump.xml
index 117b9eb6d08..d994a21d9b5 100644
--- a/man/systemd-coredump.xml
+++ b/man/systemd-coredump.xml
@@ -352,6 +352,20 @@ flags: ...
+
+ COREDUMP_PKGMETA_PACKAGE=
+ COREDUMP_PKGMETA_PACKAGEVERSION=
+ COREDUMP_PKGMETA_JSON=
+
+ If the executable contained .package metadata ELF notes, they will be
+ parsed and attached. The package and packageVersion
+ of the 'main' ELF module (ie: the excutable) will be appended individually. The
+ JSON-formatted content of all modules will be appended as a single JSON object, each with
+ the module name as the key. For more information about this metadata format and content, see
+ the coredump metadata spec.
+
+
+
MESSAGE=
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 2fb2404500e..be813f57f19 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -703,14 +703,16 @@ static int submit_coredump(
struct iovec_wrapper *iovw,
int input_fd) {
+ _cleanup_(json_variant_unrefp) JsonVariant *json_metadata = NULL;
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
_cleanup_free_ char *filename = NULL, *coredump_data = NULL;
_cleanup_free_ char *stacktrace = NULL;
char *core_message;
+ const char *module_name;
uint64_t coredump_size = UINT64_MAX;
bool truncated = false;
+ JsonVariant *module_json;
int r;
-
assert(context);
assert(iovw);
assert(input_fd >= 0);
@@ -757,7 +759,7 @@ static int submit_coredump(
"than %"PRIu64" (the configured maximum)",
coredump_size, arg_process_size_max);
} else
- coredump_make_stack_trace(coredump_fd, context->meta[META_EXE], &stacktrace);
+ coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
#endif
log:
@@ -781,6 +783,67 @@ log:
if (truncated)
(void) iovw_put_string_field(iovw, "COREDUMP_TRUNCATED=", "1");
+ /* If we managed to parse any ELF metadata (build-id, ELF package meta),
+ * attach it as journal metadata. */
+ if (json_metadata) {
+ _cleanup_free_ char *formatted_json = NULL;
+
+ r = json_variant_format(json_metadata, 0, &formatted_json);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format JSON package metadata: %m");
+
+ (void) iovw_put_string_field(iovw, "COREDUMP_PKGMETA_JSON=", formatted_json);
+ }
+
+ JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, json_metadata) {
+ _cleanup_free_ char *module_basename = NULL, *exe_basename = NULL;
+ const char *key;
+ JsonVariant *w;
+
+ /* The module name, most likely parsed from the ELF core file,
+ * sometimes contains the full path and sometimes does not. */
+ r = path_extract_filename(module_name, &module_basename);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse module basename: %m");
+ r = path_extract_filename(context->meta[META_EXE], &exe_basename);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse executable basename: %m");
+
+ /* We only add structured fields for the 'main' ELF module */
+ if (!streq(module_basename, exe_basename))
+ continue;
+
+ /* Cannot nest two JSON_VARIANT_OBJECT_FOREACH as they define the same
+ * iterator variable '_state' */
+ for (struct json_variant_foreach_state _state2 = { (module_json), 0 }; \
+ json_variant_is_object(_state2.variant) && \
+ _state2.idx < json_variant_elements(_state2.variant) && \
+ ({ key = json_variant_string(json_variant_by_index(_state2.variant, _state2.idx)); \
+ w = json_variant_by_index(_state2.variant, _state2.idx + 1); \
+ true; }); \
+ _state2.idx += 2) {
+ _cleanup_free_ char *metadata_id = NULL, *key_upper = NULL;
+
+ if (!json_variant_is_string(w))
+ continue;
+
+ if (!STR_IN_SET(key, "package", "packageVersion"))
+ continue;
+
+ /* Journal metadata field names need to be upper case */
+ key_upper = strdup(key);
+ if (!key_upper)
+ return log_oom();
+ key_upper = ascii_strupper(key_upper);
+
+ metadata_id = strjoin("COREDUMP_PKGMETA_", key_upper, "=");
+ if (!metadata_id)
+ return log_oom();
+
+ (void) iovw_put_string_field(iovw, metadata_id, json_variant_string(w));
+ }
+ }
+
/* Optionally store the entire coredump in the journal */
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
if (coredump_size <= arg_journal_size_max) {
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 0c4ef2e1234..3418753a31b 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -545,7 +545,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*slice = NULL, *cgroup = NULL, *owner_uid = NULL,
*message = NULL, *timestamp = NULL, *filename = NULL,
- *truncated = NULL, *coredump = NULL;
+ *truncated = NULL, *coredump = NULL,
+ *pkgmeta_name = NULL, *pkgmeta_version = NULL, *pkgmeta_json = NULL;
const void *d;
size_t l;
bool normal_coredump;
@@ -574,6 +575,9 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
RETRIEVE(d, l, "COREDUMP", coredump);
+ RETRIEVE(d, l, "COREDUMP_PKGMETA_PACKAGE", pkgmeta_name);
+ RETRIEVE(d, l, "COREDUMP_PKGMETA_PACKAGEVERSION", pkgmeta_version);
+ RETRIEVE(d, l, "COREDUMP_PKGMETA_JSON", pkgmeta_json);
RETRIEVE(d, l, "_BOOT_ID", boot_id);
RETRIEVE(d, l, "_MACHINE_ID", machine_id);
RETRIEVE(d, l, "MESSAGE", message);
@@ -716,6 +720,68 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
else
fprintf(file, " Storage: none\n");
+ if (pkgmeta_name && pkgmeta_version)
+ fprintf(file, " Package: %s/%s\n", pkgmeta_name, pkgmeta_version);
+
+ /* Print out the build-id of the 'main' ELF module, by matching the JSON key
+ * with the 'exe' field. */
+ if (exe && pkgmeta_json) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ r = json_parse(pkgmeta_json, 0, &v, NULL, NULL);
+ if (r < 0) {
+ log_warning_errno(r, "json_parse on %s failed, ignoring: %m", pkgmeta_json);
+ } else {
+ const char *module_name;
+ JsonVariant *module_json;
+
+ /* Cannot nest two JSON_VARIANT_OBJECT_FOREACH as they define the same
+ * iterator variable '_state' */
+ for (struct json_variant_foreach_state _state2 = { (v), 0 }; \
+ json_variant_is_object(_state2.variant) && \
+ _state2.idx < json_variant_elements(_state2.variant) && \
+ ({ module_name = json_variant_string(json_variant_by_index(_state2.variant, _state2.idx)); \
+ module_json = json_variant_by_index(_state2.variant, _state2.idx + 1); \
+ true; }); \
+ _state2.idx += 2) {
+ _cleanup_free_ char *module_basename = NULL, *exe_basename = NULL;
+ const char *key;
+ JsonVariant *w;
+
+ /* The module name, most likely parsed from the ELF core file,
+ * sometimes contains the full path and sometimes does not. */
+ r = path_extract_filename(module_name, &module_basename);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse module basename: %m");
+ break;
+ }
+ r = path_extract_filename(exe, &exe_basename);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse executable basename: %m");
+ break;
+ }
+
+ /* We only print the build-id for the 'main' ELF module */
+ if (!streq(module_basename, exe_basename))
+ continue;
+
+ JSON_VARIANT_OBJECT_FOREACH(key, w, module_json) {
+ if (!json_variant_is_string(w))
+ continue;
+
+ if (!streq(key, "buildid"))
+ continue;
+
+ fprintf(file, " build-id: %s\n", json_variant_string(w));
+
+ break;
+ }
+
+ break;
+ }
+ }
+ }
+
if (message) {
_cleanup_free_ char *m = NULL;
diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c
index a29ab1211c2..c16f12f09b4 100644
--- a/src/coredump/stacktrace.c
+++ b/src/coredump/stacktrace.c
@@ -1,7 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include
+#include
#include
+#include
#include
#include
@@ -9,6 +11,7 @@
#include "fileio.h"
#include "fd-util.h"
#include "format-util.h"
+#include "hexdecoct.h"
#include "macro.h"
#include "stacktrace.h"
#include "string-util.h"
@@ -16,6 +19,7 @@
#define FRAMES_MAX 64
#define THREADS_MAX 64
+#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
struct stack_context {
FILE *f;
@@ -23,6 +27,8 @@ struct stack_context {
Elf *elf;
unsigned n_thread;
unsigned n_frame;
+ JsonVariant **package_metadata;
+ Set **modules;
};
static int frame_callback(Dwfl_Frame *frame, void *userdata) {
@@ -111,14 +117,227 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) {
return DWARF_CB_OK;
}
-static int make_stack_trace(int fd, const char *executable, char **ret) {
+static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, struct stack_context *c) {
+ size_t n_program_headers;
+ int r;
+
+ assert(name);
+ assert(elf);
+ assert(c);
+
+ /* When iterating over PT_LOAD we will visit modules more than once */
+ if (set_contains(*c->modules, name))
+ return DWARF_CB_OK;
+
+ r = elf_getphdrnum(elf, &n_program_headers);
+ if (r < 0) /* Not the handle we are looking for - that's ok, skip it */
+ return DWARF_CB_OK;
+
+ /* Iterate over all program headers in that ELF object. These will have been copied by
+ * the kernel verbatim when the core file is generated. */
+ for (size_t i = 0; i < n_program_headers; ++i) {
+ size_t note_offset = 0, name_offset, desc_offset;
+ GElf_Phdr mem, *program_header;
+ GElf_Nhdr note_header;
+ Elf_Data *data;
+
+ /* Package metadata is in PT_NOTE headers. */
+ program_header = gelf_getphdr(elf, i, &mem);
+ if (!program_header || program_header->p_type != PT_NOTE)
+ continue;
+
+ /* Fortunately there is an iterator we can use to walk over the
+ * elements of a PT_NOTE program header. We are interested in the
+ * note with type. */
+ data = elf_getdata_rawchunk(elf,
+ program_header->p_offset,
+ program_header->p_filesz,
+ ELF_T_NHDR);
+
+ while (note_offset < data->d_size &&
+ (note_offset = gelf_getnote(data, note_offset, ¬e_header, &name_offset, &desc_offset)) > 0) {
+ const char *note_name = (const char *)data->d_buf + name_offset;
+ const char *payload = (const char *)data->d_buf + desc_offset;
+
+ if (note_header.n_namesz == 0 || note_header.n_descsz == 0)
+ continue;
+
+ /* Package metadata might have different owners, but the
+ * magic ID is always the same. */
+ if (note_header.n_type == ELF_PACKAGE_METADATA_ID) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+ char *name_key = NULL;
+
+ r = json_parse(payload, 0, &v, NULL, NULL);
+ if (r < 0) {
+ log_error_errno(r, "json_parse on %s failed: %m", payload);
+ return DWARF_CB_ABORT;
+ }
+
+ /* First pretty-print to the buffer, so that the metadata goes as
+ * plaintext in the journal. */
+ fprintf(c->f, "Metadata for module %s owned by %s found: ",
+ name, note_name);
+ json_variant_dump(v, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, c->f, NULL);
+ fputc('\n', c->f);
+
+ /* Secondly, if we have a build-id, merge it in the same JSON object
+ * so that it apperas all nicely together in the logs/metadata. */
+ if (id_json) {
+ r = json_variant_merge(&v, id_json);
+ if (r < 0) {
+ log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
+ return DWARF_CB_ABORT;
+ }
+ }
+
+ /* 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 = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name, JSON_BUILD_VARIANT(v))));
+ if (r < 0) {
+ log_error_errno(r, "Failed to build JSON object: %m");
+ return DWARF_CB_ABORT;
+ }
+ r = json_variant_merge(c->package_metadata, w);
+ if (r < 0) {
+ log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ /* Finally stash the name, so we avoid double visits. */
+ name_key = strdup(name);
+ if (!name_key) {
+ log_oom();
+ return DWARF_CB_ABORT;
+ }
+ r = set_ensure_consume(c->modules, &string_hash_ops, name_key);
+ if (r < 0) {
+ log_error_errno(r, "set_ensure_consume failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ return DWARF_CB_OK;
+ }
+ }
+ }
+
+ /* Didn't find package metadata for this module - that's ok, just go to the next. */
+ return DWARF_CB_OK;
+}
+
+static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, Dwarf_Addr start, void *arg) {
+ _cleanup_(json_variant_unrefp) JsonVariant *id_json = NULL;
+ struct stack_context *c = arg;
+ size_t n_program_headers;
+ GElf_Addr id_vaddr, bias;
+ const unsigned char *id;
+ int id_len, r;
+ Elf *elf;
+
+ assert(mod);
+ assert(c);
+
+ if (!name)
+ name = "(unnamed)"; /* For logging purposes */
+
+ /* We are iterating on each "module", which is what dwfl calls ELF objects contained in the
+ * core file, and extracting the build-id first and then the package metadata.
+ * We proceed in a best-effort fashion - not all ELF objects might contain both or either.
+ * The build-id is easy, as libdwfl parses it during the dwfl_core_file_report() call and
+ * stores it separately in an internal library struct. */
+ id_len = dwfl_module_build_id(mod, &id, &id_vaddr);
+ if (id_len <= 0) {
+ /* If we don't find a build-id, note it in the journal message, and try
+ * anyway to find the package metadata. It's unlikely to have the latter
+ * without the former, but there's no hard rule. */
+ fprintf(c->f, "Found module %s without build-id\n", name);
+ } else {
+ _cleanup_free_ char *id_hex = NULL, *id_hex_prefixed = NULL;
+
+ id_hex = hexmem(id, id_len);
+ if (!id_hex) {
+ log_oom();
+ return DWARF_CB_ABORT;
+ }
+
+ fprintf(c->f, "Found module %s with build-id: %s\n", name, id_hex);
+
+ /* We will later parse package metadata json and pass it to our caller. Prepare the
+ * build-id in json format too, so that it can be appended and parsed cleanly. It
+ * will then be added as metadata to the journal message with the stack trace. */
+ id_hex_prefixed = strjoin("{\"buildid\":\"", id_hex, "\"}");
+ if (!id_hex_prefixed) {
+ log_oom();
+ return DWARF_CB_ABORT;
+ }
+ r = json_parse(id_hex_prefixed, 0, &id_json, NULL, NULL);
+ if (r < 0) {
+ log_error_errno(r, "json_parse on %s failed: %m", id_hex_prefixed);
+ return DWARF_CB_ABORT;
+ }
+ }
+
+ /* The .note.package metadata is more difficult. From the module, we need to get a reference
+ * to the ELF object first. We might be lucky and just get it from elfutils. */
+ elf = dwfl_module_getelf(mod, &bias);
+ if (elf)
+ return parse_package_metadata(name, id_json, elf, c);
+
+ /* We did not get the ELF object. That is likely because we didn't get direct
+ * access to the executable, and the version of elfutils does not yet support
+ * parsing it out of the core file directly.
+ * So fallback to manual extraction - get the PT_LOAD section from the core,
+ * and if it's the right one we can interpret it as an Elf object, and parse
+ * its notes manually. */
+
+ r = elf_getphdrnum(c->elf, &n_program_headers);
+ if (r < 0) {
+ log_warning("Could not parse number of program headers from core file: %s",
+ elf_errmsg(-1)); /* -1 retrieves the most recent error */
+ return DWARF_CB_OK;
+ }
+
+ for (size_t i = 0; i < n_program_headers; ++i) {
+ GElf_Phdr mem, *program_header;
+ Elf_Data *data;
+
+ /* The core file stores the ELF files in the PT_LOAD segment .*/
+ program_header = gelf_getphdr(c->elf, i, &mem);
+ if (!program_header || program_header->p_type != PT_LOAD)
+ continue;
+
+ /* Now get a usable Elf reference, and parse the notes from it. */
+ data = elf_getdata_rawchunk(c->elf,
+ program_header->p_offset,
+ program_header->p_filesz,
+ ELF_T_NHDR);
+
+ Elf *memelf = elf_memory(data->d_buf, data->d_size);
+ if (!memelf)
+ continue;
+ r = parse_package_metadata(name, id_json, memelf, c);
+ if (r != DWARF_CB_OK)
+ return r;
+ }
+
+ return DWARF_CB_OK;
+}
+
+static int parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
static const Dwfl_Callbacks callbacks = {
.find_elf = dwfl_build_id_find_elf,
+ .section_address = dwfl_offline_section_address,
.find_debuginfo = dwfl_standard_find_debuginfo,
};
- struct stack_context c = {};
+ _cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
+ _cleanup_(set_freep) Set *modules = NULL;
+ struct stack_context c = {
+ .package_metadata = &package_metadata,
+ .modules = &modules,
+ };
char *buf = NULL;
size_t sz = 0;
int r;
@@ -157,6 +376,11 @@ static int make_stack_trace(int fd, const char *executable, char **ret) {
goto finish;
}
+ if (dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
r = -EINVAL;
goto finish;
@@ -170,6 +394,8 @@ static int make_stack_trace(int fd, const char *executable, char **ret) {
c.f = safe_fclose(c.f);
*ret = TAKE_PTR(buf);
+ if (ret_package_metadata)
+ *ret_package_metadata = TAKE_PTR(package_metadata);
r = 0;
@@ -187,10 +413,10 @@ finish:
return r;
}
-void coredump_make_stack_trace(int fd, const char *executable, char **ret) {
+void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
int r;
- r = make_stack_trace(fd, executable, ret);
+ r = parse_core(fd, executable, ret, ret_package_metadata);
if (r == -EINVAL)
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else if (r < 0)
diff --git a/src/coredump/stacktrace.h b/src/coredump/stacktrace.h
index b9357484042..5039b934dda 100644
--- a/src/coredump/stacktrace.h
+++ b/src/coredump/stacktrace.h
@@ -1,4 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-void coredump_make_stack_trace(int fd, const char *executable, char **ret);
+#include "json.h"
+
+void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata);