Coverage Report

Created: 2026-05-31 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/elfutils/libdwfl/linux-pid-attach.c
Line
Count
Source
1
/* Get Dwarf Frame state for target live PID process.
2
   Copyright (C) 2013, 2014, 2015, 2018, 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 "libelfP.h"
36
#include "libdwflP.h"
37
#include <sys/types.h>
38
#include <sys/stat.h>
39
#include <fcntl.h>
40
#include <dirent.h>
41
42
#ifdef __linux__
43
44
#include <sys/uio.h>
45
#include <sys/ptrace.h>
46
#include <sys/syscall.h>
47
#include <sys/wait.h>
48
49
static bool
50
linux_proc_pid_is_stopped (pid_t pid)
51
0
{
52
0
  char buffer[64];
53
0
  FILE *procfile;
54
0
  bool retval, have_state;
55
56
0
  snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid);
57
0
  procfile = fopen (buffer, "r");
58
0
  if (procfile == NULL)
59
0
    return false;
60
61
0
  have_state = false;
62
0
  while (fgets (buffer, sizeof (buffer), procfile) != NULL)
63
0
    if (startswith (buffer, "State:"))
64
0
      {
65
0
  have_state = true;
66
0
  break;
67
0
      }
68
0
  retval = (have_state && strstr (buffer, "T (stopped)") != NULL);
69
0
  fclose (procfile);
70
0
  return retval;
71
0
}
72
73
bool
74
internal_function
75
__libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
76
0
{
77
0
  if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
78
0
    {
79
0
      __libdwfl_seterrno (DWFL_E_ERRNO);
80
0
      return false;
81
0
    }
82
0
  *tid_was_stoppedp = linux_proc_pid_is_stopped (tid);
83
0
  if (*tid_was_stoppedp)
84
0
    {
85
      /* Make sure there is a SIGSTOP signal pending even when the process is
86
   already State: T (stopped).  Older kernels might fail to generate
87
   a SIGSTOP notification in that case in response to our PTRACE_ATTACH
88
   above.  Which would make the waitpid below wait forever.  So emulate
89
   it.  Since there can only be one SIGSTOP notification pending this is
90
   safe.  See also gdb/linux-nat.c linux_nat_post_attach_wait.  */
91
0
      syscall (__NR_tkill, tid, SIGSTOP);
92
0
      ptrace (PTRACE_CONT, tid, NULL, NULL);
93
0
    }
94
0
  for (;;)
95
0
    {
96
0
      int status;
97
0
      if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status))
98
0
  {
99
0
    int saved_errno = errno;
100
0
    ptrace (PTRACE_DETACH, tid, NULL, NULL);
101
0
    errno = saved_errno;
102
0
    __libdwfl_seterrno (DWFL_E_ERRNO);
103
0
    return false;
104
0
  }
105
0
      if (WSTOPSIG (status) == SIGSTOP)
106
0
  break;
107
0
      if (ptrace (PTRACE_CONT, tid, NULL,
108
0
      (void *) (uintptr_t) WSTOPSIG (status)) != 0)
109
0
  {
110
0
    int saved_errno = errno;
111
0
    ptrace (PTRACE_DETACH, tid, NULL, NULL);
112
0
    errno = saved_errno;
113
0
    __libdwfl_seterrno (DWFL_E_ERRNO);
114
0
    return false;
115
0
  }
116
0
    }
117
0
  return true;
118
0
}
119
120
#ifdef HAVE_PROCESS_VM_READV
121
/* Note that the result word size depends on the architecture word size.
122
   That is sizeof long. */
123
static bool
124
read_cached_memory (struct __libdwfl_pid_arg *pid_arg,
125
        Dwarf_Addr addr, Dwarf_Word *result)
126
0
{
127
  /* Let the ptrace fallback deal with the corner case of the address
128
     possibly crossing a page boundary.  */
129
0
  if ((addr & ((Dwarf_Addr)__LIBDWFL_REMOTE_MEM_CACHE_SIZE - 1))
130
0
      > (Dwarf_Addr)__LIBDWFL_REMOTE_MEM_CACHE_SIZE - sizeof (unsigned long))
131
0
    return false;
132
133
0
  struct __libdwfl_remote_mem_cache *mem_cache = pid_arg->mem_cache;
134
0
  if (mem_cache == NULL)
135
0
    {
136
0
      size_t mem_cache_size = sizeof (struct __libdwfl_remote_mem_cache);
137
0
      mem_cache = malloc (mem_cache_size);
138
0
      if (mem_cache == NULL)
139
0
  return false;
140
141
0
      mem_cache->addr = 0;
142
0
      mem_cache->len = 0;
143
0
      pid_arg->mem_cache = mem_cache;
144
0
    }
145
146
0
  unsigned char *d;
147
0
  if (addr >= mem_cache->addr && addr - mem_cache->addr < mem_cache->len)
148
0
    {
149
0
      d = &mem_cache->buf[addr - mem_cache->addr];
150
0
      if ((((uintptr_t) d) & (sizeof (unsigned long) - 1)) == 0)
151
0
  *result = *(unsigned long *) d;
152
0
      else
153
0
  memcpy (result, d, sizeof (unsigned long));
154
0
      return true;
155
0
    }
156
157
0
  struct iovec local, remote;
158
0
  mem_cache->addr = addr & ~((Dwarf_Addr)__LIBDWFL_REMOTE_MEM_CACHE_SIZE - 1);
159
0
  local.iov_base = mem_cache->buf;
160
0
  local.iov_len = __LIBDWFL_REMOTE_MEM_CACHE_SIZE;
161
0
  remote.iov_base = (void *) (uintptr_t) mem_cache->addr;
162
0
  remote.iov_len = __LIBDWFL_REMOTE_MEM_CACHE_SIZE;
163
164
0
  ssize_t res = process_vm_readv (pid_arg->tid_attached,
165
0
          &local, 1, &remote, 1, 0);
166
0
  if (res != __LIBDWFL_REMOTE_MEM_CACHE_SIZE)
167
0
    {
168
0
      mem_cache->len = 0;
169
0
      return false;
170
0
    }
171
172
0
  mem_cache->len = res;
173
0
  d = &mem_cache->buf[addr - mem_cache->addr];
174
0
  if ((((uintptr_t) d) & (sizeof (unsigned long) - 1)) == 0)
175
0
    *result = *(unsigned long *) d;
176
0
  else
177
0
    memcpy (result, d, sizeof (unsigned long));
178
0
  return true;
179
0
}
180
#endif /* HAVE_PROCESS_VM_READV */
181
182
static void
183
clear_cached_memory (struct __libdwfl_pid_arg *pid_arg)
184
0
{
185
0
  struct __libdwfl_remote_mem_cache *mem_cache = pid_arg->mem_cache;
186
0
  if (mem_cache != NULL)
187
0
    mem_cache->len = 0;
188
0
}
189
190
/* Note that the result word size depends on the architecture word size.
191
   That is sizeof long. */
192
static bool
193
pid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
194
0
{
195
0
  struct __libdwfl_pid_arg *pid_arg = arg;
196
0
  pid_t tid = pid_arg->tid_attached;
197
0
  Dwfl_Process *process = dwfl->process;
198
0
  assert (tid > 0);
199
200
0
#ifdef HAVE_PROCESS_VM_READV
201
0
  if (read_cached_memory (pid_arg, addr, result))
202
0
    {
203
0
#if SIZEOF_LONG == 8
204
# if BYTE_ORDER == BIG_ENDIAN
205
      if (ebl_get_elfclass (process->ebl) == ELFCLASS32)
206
  *result >>= 32;
207
# endif
208
0
#endif
209
0
    return true;
210
0
    }
211
0
#endif
212
213
0
  if (ebl_get_elfclass (process->ebl) == ELFCLASS64)
214
0
    {
215
0
#if SIZEOF_LONG == 8
216
0
      errno = 0;
217
0
      *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
218
0
      return errno == 0;
219
#else /* SIZEOF_LONG != 8 */
220
      /* This should not happen.  */
221
      return false;
222
#endif /* SIZEOF_LONG != 8 */
223
0
    }
224
0
#if SIZEOF_LONG == 8
225
  /* We do not care about reads unaliged to 4 bytes boundary.
226
     But 0x...ffc read of 8 bytes could overrun a page.  */
227
0
  bool lowered = (addr & 4) != 0;
228
0
  if (lowered)
229
0
    addr -= 4;
230
0
#endif /* SIZEOF_LONG == 8 */
231
0
  errno = 0;
232
0
  *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
233
0
  if (errno != 0)
234
0
    return false;
235
0
#if SIZEOF_LONG == 8
236
# if BYTE_ORDER == BIG_ENDIAN
237
  if (! lowered)
238
    *result >>= 32;
239
# else
240
0
  if (lowered)
241
0
    *result >>= 32;
242
0
# endif
243
0
#endif /* SIZEOF_LONG == 8 */
244
0
  *result &= 0xffffffff;
245
0
  return true;
246
0
}
247
248
static pid_t
249
pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
250
     void **thread_argp)
251
0
{
252
0
  struct __libdwfl_pid_arg *pid_arg = dwfl_arg;
253
0
  struct dirent *dirent;
254
  /* Start fresh on first traversal. */
255
0
  if (*thread_argp == NULL)
256
0
    rewinddir (pid_arg->dir);
257
0
  do
258
0
    {
259
0
      errno = 0;
260
0
      dirent = readdir (pid_arg->dir);
261
0
      if (dirent == NULL)
262
0
  {
263
0
    if (errno != 0)
264
0
      {
265
0
        __libdwfl_seterrno (DWFL_E_ERRNO);
266
0
        return -1;
267
0
      }
268
0
    return 0;
269
0
  }
270
0
    }
271
0
  while (strcmp (dirent->d_name, ".") == 0
272
0
   || strcmp (dirent->d_name, "..") == 0);
273
0
  char *end;
274
0
  errno = 0;
275
0
  long tidl = strtol (dirent->d_name, &end, 10);
276
0
  if (errno != 0)
277
0
    {
278
0
      __libdwfl_seterrno (DWFL_E_ERRNO);
279
0
      return -1;
280
0
    }
281
0
  pid_t tid = tidl;
282
0
  if (tidl <= 0 || (end && *end) || tid != tidl)
283
0
    {
284
0
      __libdwfl_seterrno (DWFL_E_PARSE_PROC);
285
0
      return -1;
286
0
    }
287
0
  *thread_argp = dwfl_arg;
288
0
  return tid;
289
0
}
290
291
/* Just checks that the thread id exists.  */
292
static bool
293
pid_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
294
         void *dwfl_arg, void **thread_argp)
295
0
{
296
0
  *thread_argp = dwfl_arg;
297
0
  if (kill (tid, 0) < 0)
298
0
    {
299
0
      __libdwfl_seterrno (DWFL_E_ERRNO);
300
0
      return false;
301
0
    }
302
0
  return true;
303
0
}
304
305
static bool
306
pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
307
0
{
308
0
  struct __libdwfl_pid_arg *pid_arg = thread_arg;
309
0
  assert (pid_arg->tid_attached == 0);
310
0
  pid_t tid = INTUSE(dwfl_thread_tid) (thread);
311
0
  if (! pid_arg->assume_ptrace_stopped
312
0
      && ! __libdwfl_ptrace_attach (tid, &pid_arg->tid_was_stopped))
313
0
    return false;
314
0
  pid_arg->tid_attached = tid;
315
0
  Dwfl_Process *process = thread->process;
316
0
  Ebl *ebl = process->ebl;
317
0
  return ebl_set_initial_registers_tid (ebl, tid,
318
0
          __libdwfl_set_initial_registers_thread, thread);
319
0
}
320
321
static void
322
pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
323
0
{
324
0
  struct __libdwfl_pid_arg *pid_arg = dwfl_arg;
325
0
  elf_end (pid_arg->elf);
326
0
  free (pid_arg->mem_cache);
327
0
  close (pid_arg->elf_fd);
328
0
  closedir (pid_arg->dir);
329
0
  free (pid_arg);
330
0
}
331
332
void
333
internal_function
334
__libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped)
335
0
{
336
  /* This handling is needed only on older Linux kernels such as
337
     2.6.32-358.23.2.el6.ppc64.  Later kernels such as
338
     3.11.7-200.fc19.x86_64 remember the T (stopped) state
339
     themselves and no longer need to pass SIGSTOP during
340
     PTRACE_DETACH.  */
341
0
  ptrace (PTRACE_DETACH, tid, NULL,
342
0
    (void *) (intptr_t) (tid_was_stopped ? SIGSTOP : 0));
343
0
}
344
345
static void
346
pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
347
0
{
348
0
  struct __libdwfl_pid_arg *pid_arg = thread_arg;
349
0
  pid_t tid = INTUSE(dwfl_thread_tid) (thread);
350
0
  assert (pid_arg->tid_attached == tid);
351
0
  pid_arg->tid_attached = 0;
352
0
  clear_cached_memory (pid_arg);
353
0
  if (! pid_arg->assume_ptrace_stopped)
354
0
    __libdwfl_ptrace_detach (tid, pid_arg->tid_was_stopped);
355
0
}
356
357
static const Dwfl_Thread_Callbacks pid_thread_callbacks =
358
{
359
  pid_next_thread,
360
  pid_getthread,
361
  pid_memory_read,
362
  pid_set_initial_registers,
363
  pid_detach,
364
  pid_thread_detach,
365
};
366
367
int
368
dwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid, bool assume_ptrace_stopped)
369
0
{
370
0
  char buffer[36];
371
0
  FILE *procfile;
372
0
  int err = 0; /* The errno to return and set for dwfl->attcherr.  */
373
374
  /* Make sure to report the actual PID (thread group leader) to
375
     dwfl_attach_state.  */
376
0
  snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid);
377
0
  procfile = fopen (buffer, "r");
378
0
  if (procfile == NULL)
379
0
    {
380
0
      err = errno;
381
0
    fail:
382
0
      if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
383
0
  {
384
0
    errno = err;
385
0
    dwfl->attacherr = __libdwfl_canon_error (DWFL_E_ERRNO);
386
0
  }
387
0
      return err;
388
0
    }
389
390
0
  char *line = NULL;
391
0
  size_t linelen = 0;
392
0
  while (getline (&line, &linelen, procfile) >= 0)
393
0
    if (startswith (line, "Tgid:"))
394
0
      {
395
0
  errno = 0;
396
0
  char *endptr;
397
0
  long val = strtol (&line[5], &endptr, 10);
398
0
  if ((errno == ERANGE && val == LONG_MAX)
399
0
      || *endptr != '\n' || val < 0 || val != (pid_t) val)
400
0
    pid = 0;
401
0
  else
402
0
    pid = (pid_t) val;
403
0
  break;
404
0
      }
405
0
  free (line);
406
0
  fclose (procfile);
407
408
0
  if (pid == 0)
409
0
    {
410
0
      err = ESRCH;
411
0
      goto fail;
412
0
    }
413
414
0
  char name[64];
415
0
  int i = snprintf (name, sizeof (name), "/proc/%ld/task", (long) pid);
416
0
  if (i <= 0 || i >= (ssize_t) sizeof (name) - 1)
417
0
    {
418
0
      errno = -ENOMEM;
419
0
      goto fail;
420
0
    }
421
0
  DIR *dir = opendir (name);
422
0
  if (dir == NULL)
423
0
    {
424
0
      err = errno;
425
0
      goto fail;
426
0
    }
427
428
0
  Elf *elf;
429
0
  i = snprintf (name, sizeof (name), "/proc/%ld/exe", (long) pid);
430
0
  assert (i > 0 && i < (ssize_t) sizeof (name) - 1);
431
0
  int elf_fd = open (name, O_RDONLY);
432
0
  if (elf_fd >= 0)
433
0
    {
434
0
      elf = elf_begin (elf_fd, ELF_C_READ_MMAP, NULL);
435
0
      if (elf == NULL)
436
0
  {
437
    /* Just ignore, dwfl_attach_state will fall back to trying
438
       to associate the Dwfl with one of the existing DWfl_Module
439
       ELF images (to know the machine/class backend to use).  */
440
0
    close (elf_fd);
441
0
    elf_fd = -1;
442
0
  }
443
0
    }
444
0
  else
445
0
    elf = NULL;
446
0
  struct __libdwfl_pid_arg *pid_arg = malloc (sizeof *pid_arg);
447
0
  if (pid_arg == NULL)
448
0
    {
449
0
      elf_end (elf);
450
0
      close (elf_fd);
451
0
      closedir (dir);
452
0
      err = ENOMEM;
453
0
      goto fail;
454
0
    }
455
0
  pid_arg->dir = dir;
456
0
  pid_arg->elf = elf;
457
0
  pid_arg->elf_fd = elf_fd;
458
0
  pid_arg->mem_cache = NULL;
459
0
  pid_arg->tid_attached = 0;
460
0
  pid_arg->assume_ptrace_stopped = assume_ptrace_stopped;
461
0
  if (! INTUSE(dwfl_attach_state) (dwfl, elf, pid, &pid_thread_callbacks,
462
0
           pid_arg))
463
0
    {
464
0
      elf_end (elf);
465
0
      close (elf_fd);
466
0
      closedir (dir);
467
0
      free (pid_arg);
468
0
      return -1;
469
0
    }
470
0
  return 0;
471
0
}
472
INTDEF (dwfl_linux_proc_attach)
473
474
struct __libdwfl_pid_arg *
475
internal_function
476
__libdwfl_get_pid_arg (Dwfl *dwfl)
477
0
{
478
0
  if (dwfl != NULL && dwfl->process != NULL
479
0
      && dwfl->process->callbacks == &pid_thread_callbacks)
480
0
    return (struct __libdwfl_pid_arg *) dwfl->process->callbacks_arg;
481
482
0
  return NULL;
483
0
}
484
485
#else /* __linux__ */
486
487
bool
488
internal_function
489
__libdwfl_ptrace_attach (pid_t tid __attribute__ ((unused)),
490
       bool *tid_was_stoppedp __attribute__ ((unused)))
491
{
492
  errno = ENOSYS;
493
  __libdwfl_seterrno (DWFL_E_ERRNO);
494
  return false;
495
}
496
497
void
498
internal_function
499
__libdwfl_ptrace_detach (pid_t tid __attribute__ ((unused)),
500
       bool tid_was_stopped __attribute__ ((unused)))
501
{
502
}
503
504
int
505
dwfl_linux_proc_attach (Dwfl *dwfl __attribute__ ((unused)),
506
      pid_t pid __attribute__ ((unused)),
507
      bool assume_ptrace_stopped __attribute__ ((unused)))
508
{
509
  return ENOSYS;
510
}
511
INTDEF (dwfl_linux_proc_attach)
512
513
struct __libdwfl_pid_arg *
514
internal_function
515
__libdwfl_get_pid_arg (Dwfl *dwfl __attribute__ ((unused)))
516
{
517
  return NULL;
518
}
519
520
#endif /* ! __linux __ */
521