diff options
Diffstat (limited to 'src/libdwfl/linux-proc-maps.c')
-rw-r--r-- | src/libdwfl/linux-proc-maps.c | 422 |
1 files changed, 0 insertions, 422 deletions
diff --git a/src/libdwfl/linux-proc-maps.c b/src/libdwfl/linux-proc-maps.c deleted file mode 100644 index d0858342..00000000 --- a/src/libdwfl/linux-proc-maps.c +++ /dev/null @@ -1,422 +0,0 @@ -/* Standard libdwfl callbacks for debugging a live Linux process. - Copyright (C) 2005-2010, 2013, 2014 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see <http://www.gnu.org/licenses/>. */ - -#include "libdwflP.h" -#include <inttypes.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <errno.h> -#include <stdio.h> -#include <stdio_ext.h> -#include <stdbool.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <unistd.h> -#include <assert.h> -#include <endian.h> -#include "system.h" - - -#define PROCMAPSFMT "/proc/%d/maps" -#define PROCMEMFMT "/proc/%d/mem" -#define PROCAUXVFMT "/proc/%d/auxv" -#define PROCEXEFMT "/proc/%d/exe" - - -/* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable. Return - ELFCLASSNONE for an error. */ - -static unsigned char -get_pid_class (pid_t pid) -{ - char *fname; - if (asprintf (&fname, PROCEXEFMT, pid) < 0) - return ELFCLASSNONE; - - int fd = open64 (fname, O_RDONLY); - free (fname); - if (fd < 0) - return ELFCLASSNONE; - - unsigned char buf[EI_CLASS + 1]; - ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0); - close (fd); - if (nread != sizeof buf || buf[EI_MAG0] != ELFMAG0 - || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2 - || buf[EI_MAG3] != ELFMAG3 - || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32)) - return ELFCLASSNONE; - - return buf[EI_CLASS]; -} - -/* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. - - It would be easiest to call get_pid_class and parse everything according to - the 32-bit or 64-bit class. But this would bring the overhead of syscalls - to open and read the "/proc/%d/exe" file. - - Therefore this function tries to parse the "/proc/%d/auxv" content both - ways, as if it were the 32-bit format and also if it were the 64-bit format. - Only if it gives some valid data in both cases get_pid_class gets called. - In most cases only one of the format bit sizes gives valid data and the - get_pid_class call overhead can be saved. */ - -static int -grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr) -{ - char *fname; - if (asprintf (&fname, PROCAUXVFMT, pid) < 0) - return ENOMEM; - - int fd = open64 (fname, O_RDONLY); - free (fname); - if (fd < 0) - return errno == ENOENT ? 0 : errno; - - GElf_Addr sysinfo_ehdr64 = 0; - GElf_Addr sysinfo_ehdr32 = 0; - GElf_Addr segment_align64 = dwfl->segment_align; - GElf_Addr segment_align32 = dwfl->segment_align; - off_t offset = 0; - ssize_t nread; - union - { - Elf64_auxv_t a64[64]; - Elf32_auxv_t a32[128]; - } d; - do - { - eu_static_assert (sizeof d.a64 == sizeof d.a32); - nread = pread_retry (fd, d.a64, sizeof d.a64, offset); - if (nread < 0) - { - int ret = errno; - close (fd); - return ret; - } - for (size_t a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++) - { - const Elf32_auxv_t *a32 = d.a32 + a32i; - switch (a32->a_type) - { - case AT_SYSINFO_EHDR: - sysinfo_ehdr32 = a32->a_un.a_val; - break; - case AT_PAGESZ: - segment_align32 = a32->a_un.a_val; - break; - } - } - for (size_t a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++) - { - const Elf64_auxv_t *a64 = d.a64 + a64i; - switch (a64->a_type) - { - case AT_SYSINFO_EHDR: - sysinfo_ehdr64 = a64->a_un.a_val; - break; - case AT_PAGESZ: - segment_align64 = a64->a_un.a_val; - break; - } - } - offset += nread; - } - while (nread == sizeof d.a64); - - close (fd); - - bool valid64 = sysinfo_ehdr64 != 0 || segment_align64 != dwfl->segment_align; - bool valid32 = sysinfo_ehdr32 != 0 || segment_align32 != dwfl->segment_align; - - unsigned char pid_class = ELFCLASSNONE; - if (valid64 && valid32) - pid_class = get_pid_class (pid); - - if (pid_class == ELFCLASS64 || (valid64 && ! valid32)) - { - *sysinfo_ehdr = sysinfo_ehdr64; - dwfl->segment_align = segment_align64; - return 0; - } - if (pid_class == ELFCLASS32 || (! valid64 && valid32)) - { - *sysinfo_ehdr = sysinfo_ehdr32; - dwfl->segment_align = segment_align32; - return 0; - } - return ENOEXEC; -} - -static int -proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid) -{ - unsigned int last_dmajor = -1, last_dminor = -1; - uint64_t last_ino = -1; - char *last_file = NULL; - Dwarf_Addr low = 0, high = 0; - - inline bool report (void) - { - if (last_file != NULL) - { - Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file, - low, high); - free (last_file); - last_file = NULL; - if (unlikely (mod == NULL)) - return true; - } - return false; - } - - char *line = NULL; - size_t linesz; - ssize_t len; - while ((len = getline (&line, &linesz, f)) > 0) - { - if (line[len - 1] == '\n') - line[len - 1] = '\0'; - - Dwarf_Addr start, end, offset; - unsigned int dmajor, dminor; - uint64_t ino; - int nread = -1; - if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64 - " %x:%x %" PRIi64 " %n", - &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6 - || nread <= 0) - { - free (line); - return ENOEXEC; - } - - /* If this is the special mapping AT_SYSINFO_EHDR pointed us at, - report the last one and then this special one. */ - if (start == sysinfo_ehdr && start != 0) - { - if (report ()) - { - bad_report: - free (line); - return -1; - } - - low = start; - high = end; - if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0 - || report ()) - goto bad_report; - } - - char *file = line + nread + strspn (line + nread, " \t"); - if (file[0] != '/' || (ino == 0 && dmajor == 0 && dminor == 0)) - /* This line doesn't indicate a file mapping. */ - continue; - - if (last_file != NULL - && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor) - { - /* This is another portion of the same file's mapping. */ - if (strcmp (last_file, file) != 0) - goto bad_report; - high = end; - } - else - { - /* This is a different file mapping. Report the last one. */ - if (report ()) - goto bad_report; - low = start; - high = end; - last_file = strdup (file); - last_ino = ino; - last_dmajor = dmajor; - last_dminor = dminor; - } - } - free (line); - - int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC; - - /* Report the final one. */ - bool lose = report (); - - return result != 0 ? result : lose ? -1 : 0; -} - -int -dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f) -{ - return proc_maps_report (dwfl, f, 0, 0); -} -INTDEF (dwfl_linux_proc_maps_report) - -int -dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid) -{ - if (dwfl == NULL) - return -1; - - /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it. */ - GElf_Addr sysinfo_ehdr = 0; - int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr); - if (result != 0) - return result; - - char *fname; - if (asprintf (&fname, PROCMAPSFMT, pid) < 0) - return ENOMEM; - - FILE *f = fopen (fname, "r"); - free (fname); - if (f == NULL) - return errno; - - (void) __fsetlocking (f, FSETLOCKING_BYCALLER); - - result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid); - - fclose (f); - - return result; -} -INTDEF (dwfl_linux_proc_report) - -static ssize_t -read_proc_memory (void *arg, void *data, GElf_Addr address, - size_t minread, size_t maxread) -{ - const int fd = *(const int *) arg; - ssize_t nread = pread64 (fd, data, maxread, (off64_t) address); - /* Some kernels don't actually let us do this read, ignore those errors. */ - if (nread < 0 && (errno == EINVAL || errno == EPERM)) - return 0; - if (nread > 0 && (size_t) nread < minread) - nread = 0; - return nread; -} - -extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma, - GElf_Xword pagesize, - GElf_Addr *loadbasep, - ssize_t (*read_memory) (void *arg, - void *data, - GElf_Addr address, - size_t minread, - size_t maxread), - void *arg); - - -/* Dwfl_Callbacks.find_elf */ - -int -dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)), - void **userdata __attribute__ ((unused)), - const char *module_name, Dwarf_Addr base, - char **file_name, Elf **elfp) -{ - int pid = -1; - if (module_name[0] == '/') - { - /* When this callback is used together with dwfl_linux_proc_report - then we might see mappings of special character devices. Make - sure we only open and return regular files. Special devices - might hang on open or read. (deleted) files are super special. - The image might come from memory if we are attached. */ - struct stat sb; - if (stat (module_name, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG) - { - if (strcmp (strrchr (module_name, ' ') ?: "", " (deleted)") == 0) - pid = INTUSE(dwfl_pid) (mod->dwfl); - else - return -1; - } - - if (pid == -1) - { - int fd = open64 (module_name, O_RDONLY); - if (fd >= 0) - { - *file_name = strdup (module_name); - if (*file_name == NULL) - { - close (fd); - return ENOMEM; - } - } - return fd; - } - } - - if (pid != -1 || sscanf (module_name, "[vdso: %d]", &pid) == 1) - { - /* Special case for in-memory ELF image. */ - - bool detach = false; - bool tid_was_stopped = false; - struct __libdwfl_pid_arg *pid_arg = __libdwfl_get_pid_arg (mod->dwfl); - if (pid_arg != NULL && ! pid_arg->assume_ptrace_stopped) - { - /* If any thread is already attached we are fine. Read - through that thread. It doesn't have to be the main - thread pid. */ - pid_t tid = pid_arg->tid_attached; - if (tid != 0) - pid = tid; - else - detach = __libdwfl_ptrace_attach (pid, &tid_was_stopped); - } - - char *fname; - if (asprintf (&fname, PROCMEMFMT, pid) < 0) - goto detach; - - int fd = open64 (fname, O_RDONLY); - free (fname); - if (fd < 0) - goto detach; - - *elfp = elf_from_remote_memory (base, getpagesize (), NULL, - &read_proc_memory, &fd); - - close (fd); - - *file_name = NULL; - - detach: - if (detach) - __libdwfl_ptrace_detach (pid, tid_was_stopped); - return -1; - } - - return -1; -} -INTDEF (dwfl_linux_proc_find_elf) |