Home | History | Annotate | Download | only in libdwfl
      1 /* Standard libdwfl callbacks for debugging a live Linux process.
      2    Copyright (C) 2005, 2007, 2008 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    In addition, as a special exception, Red Hat, Inc. gives You the
     19    additional right to link the code of Red Hat elfutils with code licensed
     20    under any Open Source Initiative certified open source license
     21    (http://www.opensource.org/licenses/index.php) which requires the
     22    distribution of source code with any binary distribution and to
     23    distribute linked combinations of the two.  Non-GPL Code permitted under
     24    this exception must only link to the code of Red Hat elfutils through
     25    those well defined interfaces identified in the file named EXCEPTION
     26    found in the source code files (the "Approved Interfaces").  The files
     27    of Non-GPL Code may instantiate templates or use macros or inline
     28    functions from the Approved Interfaces without causing the resulting
     29    work to be covered by the GNU General Public License.  Only Red Hat,
     30    Inc. may make changes or additions to the list of Approved Interfaces.
     31    Red Hat's grant of this exception is conditioned upon your not adding
     32    any new exceptions.  If you wish to add a new Approved Interface or
     33    exception, please contact Red Hat.  You must obey the GNU General Public
     34    License in all respects for all of the Red Hat elfutils code and other
     35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     36    covered by this exception.  If you modify this file, you may extend this
     37    exception to your version of the file, but you are not obligated to do
     38    so.  If you do not wish to provide this exception without modification,
     39    you must delete this exception statement from your version and license
     40    this file solely under the GPL without exception.
     41 
     42    Red Hat elfutils is an included package of the Open Invention Network.
     43    An included package of the Open Invention Network is a package for which
     44    Open Invention Network licensees cross-license their patents.  No patent
     45    license is granted, either expressly or impliedly, by designation as an
     46    included package.  Should you wish to participate in the Open Invention
     47    Network licensing program, please visit www.openinventionnetwork.com
     48    <http://www.openinventionnetwork.com>.  */
     49 
     50 #include "libdwflP.h"
     51 #include <inttypes.h>
     52 #include <sys/types.h>
     53 #include <errno.h>
     54 #include <stdio.h>
     55 #include <stdio_ext.h>
     56 #include <stdbool.h>
     57 #include <string.h>
     58 #include <stdlib.h>
     59 #include <fcntl.h>
     60 #include <unistd.h>
     61 #include <assert.h>
     62 #include <endian.h>
     63 
     64 
     65 #define PROCMAPSFMT	"/proc/%d/maps"
     66 #define PROCMEMFMT	"/proc/%d/mem"
     67 #define PROCAUXVFMT	"/proc/%d/auxv"
     68 
     69 
     70 /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.  */
     71 
     72 static int
     73 find_sysinfo_ehdr (pid_t pid, GElf_Addr *sysinfo_ehdr)
     74 {
     75   char *fname;
     76   if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
     77     return ENOMEM;
     78 
     79   int fd = open64 (fname, O_RDONLY);
     80   free (fname);
     81   if (fd < 0)
     82     return errno == ENOENT ? 0 : errno;
     83 
     84   ssize_t nread;
     85   do
     86     {
     87       union
     88       {
     89 	char buffer[sizeof (long int) * 2 * 64];
     90 	Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)];
     91 	Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)];
     92       } d;
     93       nread = read (fd, &d, sizeof d);
     94       if (nread > 0)
     95 	{
     96 	  switch (sizeof (long int))
     97 	    {
     98 	    case 4:
     99 	      for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
    100 		if (d.a32[i].a_type == AT_SYSINFO_EHDR)
    101 		  {
    102 		    *sysinfo_ehdr = d.a32[i].a_un.a_val;
    103 		    nread = 0;
    104 		    break;
    105 		  }
    106 	      break;
    107 	    case 8:
    108 	      for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
    109 		if (d.a64[i].a_type == AT_SYSINFO_EHDR)
    110 		  {
    111 		    *sysinfo_ehdr = d.a64[i].a_un.a_val;
    112 		    nread = 0;
    113 		    break;
    114 		  }
    115 	      break;
    116 	    default:
    117 	      abort ();
    118 	      break;
    119 	    }
    120 	}
    121     }
    122   while (nread > 0);
    123 
    124   close (fd);
    125 
    126   return nread < 0 ? errno : 0;
    127 }
    128 
    129 static int
    130 proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
    131 {
    132   unsigned int last_dmajor = -1, last_dminor = -1;
    133   uint64_t last_ino = -1;
    134   char *last_file = NULL;
    135   Dwarf_Addr low = 0, high = 0;
    136 
    137   inline bool report (void)
    138     {
    139       if (last_file != NULL)
    140 	{
    141 	  Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
    142 							 low, high);
    143 	  free (last_file);
    144 	  last_file = NULL;
    145 	  if (unlikely (mod == NULL))
    146 	    return true;
    147 	}
    148       return false;
    149     }
    150 
    151   char *line = NULL;
    152   size_t linesz;
    153   ssize_t len;
    154   while ((len = getline (&line, &linesz, f)) > 0)
    155     {
    156       if (line[len - 1] == '\n')
    157 	line[len - 1] = '\0';
    158 
    159       Dwarf_Addr start, end, offset;
    160       unsigned int dmajor, dminor;
    161       uint64_t ino;
    162       int nread = -1;
    163       if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
    164 		  " %x:%x %" PRIi64 " %n",
    165 		  &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
    166 	  || nread <= 0)
    167 	{
    168 	  free (line);
    169 	  return ENOEXEC;
    170 	}
    171 
    172       /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
    173 	 report the last one and then this special one.  */
    174       if (start == sysinfo_ehdr && start != 0)
    175 	{
    176 	  if (report ())
    177 	    {
    178 	    bad_report:
    179 	      free (line);
    180 	      fclose (f);
    181 	      return -1;
    182 	    }
    183 
    184 	  low = start;
    185 	  high = end;
    186 	  if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
    187 	      || report ())
    188 	    goto bad_report;
    189 	}
    190 
    191       char *file = line + nread + strspn (line + nread, " \t");
    192       if (file[0] == '\0' || (ino == 0 && dmajor == 0 && dminor == 0))
    193 	/* This line doesn't indicate a file mapping.  */
    194 	continue;
    195 
    196       if (last_file != NULL
    197 	  && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
    198 	{
    199 	  /* This is another portion of the same file's mapping.  */
    200 	  assert (!strcmp (last_file, file));
    201 	  high = end;
    202 	}
    203       else
    204 	{
    205 	  /* This is a different file mapping.  Report the last one.  */
    206 	  if (report ())
    207 	    goto bad_report;
    208 	  low = start;
    209 	  high = end;
    210 	  last_file = strdup (file);
    211 	  last_ino = ino;
    212 	  last_dmajor = dmajor;
    213 	  last_dminor = dminor;
    214 	}
    215     }
    216   free (line);
    217 
    218   int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
    219 
    220   /* Report the final one.  */
    221   bool lose = report ();
    222 
    223   return result != 0 ? result : lose ? -1 : 0;
    224 }
    225 
    226 int
    227 dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
    228 {
    229   return proc_maps_report (dwfl, f, 0, 0);
    230 }
    231 INTDEF (dwfl_linux_proc_maps_report)
    232 
    233 int
    234 dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
    235 {
    236   if (dwfl == NULL)
    237     return -1;
    238 
    239   /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it.  */
    240   GElf_Addr sysinfo_ehdr = 0;
    241   int result = find_sysinfo_ehdr (pid, &sysinfo_ehdr);
    242   if (result != 0)
    243     return result;
    244 
    245   char *fname;
    246   if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
    247     return ENOMEM;
    248 
    249   FILE *f = fopen (fname, "r");
    250   free (fname);
    251   if (f == NULL)
    252     return errno;
    253 
    254   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
    255 
    256   result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
    257 
    258   fclose (f);
    259 
    260   return result;
    261 }
    262 INTDEF (dwfl_linux_proc_report)
    263 
    264 static ssize_t
    265 read_proc_memory (void *arg, void *data, GElf_Addr address,
    266 		  size_t minread, size_t maxread)
    267 {
    268   const int fd = *(const int *) arg;
    269   ssize_t nread = pread64 (fd, data, maxread, (off64_t) address);
    270   /* Some kernels don't actually let us do this read, ignore those errors.  */
    271   if (nread < 0 && (errno == EINVAL || errno == EPERM))
    272     return 0;
    273   if (nread > 0 && (size_t) nread < minread)
    274     nread = 0;
    275   return nread;
    276 }
    277 
    278 extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
    279 				    GElf_Addr *loadbasep,
    280 				    ssize_t (*read_memory) (void *arg,
    281 							    void *data,
    282 							    GElf_Addr address,
    283 							    size_t minread,
    284 							    size_t maxread),
    285 				    void *arg);
    286 
    287 
    288 /* Dwfl_Callbacks.find_elf */
    289 
    290 int
    291 dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
    292 			  void **userdata __attribute__ ((unused)),
    293 			  const char *module_name, Dwarf_Addr base,
    294 			  char **file_name, Elf **elfp)
    295 {
    296   if (module_name[0] == '/')
    297     {
    298       int fd = open64 (module_name, O_RDONLY);
    299       if (fd >= 0)
    300 	{
    301 	  *file_name = strdup (module_name);
    302 	  if (*file_name == NULL)
    303 	    {
    304 	      close (fd);
    305 	      return ENOMEM;
    306 	    }
    307 	}
    308       return fd;
    309     }
    310 
    311   int pid;
    312   if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
    313     {
    314       /* Special case for in-memory ELF image.  */
    315 
    316       char *fname;
    317       if (asprintf (&fname, PROCMEMFMT, pid) < 0)
    318 	return -1;
    319 
    320       int fd = open64 (fname, O_RDONLY);
    321       free (fname);
    322       if (fd < 0)
    323 	return -1;
    324 
    325       *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
    326 
    327       close (fd);
    328 
    329       *file_name = NULL;
    330       return -1;
    331     }
    332 
    333   abort ();
    334   return -1;
    335 }
    336 INTDEF (dwfl_linux_proc_find_elf)
    337