Coverage Report

Created: 2026-09-07 06:08

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