1da0c48c4Sopenharmony_ci/* Standard libdwfl callbacks for debugging a live Linux process.
2da0c48c4Sopenharmony_ci   Copyright (C) 2005-2010, 2013, 2014, 2016 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci
5da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
6da0c48c4Sopenharmony_ci   it under the terms of either
7da0c48c4Sopenharmony_ci
8da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
9da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
10da0c48c4Sopenharmony_ci       your option) any later version
11da0c48c4Sopenharmony_ci
12da0c48c4Sopenharmony_ci   or
13da0c48c4Sopenharmony_ci
14da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
15da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
16da0c48c4Sopenharmony_ci       your option) any later version
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci   or both in parallel, as here.
19da0c48c4Sopenharmony_ci
20da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
21da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
22da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23da0c48c4Sopenharmony_ci   General Public License for more details.
24da0c48c4Sopenharmony_ci
25da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
26da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
27da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
28da0c48c4Sopenharmony_ci
29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
30da0c48c4Sopenharmony_ci# include <config.h>
31da0c48c4Sopenharmony_ci#endif
32da0c48c4Sopenharmony_ci
33da0c48c4Sopenharmony_ci#include "libdwflP.h"
34da0c48c4Sopenharmony_ci#include <inttypes.h>
35da0c48c4Sopenharmony_ci#include <sys/types.h>
36da0c48c4Sopenharmony_ci#include <sys/stat.h>
37da0c48c4Sopenharmony_ci#include <errno.h>
38da0c48c4Sopenharmony_ci#include <stdio.h>
39da0c48c4Sopenharmony_ci#include <stdio_ext.h>
40da0c48c4Sopenharmony_ci#include <stdbool.h>
41da0c48c4Sopenharmony_ci#include <string.h>
42da0c48c4Sopenharmony_ci#include <stdlib.h>
43da0c48c4Sopenharmony_ci#include <fcntl.h>
44da0c48c4Sopenharmony_ci#include <unistd.h>
45da0c48c4Sopenharmony_ci#include <assert.h>
46da0c48c4Sopenharmony_ci#include <endian.h>
47da0c48c4Sopenharmony_ci#include "system.h"
48da0c48c4Sopenharmony_ci
49da0c48c4Sopenharmony_ci
50da0c48c4Sopenharmony_ci#define PROCMAPSFMT	"/proc/%d/maps"
51da0c48c4Sopenharmony_ci#define PROCMEMFMT	"/proc/%d/mem"
52da0c48c4Sopenharmony_ci#define PROCAUXVFMT	"/proc/%d/auxv"
53da0c48c4Sopenharmony_ci#define PROCEXEFMT	"/proc/%d/exe"
54da0c48c4Sopenharmony_ci
55da0c48c4Sopenharmony_ci
56da0c48c4Sopenharmony_ci/* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable.  Return
57da0c48c4Sopenharmony_ci   ELFCLASSNONE for an error.  */
58da0c48c4Sopenharmony_ci
59da0c48c4Sopenharmony_cistatic unsigned char
60da0c48c4Sopenharmony_ciget_pid_class (pid_t pid)
61da0c48c4Sopenharmony_ci{
62da0c48c4Sopenharmony_ci  char *fname;
63da0c48c4Sopenharmony_ci  if (asprintf (&fname, PROCEXEFMT, pid) < 0)
64da0c48c4Sopenharmony_ci    return ELFCLASSNONE;
65da0c48c4Sopenharmony_ci
66da0c48c4Sopenharmony_ci  int fd = open (fname, O_RDONLY);
67da0c48c4Sopenharmony_ci  free (fname);
68da0c48c4Sopenharmony_ci  if (fd < 0)
69da0c48c4Sopenharmony_ci    return ELFCLASSNONE;
70da0c48c4Sopenharmony_ci
71da0c48c4Sopenharmony_ci  unsigned char buf[EI_CLASS + 1];
72da0c48c4Sopenharmony_ci  ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0);
73da0c48c4Sopenharmony_ci  close (fd);
74da0c48c4Sopenharmony_ci  if (nread != sizeof buf || buf[EI_MAG0] != ELFMAG0
75da0c48c4Sopenharmony_ci      || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2
76da0c48c4Sopenharmony_ci      || buf[EI_MAG3] != ELFMAG3
77da0c48c4Sopenharmony_ci      || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32))
78da0c48c4Sopenharmony_ci    return ELFCLASSNONE;
79da0c48c4Sopenharmony_ci
80da0c48c4Sopenharmony_ci  return buf[EI_CLASS];
81da0c48c4Sopenharmony_ci}
82da0c48c4Sopenharmony_ci
83da0c48c4Sopenharmony_ci/* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.
84da0c48c4Sopenharmony_ci
85da0c48c4Sopenharmony_ci   It would be easiest to call get_pid_class and parse everything according to
86da0c48c4Sopenharmony_ci   the 32-bit or 64-bit class.  But this would bring the overhead of syscalls
87da0c48c4Sopenharmony_ci   to open and read the "/proc/%d/exe" file.
88da0c48c4Sopenharmony_ci
89da0c48c4Sopenharmony_ci   Therefore this function tries to parse the "/proc/%d/auxv" content both
90da0c48c4Sopenharmony_ci   ways, as if it were the 32-bit format and also if it were the 64-bit format.
91da0c48c4Sopenharmony_ci   Only if it gives some valid data in both cases get_pid_class gets called.
92da0c48c4Sopenharmony_ci   In most cases only one of the format bit sizes gives valid data and the
93da0c48c4Sopenharmony_ci   get_pid_class call overhead can be saved.  */
94da0c48c4Sopenharmony_ci
95da0c48c4Sopenharmony_cistatic int
96da0c48c4Sopenharmony_cigrovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
97da0c48c4Sopenharmony_ci{
98da0c48c4Sopenharmony_ci  char *fname;
99da0c48c4Sopenharmony_ci  if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
100da0c48c4Sopenharmony_ci    return ENOMEM;
101da0c48c4Sopenharmony_ci
102da0c48c4Sopenharmony_ci  int fd = open (fname, O_RDONLY);
103da0c48c4Sopenharmony_ci  free (fname);
104da0c48c4Sopenharmony_ci  if (fd < 0)
105da0c48c4Sopenharmony_ci    return errno == ENOENT ? 0 : errno;
106da0c48c4Sopenharmony_ci
107da0c48c4Sopenharmony_ci  GElf_Addr sysinfo_ehdr64 = 0;
108da0c48c4Sopenharmony_ci  GElf_Addr sysinfo_ehdr32 = 0;
109da0c48c4Sopenharmony_ci  GElf_Addr segment_align64 = dwfl->segment_align;
110da0c48c4Sopenharmony_ci  GElf_Addr segment_align32 = dwfl->segment_align;
111da0c48c4Sopenharmony_ci  off_t offset = 0;
112da0c48c4Sopenharmony_ci  ssize_t nread;
113da0c48c4Sopenharmony_ci  union
114da0c48c4Sopenharmony_ci  {
115da0c48c4Sopenharmony_ci    Elf64_auxv_t a64[64];
116da0c48c4Sopenharmony_ci    Elf32_auxv_t a32[128];
117da0c48c4Sopenharmony_ci  } d;
118da0c48c4Sopenharmony_ci  do
119da0c48c4Sopenharmony_ci    {
120da0c48c4Sopenharmony_ci      eu_static_assert (sizeof d.a64 == sizeof d.a32);
121da0c48c4Sopenharmony_ci      nread = pread_retry (fd, d.a64, sizeof d.a64, offset);
122da0c48c4Sopenharmony_ci      if (nread < 0)
123da0c48c4Sopenharmony_ci	{
124da0c48c4Sopenharmony_ci	  int ret = errno;
125da0c48c4Sopenharmony_ci	  close (fd);
126da0c48c4Sopenharmony_ci	  return ret;
127da0c48c4Sopenharmony_ci	}
128da0c48c4Sopenharmony_ci      for (size_t a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++)
129da0c48c4Sopenharmony_ci	{
130da0c48c4Sopenharmony_ci	  const Elf32_auxv_t *a32 = d.a32 + a32i;
131da0c48c4Sopenharmony_ci	  switch (a32->a_type)
132da0c48c4Sopenharmony_ci	  {
133da0c48c4Sopenharmony_ci	    case AT_SYSINFO_EHDR:
134da0c48c4Sopenharmony_ci	      sysinfo_ehdr32 = a32->a_un.a_val;
135da0c48c4Sopenharmony_ci	      break;
136da0c48c4Sopenharmony_ci	    case AT_PAGESZ:
137da0c48c4Sopenharmony_ci	      segment_align32 = a32->a_un.a_val;
138da0c48c4Sopenharmony_ci	      break;
139da0c48c4Sopenharmony_ci	  }
140da0c48c4Sopenharmony_ci	}
141da0c48c4Sopenharmony_ci      for (size_t a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++)
142da0c48c4Sopenharmony_ci	{
143da0c48c4Sopenharmony_ci	  const Elf64_auxv_t *a64 = d.a64 + a64i;
144da0c48c4Sopenharmony_ci	  switch (a64->a_type)
145da0c48c4Sopenharmony_ci	  {
146da0c48c4Sopenharmony_ci	    case AT_SYSINFO_EHDR:
147da0c48c4Sopenharmony_ci	      sysinfo_ehdr64 = a64->a_un.a_val;
148da0c48c4Sopenharmony_ci	      break;
149da0c48c4Sopenharmony_ci	    case AT_PAGESZ:
150da0c48c4Sopenharmony_ci	      segment_align64 = a64->a_un.a_val;
151da0c48c4Sopenharmony_ci	      break;
152da0c48c4Sopenharmony_ci	  }
153da0c48c4Sopenharmony_ci	}
154da0c48c4Sopenharmony_ci      offset += nread;
155da0c48c4Sopenharmony_ci    }
156da0c48c4Sopenharmony_ci  while (nread == sizeof d.a64);
157da0c48c4Sopenharmony_ci
158da0c48c4Sopenharmony_ci  close (fd);
159da0c48c4Sopenharmony_ci
160da0c48c4Sopenharmony_ci  bool valid64 = sysinfo_ehdr64 != 0 || segment_align64 != dwfl->segment_align;
161da0c48c4Sopenharmony_ci  bool valid32 = sysinfo_ehdr32 != 0 || segment_align32 != dwfl->segment_align;
162da0c48c4Sopenharmony_ci
163da0c48c4Sopenharmony_ci  unsigned char pid_class = ELFCLASSNONE;
164da0c48c4Sopenharmony_ci  if (valid64 && valid32)
165da0c48c4Sopenharmony_ci    pid_class = get_pid_class (pid);
166da0c48c4Sopenharmony_ci
167da0c48c4Sopenharmony_ci  if (pid_class == ELFCLASS64 || (valid64 && ! valid32))
168da0c48c4Sopenharmony_ci    {
169da0c48c4Sopenharmony_ci      *sysinfo_ehdr = sysinfo_ehdr64;
170da0c48c4Sopenharmony_ci      dwfl->segment_align = segment_align64;
171da0c48c4Sopenharmony_ci      return 0;
172da0c48c4Sopenharmony_ci    }
173da0c48c4Sopenharmony_ci  if (pid_class == ELFCLASS32 || (! valid64 && valid32))
174da0c48c4Sopenharmony_ci    {
175da0c48c4Sopenharmony_ci      *sysinfo_ehdr = sysinfo_ehdr32;
176da0c48c4Sopenharmony_ci      dwfl->segment_align = segment_align32;
177da0c48c4Sopenharmony_ci      return 0;
178da0c48c4Sopenharmony_ci    }
179da0c48c4Sopenharmony_ci  return ENOEXEC;
180da0c48c4Sopenharmony_ci}
181da0c48c4Sopenharmony_ci
182da0c48c4Sopenharmony_cistatic inline bool
183da0c48c4Sopenharmony_cido_report (Dwfl *dwfl, char **plast_file, Dwarf_Addr low, Dwarf_Addr high)
184da0c48c4Sopenharmony_ci{
185da0c48c4Sopenharmony_ci  if (*plast_file != NULL)
186da0c48c4Sopenharmony_ci    {
187da0c48c4Sopenharmony_ci      Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, *plast_file,
188da0c48c4Sopenharmony_ci						     low, high);
189da0c48c4Sopenharmony_ci      free (*plast_file);
190da0c48c4Sopenharmony_ci      *plast_file = NULL;
191da0c48c4Sopenharmony_ci      if (unlikely (mod == NULL))
192da0c48c4Sopenharmony_ci        return true;
193da0c48c4Sopenharmony_ci    }
194da0c48c4Sopenharmony_ci  return false;
195da0c48c4Sopenharmony_ci}
196da0c48c4Sopenharmony_ci
197da0c48c4Sopenharmony_ci#define report() do_report(dwfl, &last_file, low, high)
198da0c48c4Sopenharmony_ci
199da0c48c4Sopenharmony_cistatic int
200da0c48c4Sopenharmony_ciproc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
201da0c48c4Sopenharmony_ci{
202da0c48c4Sopenharmony_ci  unsigned int last_dmajor = -1, last_dminor = -1;
203da0c48c4Sopenharmony_ci  uint64_t last_ino = -1;
204da0c48c4Sopenharmony_ci  char *last_file = NULL;
205da0c48c4Sopenharmony_ci  Dwarf_Addr low = 0, high = 0;
206da0c48c4Sopenharmony_ci
207da0c48c4Sopenharmony_ci  char *line = NULL;
208da0c48c4Sopenharmony_ci  size_t linesz;
209da0c48c4Sopenharmony_ci  ssize_t len;
210da0c48c4Sopenharmony_ci  while ((len = getline (&line, &linesz, f)) > 0)
211da0c48c4Sopenharmony_ci    {
212da0c48c4Sopenharmony_ci      if (line[len - 1] == '\n')
213da0c48c4Sopenharmony_ci	line[len - 1] = '\0';
214da0c48c4Sopenharmony_ci
215da0c48c4Sopenharmony_ci      Dwarf_Addr start, end, offset;
216da0c48c4Sopenharmony_ci      unsigned int dmajor, dminor;
217da0c48c4Sopenharmony_ci      uint64_t ino;
218da0c48c4Sopenharmony_ci      int nread = -1;
219da0c48c4Sopenharmony_ci      if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
220da0c48c4Sopenharmony_ci		  " %x:%x %" PRIu64 " %n",
221da0c48c4Sopenharmony_ci		  &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
222da0c48c4Sopenharmony_ci	  || nread <= 0)
223da0c48c4Sopenharmony_ci	{
224da0c48c4Sopenharmony_ci	  free (line);
225da0c48c4Sopenharmony_ci	  free (last_file);
226da0c48c4Sopenharmony_ci	  return ENOEXEC;
227da0c48c4Sopenharmony_ci	}
228da0c48c4Sopenharmony_ci
229da0c48c4Sopenharmony_ci      /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
230da0c48c4Sopenharmony_ci	 report the last one and then this special one.  */
231da0c48c4Sopenharmony_ci      if (start == sysinfo_ehdr && start != 0)
232da0c48c4Sopenharmony_ci	{
233da0c48c4Sopenharmony_ci	  if (report ())
234da0c48c4Sopenharmony_ci	    {
235da0c48c4Sopenharmony_ci	    bad_report:
236da0c48c4Sopenharmony_ci	      free (line);
237da0c48c4Sopenharmony_ci	      return -1;
238da0c48c4Sopenharmony_ci	    }
239da0c48c4Sopenharmony_ci
240da0c48c4Sopenharmony_ci	  low = start;
241da0c48c4Sopenharmony_ci	  high = end;
242da0c48c4Sopenharmony_ci	  if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
243da0c48c4Sopenharmony_ci	      || report ())
244da0c48c4Sopenharmony_ci	    goto bad_report;
245da0c48c4Sopenharmony_ci	}
246da0c48c4Sopenharmony_ci
247da0c48c4Sopenharmony_ci      char *file = line + nread + strspn (line + nread, " \t");
248da0c48c4Sopenharmony_ci      if (file[0] != '/' || (ino == 0 && dmajor == 0 && dminor == 0))
249da0c48c4Sopenharmony_ci	/* This line doesn't indicate a file mapping.  */
250da0c48c4Sopenharmony_ci	continue;
251da0c48c4Sopenharmony_ci
252da0c48c4Sopenharmony_ci      if (last_file != NULL
253da0c48c4Sopenharmony_ci	  && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
254da0c48c4Sopenharmony_ci	{
255da0c48c4Sopenharmony_ci	  /* This is another portion of the same file's mapping.  */
256da0c48c4Sopenharmony_ci	  if (strcmp (last_file, file) != 0)
257da0c48c4Sopenharmony_ci	    {
258da0c48c4Sopenharmony_ci	      free (last_file);
259da0c48c4Sopenharmony_ci	      goto bad_report;
260da0c48c4Sopenharmony_ci	    }
261da0c48c4Sopenharmony_ci	  high = end;
262da0c48c4Sopenharmony_ci	}
263da0c48c4Sopenharmony_ci      else
264da0c48c4Sopenharmony_ci	{
265da0c48c4Sopenharmony_ci	  /* This is a different file mapping.  Report the last one.  */
266da0c48c4Sopenharmony_ci	  if (report ())
267da0c48c4Sopenharmony_ci	    goto bad_report;
268da0c48c4Sopenharmony_ci	  low = start;
269da0c48c4Sopenharmony_ci	  high = end;
270da0c48c4Sopenharmony_ci	  last_file = strdup (file);
271da0c48c4Sopenharmony_ci	  last_ino = ino;
272da0c48c4Sopenharmony_ci	  last_dmajor = dmajor;
273da0c48c4Sopenharmony_ci	  last_dminor = dminor;
274da0c48c4Sopenharmony_ci	}
275da0c48c4Sopenharmony_ci    }
276da0c48c4Sopenharmony_ci  free (line);
277da0c48c4Sopenharmony_ci
278da0c48c4Sopenharmony_ci  int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
279da0c48c4Sopenharmony_ci
280da0c48c4Sopenharmony_ci  /* Report the final one.  */
281da0c48c4Sopenharmony_ci  bool lose = report ();
282da0c48c4Sopenharmony_ci
283da0c48c4Sopenharmony_ci  return result != 0 ? result : lose ? -1 : 0;
284da0c48c4Sopenharmony_ci}
285da0c48c4Sopenharmony_ci
286da0c48c4Sopenharmony_ciint
287da0c48c4Sopenharmony_cidwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
288da0c48c4Sopenharmony_ci{
289da0c48c4Sopenharmony_ci  return proc_maps_report (dwfl, f, 0, 0);
290da0c48c4Sopenharmony_ci}
291da0c48c4Sopenharmony_ciINTDEF (dwfl_linux_proc_maps_report)
292da0c48c4Sopenharmony_ci
293da0c48c4Sopenharmony_ciint
294da0c48c4Sopenharmony_cidwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
295da0c48c4Sopenharmony_ci{
296da0c48c4Sopenharmony_ci  if (dwfl == NULL)
297da0c48c4Sopenharmony_ci    return -1;
298da0c48c4Sopenharmony_ci
299da0c48c4Sopenharmony_ci  /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it.  */
300da0c48c4Sopenharmony_ci  GElf_Addr sysinfo_ehdr = 0;
301da0c48c4Sopenharmony_ci  int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr);
302da0c48c4Sopenharmony_ci  if (result != 0)
303da0c48c4Sopenharmony_ci    return result;
304da0c48c4Sopenharmony_ci
305da0c48c4Sopenharmony_ci  char *fname;
306da0c48c4Sopenharmony_ci  if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
307da0c48c4Sopenharmony_ci    return ENOMEM;
308da0c48c4Sopenharmony_ci
309da0c48c4Sopenharmony_ci  FILE *f = fopen (fname, "r");
310da0c48c4Sopenharmony_ci  free (fname);
311da0c48c4Sopenharmony_ci  if (f == NULL)
312da0c48c4Sopenharmony_ci    return errno;
313da0c48c4Sopenharmony_ci
314da0c48c4Sopenharmony_ci  (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
315da0c48c4Sopenharmony_ci
316da0c48c4Sopenharmony_ci  result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
317da0c48c4Sopenharmony_ci
318da0c48c4Sopenharmony_ci  fclose (f);
319da0c48c4Sopenharmony_ci
320da0c48c4Sopenharmony_ci  return result;
321da0c48c4Sopenharmony_ci}
322da0c48c4Sopenharmony_ciINTDEF (dwfl_linux_proc_report)
323da0c48c4Sopenharmony_ci
324da0c48c4Sopenharmony_cistatic ssize_t
325da0c48c4Sopenharmony_ciread_proc_memory (void *arg, void *data, GElf_Addr address,
326da0c48c4Sopenharmony_ci		  size_t minread, size_t maxread)
327da0c48c4Sopenharmony_ci{
328da0c48c4Sopenharmony_ci  const int fd = *(const int *) arg;
329da0c48c4Sopenharmony_ci
330da0c48c4Sopenharmony_ci  /* This code relies on the fact the Linux kernel accepts negative
331da0c48c4Sopenharmony_ci     offsets when seeking /dev/$$/mem files, as a special case. In
332da0c48c4Sopenharmony_ci     particular pread cannot be used here, because it will always
333da0c48c4Sopenharmony_ci     return EINVAL when passed a negative offset.  */
334da0c48c4Sopenharmony_ci
335da0c48c4Sopenharmony_ci  if (lseek (fd, (off_t) address, SEEK_SET) == -1)
336da0c48c4Sopenharmony_ci    return -1;
337da0c48c4Sopenharmony_ci
338da0c48c4Sopenharmony_ci  ssize_t nread = read (fd, data, maxread);
339da0c48c4Sopenharmony_ci
340da0c48c4Sopenharmony_ci  if (nread > 0 && (size_t) nread < minread)
341da0c48c4Sopenharmony_ci    nread = 0;
342da0c48c4Sopenharmony_ci  return nread;
343da0c48c4Sopenharmony_ci}
344da0c48c4Sopenharmony_ci
345da0c48c4Sopenharmony_ciextern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
346da0c48c4Sopenharmony_ci				    GElf_Xword pagesize,
347da0c48c4Sopenharmony_ci				    GElf_Addr *loadbasep,
348da0c48c4Sopenharmony_ci				    ssize_t (*read_memory) (void *arg,
349da0c48c4Sopenharmony_ci							    void *data,
350da0c48c4Sopenharmony_ci							    GElf_Addr address,
351da0c48c4Sopenharmony_ci							    size_t minread,
352da0c48c4Sopenharmony_ci							    size_t maxread),
353da0c48c4Sopenharmony_ci				    void *arg);
354da0c48c4Sopenharmony_ci
355da0c48c4Sopenharmony_ci
356da0c48c4Sopenharmony_ci/* Dwfl_Callbacks.find_elf */
357da0c48c4Sopenharmony_ci
358da0c48c4Sopenharmony_ciint
359da0c48c4Sopenharmony_cidwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
360da0c48c4Sopenharmony_ci			  void **userdata __attribute__ ((unused)),
361da0c48c4Sopenharmony_ci			  const char *module_name, Dwarf_Addr base,
362da0c48c4Sopenharmony_ci			  char **file_name, Elf **elfp)
363da0c48c4Sopenharmony_ci{
364da0c48c4Sopenharmony_ci  int pid = -1;
365da0c48c4Sopenharmony_ci  if (module_name[0] == '/')
366da0c48c4Sopenharmony_ci    {
367da0c48c4Sopenharmony_ci      /* When this callback is used together with dwfl_linux_proc_report
368da0c48c4Sopenharmony_ci	 then we might see mappings of special character devices.  Make
369da0c48c4Sopenharmony_ci	 sure we only open and return regular files.  Special devices
370da0c48c4Sopenharmony_ci	 might hang on open or read.  (deleted) files are super special.
371da0c48c4Sopenharmony_ci	 The image might come from memory if we are attached.  */
372da0c48c4Sopenharmony_ci      struct stat sb;
373da0c48c4Sopenharmony_ci      if (stat (module_name, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG)
374da0c48c4Sopenharmony_ci	{
375da0c48c4Sopenharmony_ci	  if (strcmp (strrchr (module_name, ' ') ?: "", " (deleted)") == 0)
376da0c48c4Sopenharmony_ci	    pid = INTUSE(dwfl_pid) (mod->dwfl);
377da0c48c4Sopenharmony_ci	  else
378da0c48c4Sopenharmony_ci	    return -1;
379da0c48c4Sopenharmony_ci	}
380da0c48c4Sopenharmony_ci
381da0c48c4Sopenharmony_ci      if (pid == -1)
382da0c48c4Sopenharmony_ci	{
383da0c48c4Sopenharmony_ci	  int fd = open (module_name, O_RDONLY);
384da0c48c4Sopenharmony_ci	  if (fd >= 0)
385da0c48c4Sopenharmony_ci	    {
386da0c48c4Sopenharmony_ci	      *file_name = strdup (module_name);
387da0c48c4Sopenharmony_ci	      if (*file_name == NULL)
388da0c48c4Sopenharmony_ci		{
389da0c48c4Sopenharmony_ci		  close (fd);
390da0c48c4Sopenharmony_ci		  return ENOMEM;
391da0c48c4Sopenharmony_ci		}
392da0c48c4Sopenharmony_ci	    }
393da0c48c4Sopenharmony_ci	  return fd;
394da0c48c4Sopenharmony_ci	}
395da0c48c4Sopenharmony_ci    }
396da0c48c4Sopenharmony_ci
397da0c48c4Sopenharmony_ci  if (pid != -1 || sscanf (module_name, "[vdso: %d]", &pid) == 1)
398da0c48c4Sopenharmony_ci    {
399da0c48c4Sopenharmony_ci      /* Special case for in-memory ELF image.  */
400da0c48c4Sopenharmony_ci
401da0c48c4Sopenharmony_ci      bool detach = false;
402da0c48c4Sopenharmony_ci      bool tid_was_stopped = false;
403da0c48c4Sopenharmony_ci      struct __libdwfl_pid_arg *pid_arg = __libdwfl_get_pid_arg (mod->dwfl);
404da0c48c4Sopenharmony_ci      if (pid_arg != NULL && ! pid_arg->assume_ptrace_stopped)
405da0c48c4Sopenharmony_ci	{
406da0c48c4Sopenharmony_ci	  /* If any thread is already attached we are fine.  Read
407da0c48c4Sopenharmony_ci	     through that thread.  It doesn't have to be the main
408da0c48c4Sopenharmony_ci	     thread pid.  */
409da0c48c4Sopenharmony_ci	  pid_t tid = pid_arg->tid_attached;
410da0c48c4Sopenharmony_ci	  if (tid != 0)
411da0c48c4Sopenharmony_ci	    pid = tid;
412da0c48c4Sopenharmony_ci	  else
413da0c48c4Sopenharmony_ci	    detach = __libdwfl_ptrace_attach (pid, &tid_was_stopped);
414da0c48c4Sopenharmony_ci	}
415da0c48c4Sopenharmony_ci
416da0c48c4Sopenharmony_ci      char *fname;
417da0c48c4Sopenharmony_ci      if (asprintf (&fname, PROCMEMFMT, pid) < 0)
418da0c48c4Sopenharmony_ci	goto detach;
419da0c48c4Sopenharmony_ci
420da0c48c4Sopenharmony_ci      int fd = open (fname, O_RDONLY);
421da0c48c4Sopenharmony_ci      free (fname);
422da0c48c4Sopenharmony_ci      if (fd < 0)
423da0c48c4Sopenharmony_ci	goto detach;
424da0c48c4Sopenharmony_ci
425da0c48c4Sopenharmony_ci      *elfp = elf_from_remote_memory (base, sysconf (_SC_PAGESIZE), NULL,
426da0c48c4Sopenharmony_ci				      &read_proc_memory, &fd);
427da0c48c4Sopenharmony_ci
428da0c48c4Sopenharmony_ci      close (fd);
429da0c48c4Sopenharmony_ci
430da0c48c4Sopenharmony_ci      *file_name = NULL;
431da0c48c4Sopenharmony_ci
432da0c48c4Sopenharmony_ci    detach:
433da0c48c4Sopenharmony_ci      if (detach)
434da0c48c4Sopenharmony_ci	__libdwfl_ptrace_detach (pid, tid_was_stopped);
435da0c48c4Sopenharmony_ci      return -1;
436da0c48c4Sopenharmony_ci    }
437da0c48c4Sopenharmony_ci
438da0c48c4Sopenharmony_ci  return -1;
439da0c48c4Sopenharmony_ci}
440da0c48c4Sopenharmony_ciINTDEF (dwfl_linux_proc_find_elf)
441