1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-26 14:04:03 +03:00

Merge pull request #19135 from bluca/coredump_decode

coredump: parse build-id and .note.package
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2021-04-07 07:41:16 +02:00 committed by GitHub
commit 3e3ab11b9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 489 additions and 8 deletions

View File

@ -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;
```

View File

@ -352,6 +352,20 @@ flags: ...
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>COREDUMP_PKGMETA_PACKAGE=</varname></term>
<term><varname>COREDUMP_PKGMETA_PACKAGEVERSION=</varname></term>
<term><varname>COREDUMP_PKGMETA_JSON=</varname></term>
<listitem><para>If the executable contained .package metadata ELF notes, they will be
parsed and attached. The <varname>package</varname> and <varname>packageVersion</varname>
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
<ulink url="https://systemd.io/COREDUMP_PACKAGE_METADATA/">the coredump metadata spec</ulink>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MESSAGE=</varname></term>

View File

@ -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) {

View File

@ -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;

View File

@ -1,7 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <dwarf.h>
#include <elfutils/libdwelf.h>
#include <elfutils/libdwfl.h>
#include <libelf.h>
#include <sys/types.h>
#include <unistd.h>
@ -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, &note_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)

View File

@ -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);