Coverage Report

Created: 2025-11-13 06:36

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