Coverage Report

Created: 2025-11-06 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/elfutils/libdwfl/dwfl_frame.c
Line
Count
Source
1
/* Get Dwarf Frame state for target PID or core file.
2
   Copyright (C) 2013, 2014, 2024-2025 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 <system.h>
34
35
#include "libdwflP.h"
36
#include "libdwfl_stacktraceP.h"
37
38
/* Set STATE->pc_set from STATE->regs according to the backend.  Return true on
39
   success, false on error.  */
40
static bool
41
state_fetch_pc (Dwfl_Frame *state)
42
0
{
43
0
  switch (state->pc_state)
44
0
    {
45
0
    case DWFL_FRAME_STATE_PC_SET:
46
0
      return true;
47
0
    case DWFL_FRAME_STATE_PC_UNDEFINED:
48
0
      abort ();
49
0
    case DWFL_FRAME_STATE_ERROR:
50
0
      {
51
0
  Ebl *ebl = state->thread->process->ebl;
52
0
  Dwarf_CIE abi_info;
53
0
  if (ebl_abi_cfi (ebl, &abi_info) != 0)
54
0
    {
55
0
      __libdwfl_seterrno (DWFL_E_LIBEBL);
56
0
      return false;
57
0
    }
58
0
  unsigned ra = abi_info.return_address_register;
59
  /* dwarf_frame_state_reg_is_set is not applied here.  */
60
0
  if (ra >= ebl_frame_nregs (ebl))
61
0
    {
62
0
      __libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
63
0
      return false;
64
0
    }
65
0
  state->pc = state->regs[ra] + ebl_ra_offset (ebl);
66
0
  state->pc_state = DWFL_FRAME_STATE_PC_SET;
67
0
      }
68
0
      return true;
69
0
    }
70
0
  abort ();
71
0
}
72
73
/* Do not call it on your own, to be used by thread_* functions only.  */
74
75
static void
76
free_states (Dwfl_Frame *state)
77
0
{
78
0
  while (state)
79
0
    {
80
0
      Dwfl_Frame *next = state->unwound;
81
0
      free(state);
82
0
      state = next;
83
0
    }
84
0
}
85
86
static Dwfl_Frame *
87
state_alloc (Dwfl_Thread *thread)
88
0
{
89
0
  assert (thread->unwound == NULL);
90
0
  Ebl *ebl = thread->process->ebl;
91
0
  size_t nregs = ebl_frame_nregs (ebl);
92
0
  if (nregs == 0)
93
0
    return NULL;
94
0
  assert (nregs < sizeof (((Dwfl_Frame *) NULL)->regs_set) * 8);
95
0
  Dwfl_Frame *state = malloc (sizeof (*state) + sizeof (*state->regs) * nregs);
96
0
  if (state == NULL)
97
0
    return NULL;
98
0
  state->thread = thread;
99
0
  state->signal_frame = false;
100
0
  state->initial_frame = true;
101
0
  state->pc_state = DWFL_FRAME_STATE_ERROR;
102
0
  state->unwound_source = DWFL_UNWOUND_INITIAL_FRAME;
103
0
  memset (state->regs_set, 0, sizeof (state->regs_set));
104
0
  thread->unwound = state;
105
0
  state->unwound = NULL;
106
0
  return state;
107
0
}
108
109
void
110
internal_function
111
__libdwfl_process_free (Dwfl_Process *process)
112
67
{
113
67
  Dwfl *dwfl = process->dwfl;
114
67
  if (process->callbacks->detach != NULL)
115
67
    process->callbacks->detach (dwfl, process->callbacks_arg);
116
67
  assert (dwfl->process == process);
117
67
  dwfl->process = NULL;
118
67
  if (process->ebl_close)
119
67
    ebl_closebackend (process->ebl);
120
67
  free (process);
121
67
  dwfl->attacherr = DWFL_E_NOERROR;
122
67
}
123
124
/* Allocate new Dwfl_Process for DWFL.  */
125
static void
126
process_alloc (Dwfl *dwfl)
127
67
{
128
67
  Dwfl_Process *process = malloc (sizeof (*process));
129
67
  if (process == NULL)
130
0
    return;
131
67
  process->dwfl = dwfl;
132
67
  dwfl->process = process;
133
67
}
134
135
bool
136
dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid,
137
       const Dwfl_Thread_Callbacks *thread_callbacks, void *arg)
138
67
{
139
67
  if (dwfl->process != NULL)
140
0
    {
141
0
      __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
142
0
      return false;
143
0
    }
144
145
  /* Reset any previous error, we are just going to try again.  */
146
67
  dwfl->attacherr = DWFL_E_NOERROR;
147
  /* thread_callbacks is declared NN */
148
67
  if (thread_callbacks->next_thread == NULL
149
67
      || thread_callbacks->set_initial_registers == NULL)
150
0
    {
151
0
      dwfl->attacherr = DWFL_E_INVALID_ARGUMENT;
152
0
    fail:
153
0
      dwfl->attacherr = __libdwfl_canon_error (dwfl->attacherr);
154
0
      __libdwfl_seterrno (dwfl->attacherr);
155
0
      return false;
156
0
    }
157
158
67
  Ebl *ebl;
159
67
  bool ebl_close;
160
67
  if (elf != NULL)
161
67
    {
162
67
      ebl = ebl_openbackend (elf);
163
67
      ebl_close = true;
164
67
    }
165
0
  else
166
0
    {
167
0
      ebl = NULL;
168
0
      for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
169
0
  {
170
    /* Reading of the vDSO or (deleted) modules may fail as
171
       /proc/PID/mem is unreadable without PTRACE_ATTACH and
172
       we may not be PTRACE_ATTACH-ed now.  MOD would not be
173
       re-read later to unwind it when we are already
174
       PTRACE_ATTACH-ed to PID.  This happens when this function
175
       is called from dwfl_linux_proc_attach with elf == NULL.
176
       __libdwfl_module_getebl will call __libdwfl_getelf which
177
       will call the find_elf callback.  */
178
0
    if (startswith (mod->name, "[vdso: ")
179
0
        || strcmp (strrchr (mod->name, ' ') ?: "",
180
0
       " (deleted)") == 0)
181
0
      continue;
182
0
    Dwfl_Error error = __libdwfl_module_getebl (mod);
183
0
    if (error != DWFL_E_NOERROR)
184
0
      continue;
185
0
    ebl = mod->ebl;
186
0
    break;
187
0
  }
188
0
      ebl_close = false;
189
0
    }
190
67
  if (ebl == NULL)
191
0
    {
192
      /* Not identified EBL from any of the modules.  */
193
0
      dwfl->attacherr = DWFL_E_PROCESS_NO_ARCH;
194
0
      goto fail;
195
0
    }
196
67
  process_alloc (dwfl);
197
67
  Dwfl_Process *process = dwfl->process;
198
67
  if (process == NULL)
199
0
    {
200
0
      if (ebl_close)
201
0
  ebl_closebackend (ebl);
202
0
      dwfl->attacherr = DWFL_E_NOMEM;
203
0
      goto fail;
204
0
    }
205
67
  process->ebl = ebl;
206
67
  process->ebl_close = ebl_close;
207
67
  process->pid = pid;
208
67
  process->callbacks = thread_callbacks;
209
67
  process->callbacks_arg = arg;
210
211
67
  if (dwfl->tracker != NULL)
212
0
    __libdwfl_stacktrace_add_dwfl_to_tracker (dwfl);
213
214
67
  return true;
215
67
}
216
INTDEF(dwfl_attach_state)
217
218
pid_t
219
dwfl_pid (Dwfl *dwfl)
220
0
{
221
0
  if (dwfl->attacherr != DWFL_E_NOERROR)
222
0
    {
223
0
      __libdwfl_seterrno (dwfl->attacherr);
224
0
      return -1;
225
0
    }
226
227
0
  if (dwfl->process == NULL)
228
0
    {
229
0
      __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
230
0
      return -1;
231
0
    }
232
0
  return dwfl->process->pid;
233
0
}
234
INTDEF(dwfl_pid)
235
236
Dwfl *
237
dwfl_thread_dwfl (Dwfl_Thread *thread)
238
0
{
239
0
  return thread->process->dwfl;
240
0
}
241
INTDEF(dwfl_thread_dwfl)
242
243
pid_t
244
dwfl_thread_tid (Dwfl_Thread *thread)
245
0
{
246
0
  return thread->tid;
247
0
}
248
INTDEF(dwfl_thread_tid)
249
250
Dwfl_Thread *
251
dwfl_frame_thread (Dwfl_Frame *state)
252
0
{
253
0
  return state->thread;
254
0
}
255
INTDEF(dwfl_frame_thread)
256
257
Dwfl_Unwound_Source
258
dwfl_frame_unwound_source (Dwfl_Frame *state)
259
0
{
260
0
  return state->unwound_source;
261
0
}
262
INTDEF(dwfl_frame_unwound_source)
263
264
const char *
265
dwfl_unwound_source_str (Dwfl_Unwound_Source unwound_source)
266
0
{
267
0
  switch (unwound_source)
268
0
    {
269
0
    case DWFL_UNWOUND_NONE:
270
0
      return "none";
271
0
    case DWFL_UNWOUND_INITIAL_FRAME:
272
0
      return "initial";
273
0
    case DWFL_UNWOUND_EH_CFI:
274
0
      return "eh_frame";
275
0
    case DWFL_UNWOUND_DWARF_CFI:
276
0
      return "dwarf";
277
0
    case DWFL_UNWOUND_EBL:
278
0
      return "ebl";
279
0
    default:
280
0
      return "unknown";
281
0
    }
282
0
}
283
INTDEF(dwfl_unwound_source_str)
284
285
int
286
dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg),
287
     void *arg)
288
0
{
289
0
  if (dwfl->attacherr != DWFL_E_NOERROR)
290
0
    {
291
0
      __libdwfl_seterrno (dwfl->attacherr);
292
0
      return -1;
293
0
    }
294
295
0
  Dwfl_Process *process = dwfl->process;
296
0
  if (process == NULL)
297
0
    {
298
0
      __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
299
0
      return -1;
300
0
    }
301
302
0
  Dwfl_Thread thread;
303
0
  thread.process = process;
304
0
  thread.unwound = NULL;
305
0
  thread.callbacks_arg = NULL;
306
0
  thread.aarch64.pauth_insn_mask = 0;
307
308
0
  for (;;)
309
0
    {
310
0
      thread.tid = process->callbacks->next_thread (dwfl,
311
0
                process->callbacks_arg,
312
0
                &thread.callbacks_arg);
313
0
      if (thread.tid < 0)
314
0
  return -1;
315
0
      if (thread.tid == 0)
316
0
  {
317
0
    __libdwfl_seterrno (DWFL_E_NOERROR);
318
0
    return 0;
319
0
  }
320
0
      int err = callback (&thread, arg);
321
0
      if (err != DWARF_CB_OK)
322
0
  return err;
323
0
      assert (thread.unwound == NULL);
324
0
    }
325
  /* NOTREACHED */
326
0
}
327
INTDEF(dwfl_getthreads)
328
329
struct one_arg
330
{
331
  pid_t tid;
332
  bool seen;
333
  int (*callback) (Dwfl_Thread *thread, void *arg);
334
  void *arg;
335
  int ret;
336
};
337
338
static int
339
get_one_thread_cb (Dwfl_Thread *thread, void *arg)
340
0
{
341
0
  struct one_arg *oa = (struct one_arg *) arg;
342
0
  if (! oa->seen && INTUSE(dwfl_thread_tid) (thread) == oa->tid)
343
0
    {
344
0
      oa->seen = true;
345
0
      oa->ret = oa->callback (thread, oa->arg);
346
0
      return DWARF_CB_ABORT;
347
0
    }
348
349
0
  return DWARF_CB_OK;
350
0
}
351
352
/* Note not currently exported, will be when there are more Dwfl_Thread
353
   properties to query.  Use dwfl_getthread_frames for now directly.  */
354
static int
355
getthread (Dwfl *dwfl, pid_t tid,
356
     int (*callback) (Dwfl_Thread *thread, void *arg),
357
     void *arg)
358
0
{
359
0
  if (dwfl->attacherr != DWFL_E_NOERROR)
360
0
    {
361
0
      __libdwfl_seterrno (dwfl->attacherr);
362
0
      return -1;
363
0
    }
364
365
0
  Dwfl_Process *process = dwfl->process;
366
0
  if (process == NULL)
367
0
    {
368
0
      __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
369
0
      return -1;
370
0
    }
371
372
0
  if (process->callbacks->get_thread != NULL)
373
0
    {
374
0
      Dwfl_Thread thread;
375
0
      thread.process = process;
376
0
      thread.unwound = NULL;
377
0
      thread.callbacks_arg = NULL;
378
0
      thread.aarch64.pauth_insn_mask = 0;
379
380
0
      if (process->callbacks->get_thread (dwfl, tid, process->callbacks_arg,
381
0
            &thread.callbacks_arg))
382
0
  {
383
0
    thread.tid = tid;
384
0
    return callback (&thread, arg);
385
0
  }
386
387
0
      return -1;
388
0
    }
389
390
0
   struct one_arg oa = { .tid = tid, .callback = callback,
391
0
       .arg = arg, .seen = false };
392
0
   int err = INTUSE(dwfl_getthreads) (dwfl, get_one_thread_cb, &oa);
393
394
0
   if (err == DWARF_CB_ABORT && oa.seen)
395
0
     return oa.ret;
396
397
0
   if (err == DWARF_CB_OK && ! oa.seen)
398
0
     {
399
0
  errno = ESRCH;
400
0
  __libdwfl_seterrno (DWFL_E_ERRNO);
401
0
  return -1;
402
0
     }
403
404
0
   return err;
405
0
}
406
407
struct one_thread
408
{
409
  int (*callback) (Dwfl_Frame *frame, void *arg);
410
  void *arg;
411
};
412
413
static int
414
get_one_thread_frames_cb (Dwfl_Thread *thread, void *arg)
415
0
{
416
0
  struct one_thread *ot = (struct one_thread *) arg;
417
0
  return INTUSE(dwfl_thread_getframes) (thread, ot->callback, ot->arg);
418
0
}
419
420
int
421
dwfl_getthread_frames (Dwfl *dwfl, pid_t tid,
422
           int (*callback) (Dwfl_Frame *frame, void *arg),
423
           void *arg)
424
0
{
425
0
  struct one_thread ot = { .callback = callback, .arg = arg };
426
0
  return getthread (dwfl, tid, get_one_thread_frames_cb, &ot);
427
0
}
428
INTDEF(dwfl_getthread_frames)
429
430
int
431
dwfl_thread_getframes (Dwfl_Thread *thread,
432
           int (*callback) (Dwfl_Frame *state, void *arg),
433
           void *arg)
434
0
{
435
0
  Ebl *ebl = thread->process->ebl;
436
0
  if (ebl_frame_nregs (ebl) == 0)
437
0
    {
438
0
      __libdwfl_seterrno (DWFL_E_NO_UNWIND);
439
0
      return -1;
440
0
    }
441
0
  if (state_alloc (thread) == NULL)
442
0
    {
443
0
      __libdwfl_seterrno (DWFL_E_NOMEM);
444
0
      return -1;
445
0
    }
446
0
  Dwfl_Process *process = thread->process;
447
0
  if (! process->callbacks->set_initial_registers (thread,
448
0
               thread->callbacks_arg))
449
0
    {
450
0
      free_states (thread->unwound);
451
0
      thread->unwound = NULL;
452
0
      return -1;
453
0
    }
454
0
  Dwfl_Frame *state = thread->unwound;
455
0
  thread->unwound = NULL;
456
0
  if (! state_fetch_pc (state))
457
0
    {
458
0
      if (process->callbacks->thread_detach)
459
0
  process->callbacks->thread_detach (thread, thread->callbacks_arg);
460
0
      free_states (state);
461
0
      return -1;
462
0
    }
463
0
  do
464
0
    {
465
0
      int err = callback (state, arg);
466
0
      if (err != DWARF_CB_OK)
467
0
  {
468
0
    if (process->callbacks->thread_detach)
469
0
      process->callbacks->thread_detach (thread, thread->callbacks_arg);
470
0
    free_states (state);
471
0
    return err;
472
0
  }
473
0
      __libdwfl_frame_unwind (state);
474
0
      Dwfl_Frame *next = state->unwound;
475
      /* The old frame is no longer needed.  */
476
0
      free (state);
477
0
      state = next;
478
0
    }
479
0
  while (state && state->pc_state == DWFL_FRAME_STATE_PC_SET);
480
481
0
  Dwfl_Error err = dwfl_errno ();
482
0
  if (process->callbacks->thread_detach)
483
0
    process->callbacks->thread_detach (thread, thread->callbacks_arg);
484
0
  if (state == NULL || state->pc_state == DWFL_FRAME_STATE_ERROR)
485
0
    {
486
0
      free_states (state);
487
0
      __libdwfl_seterrno (err);
488
0
      return -1;
489
0
    }
490
0
  assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
491
0
  free_states (state);
492
0
  return 0;
493
0
}
494
INTDEF(dwfl_thread_getframes)