Coverage Report

Created: 2025-11-24 06:43

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
68
{
332
68
  struct core_arg *core_arg = dwfl_arg;
333
68
  ebl_closebackend (core_arg->ebl);
334
68
  free (core_arg);
335
68
}
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
16.2k
{
350
16.2k
  Dwfl_Error err = DWFL_E_NOERROR;
351
16.2k
  Ebl *ebl = ebl_openbackend (core);
352
16.2k
  if (ebl == NULL)
353
0
    {
354
0
      err = DWFL_E_LIBEBL;
355
16.1k
    fail_err:
356
16.1k
      if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
357
16.1k
  dwfl->attacherr = __libdwfl_canon_error (err);
358
16.1k
      __libdwfl_seterrno (err);
359
16.1k
      return -1;
360
0
    }
361
16.2k
  size_t nregs = ebl_frame_nregs (ebl);
362
16.2k
  if (nregs == 0)
363
8.95k
    {
364
8.95k
      err = DWFL_E_NO_UNWIND;
365
16.1k
    fail:
366
16.1k
      ebl_closebackend (ebl);
367
16.1k
      goto fail_err;
368
8.95k
    }
369
7.26k
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
370
7.26k
  if (ehdr == NULL)
371
0
    {
372
0
      err = DWFL_E_LIBELF;
373
0
      goto fail;
374
0
    }
375
7.26k
  if (ehdr->e_type != ET_CORE)
376
2.50k
    {
377
2.50k
      err = DWFL_E_NO_CORE_FILE;
378
2.50k
      goto fail;
379
2.50k
    }
380
4.76k
  size_t phnum;
381
4.76k
  if (elf_getphdrnum (core, &phnum) < 0)
382
0
    {
383
0
      err = DWFL_E_LIBELF;
384
0
      goto fail;
385
0
    }
386
4.76k
  pid_t pid = -1;
387
4.76k
  Elf_Data *note_data = NULL;
388
11.0k
  for (size_t cnt = 0; cnt < phnum; ++cnt)
389
10.8k
    {
390
10.8k
      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
391
10.8k
      if (phdr != NULL && phdr->p_type == PT_NOTE)
392
4.56k
  {
393
4.56k
    note_data = elf_getdata_rawchunk (core, phdr->p_offset,
394
4.56k
              phdr->p_filesz, (phdr->p_align == 8
395
4.56k
                   ? ELF_T_NHDR8
396
4.56k
                   : ELF_T_NHDR));
397
4.56k
    break;
398
4.56k
  }
399
10.8k
    }
400
4.76k
  if (note_data == NULL)
401
508
    {
402
508
      err = DWFL_E_LIBELF;
403
508
      goto fail;
404
508
    }
405
4.25k
  size_t offset = 0;
406
4.25k
  GElf_Nhdr nhdr;
407
4.25k
  size_t name_offset;
408
4.25k
  size_t desc_offset;
409
154k
  while (offset < note_data->d_size
410
152k
   && (offset = gelf_getnote (note_data, offset,
411
152k
            &nhdr, &name_offset, &desc_offset)) > 0)
412
150k
    {
413
      /* Do not check NAME for now, help broken Linux kernels.  */
414
150k
      const char *name = (nhdr.n_namesz == 0
415
150k
        ? "" : note_data->d_buf + name_offset);
416
150k
      const char *desc = note_data->d_buf + desc_offset;
417
150k
      GElf_Word regs_offset;
418
150k
      size_t nregloc;
419
150k
      const Ebl_Register_Location *reglocs;
420
150k
      size_t nitems;
421
150k
      const Ebl_Core_Item *items;
422
150k
      if (! ebl_core_note (ebl, &nhdr, name, desc,
423
150k
         &regs_offset, &nregloc, &reglocs, &nitems, &items))
424
148k
  {
425
    /* This note may be just not recognized, skip it.  */
426
148k
    continue;
427
148k
  }
428
2.35k
      if (nhdr.n_type != NT_PRPSINFO)
429
2.28k
  continue;
430
68
      const Ebl_Core_Item *item;
431
544
      for (item = items; item < items + nitems; item++)
432
544
  if (strcmp (item->name, "pid") == 0)
433
68
    break;
434
68
      if (item == items + nitems)
435
0
  continue;
436
68
      uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
437
68
      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
438
68
    ? be32toh (val32) : le32toh (val32));
439
68
      pid = (int32_t) val32;
440
68
      eu_static_assert (sizeof val32 <= sizeof pid);
441
68
      break;
442
68
    }
443
4.25k
  if (pid == -1)
444
4.18k
    {
445
      /* No valid NT_PRPSINFO recognized in this CORE.  */
446
4.18k
      err = DWFL_E_BADELF;
447
4.18k
      goto fail;
448
4.18k
    }
449
68
  struct core_arg *core_arg = malloc (sizeof *core_arg);
450
68
  if (core_arg == NULL)
451
0
    {
452
0
      err = DWFL_E_NOMEM;
453
0
      goto fail;
454
0
    }
455
68
  core_arg->core = core;
456
68
  core_arg->note_data = note_data;
457
68
  core_arg->thread_note_offset = 0;
458
68
  core_arg->ebl = ebl;
459
68
  if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
460
68
           core_arg))
461
0
    {
462
0
      free (core_arg);
463
0
      ebl_closebackend (ebl);
464
0
      return -1;
465
0
    }
466
68
  return pid;
467
68
}
468
INTDEF (dwfl_core_file_attach)