blob: 2611c73a42323dfbf840389527063bfa0b638e6d [file] [log] [blame]
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001/* Standard libdwfl callbacks for debugging a live Linux process.
2 Copyright (C) 2005 Red Hat, Inc.
3
4 This program is Open Source software; you can redistribute it and/or
5 modify it under the terms of the Open Software License version 1.0 as
6 published by the Open Source Initiative.
7
8 You should have received a copy of the Open Software License along
9 with this program; if not, you may obtain a copy of the Open Software
10 License version 1.0 from http://www.opensource.org/licenses/osl.php or
11 by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
12 3001 King Ranch Road, Ukiah, CA 95482. */
13
14#include "libdwflP.h"
15#include <inttypes.h>
16#include <sys/types.h>
17#include <errno.h>
18#include <stdio.h>
19#include <stdio_ext.h>
20#include <stdbool.h>
21#include <string.h>
22#include <stdlib.h>
23#include <fcntl.h>
24#include <unistd.h>
25#include <assert.h>
26#include <endian.h>
27
28
29#define PROCMAPSFMT "/proc/%d/maps"
30#define PROCMEMFMT "/proc/%d/mem"
31#define PROCAUXVFMT "/proc/%d/auxv"
32
33
34/* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */
35
36static int
37find_sysinfo_ehdr (pid_t pid, GElf_Addr *sysinfo_ehdr)
38{
39 char *fname = NULL;
40 asprintf (&fname, PROCAUXVFMT, pid);
41 if (fname == NULL)
42 return ENOMEM;
43
44 int fd = open64 (fname, O_RDONLY);
45 free (fname);
46 if (fd < 0)
47 return errno == ENOENT ? 0 : errno;
48
49 ssize_t nread;
50 do
51 {
52 union
53 {
54 char buffer[sizeof (long int) * 2 * 64];
55 Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)];
56 Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)];
57 } d;
58 nread = read (fd, &d, sizeof d);
59 if (nread > 0)
60 {
61 switch (sizeof (long int))
62 {
63 case 4:
64 for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
65 if (d.a32[i].a_type == AT_SYSINFO_EHDR)
66 {
67 *sysinfo_ehdr = d.a32[i].a_un.a_val;
68 nread = 0;
69 break;
70 }
71 break;
72 case 8:
73 for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
74 if (d.a64[i].a_type == AT_SYSINFO_EHDR)
75 {
76 *sysinfo_ehdr = d.a64[i].a_un.a_val;
77 nread = 0;
78 break;
79 }
80 break;
81 default:
82 abort ();
83 break;
84 }
85 }
86 }
87 while (nread > 0);
88
89 close (fd);
90
91 return nread < 0 ? errno : 0;
92}
93
94int
95dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
96{
97 if (dwfl == NULL)
98 return -1;
99
100 /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it. */
101 GElf_Addr sysinfo_ehdr = 0;
102 int result = find_sysinfo_ehdr (pid, &sysinfo_ehdr);
103 if (result != 0)
104 return result;
105
106 char *fname = NULL;
107 asprintf (&fname, PROCMAPSFMT, pid);
108 if (fname == NULL)
109 return ENOMEM;
110
111 FILE *f = fopen (fname, "r");
112 free (fname);
113 if (f == NULL)
114 return errno;
115
116 (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
117
118 unsigned int last_dmajor = -1, last_dminor = -1;
119 uint64_t last_ino = -1;
120 char *last_file = NULL;
121 Dwarf_Addr low = 0, high = 0;
122
123 inline bool report (void)
124 {
125 if (last_file != NULL)
126 {
127 if (INTUSE(dwfl_report_module) (dwfl, last_file, low, high) == NULL)
128 {
129 free (last_file);
130 return true;
131 }
132 last_file = NULL;
133 }
134 return false;
135 }
136
137 char *line = NULL;
138 size_t linesz;
139 ssize_t len;
140 while ((len = getline (&line, &linesz, f)) > 0)
141 {
142 if (line[len - 1] == '\n')
143 line[len - 1] = '\0';
144
145 Dwarf_Addr start, end, offset;
146 unsigned int dmajor, dminor;
147 uint64_t ino;
148 int nread = -1;
149 if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
150 " %x:%x %" PRIi64 " %n",
151 &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
152 || nread <= 0)
153 {
154 free (line);
155 return ENOEXEC;
156 }
157
158 /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
159 report the last one and then this special one. */
160 if (start == sysinfo_ehdr && start != 0)
161 {
162 if (report ())
163 {
164 bad_report:
165 free (line);
166 fclose (f);
167 return -1;
168 }
169
170 low = start;
171 high = end;
172 if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
173 || report ())
174 goto bad_report;
175 }
176
177 char *file = line + nread + strspn (line + nread, " \t");
178 if (file[0] == '\0' || (ino == 0 && dmajor == 0 && dminor == 0))
179 /* This line doesn't indicate a file mapping. */
180 continue;
181
182 if (last_file != NULL
183 && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
184 {
185 /* This is another portion of the same file's mapping. */
186 assert (!strcmp (last_file, file));
187 high = end;
188 }
189 else
190 {
191 /* This is a different file mapping. Report the last one. */
192 if (report ())
193 goto bad_report;
194 low = start;
195 high = end;
196 last_file = strdup (file);
197 last_ino = ino;
198 last_dmajor = dmajor;
199 last_dminor = dminor;
200 }
201 }
202 free (line);
203
204 result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
205 fclose (f);
206
207 /* Report the final one. */
208 bool lose = report ();
209
210 return result != 0 ? result : lose ? -1 : 0;
211}
212INTDEF (dwfl_linux_proc_report)
213
214
215static ssize_t
216read_proc_memory (void *arg, void *data, GElf_Addr address,
217 size_t minread, size_t maxread)
218{
219 const int fd = *(const int *) arg;
220 ssize_t nread = pread64 (fd, data, maxread, (off64_t) address);
221 if (nread > 0 && (size_t) nread < minread)
222 nread = 0;
223 return nread;
224}
225
226extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
227 GElf_Addr *loadbasep,
228 ssize_t (*read_memory) (void *arg,
229 void *data,
230 GElf_Addr address,
231 size_t minread,
232 size_t maxread),
233 void *arg);
234
235
236/* Dwfl_Callbacks.find_elf */
237
238int
239dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
240 void **userdata __attribute__ ((unused)),
241 const char *module_name, Dwarf_Addr base,
242 char **file_name, Elf **elfp)
243{
244 if (module_name[0] == '/')
245 {
246 int fd = open64 (module_name, O_RDONLY);
247 if (fd >= 0)
248 {
249 *file_name = strdup (module_name);
250 if (*file_name == NULL)
251 {
252 close (fd);
253 return ENOMEM;
254 }
255 }
256 return fd;
257 }
258
259 int pid;
260 if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
261 {
262 /* Special case for in-memory ELF image. */
263
264 char *fname = NULL;
265 asprintf (&fname, PROCMEMFMT, pid);
266 if (fname == NULL)
267 return -1;
268
269 int fd = open64 (fname, O_RDONLY);
270 free (fname);
271 if (fd < 0)
272 return -1;
273
274 *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
275
276 close (fd);
277
278 *file_name = NULL;
279 return -1;
280 }
281
282 abort ();
283 return -1;
284}
285INTDEF (dwfl_linux_proc_find_elf)