• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13210122091

07 Feb 2025 10:56PM UTC coverage: 71.748% (-0.07%) from 71.813%
13210122091

push

github

web-flow
network: allow to configure routing policy rule even if requesting interface is not activated yet (#36257)

Fixes a regression caused by 4f8b153d9
(v257).
Fixes #36244.

16 of 16 new or added lines in 2 files covered. (100.0%)

3079 existing lines in 54 files now uncovered.

292917 of 408258 relevant lines covered (71.75%)

714241.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.81
/src/shared/elf-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#if HAVE_ELFUTILS
4

5
#include <dwarf.h>
6
#include <elfutils/libdwelf.h>
7
#include <elfutils/libdwfl.h>
8
#include <libelf.h>
9
#include <sys/prctl.h>
10
#include <sys/resource.h>
11
#include <sys/types.h>
12
#include <unistd.h>
13

14
#include "alloc-util.h"
15
#include "dlfcn-util.h"
16
#include "elf-util.h"
17
#include "errno-util.h"
18
#include "escape.h"
19
#include "fileio.h"
20
#include "fd-util.h"
21
#include "format-util.h"
22
#include "hexdecoct.h"
23
#include "io-util.h"
24
#include "macro.h"
25
#include "memstream-util.h"
26
#include "path-util.h"
27
#include "process-util.h"
28
#include "rlimit-util.h"
29
#include "string-util.h"
30

31
#define FRAMES_MAX 64
32
#define THREADS_MAX 64
33
#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
34

35
/* The amount of data we're willing to write to each of the output pipes. */
36
#define COREDUMP_PIPE_MAX (1024*1024U)
37

38
static void *dw_dl = NULL;
39
static void *elf_dl = NULL;
40

41
/* libdw symbols */
42
static DLSYM_PROTOTYPE(dwarf_attr_integrate) = NULL;
43
static DLSYM_PROTOTYPE(dwarf_diename) = NULL;
44
static DLSYM_PROTOTYPE(dwarf_formstring) = NULL;
45
static DLSYM_PROTOTYPE(dwarf_getscopes) = NULL;
46
static DLSYM_PROTOTYPE(dwarf_getscopes_die) = NULL;
47
static DLSYM_PROTOTYPE(dwelf_elf_begin) = NULL;
48
#if HAVE_DWELF_ELF_E_MACHINE_STRING
49
static DLSYM_PROTOTYPE(dwelf_elf_e_machine_string) = NULL;
50
#endif
51
static DLSYM_PROTOTYPE(dwelf_elf_gnu_build_id) = NULL;
52
static DLSYM_PROTOTYPE(dwarf_tag) = NULL;
53
static DLSYM_PROTOTYPE(dwfl_addrmodule) = NULL;
54
static DLSYM_PROTOTYPE(dwfl_begin) = NULL;
55
static DLSYM_PROTOTYPE(dwfl_build_id_find_elf) = NULL;
56
static DLSYM_PROTOTYPE(dwfl_core_file_attach) = NULL;
57
static DLSYM_PROTOTYPE(dwfl_core_file_report) = NULL;
58
#if HAVE_DWFL_SET_SYSROOT
59
static DLSYM_PROTOTYPE(dwfl_set_sysroot) = NULL;
60
#endif
61
static DLSYM_PROTOTYPE(dwfl_end) = NULL;
62
static DLSYM_PROTOTYPE(dwfl_errmsg) = NULL;
63
static DLSYM_PROTOTYPE(dwfl_errno) = NULL;
64
static DLSYM_PROTOTYPE(dwfl_frame_pc) = NULL;
65
static DLSYM_PROTOTYPE(dwfl_getmodules) = NULL;
66
static DLSYM_PROTOTYPE(dwfl_getthreads) = NULL;
67
static DLSYM_PROTOTYPE(dwfl_module_addrdie) = NULL;
68
static DLSYM_PROTOTYPE(dwfl_module_addrname) = NULL;
69
static DLSYM_PROTOTYPE(dwfl_module_build_id) = NULL;
70
static DLSYM_PROTOTYPE(dwfl_module_getelf) = NULL;
71
static DLSYM_PROTOTYPE(dwfl_module_info) = NULL;
72
static DLSYM_PROTOTYPE(dwfl_offline_section_address) = NULL;
73
static DLSYM_PROTOTYPE(dwfl_report_end) = NULL;
74
static DLSYM_PROTOTYPE(dwfl_standard_find_debuginfo) = NULL;
75
static DLSYM_PROTOTYPE(dwfl_thread_getframes) = NULL;
76
static DLSYM_PROTOTYPE(dwfl_thread_tid) = NULL;
77

78
/* libelf symbols */
79
static DLSYM_PROTOTYPE(elf_begin) = NULL;
80
static DLSYM_PROTOTYPE(elf_end) = NULL;
81
static DLSYM_PROTOTYPE(elf_getdata_rawchunk) = NULL;
82
static DLSYM_PROTOTYPE(gelf_getehdr) = NULL;
83
static DLSYM_PROTOTYPE(elf_getphdrnum) = NULL;
84
static DLSYM_PROTOTYPE(elf_errmsg) = NULL;
85
static DLSYM_PROTOTYPE(elf_errno) = NULL;
86
static DLSYM_PROTOTYPE(elf_memory) = NULL;
87
static DLSYM_PROTOTYPE(elf_version) = NULL;
88
static DLSYM_PROTOTYPE(gelf_getphdr) = NULL;
89
static DLSYM_PROTOTYPE(gelf_getnote) = NULL;
90

91
int dlopen_dw(void) {
25✔
92
        int r;
25✔
93

94
        ELF_NOTE_DLOPEN("dw",
25✔
95
                        "Support for backtrace and ELF package metadata decoding from core files",
96
                        ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
97
                        "libdw.so.1");
98

99
        r = dlopen_many_sym_or_warn(
25✔
100
                        &dw_dl, "libdw.so.1", LOG_DEBUG,
101
                        DLSYM_ARG(dwarf_getscopes),
102
                        DLSYM_ARG(dwarf_getscopes_die),
103
                        DLSYM_ARG(dwarf_tag),
104
                        DLSYM_ARG(dwarf_attr_integrate),
105
                        DLSYM_ARG(dwarf_formstring),
106
                        DLSYM_ARG(dwarf_diename),
107
                        DLSYM_ARG(dwelf_elf_gnu_build_id),
108
                        DLSYM_ARG(dwelf_elf_begin),
109
#if HAVE_DWELF_ELF_E_MACHINE_STRING
110
                        DLSYM_ARG(dwelf_elf_e_machine_string),
111
#endif
112
                        DLSYM_ARG(dwfl_addrmodule),
113
                        DLSYM_ARG(dwfl_frame_pc),
114
                        DLSYM_ARG(dwfl_module_addrdie),
115
                        DLSYM_ARG(dwfl_module_addrname),
116
                        DLSYM_ARG(dwfl_module_info),
117
                        DLSYM_ARG(dwfl_module_build_id),
118
                        DLSYM_ARG(dwfl_module_getelf),
119
                        DLSYM_ARG(dwfl_begin),
120
                        DLSYM_ARG(dwfl_core_file_report),
121
#if HAVE_DWFL_SET_SYSROOT
122
                        DLSYM_ARG(dwfl_set_sysroot),
123
#endif
124
                        DLSYM_ARG(dwfl_report_end),
125
                        DLSYM_ARG(dwfl_getmodules),
126
                        DLSYM_ARG(dwfl_core_file_attach),
127
                        DLSYM_ARG(dwfl_end),
128
                        DLSYM_ARG(dwfl_errmsg),
129
                        DLSYM_ARG(dwfl_errno),
130
                        DLSYM_ARG(dwfl_build_id_find_elf),
131
                        DLSYM_ARG(dwfl_standard_find_debuginfo),
132
                        DLSYM_ARG(dwfl_thread_tid),
133
                        DLSYM_ARG(dwfl_thread_getframes),
134
                        DLSYM_ARG(dwfl_getthreads),
135
                        DLSYM_ARG(dwfl_offline_section_address));
136
        if (r <= 0)
25✔
137
                return r;
×
138

139
        return 1;
140
}
141

142
int dlopen_elf(void) {
25✔
143
        int r;
25✔
144

145
        ELF_NOTE_DLOPEN("elf",
25✔
146
                        "Support for backtraces and reading ELF package metadata from core files",
147
                        ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
148
                        "libelf.so.1");
149

150
        r = dlopen_many_sym_or_warn(
25✔
151
                        &elf_dl, "libelf.so.1", LOG_DEBUG,
152
                        DLSYM_ARG(elf_begin),
153
                        DLSYM_ARG(elf_end),
154
                        DLSYM_ARG(elf_getphdrnum),
155
                        DLSYM_ARG(elf_getdata_rawchunk),
156
                        DLSYM_ARG(elf_errmsg),
157
                        DLSYM_ARG(elf_errno),
158
                        DLSYM_ARG(elf_memory),
159
                        DLSYM_ARG(elf_version),
160
                        DLSYM_ARG(gelf_getehdr),
161
                        DLSYM_ARG(gelf_getphdr),
162
                        DLSYM_ARG(gelf_getnote));
163
        if (r <= 0)
25✔
164
                return r;
×
165

166
        return 1;
167
}
168

169
typedef struct StackContext {
170
        MemStream m;
171
        Dwfl *dwfl;
172
        Elf *elf;
173
        unsigned n_thread;
174
        unsigned n_frame;
175
        sd_json_variant **package_metadata;
176
        Set **modules;
177
} StackContext;
178

179
static void stack_context_done(StackContext *c) {
23✔
180
        assert(c);
23✔
181

182
        memstream_done(&c->m);
23✔
183

184
        if (c->dwfl) {
23✔
185
                sym_dwfl_end(c->dwfl);
11✔
186
                c->dwfl = NULL;
11✔
187
        }
188

189
        if (c->elf) {
23✔
190
                sym_elf_end(c->elf);
23✔
191
                c->elf = NULL;
23✔
192
        }
193
}
23✔
194

195
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Elf *, sym_elf_end, NULL);
×
196

197
static int frame_callback(Dwfl_Frame *frame, void *userdata) {
63✔
198
        StackContext *c = ASSERT_PTR(userdata);
63✔
199
        Dwarf_Addr pc, pc_adjusted;
63✔
200
        const char *fname = NULL, *symbol = NULL;
63✔
201
        Dwfl_Module *module;
63✔
202
        bool is_activation;
63✔
203
        uint64_t module_offset = 0;
63✔
204

205
        assert(frame);
63✔
206

207
        if (c->n_frame >= FRAMES_MAX)
63✔
208
                return DWARF_CB_ABORT;
63✔
209

210
        if (!sym_dwfl_frame_pc(frame, &pc, &is_activation))
63✔
211
                return DWARF_CB_ABORT;
212

213
        pc_adjusted = pc - (is_activation ? 0 : 1);
63✔
214

215
        module = sym_dwfl_addrmodule(c->dwfl, pc_adjusted);
63✔
216
        if (module) {
63✔
217
                Dwarf_Addr start, bias = 0;
40✔
218
                Dwarf_Die *cudie;
40✔
219

220
                cudie = sym_dwfl_module_addrdie(module, pc_adjusted, &bias);
40✔
221
                if (cudie) {
40✔
UNCOV
222
                        _cleanup_free_ Dwarf_Die *scopes = NULL;
×
UNCOV
223
                        int n;
×
224

UNCOV
225
                        n = sym_dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
×
UNCOV
226
                        if (n > 0)
×
UNCOV
227
                                for (Dwarf_Die *s = scopes; s && s < scopes + n; s++) {
×
UNCOV
228
                                        Dwarf_Attribute *a, space;
×
229

UNCOV
230
                                        if (!IN_SET(sym_dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point))
×
231
                                                continue;
×
232

UNCOV
233
                                        a = sym_dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
×
UNCOV
234
                                        if (!a)
×
UNCOV
235
                                                a = sym_dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
×
UNCOV
236
                                        if (a)
×
237
                                                symbol = sym_dwarf_formstring(a);
×
UNCOV
238
                                        if (!symbol)
×
UNCOV
239
                                                symbol = sym_dwarf_diename(s);
×
240

UNCOV
241
                                        if (symbol)
×
242
                                                break;
243
                                }
244
                }
245

UNCOV
246
                if (!symbol)
×
247
                        symbol = sym_dwfl_module_addrname(module, pc_adjusted);
40✔
248

249
                fname = sym_dwfl_module_info(module, NULL, &start, NULL, NULL, NULL, NULL, NULL);
40✔
250
                module_offset = pc - start;
40✔
251
        }
252

253
        if (c->m.f)
63✔
254
                fprintf(c->m.f, "#%-2u 0x%016" PRIx64 " %s (%s + 0x%" PRIx64 ")\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname), module_offset);
133✔
255
        c->n_frame++;
63✔
256

257
        return DWARF_CB_OK;
63✔
258
}
259

260
static int thread_callback(Dwfl_Thread *thread, void *userdata) {
9✔
261
        StackContext *c = ASSERT_PTR(userdata);
9✔
262
        pid_t tid;
9✔
263

264
        assert(thread);
9✔
265

266
        if (c->n_thread >= THREADS_MAX)
9✔
267
                return DWARF_CB_ABORT;
268

269
        if (c->n_thread != 0 && c->m.f)
9✔
270
                fputc('\n', c->m.f);
×
271

272
        c->n_frame = 0;
9✔
273

274
        if (c->m.f) {
9✔
275
                tid = sym_dwfl_thread_tid(thread);
9✔
276
                fprintf(c->m.f, "Stack trace of thread " PID_FMT ":\n", tid);
9✔
277
        }
278

279
        if (sym_dwfl_thread_getframes(thread, frame_callback, c) < 0)
9✔
280
                return DWARF_CB_ABORT;
281

UNCOV
282
        c->n_thread++;
×
283

UNCOV
284
        return DWARF_CB_OK;
×
285
}
286

287
static char* build_package_reference(
×
288
                const char *type,
289
                const char *name,
290
                const char *version,
291
                const char *arch) {
292

293
        /* Construct an identifier for a specific version of the package. The syntax is most suitable for
294
         * rpm: the resulting string can be used directly in queries and rpm/dnf/yum commands. For dpkg and
295
         * other systems, it might not be usable directly, but users should still be able to figure out the
296
         * meaning.
297
         */
298

299
        return strjoin(type ?: "package",
×
300
                       " ",
301
                       name,
302

303
                       version ? "-" : "",
304
                       strempty(version),
305

306
                       /* arch is meaningful even without version, so always print it */
307
                       arch ? "." : "",
308
                       strempty(arch));
309
}
310

311
static void report_module_metadata(StackContext *c, const char *name, sd_json_variant *metadata) {
×
312
        assert(c);
×
313
        assert(name);
×
314

315
        if (!c->m.f)
×
316
                return;
317

318
        fprintf(c->m.f, "Module %s", name);
×
319

320
        if (metadata) {
×
321
                const char
×
322
                        *build_id = sd_json_variant_string(sd_json_variant_by_key(metadata, "buildId")),
×
323
                        *type = sd_json_variant_string(sd_json_variant_by_key(metadata, "type")),
×
324
                        *package = sd_json_variant_string(sd_json_variant_by_key(metadata, "name")),
×
325
                        *version = sd_json_variant_string(sd_json_variant_by_key(metadata, "version")),
×
326
                        *arch = sd_json_variant_string(sd_json_variant_by_key(metadata, "architecture"));
×
327

328
                if (package) {
×
329
                        /* Version/architecture is only meaningful with a package name.
330
                         * Skip the detailed fields if package is unknown. */
331
                        _cleanup_free_ char *id = build_package_reference(type, package, version, arch);
×
332
                        fprintf(c->m.f, " from %s", strnull(id));
×
333
                }
334

335
                if (build_id && !(package && version))
×
336
                        fprintf(c->m.f, ", build-id=%s", build_id);
×
337
        }
338

339
        fputs("\n", c->m.f);
×
340
}
341

342
static int parse_package_metadata(const char *name, sd_json_variant *id_json, Elf *elf, bool *ret_interpreter_found, StackContext *c) {
26✔
343
        bool interpreter_found = false;
26✔
344
        size_t n_program_headers;
26✔
345
        int r;
26✔
346

347
        assert(name);
26✔
348
        assert(elf);
26✔
349
        assert(c);
26✔
350

351
        /* When iterating over PT_LOAD we will visit modules more than once */
352
        if (set_contains(*c->modules, name))
26✔
353
                return 0;
26✔
354

355
        r = sym_elf_getphdrnum(elf, &n_program_headers);
26✔
356
        if (r < 0) /* Not the handle we are looking for - that's ok, skip it */
26✔
357
                return 0;
358

359
        /* Iterate over all program headers in that ELF object. These will have been copied by
360
         * the kernel verbatim when the core file is generated. */
361
        for (size_t i = 0; i < n_program_headers; ++i) {
275✔
362
                GElf_Phdr mem, *program_header;
249✔
363
                GElf_Nhdr note_header;
249✔
364
                Elf_Data *data;
249✔
365

366
                /* Package metadata is in PT_NOTE headers. */
367
                program_header = sym_gelf_getphdr(elf, i, &mem);
249✔
368
                if (!program_header || !IN_SET(program_header->p_type, PT_NOTE, PT_INTERP))
249✔
369
                        continue;
206✔
370

371
                if (program_header->p_type == PT_INTERP) {
52✔
372
                        interpreter_found = true;
9✔
373
                        continue;
9✔
374
                }
375

376
                /* Fortunately there is an iterator we can use to walk over the
377
                 * elements of a PT_NOTE program header. We are interested in the
378
                 * note with type. */
379
                data = sym_elf_getdata_rawchunk(elf,
86✔
380
                                                program_header->p_offset,
43✔
381
                                                program_header->p_filesz,
382
                                                ELF_T_NHDR);
383
                if (!data)
43✔
384
                        continue;
×
385

386
                for (size_t note_offset = 0, name_offset, desc_offset;
387
                     note_offset < data->d_size &&
113✔
388
                     (note_offset = sym_gelf_getnote(data, note_offset, &note_header, &name_offset, &desc_offset)) > 0;) {
70✔
389

390
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
70✔
391
                        const char *payload = (const char *)data->d_buf + desc_offset;
70✔
392

393
                        if (note_header.n_namesz == 0 || note_header.n_descsz == 0)
70✔
394
                                continue;
×
395

396
                        /* Package metadata might have different owners, but the
397
                         * magic ID is always the same. */
398
                        if (note_header.n_type != ELF_PACKAGE_METADATA_ID)
70✔
399
                                continue;
70✔
400

401
                        _cleanup_free_ char *payload_0suffixed = NULL;
×
402
                        assert(note_offset > desc_offset);
×
403
                        size_t payload_len = note_offset - desc_offset;
×
404

405
                        /* If we are lucky and the payload is NUL-padded, we don't need to copy the string.
406
                         * But if happens to go all the way until the end of the buffer, make a copy. */
407
                        if (payload[payload_len-1] != '\0') {
×
408
                                payload_0suffixed = memdup_suffix0(payload, payload_len);
×
409
                                if (!payload_0suffixed)
×
410
                                        return log_oom();
×
411
                                payload = payload_0suffixed;
412
                        }
413

414
                        r = sd_json_parse(payload, 0, &v, NULL, NULL);
×
415
                        if (r < 0) {
×
416
                                _cleanup_free_ char *esc = cescape(payload);
×
417
                                return log_error_errno(r, "json_parse on \"%s\" failed: %m", strnull(esc));
×
418
                        }
419

420
                        /* If we have a build-id, merge it in the same JSON object so that it appears all
421
                         * nicely together in the logs/metadata. */
422
                        if (id_json) {
×
423
                                r = sd_json_variant_merge_object(&v, id_json);
×
424
                                if (r < 0)
×
425
                                        return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m");
×
426
                        }
427

428
                        /* Pretty-print to the buffer, so that the metadata goes as plaintext in the
429
                         * journal. */
430
                        report_module_metadata(c, name, v);
×
431

432
                        /* Then we build a new object using the module name as the key, and merge it
433
                         * with the previous parses, so that in the end it all fits together in a single
434
                         * JSON blob. */
435
                        r = sd_json_buildo(&w, SD_JSON_BUILD_PAIR(name, SD_JSON_BUILD_VARIANT(v)));
×
436
                        if (r < 0)
×
437
                                return log_error_errno(r, "Failed to build JSON object: %m");
×
438

439
                        r = sd_json_variant_merge_object(c->package_metadata, w);
×
440
                        if (r < 0)
×
441
                                return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m");
×
442

443
                        /* Finally stash the name, so we avoid double visits. */
444
                        r = set_put_strdup(c->modules, name);
×
445
                        if (r < 0)
×
446
                                return log_error_errno(r, "set_put_strdup failed: %m");
×
447

448
                        if (ret_interpreter_found)
×
449
                                *ret_interpreter_found = interpreter_found;
×
450

451
                        return 1;
452
                }
453
        }
454

455
        if (ret_interpreter_found)
26✔
456
                *ret_interpreter_found = interpreter_found;
1✔
457

458
        /* Didn't find package metadata for this module - that's ok, just go to the next. */
459
        return 0;
460
}
461

462
/* Get the build-id out of an ELF object or a dwarf core module. */
463
static int parse_buildid(Dwfl_Module *mod, Elf *elf, const char *name, StackContext *c, sd_json_variant **ret_id_json) {
26✔
464
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *id_json = NULL;
26✔
465
        const unsigned char *id;
26✔
466
        GElf_Addr id_vaddr;
26✔
467
        ssize_t id_len;
26✔
468
        int r;
26✔
469

470
        assert(mod || elf);
26✔
471
        assert(name);
26✔
472
        assert(c);
26✔
473

474
        if (mod)
26✔
475
                id_len = sym_dwfl_module_build_id(mod, &id, &id_vaddr);
25✔
476
        else
477
                id_len = sym_dwelf_elf_gnu_build_id(elf, (const void **)&id);
1✔
478
        if (id_len <= 0) {
26✔
479
                /* If we don't find a build-id, note it in the journal message, and try
480
                 * anyway to find the package metadata. It's unlikely to have the latter
481
                 * without the former, but there's no hard rule. */
482
                if (c->m.f)
×
483
                        fprintf(c->m.f, "Module %s without build-id.\n", name);
×
484
        } else {
485
                /* We will later parse package metadata json and pass it to our caller. Prepare the
486
                * build-id in json format too, so that it can be appended and parsed cleanly. It
487
                * will then be added as metadata to the journal message with the stack trace. */
488
                r = sd_json_buildo(&id_json, SD_JSON_BUILD_PAIR("buildId", SD_JSON_BUILD_HEX(id, id_len)));
26✔
489
                if (r < 0)
26✔
490
                        return log_error_errno(r, "json_build on buildId failed: %m");
×
491
        }
492

493
        if (ret_id_json)
26✔
494
                *ret_id_json = TAKE_PTR(id_json);
26✔
495

496
        return 0;
497
}
498

499
static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, Dwarf_Addr start, void *arg) {
25✔
500
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *id_json = NULL;
25✔
501
        StackContext *c = ASSERT_PTR(arg);
25✔
502
        size_t n_program_headers;
25✔
503
        GElf_Addr bias;
25✔
504
        int r;
25✔
505
        Elf *elf;
25✔
506

507
        assert(mod);
25✔
508

509
        if (!name)
25✔
510
                name = "(unnamed)"; /* For logging purposes */
×
511

512
        /* We are iterating on each "module", which is what dwfl calls ELF objects contained in the
513
         * core file, and extracting the build-id first and then the package metadata.
514
         * We proceed in a best-effort fashion - not all ELF objects might contain both or either.
515
         * The build-id is easy, as libdwfl parses it during the sym_dwfl_core_file_report() call and
516
         * stores it separately in an internal library struct. */
517
        r = parse_buildid(mod, NULL, name, c, &id_json);
25✔
518
        if (r < 0)
25✔
519
                return DWARF_CB_ABORT;
520

521
        /* The .note.package metadata is more difficult. From the module, we need to get a reference
522
         * to the ELF object first. We might be lucky and just get it from elfutils. */
523
        elf = sym_dwfl_module_getelf(mod, &bias);
25✔
524
        if (elf) {
25✔
525
                r = parse_package_metadata(name, id_json, elf, NULL, c);
25✔
526
                if (r < 0)
25✔
527
                        return DWARF_CB_ABORT;
528
                if (r > 0)
25✔
529
                        return DWARF_CB_OK;
530
        } else
531
                elf = c->elf;
×
532

533
        /* We did not get the ELF object, or it's just a reference to the core. That is likely
534
         * because we didn't get direct access to the executable, and the version of elfutils does
535
         * not yet support parsing it out of the core file directly.
536
         * So fallback to manual extraction - get the PT_LOAD section from the core,
537
         * and if it's the right one we can interpret it as an Elf object, and parse
538
         * its notes manually. */
539

540
        r = sym_elf_getphdrnum(elf, &n_program_headers);
25✔
541
        if (r < 0) {
25✔
542
                log_warning("Could not parse number of program headers from core file: %s",
×
543
                            sym_elf_errmsg(-1)); /* -1 retrieves the most recent error */
544
                report_module_metadata(c, name, id_json);
×
545

546
                return DWARF_CB_OK;
547
        }
548

549
        for (size_t i = 0; i < n_program_headers; ++i) {
261✔
550
                GElf_Phdr mem, *program_header;
236✔
551
                Elf_Data *data;
236✔
552
                GElf_Addr end_of_segment;
236✔
553

554
                /* The core file stores the ELF files in the PT_LOAD segment. */
555
                program_header = sym_gelf_getphdr(elf, i, &mem);
236✔
556
                if (!program_header || program_header->p_type != PT_LOAD)
236✔
557
                        continue;
236✔
558

559
                /* Check that the end of segment is a valid address. */
560
                if (!ADD_SAFE(&end_of_segment, program_header->p_vaddr, program_header->p_memsz)) {
73✔
561
                        log_error("Abort due to corrupted core dump, end of segment address %#zx + %#zx overflows", (size_t)program_header->p_vaddr, (size_t)program_header->p_memsz);
×
562
                        return DWARF_CB_ABORT;
×
563
                }
564

565
                /* This PT_LOAD segment doesn't contain the start address, so it can't be the module we are looking for. */
566
                if (start < program_header->p_vaddr || start >= end_of_segment)
73✔
567
                        continue;
73✔
568

569
                /* Now get a usable Elf reference, and parse the notes from it. */
570
                data = sym_elf_getdata_rawchunk(elf,
×
571
                                                program_header->p_offset,
×
572
                                                program_header->p_filesz,
573
                                                ELF_T_NHDR);
574
                if (!data)
×
575
                        continue;
×
576

577
                _cleanup_(sym_elf_endp) Elf *memelf = sym_elf_memory(data->d_buf, data->d_size);
×
578
                if (!memelf)
×
579
                        continue;
×
580
                r = parse_package_metadata(name, id_json, memelf, NULL, c);
×
581
                if (r < 0)
×
582
                        return DWARF_CB_ABORT;
×
583
                if (r > 0)
×
584
                        break;
585
        }
586

587
        return DWARF_CB_OK;
588
}
589

590
static int parse_core(int fd, const char *root, char **ret, sd_json_variant **ret_package_metadata) {
11✔
591

592
        const Dwfl_Callbacks callbacks = {
11✔
593
                .find_elf = sym_dwfl_build_id_find_elf,
594
                .section_address = sym_dwfl_offline_section_address,
595
                .find_debuginfo = sym_dwfl_standard_find_debuginfo,
596
        };
597

598
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL;
11✔
599
        _cleanup_set_free_ Set *modules = NULL;
11✔
600
        _cleanup_(stack_context_done) StackContext c = {
11✔
601
                .package_metadata = &package_metadata,
602
                .modules = &modules,
603
        };
604
        int r;
11✔
605

606
        assert(fd >= 0);
11✔
607

608
        if (lseek(fd, 0, SEEK_SET) < 0)
11✔
609
                return log_warning_errno(errno, "Failed to seek to beginning of the core file: %m");
×
610

611
        if (ret && !memstream_init(&c.m))
11✔
612
                return log_oom();
×
613

614
        sym_elf_version(EV_CURRENT);
11✔
615

616
        c.elf = sym_elf_begin(fd, ELF_C_READ_MMAP, NULL);
11✔
617
        if (!c.elf)
11✔
618
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, elf_begin() failed: %s", sym_elf_errmsg(sym_elf_errno()));
×
619

620
        c.dwfl = sym_dwfl_begin(&callbacks);
11✔
621
        if (!c.dwfl)
11✔
622
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_begin() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
×
623

624
        if (empty_or_root(root))
11✔
625
                root = NULL;
626
#if HAVE_DWFL_SET_SYSROOT
627
        if (root && sym_dwfl_set_sysroot(c.dwfl, root) < 0)
×
628
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not set root directory, dwfl_set_sysroot() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
×
629
#else
630
        if (root)
631
                log_warning("Compiled without dwfl_set_sysroot() support, ignoring provided root directory.");
632
#endif
633

634
        if (sym_dwfl_core_file_report(c.dwfl, c.elf, NULL) < 0)
11✔
635
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_core_file_report() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
2✔
636

637
        if (sym_dwfl_report_end(c.dwfl, NULL, NULL) != 0)
9✔
638
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_report_end() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
×
639

640
        if (sym_dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0)
9✔
641
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getmodules() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
×
642

643
        if (sym_dwfl_core_file_attach(c.dwfl, c.elf) < 0)
9✔
644
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_core_file_attach() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
×
645

646
        if (sym_dwfl_getthreads(c.dwfl, thread_callback, &c) < 0)
9✔
647
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getthreads() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
×
648

649
        if (ret) {
9✔
650
                r = memstream_finalize(&c.m, ret, NULL);
9✔
651
                if (r < 0)
9✔
652
                        return log_warning_errno(r, "Could not parse core file, flushing file buffer failed: %m");
×
653
        }
654

655
        if (ret_package_metadata)
9✔
656
                *ret_package_metadata = TAKE_PTR(package_metadata);
9✔
657

658
        return 0;
659
}
660

661
static int parse_elf(int fd, const char *executable, const char *root, char **ret, sd_json_variant **ret_package_metadata) {
12✔
662
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *elf_metadata = NULL;
13✔
663
        _cleanup_set_free_ Set *modules = NULL;
12✔
664
        _cleanup_(stack_context_done) StackContext c = {
12✔
665
                .package_metadata = &package_metadata,
666
                .modules = &modules,
667
        };
668
        const char *elf_type;
12✔
669
        GElf_Ehdr elf_header;
12✔
670
        int r;
12✔
671

672
        assert(fd >= 0);
12✔
673

674
        if (lseek(fd, 0, SEEK_SET) < 0)
12✔
675
                return log_warning_errno(errno, "Failed to seek to beginning of the ELF file: %m");
×
676

677
        if (ret && !memstream_init(&c.m))
12✔
678
                return log_oom();
×
679

680
        sym_elf_version(EV_CURRENT);
12✔
681

682
        c.elf = sym_elf_begin(fd, ELF_C_READ_MMAP, NULL);
12✔
683
        if (!c.elf)
12✔
684
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse ELF file, elf_begin() failed: %s", sym_elf_errmsg(sym_elf_errno()));
×
685

686
        if (!sym_gelf_getehdr(c.elf, &elf_header))
12✔
687
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse ELF file, gelf_getehdr() failed: %s", sym_elf_errmsg(sym_elf_errno()));
×
688

689
        if (elf_header.e_type == ET_CORE) {
12✔
690
                _cleanup_free_ char *out = NULL;
11✔
691

692
                r = parse_core(fd, root, ret ? &out : NULL, &package_metadata);
11✔
693
                if (r < 0)
11✔
694
                        return log_warning_errno(r, "Failed to inspect core file: %m");
2✔
695

696
                if (out)
9✔
697
                        fprintf(c.m.f, "%s", out);
9✔
698

699
                elf_type = "coredump";
9✔
700
        } else {
701
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *id_json = NULL;
1✔
702
                const char *e = executable ?: "(unnamed)";
1✔
703
                bool interpreter_found = false;
1✔
704

705
                r = parse_buildid(NULL, c.elf, e, &c, &id_json);
1✔
706
                if (r < 0)
1✔
707
                        return log_warning_errno(r, "Failed to parse build-id of ELF file: %m");
×
708

709
                r = parse_package_metadata(e, id_json, c.elf, &interpreter_found, &c);
1✔
710
                if (r < 0)
1✔
711
                        return log_warning_errno(r, "Failed to parse package metadata of ELF file: %m");
×
712

713
                /* If we found a build-id and nothing else, return at least that. */
714
                if (!package_metadata && id_json) {
1✔
715
                        r = sd_json_buildo(&package_metadata, SD_JSON_BUILD_PAIR(e, SD_JSON_BUILD_VARIANT(id_json)));
1✔
716
                        if (r < 0)
1✔
717
                                return log_warning_errno(r, "Failed to build JSON object: %m");
×
718
                }
719

720
                if (interpreter_found)
1✔
721
                        elf_type = "executable";
722
                else
723
                        elf_type = "library";
×
724
        }
725

726
        /* Note that e_type is always DYN for both executables and libraries, so we can't tell them apart from the header,
727
         * but we will search for the PT_INTERP section when parsing the metadata. */
728
        r = sd_json_buildo(&elf_metadata, SD_JSON_BUILD_PAIR("elfType", SD_JSON_BUILD_STRING(elf_type)));
10✔
729
        if (r < 0)
10✔
730
                return log_warning_errno(r, "Failed to build JSON object: %m");
×
731

732
#if HAVE_DWELF_ELF_E_MACHINE_STRING
733
        const char *elf_architecture = sym_dwelf_elf_e_machine_string(elf_header.e_machine);
10✔
734
        if (elf_architecture) {
10✔
735
                r = sd_json_variant_merge_objectbo(
10✔
736
                                &elf_metadata,
737
                                SD_JSON_BUILD_PAIR("elfArchitecture", SD_JSON_BUILD_STRING(elf_architecture)));
738
                if (r < 0)
10✔
739
                        return log_warning_errno(r, "Failed to add elfArchitecture field: %m");
×
740

741
                if (ret)
10✔
742
                        fprintf(c.m.f, "ELF object binary architecture: %s\n", elf_architecture);
10✔
743
        }
744
#endif
745

746
        /* We always at least have the ELF type, so merge that (and possibly the arch). */
747
        r = sd_json_variant_merge_object(&elf_metadata, package_metadata);
10✔
748
        if (r < 0)
10✔
749
                return log_warning_errno(r, "Failed to merge JSON objects: %m");
×
750

751
        if (ret) {
10✔
752
                r = memstream_finalize(&c.m, ret, NULL);
10✔
753
                if (r < 0)
10✔
754
                        return log_warning_errno(r, "Could not parse ELF file, flushing file buffer failed: %m");
×
755
        }
756

757
        if (ret_package_metadata)
10✔
758
                *ret_package_metadata = TAKE_PTR(elf_metadata);
10✔
759

760
        return 0;
761
}
762

763
int parse_elf_object(int fd, const char *executable, const char *root, bool fork_disable_dump, char **ret, sd_json_variant **ret_package_metadata) {
24✔
764
        _cleanup_close_pair_ int error_pipe[2] = EBADF_PAIR,
12✔
765
                                 return_pipe[2] = EBADF_PAIR,
12✔
766
                                 json_pipe[2] = EBADF_PAIR;
12✔
767
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL;
12✔
768
        _cleanup_free_ char *buf = NULL;
12✔
769
        int r;
24✔
770

771
        assert(fd >= 0);
24✔
772

773
        r = dlopen_dw();
24✔
774
        if (r < 0)
24✔
775
                return r;
776

777
        r = dlopen_elf();
24✔
778
        if (r < 0)
24✔
779
                return r;
780

781
        r = RET_NERRNO(pipe2(error_pipe, O_CLOEXEC|O_NONBLOCK));
24✔
782
        if (r < 0)
×
783
                return r;
784

785
        if (ret) {
24✔
786
                r = RET_NERRNO(pipe2(return_pipe, O_CLOEXEC|O_NONBLOCK));
24✔
787
                if (r < 0)
×
788
                        return r;
789
        }
790

791
        if (ret_package_metadata) {
24✔
792
                r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC|O_NONBLOCK));
24✔
793
                if (r < 0)
×
794
                        return r;
795
        }
796

797
        /* Parsing possibly malformed data is crash-happy, so fork. In case we crash,
798
         * the core file will not be lost, and the messages will still be attached to
799
         * the journal. Reading the elf object might be slow, but it still has an upper
800
         * bound since the core files have an upper size limit. It's also not doing any
801
         * system call or interacting with the system in any way, besides reading from
802
         * the file descriptor and writing into these four pipes. */
803
        r = safe_fork_full("(sd-parse-elf)",
48✔
804
                           NULL,
805
                           (int[]){ fd, error_pipe[1], return_pipe[1], json_pipe[1] },
24✔
806
                           4,
807
                           FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_WAIT|FORK_REOPEN_LOG,
808
                           NULL);
809
        if (r < 0) {
24✔
810
                if (r == -EPROTO) { /* We should have the errno from the child, but don't clobber original error */
2✔
811
                        ssize_t k;
2✔
812
                        int e;
2✔
813

814
                        k = read(error_pipe[0], &e, sizeof(e));
2✔
815
                        if (k < 0 && errno != EAGAIN) /* Pipe is non-blocking, EAGAIN means there's nothing */
2✔
816
                                return -errno;
2✔
817
                        if (k == sizeof(e))
2✔
818
                                return e; /* propagate error sent to us from child */
2✔
819
                        if (k != 0)
×
820
                                return -EIO;
821
                }
822

823
                return r;
×
824
        }
825
        if (r == 0) {
22✔
826
                /* We want to avoid loops, given this can be called from systemd-coredump */
827
                if (fork_disable_dump) {
12✔
828
                        r = RET_NERRNO(prctl(PR_SET_DUMPABLE, 0));
×
829
                        if (r < 0)
×
830
                                report_errno_and_exit(error_pipe[1], r);
×
831
                }
832

833
                r = parse_elf(fd, executable, root, ret ? &buf : NULL, ret_package_metadata ? &package_metadata : NULL);
12✔
834
                if (r < 0)
12✔
835
                        report_errno_and_exit(error_pipe[1], r);
2✔
836

837
                if (buf) {
10✔
838
                        size_t len = strlen(buf);
10✔
839

840
                        if (len > COREDUMP_PIPE_MAX) {
10✔
841
                                /* This is iffy. A backtrace can be a few hundred kilobytes, but too much is
842
                                 * too much. Let's log a warning and ignore the rest. */
843
                                log_warning("Generated backtrace is %zu bytes (more than the limit of %u bytes), backtrace will be truncated.",
×
844
                                            len, COREDUMP_PIPE_MAX);
845
                                len = COREDUMP_PIPE_MAX;
846
                        }
847

848
                        /* Bump the space for the returned string.
849
                         * Failure is ignored, because partial output is still useful. */
850
                        (void) fcntl(return_pipe[1], F_SETPIPE_SZ, len);
10✔
851

852
                        r = loop_write(return_pipe[1], buf, len);
10✔
853
                        if (r == -EAGAIN)
10✔
854
                                log_warning("Write failed, backtrace will be truncated.");
×
855
                        else if (r < 0)
10✔
856
                                report_errno_and_exit(error_pipe[1], r);
×
857

858
                        return_pipe[1] = safe_close(return_pipe[1]);
10✔
859
                }
860

861
                if (package_metadata) {
10✔
862
                        _cleanup_fclose_ FILE *json_out = NULL;
×
863

864
                        /* Bump the space for the returned string. We don't know how much space we'll need in
865
                         * advance, so we'll just try to write as much as possible and maybe fail later. */
866
                        (void) fcntl(json_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX);
10✔
867

868
                        json_out = take_fdopen(&json_pipe[1], "w");
10✔
869
                        if (!json_out)
10✔
870
                                report_errno_and_exit(error_pipe[1], -errno);
×
871

872
                        r = sd_json_variant_dump(package_metadata, SD_JSON_FORMAT_FLUSH, json_out, NULL);
10✔
873
                        if (r < 0)
10✔
874
                                log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m");
10✔
875
                }
876

877
                _exit(EXIT_SUCCESS);
10✔
878
        }
879

880
        error_pipe[1] = safe_close(error_pipe[1]);
10✔
881
        return_pipe[1] = safe_close(return_pipe[1]);
10✔
882
        json_pipe[1] = safe_close(json_pipe[1]);
10✔
883

884
        if (ret) {
10✔
885
                _cleanup_fclose_ FILE *in = NULL;
×
886

887
                in = take_fdopen(&return_pipe[0], "r");
10✔
888
                if (!in)
10✔
889
                        return -errno;
×
890

891
                r = read_full_stream(in, &buf, NULL);
10✔
892
                if (r < 0)
10✔
893
                        return r;
894
        }
895

896
        if (ret_package_metadata) {
10✔
897
                _cleanup_fclose_ FILE *json_in = NULL;
12✔
898

899
                json_in = take_fdopen(&json_pipe[0], "r");
10✔
900
                if (!json_in)
10✔
901
                        return -errno;
×
902

903
                r = sd_json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL);
10✔
904
                if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */
10✔
905
                        log_warning_errno(r, "Failed to read or parse json metadata, ignoring: %m");
10✔
906
        }
907

908
        if (ret)
10✔
909
                *ret = TAKE_PTR(buf);
10✔
910
        if (ret_package_metadata)
10✔
911
                *ret_package_metadata = TAKE_PTR(package_metadata);
10✔
912

913
        return 0;
914
}
915

916
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc