LCOV - code coverage report
Current view: top level - libdwfl - linux-proc-maps.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 127 154 82.5 %
Date: 2017-01-05 09:15:16 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Standard libdwfl callbacks for debugging a live Linux process.
       2             :    Copyright (C) 2005-2010, 2013, 2014, 2016 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 <sys/stat.h>
      33             : #include <errno.h>
      34             : #include <stdio.h>
      35             : #include <stdio_ext.h>
      36             : #include <stdbool.h>
      37             : #include <string.h>
      38             : #include <stdlib.h>
      39             : #include <fcntl.h>
      40             : #include <unistd.h>
      41             : #include <assert.h>
      42             : #include <endian.h>
      43             : #include "system.h"
      44             : 
      45             : 
      46             : #define PROCMAPSFMT     "/proc/%d/maps"
      47             : #define PROCMEMFMT      "/proc/%d/mem"
      48             : #define PROCAUXVFMT     "/proc/%d/auxv"
      49             : #define PROCEXEFMT      "/proc/%d/exe"
      50             : 
      51             : 
      52             : /* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable.  Return
      53             :    ELFCLASSNONE for an error.  */
      54             : 
      55             : static unsigned char
      56           0 : get_pid_class (pid_t pid)
      57             : {
      58             :   char *fname;
      59           0 :   if (asprintf (&fname, PROCEXEFMT, pid) < 0)
      60             :     return ELFCLASSNONE;
      61             : 
      62           0 :   int fd = open (fname, O_RDONLY);
      63           0 :   free (fname);
      64           0 :   if (fd < 0)
      65             :     return ELFCLASSNONE;
      66             : 
      67             :   unsigned char buf[EI_CLASS + 1];
      68           0 :   ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0);
      69           0 :   close (fd);
      70           0 :   if (nread != sizeof buf || buf[EI_MAG0] != ELFMAG0
      71           0 :       || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2
      72           0 :       || buf[EI_MAG3] != ELFMAG3
      73           0 :       || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32))
      74             :     return ELFCLASSNONE;
      75             : 
      76           0 :   return buf[EI_CLASS];
      77             : }
      78             : 
      79             : /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.
      80             : 
      81             :    It would be easiest to call get_pid_class and parse everything according to
      82             :    the 32-bit or 64-bit class.  But this would bring the overhead of syscalls
      83             :    to open and read the "/proc/%d/exe" file.
      84             : 
      85             :    Therefore this function tries to parse the "/proc/%d/auxv" content both
      86             :    ways, as if it were the 32-bit format and also if it were the 64-bit format.
      87             :    Only if it gives some valid data in both cases get_pid_class gets called.
      88             :    In most cases only one of the format bit sizes gives valid data and the
      89             :    get_pid_class call overhead can be saved.  */
      90             : 
      91             : static int
      92        5011 : grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
      93             : {
      94             :   char *fname;
      95        5011 :   if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
      96             :     return ENOMEM;
      97             : 
      98        5011 :   int fd = open (fname, O_RDONLY);
      99        5011 :   free (fname);
     100        5011 :   if (fd < 0)
     101           0 :     return errno == ENOENT ? 0 : errno;
     102             : 
     103        5011 :   GElf_Addr sysinfo_ehdr64 = 0;
     104        5011 :   GElf_Addr sysinfo_ehdr32 = 0;
     105        5011 :   GElf_Addr segment_align64 = dwfl->segment_align;
     106        5011 :   GElf_Addr segment_align32 = dwfl->segment_align;
     107        5011 :   off_t offset = 0;
     108             :   ssize_t nread;
     109             :   union
     110             :   {
     111             :     Elf64_auxv_t a64[64];
     112             :     Elf32_auxv_t a32[128];
     113             :   } d;
     114             :   do
     115             :     {
     116             :       eu_static_assert (sizeof d.a64 == sizeof d.a32);
     117        5011 :       nread = pread_retry (fd, d.a64, sizeof d.a64, offset);
     118        5011 :       if (nread < 0)
     119             :         {
     120           0 :           int ret = errno;
     121           0 :           close (fd);
     122             :           return ret;
     123             :         }
     124      190386 :       for (size_t a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++)
     125             :         {
     126      190386 :           const Elf32_auxv_t *a32 = d.a32 + a32i;
     127      190386 :           switch (a32->a_type)
     128             :           {
     129             :             case AT_SYSINFO_EHDR:
     130        5011 :               sysinfo_ehdr32 = a32->a_un.a_val;
     131             :               break;
     132             :             case AT_PAGESZ:
     133        5011 :               segment_align32 = a32->a_un.a_val;
     134             :               break;
     135             :           }
     136             :         }
     137       95193 :       for (size_t a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++)
     138             :         {
     139       95193 :           const Elf64_auxv_t *a64 = d.a64 + a64i;
     140       95193 :           switch (a64->a_type)
     141             :           {
     142             :             case AT_SYSINFO_EHDR:
     143        5009 :               sysinfo_ehdr64 = a64->a_un.a_val;
     144             :               break;
     145             :             case AT_PAGESZ:
     146        5009 :               segment_align64 = a64->a_un.a_val;
     147             :               break;
     148             :           }
     149             :         }
     150        5011 :       offset += nread;
     151             :     }
     152        5011 :   while (nread == sizeof d.a64);
     153             : 
     154        5011 :   close (fd);
     155             : 
     156        5011 :   bool valid64 = sysinfo_ehdr64 != 0 || segment_align64 != dwfl->segment_align;
     157        5011 :   bool valid32 = sysinfo_ehdr32 != 0 || segment_align32 != dwfl->segment_align;
     158             : 
     159        5011 :   unsigned char pid_class = ELFCLASSNONE;
     160        5011 :   if (valid64 && valid32)
     161           0 :     pid_class = get_pid_class (pid);
     162             : 
     163        5011 :   if (pid_class == ELFCLASS64 || (valid64 && ! valid32))
     164             :     {
     165        5009 :       *sysinfo_ehdr = sysinfo_ehdr64;
     166        5009 :       dwfl->segment_align = segment_align64;
     167             :       return 0;
     168             :     }
     169           2 :   if (pid_class == ELFCLASS32 || (! valid64 && valid32))
     170             :     {
     171           2 :       *sysinfo_ehdr = sysinfo_ehdr32;
     172           2 :       dwfl->segment_align = segment_align32;
     173             :       return 0;
     174             :     }
     175             :   return ENOEXEC;
     176             : }
     177             : 
     178             : static inline bool
     179       55107 : do_report (Dwfl *dwfl, char **plast_file, Dwarf_Addr low, Dwarf_Addr high)
     180             : {
     181       55107 :   if (*plast_file != NULL)
     182             :     {
     183       45079 :       Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, *plast_file,
     184             :                                                      low, high);
     185       45079 :       free (*plast_file);
     186       45079 :       *plast_file = NULL;
     187       45079 :       if (unlikely (mod == NULL))
     188             :         return true;
     189             :     }
     190             :   return false;
     191             : }
     192             : 
     193             : #define report() do_report(dwfl, &last_file, low, high)
     194             : 
     195             : static int
     196        5017 : proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
     197             : {
     198        5017 :   unsigned int last_dmajor = -1, last_dminor = -1;
     199        5017 :   uint64_t last_ino = -1;
     200        5017 :   char *last_file = NULL;
     201        5017 :   Dwarf_Addr low = 0, high = 0;
     202             : 
     203        5017 :   char *line = NULL;
     204             :   size_t linesz;
     205             :   ssize_t len;
     206      215391 :   while ((len = getline (&line, &linesz, f)) > 0)
     207             :     {
     208      205357 :       if (line[len - 1] == '\n')
     209      205357 :         line[len - 1] = '\0';
     210             : 
     211             :       Dwarf_Addr start, end, offset;
     212             :       unsigned int dmajor, dminor;
     213             :       uint64_t ino;
     214      205357 :       int nread = -1;
     215      205357 :       if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
     216             :                   " %x:%x %" PRIi64 " %n",
     217             :                   &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
     218      205357 :           || nread <= 0)
     219             :         {
     220           0 :           free (line);
     221           0 :           free (last_file);
     222           0 :           return ENOEXEC;
     223             :         }
     224             : 
     225             :       /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
     226             :          report the last one and then this special one.  */
     227      205357 :       if (start == sysinfo_ehdr && start != 0)
     228             :         {
     229        5011 :           if (report ())
     230             :             {
     231             :             bad_report:
     232           0 :               free (line);
     233           0 :               return -1;
     234             :             }
     235             : 
     236        5011 :           low = start;
     237        5011 :           high = end;
     238        5011 :           if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
     239        5011 :               || report ())
     240             :             goto bad_report;
     241             :         }
     242             : 
     243      410714 :       char *file = line + nread + strspn (line + nread, " \t");
     244      205357 :       if (file[0] != '/' || (ino == 0 && dmajor == 0 && dminor == 0))
     245             :         /* This line doesn't indicate a file mapping.  */
     246       60137 :         continue;
     247             : 
     248      145220 :       if (last_file != NULL
     249      140201 :           && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
     250             :         {
     251             :           /* This is another portion of the same file's mapping.  */
     252      105152 :           if (strcmp (last_file, file) != 0)
     253             :             {
     254           0 :               free (last_file);
     255           0 :               goto bad_report;
     256             :             }
     257      105152 :           high = end;
     258             :         }
     259             :       else
     260             :         {
     261             :           /* This is a different file mapping.  Report the last one.  */
     262       40068 :           if (report ())
     263             :             goto bad_report;
     264       40068 :           low = start;
     265       40068 :           high = end;
     266       40068 :           last_file = strdup (file);
     267       40068 :           last_ino = ino;
     268       40068 :           last_dmajor = dmajor;
     269       40068 :           last_dminor = dminor;
     270             :         }
     271             :     }
     272        5017 :   free (line);
     273             : 
     274       10034 :   int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
     275             : 
     276             :   /* Report the final one.  */
     277        5017 :   bool lose = report ();
     278             : 
     279        5017 :   return result != 0 ? result : lose ? -1 : 0;
     280             : }
     281             : 
     282             : int
     283           6 : dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
     284             : {
     285           6 :   return proc_maps_report (dwfl, f, 0, 0);
     286             : }
     287             : INTDEF (dwfl_linux_proc_maps_report)
     288             : 
     289             : int
     290        5011 : dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
     291             : {
     292        5011 :   if (dwfl == NULL)
     293             :     return -1;
     294             : 
     295             :   /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it.  */
     296        5011 :   GElf_Addr sysinfo_ehdr = 0;
     297        5011 :   int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr);
     298        5011 :   if (result != 0)
     299             :     return result;
     300             : 
     301             :   char *fname;
     302        5011 :   if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
     303             :     return ENOMEM;
     304             : 
     305        5011 :   FILE *f = fopen (fname, "r");
     306        5011 :   free (fname);
     307        5011 :   if (f == NULL)
     308           0 :     return errno;
     309             : 
     310        5011 :   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
     311             : 
     312        5011 :   result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
     313             : 
     314        5011 :   fclose (f);
     315             : 
     316        5011 :   return result;
     317             : }
     318             : INTDEF (dwfl_linux_proc_report)
     319             : 
     320             : static ssize_t
     321          15 : read_proc_memory (void *arg, void *data, GElf_Addr address,
     322             :                   size_t minread, size_t maxread)
     323             : {
     324          15 :   const int fd = *(const int *) arg;
     325             : 
     326             :   /* This code relies on the fact the Linux kernel accepts negative
     327             :      offsets when seeking /dev/$$/mem files, as a special case. In
     328             :      particular pread cannot be used here, because it will always
     329             :      return EINVAL when passed a negative offset.  */
     330             : 
     331          15 :   if (lseek (fd, (off_t) address, SEEK_SET) == -1)
     332             :     return -1;
     333             : 
     334          15 :   ssize_t nread = read (fd, data, maxread);
     335             : 
     336          15 :   if (nread > 0 && (size_t) nread < minread)
     337           0 :     nread = 0;
     338             :   return nread;
     339             : }
     340             : 
     341             : extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
     342             :                                     GElf_Xword pagesize,
     343             :                                     GElf_Addr *loadbasep,
     344             :                                     ssize_t (*read_memory) (void *arg,
     345             :                                                             void *data,
     346             :                                                             GElf_Addr address,
     347             :                                                             size_t minread,
     348             :                                                             size_t maxread),
     349             :                                     void *arg);
     350             : 
     351             : 
     352             : /* Dwfl_Callbacks.find_elf */
     353             : 
     354             : int
     355        5043 : dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
     356             :                           void **userdata __attribute__ ((unused)),
     357             :                           const char *module_name, Dwarf_Addr base,
     358             :                           char **file_name, Elf **elfp)
     359             : {
     360        5043 :   int pid = -1;
     361        5043 :   if (module_name[0] == '/')
     362             :     {
     363             :       /* When this callback is used together with dwfl_linux_proc_report
     364             :          then we might see mappings of special character devices.  Make
     365             :          sure we only open and return regular files.  Special devices
     366             :          might hang on open or read.  (deleted) files are super special.
     367             :          The image might come from memory if we are attached.  */
     368             :       struct stat sb;
     369        5039 :       if (stat (module_name, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG)
     370             :         {
     371           3 :           if (strcmp (strrchr (module_name, ' ') ?: "", " (deleted)") == 0)
     372           1 :             pid = INTUSE(dwfl_pid) (mod->dwfl);
     373             :           else
     374        5038 :             return -1;
     375             :         }
     376             : 
     377        5037 :       if (pid == -1)
     378             :         {
     379        5036 :           int fd = open (module_name, O_RDONLY);
     380        5036 :           if (fd >= 0)
     381             :             {
     382        5036 :               *file_name = strdup (module_name);
     383        5036 :               if (*file_name == NULL)
     384             :                 {
     385           0 :                   close (fd);
     386           0 :                   return ENOMEM;
     387             :                 }
     388             :             }
     389             :           return fd;
     390             :         }
     391             :     }
     392             : 
     393           5 :   if (pid != -1 || sscanf (module_name, "[vdso: %d]", &pid) == 1)
     394             :     {
     395             :       /* Special case for in-memory ELF image.  */
     396             : 
     397           5 :       bool detach = false;
     398           5 :       bool tid_was_stopped = false;
     399           5 :       struct __libdwfl_pid_arg *pid_arg = __libdwfl_get_pid_arg (mod->dwfl);
     400           5 :       if (pid_arg != NULL && ! pid_arg->assume_ptrace_stopped)
     401             :         {
     402             :           /* If any thread is already attached we are fine.  Read
     403             :              through that thread.  It doesn't have to be the main
     404             :              thread pid.  */
     405           4 :           pid_t tid = pid_arg->tid_attached;
     406           4 :           if (tid != 0)
     407           1 :             pid = tid;
     408             :           else
     409           3 :             detach = __libdwfl_ptrace_attach (pid, &tid_was_stopped);
     410             :         }
     411             : 
     412             :       char *fname;
     413           5 :       if (asprintf (&fname, PROCMEMFMT, pid) < 0)
     414             :         goto detach;
     415             : 
     416           5 :       int fd = open (fname, O_RDONLY);
     417           5 :       free (fname);
     418           5 :       if (fd < 0)
     419             :         goto detach;
     420             : 
     421           5 :       *elfp = elf_from_remote_memory (base, getpagesize (), NULL,
     422             :                                       &read_proc_memory, &fd);
     423             : 
     424           5 :       close (fd);
     425             : 
     426           5 :       *file_name = NULL;
     427             : 
     428             :     detach:
     429           5 :       if (detach)
     430           2 :         __libdwfl_ptrace_detach (pid, tid_was_stopped);
     431             :       return -1;
     432             :     }
     433             : 
     434             :   return -1;
     435             : }
     436             : INTDEF (dwfl_linux_proc_find_elf)

Generated by: LCOV version 1.12