Coverage Report

Created: 2026-09-03 06:53

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
0
  return true;
293
0
}
294
295
static void
296
core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
297
82
{
298
82
  struct core_arg *core_arg = dwfl_arg;
299
82
  ebl_closebackend (core_arg->ebl);
300
82
  free (core_arg);
301
82
}
302
303
static const Dwfl_Thread_Callbacks core_thread_callbacks =
304
{
305
  core_next_thread,
306
  NULL, /* get_thread */
307
  core_memory_read,
308
  core_set_initial_registers,
309
  core_detach,
310
  NULL, /* core_thread_detach */
311
};
312
313
int
314
dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
315
17.5k
{
316
17.5k
  Dwfl_Error err = DWFL_E_NOERROR;
317
17.5k
  Ebl *ebl = ebl_openbackend (core);
318
17.5k
  if (ebl == NULL)
319
0
    {
320
0
      err = DWFL_E_LIBEBL;
321
17.4k
    fail_err:
322
17.4k
      if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
323
17.4k
  dwfl->attacherr = __libdwfl_canon_error (err);
324
17.4k
      __libdwfl_seterrno (err);
325
17.4k
      return -1;
326
0
    }
327
17.5k
  size_t nregs = ebl_frame_nregs (ebl);
328
17.5k
  if (nregs == 0)
329
10.1k
    {
330
10.1k
      err = DWFL_E_NO_UNWIND;
331
17.4k
    fail:
332
17.4k
      ebl_closebackend (ebl);
333
17.4k
      goto fail_err;
334
10.1k
    }
335
7.39k
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
336
7.39k
  if (ehdr == NULL)
337
0
    {
338
0
      err = DWFL_E_LIBELF;
339
0
      goto fail;
340
0
    }
341
7.39k
  if (ehdr->e_type != ET_CORE)
342
2.53k
    {
343
2.53k
      err = DWFL_E_NO_CORE_FILE;
344
2.53k
      goto fail;
345
2.53k
    }
346
4.86k
  size_t phnum;
347
4.86k
  if (elf_getphdrnum (core, &phnum) < 0)
348
0
    {
349
0
      err = DWFL_E_LIBELF;
350
0
      goto fail;
351
0
    }
352
4.86k
  pid_t pid = -1;
353
4.86k
  Elf_Data *note_data = NULL;
354
10.1k
  for (size_t cnt = 0; cnt < phnum; ++cnt)
355
9.97k
    {
356
9.97k
      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
357
9.97k
      if (phdr != NULL && phdr->p_type == PT_NOTE)
358
4.70k
  {
359
4.70k
    note_data = elf_getdata_rawchunk (core, phdr->p_offset,
360
4.70k
              phdr->p_filesz, (phdr->p_align == 8
361
4.70k
                   ? ELF_T_NHDR8
362
4.70k
                   : ELF_T_NHDR));
363
4.70k
    break;
364
4.70k
  }
365
9.97k
    }
366
4.86k
  if (note_data == NULL)
367
471
    {
368
471
      err = DWFL_E_LIBELF;
369
471
      goto fail;
370
471
    }
371
4.39k
  size_t offset = 0;
372
4.39k
  GElf_Nhdr nhdr;
373
4.39k
  size_t name_offset;
374
4.39k
  size_t desc_offset;
375
125k
  while (offset < note_data->d_size
376
123k
   && (offset = gelf_getnote (note_data, offset,
377
123k
            &nhdr, &name_offset, &desc_offset)) > 0)
378
121k
    {
379
      /* Do not check NAME for now, help broken Linux kernels.  */
380
121k
      const char *name = (nhdr.n_namesz == 0
381
121k
        ? "" : note_data->d_buf + name_offset);
382
121k
      const char *desc = note_data->d_buf + desc_offset;
383
121k
      GElf_Word regs_offset;
384
121k
      size_t nregloc;
385
121k
      const Ebl_Register_Location *reglocs;
386
121k
      size_t nitems;
387
121k
      const Ebl_Core_Item *items;
388
121k
      if (! ebl_core_note (ebl, &nhdr, name, desc,
389
121k
         &regs_offset, &nregloc, &reglocs, &nitems, &items))
390
117k
  {
391
    /* This note may be just not recognized, skip it.  */
392
117k
    continue;
393
117k
  }
394
3.98k
      if (nhdr.n_type != NT_PRPSINFO)
395
3.89k
  continue;
396
82
      const Ebl_Core_Item *item;
397
656
      for (item = items; item < items + nitems; item++)
398
656
  if (strcmp (item->name, "pid") == 0)
399
82
    break;
400
82
      if (item == items + nitems)
401
0
  continue;
402
82
      uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
403
82
      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
404
82
    ? be32toh (val32) : le32toh (val32));
405
82
      pid = (int32_t) val32;
406
82
      eu_static_assert (sizeof val32 <= sizeof pid);
407
82
      break;
408
82
    }
409
4.39k
  if (pid == -1)
410
4.31k
    {
411
      /* No valid NT_PRPSINFO recognized in this CORE.  */
412
4.31k
      err = DWFL_E_BADELF;
413
4.31k
      goto fail;
414
4.31k
    }
415
82
  struct core_arg *core_arg = malloc (sizeof *core_arg);
416
82
  if (core_arg == NULL)
417
0
    {
418
0
      err = DWFL_E_NOMEM;
419
0
      goto fail;
420
0
    }
421
82
  core_arg->core = core;
422
82
  core_arg->note_data = note_data;
423
82
  core_arg->thread_note_offset = 0;
424
82
  core_arg->ebl = ebl;
425
82
  if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
426
82
           core_arg))
427
0
    {
428
0
      free (core_arg);
429
0
      ebl_closebackend (ebl);
430
0
      return -1;
431
0
    }
432
82
  return pid;
433
82
}
434
INTDEF (dwfl_core_file_attach)