• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Standard libdwfl callbacks for debugging a live Linux process.
2    Copyright (C) 2005, 2007, 2008 Red Hat, Inc.
3    This file is part of Red Hat elfutils.
4 
5    Red Hat elfutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by the
7    Free Software Foundation; version 2 of the License.
8 
9    Red Hat elfutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with Red Hat elfutils; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17 
18    In addition, as a special exception, Red Hat, Inc. gives You the
19    additional right to link the code of Red Hat elfutils with code licensed
20    under any Open Source Initiative certified open source license
21    (http://www.opensource.org/licenses/index.php) which requires the
22    distribution of source code with any binary distribution and to
23    distribute linked combinations of the two.  Non-GPL Code permitted under
24    this exception must only link to the code of Red Hat elfutils through
25    those well defined interfaces identified in the file named EXCEPTION
26    found in the source code files (the "Approved Interfaces").  The files
27    of Non-GPL Code may instantiate templates or use macros or inline
28    functions from the Approved Interfaces without causing the resulting
29    work to be covered by the GNU General Public License.  Only Red Hat,
30    Inc. may make changes or additions to the list of Approved Interfaces.
31    Red Hat's grant of this exception is conditioned upon your not adding
32    any new exceptions.  If you wish to add a new Approved Interface or
33    exception, please contact Red Hat.  You must obey the GNU General Public
34    License in all respects for all of the Red Hat elfutils code and other
35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
36    covered by this exception.  If you modify this file, you may extend this
37    exception to your version of the file, but you are not obligated to do
38    so.  If you do not wish to provide this exception without modification,
39    you must delete this exception statement from your version and license
40    this file solely under the GPL without exception.
41 
42    Red Hat elfutils is an included package of the Open Invention Network.
43    An included package of the Open Invention Network is a package for which
44    Open Invention Network licensees cross-license their patents.  No patent
45    license is granted, either expressly or impliedly, by designation as an
46    included package.  Should you wish to participate in the Open Invention
47    Network licensing program, please visit www.openinventionnetwork.com
48    <http://www.openinventionnetwork.com>.  */
49 
50 #include "libdwflP.h"
51 #include <inttypes.h>
52 #include <sys/types.h>
53 #include <errno.h>
54 #include <stdio.h>
55 #include <stdio_ext.h>
56 #include <stdbool.h>
57 #include <string.h>
58 #include <stdlib.h>
59 #include <fcntl.h>
60 #include <unistd.h>
61 #include <assert.h>
62 #include <endian.h>
63 
64 
65 #define PROCMAPSFMT	"/proc/%d/maps"
66 #define PROCMEMFMT	"/proc/%d/mem"
67 #define PROCAUXVFMT	"/proc/%d/auxv"
68 
69 
70 /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.  */
71 
72 static int
find_sysinfo_ehdr(pid_t pid,GElf_Addr * sysinfo_ehdr)73 find_sysinfo_ehdr (pid_t pid, GElf_Addr *sysinfo_ehdr)
74 {
75   char *fname;
76   if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
77     return ENOMEM;
78 
79   int fd = open64 (fname, O_RDONLY);
80   free (fname);
81   if (fd < 0)
82     return errno == ENOENT ? 0 : errno;
83 
84   ssize_t nread;
85   do
86     {
87       union
88       {
89 	char buffer[sizeof (long int) * 2 * 64];
90 	Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)];
91 	Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)];
92       } d;
93       nread = read (fd, &d, sizeof d);
94       if (nread > 0)
95 	{
96 	  switch (sizeof (long int))
97 	    {
98 	    case 4:
99 	      for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
100 		if (d.a32[i].a_type == AT_SYSINFO_EHDR)
101 		  {
102 		    *sysinfo_ehdr = d.a32[i].a_un.a_val;
103 		    nread = 0;
104 		    break;
105 		  }
106 	      break;
107 	    case 8:
108 	      for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
109 		if (d.a64[i].a_type == AT_SYSINFO_EHDR)
110 		  {
111 		    *sysinfo_ehdr = d.a64[i].a_un.a_val;
112 		    nread = 0;
113 		    break;
114 		  }
115 	      break;
116 	    default:
117 	      abort ();
118 	      break;
119 	    }
120 	}
121     }
122   while (nread > 0);
123 
124   close (fd);
125 
126   return nread < 0 ? errno : 0;
127 }
128 
129 static int
proc_maps_report(Dwfl * dwfl,FILE * f,GElf_Addr sysinfo_ehdr,pid_t pid)130 proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
131 {
132   unsigned int last_dmajor = -1, last_dminor = -1;
133   uint64_t last_ino = -1;
134   char *last_file = NULL;
135   Dwarf_Addr low = 0, high = 0;
136 
137   inline bool report (void)
138     {
139       if (last_file != NULL)
140 	{
141 	  Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
142 							 low, high);
143 	  free (last_file);
144 	  last_file = NULL;
145 	  if (unlikely (mod == NULL))
146 	    return true;
147 	}
148       return false;
149     }
150 
151   char *line = NULL;
152   size_t linesz;
153   ssize_t len;
154   while ((len = getline (&line, &linesz, f)) > 0)
155     {
156       if (line[len - 1] == '\n')
157 	line[len - 1] = '\0';
158 
159       Dwarf_Addr start, end, offset;
160       unsigned int dmajor, dminor;
161       uint64_t ino;
162       int nread = -1;
163       if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
164 		  " %x:%x %" PRIi64 " %n",
165 		  &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
166 	  || nread <= 0)
167 	{
168 	  free (line);
169 	  return ENOEXEC;
170 	}
171 
172       /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
173 	 report the last one and then this special one.  */
174       if (start == sysinfo_ehdr && start != 0)
175 	{
176 	  if (report ())
177 	    {
178 	    bad_report:
179 	      free (line);
180 	      fclose (f);
181 	      return -1;
182 	    }
183 
184 	  low = start;
185 	  high = end;
186 	  if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
187 	      || report ())
188 	    goto bad_report;
189 	}
190 
191       char *file = line + nread + strspn (line + nread, " \t");
192       if (file[0] == '\0' || (ino == 0 && dmajor == 0 && dminor == 0))
193 	/* This line doesn't indicate a file mapping.  */
194 	continue;
195 
196       if (last_file != NULL
197 	  && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
198 	{
199 	  /* This is another portion of the same file's mapping.  */
200 	  assert (!strcmp (last_file, file));
201 	  high = end;
202 	}
203       else
204 	{
205 	  /* This is a different file mapping.  Report the last one.  */
206 	  if (report ())
207 	    goto bad_report;
208 	  low = start;
209 	  high = end;
210 	  last_file = strdup (file);
211 	  last_ino = ino;
212 	  last_dmajor = dmajor;
213 	  last_dminor = dminor;
214 	}
215     }
216   free (line);
217 
218   int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
219 
220   /* Report the final one.  */
221   bool lose = report ();
222 
223   return result != 0 ? result : lose ? -1 : 0;
224 }
225 
226 int
dwfl_linux_proc_maps_report(Dwfl * dwfl,FILE * f)227 dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
228 {
229   return proc_maps_report (dwfl, f, 0, 0);
230 }
INTDEF(dwfl_linux_proc_maps_report)231 INTDEF (dwfl_linux_proc_maps_report)
232 
233 int
234 dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
235 {
236   if (dwfl == NULL)
237     return -1;
238 
239   /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it.  */
240   GElf_Addr sysinfo_ehdr = 0;
241   int result = find_sysinfo_ehdr (pid, &sysinfo_ehdr);
242   if (result != 0)
243     return result;
244 
245   char *fname;
246   if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
247     return ENOMEM;
248 
249   FILE *f = fopen (fname, "r");
250   free (fname);
251   if (f == NULL)
252     return errno;
253 
254   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
255 
256   result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
257 
258   fclose (f);
259 
260   return result;
261 }
INTDEF(dwfl_linux_proc_report)262 INTDEF (dwfl_linux_proc_report)
263 
264 static ssize_t
265 read_proc_memory (void *arg, void *data, GElf_Addr address,
266 		  size_t minread, size_t maxread)
267 {
268   const int fd = *(const int *) arg;
269   ssize_t nread = pread64 (fd, data, maxread, (off64_t) address);
270   /* Some kernels don't actually let us do this read, ignore those errors.  */
271   if (nread < 0 && (errno == EINVAL || errno == EPERM))
272     return 0;
273   if (nread > 0 && (size_t) nread < minread)
274     nread = 0;
275   return nread;
276 }
277 
278 extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
279 				    GElf_Addr *loadbasep,
280 				    ssize_t (*read_memory) (void *arg,
281 							    void *data,
282 							    GElf_Addr address,
283 							    size_t minread,
284 							    size_t maxread),
285 				    void *arg);
286 
287 
288 /* Dwfl_Callbacks.find_elf */
289 
290 int
dwfl_linux_proc_find_elf(Dwfl_Module * mod,void ** userdata,const char * module_name,Dwarf_Addr base,char ** file_name,Elf ** elfp)291 dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
292 			  void **userdata __attribute__ ((unused)),
293 			  const char *module_name, Dwarf_Addr base,
294 			  char **file_name, Elf **elfp)
295 {
296   if (module_name[0] == '/')
297     {
298       int fd = open64 (module_name, O_RDONLY);
299       if (fd >= 0)
300 	{
301 	  *file_name = strdup (module_name);
302 	  if (*file_name == NULL)
303 	    {
304 	      close (fd);
305 	      return ENOMEM;
306 	    }
307 	}
308       return fd;
309     }
310 
311   int pid;
312   if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
313     {
314       /* Special case for in-memory ELF image.  */
315 
316       char *fname;
317       if (asprintf (&fname, PROCMEMFMT, pid) < 0)
318 	return -1;
319 
320       int fd = open64 (fname, O_RDONLY);
321       free (fname);
322       if (fd < 0)
323 	return -1;
324 
325       *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
326 
327       close (fd);
328 
329       *file_name = NULL;
330       return -1;
331     }
332 
333   abort ();
334   return -1;
335 }
336 INTDEF (dwfl_linux_proc_find_elf)
337