4fbe90d11754ec07e037e112f6875861af0bc061
[platform/upstream/elfutils.git] / libdwfl / linux-proc-maps.c
1 /* Standard libdwfl callbacks for debugging a live Linux process.
2    Copyright (C) 2005-2010 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 #include "libdwflP.h"
30 #include <inttypes.h>
31 #include <sys/types.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdio_ext.h>
35 #include <stdbool.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <assert.h>
41 #include <endian.h>
42
43
44 #define PROCMAPSFMT     "/proc/%d/maps"
45 #define PROCMEMFMT      "/proc/%d/mem"
46 #define PROCAUXVFMT     "/proc/%d/auxv"
47
48
49 /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.  */
50
51 static int
52 grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
53 {
54   char *fname;
55   if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
56     return ENOMEM;
57
58   int fd = open64 (fname, O_RDONLY);
59   free (fname);
60   if (fd < 0)
61     return errno == ENOENT ? 0 : errno;
62
63   ssize_t nread;
64   do
65     {
66       union
67       {
68         char buffer[sizeof (long int) * 2 * 64];
69         Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)];
70         Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)];
71       } d;
72       nread = read (fd, &d, sizeof d);
73       if (nread > 0)
74         {
75           switch (sizeof (long int))
76             {
77             case 4:
78               for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
79                 if (d.a32[i].a_type == AT_SYSINFO_EHDR)
80                   {
81                     *sysinfo_ehdr = d.a32[i].a_un.a_val;
82                     if (dwfl->segment_align > 1)
83                       {
84                         nread = 0;
85                         break;
86                       }
87                   }
88                 else if (d.a32[i].a_type == AT_PAGESZ
89                          && dwfl->segment_align <= 1)
90                   dwfl->segment_align = d.a32[i].a_un.a_val;
91               break;
92             case 8:
93               for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
94                 if (d.a64[i].a_type == AT_SYSINFO_EHDR)
95                   {
96                     *sysinfo_ehdr = d.a64[i].a_un.a_val;
97                     if (dwfl->segment_align > 1)
98                       {
99                         nread = 0;
100                         break;
101                       }
102                   }
103                 else if (d.a64[i].a_type == AT_PAGESZ
104                          && dwfl->segment_align <= 1)
105                   dwfl->segment_align = d.a64[i].a_un.a_val;
106               break;
107             default:
108               abort ();
109               break;
110             }
111         }
112     }
113   while (nread > 0);
114
115   close (fd);
116
117   return nread < 0 ? errno : 0;
118 }
119
120 static int
121 proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
122 {
123   unsigned int last_dmajor = -1, last_dminor = -1;
124   uint64_t last_ino = -1;
125   char *last_file = NULL;
126   Dwarf_Addr low = 0, high = 0;
127
128   inline bool report (void)
129     {
130       if (last_file != NULL)
131         {
132           Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
133                                                          low, high);
134           free (last_file);
135           last_file = NULL;
136           if (unlikely (mod == NULL))
137             return true;
138         }
139       return false;
140     }
141
142   char *line = NULL;
143   size_t linesz;
144   ssize_t len;
145   while ((len = getline (&line, &linesz, f)) > 0)
146     {
147       if (line[len - 1] == '\n')
148         line[len - 1] = '\0';
149
150       Dwarf_Addr start, end, offset;
151       unsigned int dmajor, dminor;
152       uint64_t ino;
153       int nread = -1;
154       if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
155                   " %x:%x %" PRIi64 " %n",
156                   &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
157           || nread <= 0)
158         {
159           free (line);
160           return ENOEXEC;
161         }
162
163       /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
164          report the last one and then this special one.  */
165       if (start == sysinfo_ehdr && start != 0)
166         {
167           if (report ())
168             {
169             bad_report:
170               free (line);
171               fclose (f);
172               return -1;
173             }
174
175           low = start;
176           high = end;
177           if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
178               || report ())
179             goto bad_report;
180         }
181
182       char *file = line + nread + strspn (line + nread, " \t");
183       if (file[0] == '\0' || (ino == 0 && dmajor == 0 && dminor == 0))
184         /* This line doesn't indicate a file mapping.  */
185         continue;
186
187       if (last_file != NULL
188           && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
189         {
190           /* This is another portion of the same file's mapping.  */
191           assert (!strcmp (last_file, file));
192           high = end;
193         }
194       else
195         {
196           /* This is a different file mapping.  Report the last one.  */
197           if (report ())
198             goto bad_report;
199           low = start;
200           high = end;
201           last_file = strdup (file);
202           last_ino = ino;
203           last_dmajor = dmajor;
204           last_dminor = dminor;
205         }
206     }
207   free (line);
208
209   int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
210
211   /* Report the final one.  */
212   bool lose = report ();
213
214   return result != 0 ? result : lose ? -1 : 0;
215 }
216
217 int
218 dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
219 {
220   return proc_maps_report (dwfl, f, 0, 0);
221 }
222 INTDEF (dwfl_linux_proc_maps_report)
223
224 int
225 dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
226 {
227   if (dwfl == NULL)
228     return -1;
229
230   /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it.  */
231   GElf_Addr sysinfo_ehdr = 0;
232   int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr);
233   if (result != 0)
234     return result;
235
236   char *fname;
237   if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
238     return ENOMEM;
239
240   FILE *f = fopen (fname, "r");
241   free (fname);
242   if (f == NULL)
243     return errno;
244
245   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
246
247   result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
248
249   fclose (f);
250
251   return result;
252 }
253 INTDEF (dwfl_linux_proc_report)
254
255 static ssize_t
256 read_proc_memory (void *arg, void *data, GElf_Addr address,
257                   size_t minread, size_t maxread)
258 {
259   const int fd = *(const int *) arg;
260   ssize_t nread = pread64 (fd, data, maxread, (off64_t) address);
261   /* Some kernels don't actually let us do this read, ignore those errors.  */
262   if (nread < 0 && (errno == EINVAL || errno == EPERM))
263     return 0;
264   if (nread > 0 && (size_t) nread < minread)
265     nread = 0;
266   return nread;
267 }
268
269 extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
270                                     GElf_Addr *loadbasep,
271                                     ssize_t (*read_memory) (void *arg,
272                                                             void *data,
273                                                             GElf_Addr address,
274                                                             size_t minread,
275                                                             size_t maxread),
276                                     void *arg);
277
278
279 /* Dwfl_Callbacks.find_elf */
280
281 int
282 dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
283                           void **userdata __attribute__ ((unused)),
284                           const char *module_name, Dwarf_Addr base,
285                           char **file_name, Elf **elfp)
286 {
287   if (module_name[0] == '/')
288     {
289       int fd = open64 (module_name, O_RDONLY);
290       if (fd >= 0)
291         {
292           *file_name = strdup (module_name);
293           if (*file_name == NULL)
294             {
295               close (fd);
296               return ENOMEM;
297             }
298         }
299       return fd;
300     }
301
302   int pid;
303   if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
304     {
305       /* Special case for in-memory ELF image.  */
306
307       char *fname;
308       if (asprintf (&fname, PROCMEMFMT, pid) < 0)
309         return -1;
310
311       int fd = open64 (fname, O_RDONLY);
312       free (fname);
313       if (fd < 0)
314         return -1;
315
316       *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
317
318       close (fd);
319
320       *file_name = NULL;
321       return -1;
322     }
323
324   abort ();
325   return -1;
326 }
327 INTDEF (dwfl_linux_proc_find_elf)