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.
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
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
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
18 or both in parallel, as here.
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.
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/>. */
31 #include <sys/types.h>
34 #include <stdio_ext.h>
44 #define PROCMAPSFMT "/proc/%d/maps"
45 #define PROCMEMFMT "/proc/%d/mem"
46 #define PROCAUXVFMT "/proc/%d/auxv"
49 /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */
52 grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
55 if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
58 int fd = open64 (fname, O_RDONLY);
61 return errno == ENOENT ? 0 : errno;
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)];
72 nread = read (fd, &d, sizeof d);
75 switch (sizeof (long int))
78 for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
79 if (d.a32[i].a_type == AT_SYSINFO_EHDR)
81 *sysinfo_ehdr = d.a32[i].a_un.a_val;
82 if (dwfl->segment_align > 1)
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;
93 for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
94 if (d.a64[i].a_type == AT_SYSINFO_EHDR)
96 *sysinfo_ehdr = d.a64[i].a_un.a_val;
97 if (dwfl->segment_align > 1)
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;
117 return nread < 0 ? errno : 0;
121 proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
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;
128 inline bool report (void)
130 if (last_file != NULL)
132 Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
136 if (unlikely (mod == NULL))
145 while ((len = getline (&line, &linesz, f)) > 0)
147 if (line[len - 1] == '\n')
148 line[len - 1] = '\0';
150 Dwarf_Addr start, end, offset;
151 unsigned int dmajor, dminor;
154 if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
155 " %x:%x %" PRIi64 " %n",
156 &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
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)
177 if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
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. */
187 if (last_file != NULL
188 && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
190 /* This is another portion of the same file's mapping. */
191 assert (!strcmp (last_file, file));
196 /* This is a different file mapping. Report the last one. */
201 last_file = strdup (file);
203 last_dmajor = dmajor;
204 last_dminor = dminor;
209 int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
211 /* Report the final one. */
212 bool lose = report ();
214 return result != 0 ? result : lose ? -1 : 0;
218 dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
220 return proc_maps_report (dwfl, f, 0, 0);
222 INTDEF (dwfl_linux_proc_maps_report)
225 dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
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);
237 if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
240 FILE *f = fopen (fname, "r");
245 (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
247 result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
253 INTDEF (dwfl_linux_proc_report)
256 read_proc_memory (void *arg, void *data, GElf_Addr address,
257 size_t minread, size_t maxread)
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))
264 if (nread > 0 && (size_t) nread < minread)
269 extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
270 GElf_Addr *loadbasep,
271 ssize_t (*read_memory) (void *arg,
279 /* Dwfl_Callbacks.find_elf */
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)
287 if (module_name[0] == '/')
289 int fd = open64 (module_name, O_RDONLY);
292 *file_name = strdup (module_name);
293 if (*file_name == NULL)
303 if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
305 /* Special case for in-memory ELF image. */
308 if (asprintf (&fname, PROCMEMFMT, pid) < 0)
311 int fd = open64 (fname, O_RDONLY);
316 *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
327 INTDEF (dwfl_linux_proc_find_elf)