Coverage Report

Created: 2025-11-11 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/elfutils/libdwfl/linux-core-attach.c
Line
Count
Source
1
/* Get Dwarf Frame state for target core file.
2
   Copyright (C) 2013, 2014 Red Hat, Inc.
3
   This file is part of elfutils.
4
5
   This file is free software; you can redistribute it and/or modify
6
   it under the terms of either
7
8
     * the GNU Lesser General Public License as published by the Free
9
       Software Foundation; either version 3 of the License, or (at
10
       your option) any later version
11
12
   or
13
14
     * the GNU General Public License as published by the Free
15
       Software Foundation; either version 2 of the License, or (at
16
       your option) any later version
17
18
   or both in parallel, as here.
19
20
   elfutils is distributed in the hope that it will be useful, but
21
   WITHOUT ANY WARRANTY; without even the implied warranty of
22
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
   General Public License for more details.
24
25
   You should have received copies of the GNU General Public License and
26
   the GNU Lesser General Public License along with this program.  If
27
   not, see <http://www.gnu.org/licenses/>.  */
28
29
#ifdef HAVE_CONFIG_H
30
# include <config.h>
31
#endif
32
33
#include "libdwflP.h"
34
#include <fcntl.h>
35
#include "system.h"
36
37
#include "memory-access.h"
38
39
struct core_arg
40
{
41
  Elf *core;
42
  Elf_Data *note_data;
43
  size_t thread_note_offset;
44
  Ebl *ebl;
45
};
46
47
struct thread_arg
48
{
49
  struct core_arg *core_arg;
50
  size_t note_offset;
51
};
52
53
static bool
54
core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
55
      void *dwfl_arg)
56
0
{
57
0
  Dwfl_Process *process = dwfl->process;
58
0
  struct core_arg *core_arg = dwfl_arg;
59
0
  Elf *core = core_arg->core;
60
0
  assert (core != NULL);
61
0
  static size_t phnum;
62
0
  if (elf_getphdrnum (core, &phnum) < 0)
63
0
    {
64
0
      __libdwfl_seterrno (DWFL_E_LIBELF);
65
0
      return false;
66
0
    }
67
0
  for (size_t cnt = 0; cnt < phnum; ++cnt)
68
0
    {
69
0
      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
70
0
      if (phdr == NULL || phdr->p_type != PT_LOAD)
71
0
  continue;
72
      /* Bias is zero here, a core file itself has no bias.  */
73
0
      GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
74
0
      GElf_Addr end = __libdwfl_segment_end (dwfl,
75
0
               phdr->p_vaddr + phdr->p_memsz);
76
0
      unsigned bytes = ebl_get_elfclass (process->ebl) == ELFCLASS64 ? 8 : 4;
77
0
      if (addr < start || addr + bytes > end)
78
0
  continue;
79
0
      Elf_Data *data;
80
0
      data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
81
0
           bytes, ELF_T_ADDR);
82
0
      if (data == NULL)
83
0
  {
84
0
    __libdwfl_seterrno (DWFL_E_LIBELF);
85
0
    return false;
86
0
  }
87
0
      assert (data->d_size == bytes);
88
0
      if (bytes == 8)
89
0
  *result = read_8ubyte_unaligned_noncvt (data->d_buf);
90
0
      else
91
0
  *result = read_4ubyte_unaligned_noncvt (data->d_buf);
92
0
      return true;
93
0
    }
94
0
  __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
95
0
  return false;
96
0
}
97
98
static pid_t
99
core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
100
      void **thread_argp)
101
0
{
102
0
  struct core_arg *core_arg = dwfl_arg;
103
0
  Elf *core = core_arg->core;
104
0
  GElf_Nhdr nhdr;
105
0
  size_t name_offset;
106
0
  size_t desc_offset;
107
0
  Elf_Data *note_data = core_arg->note_data;
108
0
  size_t offset;
109
110
0
  struct thread_arg *thread_arg;
111
0
  if (*thread_argp == NULL)
112
0
    {
113
0
      core_arg->thread_note_offset = 0;
114
0
      thread_arg = malloc (sizeof (*thread_arg));
115
0
      if (thread_arg == NULL)
116
0
  {
117
0
    __libdwfl_seterrno (DWFL_E_NOMEM);
118
0
    return -1;
119
0
  }
120
0
      thread_arg->core_arg = core_arg;
121
0
      *thread_argp = thread_arg;
122
0
    }
123
0
  else
124
0
    thread_arg = (struct thread_arg *) *thread_argp;
125
126
0
  while (offset = core_arg->thread_note_offset, offset < note_data->d_size
127
0
   && (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
128
0
                &nhdr, &name_offset,
129
0
                &desc_offset)) > 0)
130
0
    {
131
      /* Do not check NAME for now, help broken Linux kernels.  */
132
0
      const char *name = (nhdr.n_namesz == 0
133
0
        ? "" : note_data->d_buf + name_offset);
134
0
      const char *desc = note_data->d_buf + desc_offset;
135
0
      GElf_Word regs_offset;
136
0
      size_t nregloc;
137
0
      const Ebl_Register_Location *reglocs;
138
0
      size_t nitems;
139
0
      const Ebl_Core_Item *items;
140
0
      if (! ebl_core_note (core_arg->ebl, &nhdr, name, desc,
141
0
         &regs_offset, &nregloc, &reglocs, &nitems, &items))
142
0
  {
143
    /* This note may be just not recognized, skip it.  */
144
0
    continue;
145
0
  }
146
0
      if (nhdr.n_type != NT_PRSTATUS)
147
0
  continue;
148
0
      const Ebl_Core_Item *item;
149
0
      for (item = items; item < items + nitems; item++)
150
0
  if (strcmp (item->name, "pid") == 0)
151
0
    break;
152
0
      if (item == items + nitems)
153
0
  continue;
154
0
      uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
155
0
      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
156
0
    ? be32toh (val32) : le32toh (val32));
157
0
      pid_t tid = (int32_t) val32;
158
0
      eu_static_assert (sizeof val32 <= sizeof tid);
159
0
      thread_arg->note_offset = offset;
160
0
      return tid;
161
0
    }
162
163
0
  free (thread_arg);
164
0
  return 0;
165
0
}
166
167
static bool
168
core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
169
0
{
170
0
  struct thread_arg *thread_arg = thread_arg_voidp;
171
0
  struct core_arg *core_arg = thread_arg->core_arg;
172
0
  Elf *core = core_arg->core;
173
0
  size_t offset = thread_arg->note_offset;
174
0
  GElf_Nhdr nhdr;
175
0
  size_t name_offset;
176
0
  size_t desc_offset;
177
0
  Elf_Data *note_data = core_arg->note_data;
178
0
  size_t nregs = ebl_frame_nregs (core_arg->ebl);
179
0
  assert (nregs > 0);
180
0
  assert (offset < note_data->d_size);
181
0
  size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
182
0
             &desc_offset);
183
  /* __libdwfl_attach_state_for_core already verified the note is there.  */
184
0
  if (getnote_err == 0)
185
0
    return false;
186
  /* Do not check NAME for now, help broken Linux kernels.  */
187
0
  const char *name = (nhdr.n_namesz == 0
188
0
          ? "" : note_data->d_buf + name_offset);
189
0
  const char *desc = note_data->d_buf + desc_offset;
190
0
  GElf_Word regs_offset;
191
0
  size_t nregloc;
192
0
  const Ebl_Register_Location *reglocs;
193
0
  size_t nitems;
194
0
  const Ebl_Core_Item *items;
195
0
  int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, desc,
196
0
             &regs_offset, &nregloc, &reglocs,
197
0
             &nitems, &items);
198
  /* __libdwfl_attach_state_for_core already verified the note is there.  */
199
0
  if (core_note_err == 0 || nhdr.n_type != NT_PRSTATUS)
200
0
    return false;
201
0
  const Ebl_Core_Item *item;
202
0
  for (item = items; item < items + nitems; item++)
203
0
    if (strcmp (item->name, "pid") == 0)
204
0
      break;
205
0
  assert (item < items + nitems);
206
0
  pid_t tid;
207
0
  {
208
0
    uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
209
0
    val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
210
0
       ? be32toh (val32) : le32toh (val32));
211
0
    tid = (int32_t) val32;
212
0
    eu_static_assert (sizeof val32 <= sizeof tid);
213
0
  }
214
  /* core_next_thread already found this TID there.  */
215
0
  assert (tid == INTUSE(dwfl_thread_tid) (thread));
216
0
  for (item = items; item < items + nitems; item++)
217
0
    if (item->pc_register)
218
0
      break;
219
0
  if (item < items + nitems)
220
0
    {
221
0
      Dwarf_Word pc;
222
0
      switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
223
0
      {
224
0
  case 32:;
225
0
    uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
226
0
    val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
227
0
       ? be32toh (val32) : le32toh (val32));
228
    /* Do a host width conversion.  */
229
0
    pc = val32;
230
0
    break;
231
0
  case 64:;
232
0
    uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
233
0
    val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
234
0
       ? be64toh (val64) : le64toh (val64));
235
0
    pc = val64;
236
0
    break;
237
0
  default:
238
0
    abort ();
239
0
      }
240
0
      INTUSE(dwfl_thread_state_register_pc) (thread, pc);
241
0
    }
242
0
  desc += regs_offset;
243
0
  for (size_t regloci = 0; regloci < nregloc; regloci++)
244
0
    {
245
0
      const Ebl_Register_Location *regloc = reglocs + regloci;
246
      // Iterate even regs out of NREGS range so that we can find pc_register.
247
0
      if (regloc->bits != 32 && regloc->bits != 64)
248
0
  continue;
249
0
      const char *reg_desc = desc + regloc->offset;
250
0
      for (unsigned regno = regloc->regno;
251
0
     regno < regloc->regno + (regloc->count ?: 1U);
252
0
     regno++)
253
0
  {
254
    /* PPC provides DWARF register 65 irrelevant for
255
       CFI which clashes with register 108 (LR) we need.
256
       LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
257
       FIXME: It depends now on their order in core notes.
258
       FIXME: It uses private function.  */
259
0
    if (regno < nregs
260
0
        && __libdwfl_frame_reg_get (thread->unwound, regno, NULL) == 0)
261
0
      continue;
262
0
    Dwarf_Word val;
263
0
    switch (regloc->bits)
264
0
    {
265
0
      case 32:;
266
0
        uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
267
0
        reg_desc += sizeof val32;
268
0
        val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
269
0
           ? be32toh (val32) : le32toh (val32));
270
        /* Do a host width conversion.  */
271
0
        val = val32;
272
0
        break;
273
0
      case 64:;
274
0
        uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
275
0
        reg_desc += sizeof val64;
276
0
        val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
277
0
           ? be64toh (val64) : le64toh (val64));
278
0
        assert (sizeof (*thread->unwound->regs) == sizeof val64);
279
0
        val = val64;
280
0
        break;
281
0
      default:
282
0
        abort ();
283
0
    }
284
    /* Registers not valid for CFI are just ignored.  */
285
0
    if (regno < nregs)
286
0
      INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
287
0
    if (regloc->pc_register)
288
0
      INTUSE(dwfl_thread_state_register_pc) (thread, val);
289
0
    reg_desc += regloc->pad;
290
0
  }
291
0
    }
292
293
  /* look for any Pointer Authentication code masks on AArch64 machines */
294
0
  GElf_Ehdr ehdr_mem;
295
0
  GElf_Ehdr *ehdr = gelf_getehdr(core, &ehdr_mem);
296
0
  if (ehdr && ehdr->e_machine == EM_AARCH64)
297
0
  {
298
0
    while (offset < note_data->d_size
299
0
           && (offset = gelf_getnote (note_data, offset,
300
0
                  &nhdr, &name_offset, &desc_offset)) > 0)
301
0
    {
302
0
      if (nhdr.n_type != NT_ARM_PAC_MASK)
303
0
        continue;
304
305
0
      name = (nhdr.n_namesz == 0 ? "" : note_data->d_buf + name_offset);
306
0
      desc = note_data->d_buf + desc_offset;
307
0
      core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, desc,
308
0
                       &regs_offset, &nregloc, &reglocs,
309
0
           &nitems, &items);
310
0
      if (!core_note_err)
311
0
        break;
312
313
0
      for (item = items; item < items + nitems; item++)
314
0
        if (strcmp(item->name, "insn_mask") == 0)
315
0
          break;
316
317
0
      if (item == items + nitems)
318
0
        continue;
319
320
0
      uint64_t insn_mask = read_8ubyte_unaligned_noncvt(desc + item->offset);
321
0
      INTUSE(dwfl_thread_state_registers)(thread, -2, 1, &insn_mask);
322
0
      break;
323
0
    }
324
0
  }
325
326
0
  return true;
327
0
}
328
329
static void
330
core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
331
66
{
332
66
  struct core_arg *core_arg = dwfl_arg;
333
66
  ebl_closebackend (core_arg->ebl);
334
66
  free (core_arg);
335
66
}
336
337
static const Dwfl_Thread_Callbacks core_thread_callbacks =
338
{
339
  core_next_thread,
340
  NULL, /* get_thread */
341
  core_memory_read,
342
  core_set_initial_registers,
343
  core_detach,
344
  NULL, /* core_thread_detach */
345
};
346
347
int
348
dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
349
15.9k
{
350
15.9k
  Dwfl_Error err = DWFL_E_NOERROR;
351
15.9k
  Ebl *ebl = ebl_openbackend (core);
352
15.9k
  if (ebl == NULL)
353
0
    {
354
0
      err = DWFL_E_LIBEBL;
355
15.8k
    fail_err:
356
15.8k
      if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
357
15.8k
  dwfl->attacherr = __libdwfl_canon_error (err);
358
15.8k
      __libdwfl_seterrno (err);
359
15.8k
      return -1;
360
0
    }
361
15.9k
  size_t nregs = ebl_frame_nregs (ebl);
362
15.9k
  if (nregs == 0)
363
8.94k
    {
364
8.94k
      err = DWFL_E_NO_UNWIND;
365
15.8k
    fail:
366
15.8k
      ebl_closebackend (ebl);
367
15.8k
      goto fail_err;
368
8.94k
    }
369
7.01k
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
370
7.01k
  if (ehdr == NULL)
371
0
    {
372
0
      err = DWFL_E_LIBELF;
373
0
      goto fail;
374
0
    }
375
7.01k
  if (ehdr->e_type != ET_CORE)
376
2.40k
    {
377
2.40k
      err = DWFL_E_NO_CORE_FILE;
378
2.40k
      goto fail;
379
2.40k
    }
380
4.60k
  size_t phnum;
381
4.60k
  if (elf_getphdrnum (core, &phnum) < 0)
382
0
    {
383
0
      err = DWFL_E_LIBELF;
384
0
      goto fail;
385
0
    }
386
4.60k
  pid_t pid = -1;
387
4.60k
  Elf_Data *note_data = NULL;
388
11.1k
  for (size_t cnt = 0; cnt < phnum; ++cnt)
389
11.0k
    {
390
11.0k
      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
391
11.0k
      if (phdr != NULL && phdr->p_type == PT_NOTE)
392
4.43k
  {
393
4.43k
    note_data = elf_getdata_rawchunk (core, phdr->p_offset,
394
4.43k
              phdr->p_filesz, (phdr->p_align == 8
395
4.43k
                   ? ELF_T_NHDR8
396
4.43k
                   : ELF_T_NHDR));
397
4.43k
    break;
398
4.43k
  }
399
11.0k
    }
400
4.60k
  if (note_data == NULL)
401
474
    {
402
474
      err = DWFL_E_LIBELF;
403
474
      goto fail;
404
474
    }
405
4.13k
  size_t offset = 0;
406
4.13k
  GElf_Nhdr nhdr;
407
4.13k
  size_t name_offset;
408
4.13k
  size_t desc_offset;
409
175k
  while (offset < note_data->d_size
410
173k
   && (offset = gelf_getnote (note_data, offset,
411
173k
            &nhdr, &name_offset, &desc_offset)) > 0)
412
171k
    {
413
      /* Do not check NAME for now, help broken Linux kernels.  */
414
171k
      const char *name = (nhdr.n_namesz == 0
415
171k
        ? "" : note_data->d_buf + name_offset);
416
171k
      const char *desc = note_data->d_buf + desc_offset;
417
171k
      GElf_Word regs_offset;
418
171k
      size_t nregloc;
419
171k
      const Ebl_Register_Location *reglocs;
420
171k
      size_t nitems;
421
171k
      const Ebl_Core_Item *items;
422
171k
      if (! ebl_core_note (ebl, &nhdr, name, desc,
423
171k
         &regs_offset, &nregloc, &reglocs, &nitems, &items))
424
169k
  {
425
    /* This note may be just not recognized, skip it.  */
426
169k
    continue;
427
169k
  }
428
2.31k
      if (nhdr.n_type != NT_PRPSINFO)
429
2.24k
  continue;
430
66
      const Ebl_Core_Item *item;
431
528
      for (item = items; item < items + nitems; item++)
432
528
  if (strcmp (item->name, "pid") == 0)
433
66
    break;
434
66
      if (item == items + nitems)
435
0
  continue;
436
66
      uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
437
66
      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
438
66
    ? be32toh (val32) : le32toh (val32));
439
66
      pid = (int32_t) val32;
440
66
      eu_static_assert (sizeof val32 <= sizeof pid);
441
66
      break;
442
66
    }
443
4.13k
  if (pid == -1)
444
4.06k
    {
445
      /* No valid NT_PRPSINFO recognized in this CORE.  */
446
4.06k
      err = DWFL_E_BADELF;
447
4.06k
      goto fail;
448
4.06k
    }
449
66
  struct core_arg *core_arg = malloc (sizeof *core_arg);
450
66
  if (core_arg == NULL)
451
0
    {
452
0
      err = DWFL_E_NOMEM;
453
0
      goto fail;
454
0
    }
455
66
  core_arg->core = core;
456
66
  core_arg->note_data = note_data;
457
66
  core_arg->thread_note_offset = 0;
458
66
  core_arg->ebl = ebl;
459
66
  if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
460
66
           core_arg))
461
0
    {
462
0
      free (core_arg);
463
0
      ebl_closebackend (ebl);
464
0
      return -1;
465
0
    }
466
66
  return pid;
467
66
}
468
INTDEF (dwfl_core_file_attach)