Home | History | Annotate | Download | only in src
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2003-2004 Hewlett-Packard Co
      3    Copyright (C) 2007 David Mosberger-Tang
      4 	Contributed by David Mosberger-Tang <dmosberger (at) gmail.com>
      5 
      6 This file is part of libunwind.
      7 
      8 Permission is hereby granted, free of charge, to any person obtaining
      9 a copy of this software and associated documentation files (the
     10 "Software"), to deal in the Software without restriction, including
     11 without limitation the rights to use, copy, modify, merge, publish,
     12 distribute, sublicense, and/or sell copies of the Software, and to
     13 permit persons to whom the Software is furnished to do so, subject to
     14 the following conditions:
     15 
     16 The above copyright notice and this permission notice shall be
     17 included in all copies or substantial portions of the Software.
     18 
     19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     26 
     27 #ifndef os_linux_h
     28 #define os_linux_h
     29 
     30 #include <sys/mman.h>
     31 
     32 struct map_iterator
     33   {
     34     off_t offset;
     35     int fd;
     36     size_t buf_size;
     37     char *buf;
     38     char *buf_end;
     39     char *path;
     40   };
     41 
     42 static inline char *
     43 ltoa (char *buf, long val)
     44 {
     45   char *cp = buf, tmp;
     46   ssize_t i, len;
     47 
     48   do
     49     {
     50       *cp++ = '0' + (val % 10);
     51       val /= 10;
     52     }
     53   while (val);
     54 
     55   /* reverse the order of the digits: */
     56   len = cp - buf;
     57   --cp;
     58   for (i = 0; i < len / 2; ++i)
     59     {
     60       tmp = buf[i];
     61       buf[i] = cp[-i];
     62       cp[-i] = tmp;
     63     }
     64   return buf + len;
     65 }
     66 
     67 static inline int
     68 maps_init (struct map_iterator *mi, pid_t pid)
     69 {
     70   char path[sizeof ("/proc/0123456789/maps")], *cp;
     71 
     72   memcpy (path, "/proc/", 6);
     73   cp = ltoa (path + 6, pid);
     74   assert (cp + 6 < path + sizeof (path));
     75   memcpy (cp, "/maps", 6);
     76 
     77   mi->fd = open (path, O_RDONLY);
     78   if (mi->fd >= 0)
     79     {
     80       /* Try to allocate a page-sized buffer.  */
     81       mi->buf_size = getpagesize ();
     82       cp = mmap (NULL, mi->buf_size, PROT_READ | PROT_WRITE,
     83 		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     84       if (cp == MAP_FAILED)
     85 	{
     86 	  close(mi->fd);
     87 	  mi->fd = -1;
     88 	  return -1;
     89 	}
     90       else
     91 	{
     92 	  mi->offset = 0;
     93 	  mi->buf = mi->buf_end = cp + mi->buf_size;
     94 	  return 0;
     95 	}
     96     }
     97   return -1;
     98 }
     99 
    100 static inline char *
    101 skip_whitespace (char *cp)
    102 {
    103   if (!cp)
    104     return NULL;
    105 
    106   while (*cp == ' ' || *cp == '\t')
    107     ++cp;
    108   return cp;
    109 }
    110 
    111 static inline char *
    112 scan_hex (char *cp, unsigned long *valp)
    113 {
    114   unsigned long num_digits = 0, digit, val = 0;
    115 
    116   cp = skip_whitespace (cp);
    117   if (!cp)
    118     return NULL;
    119 
    120   while (1)
    121     {
    122       digit = *cp;
    123       if ((digit - '0') <= 9)
    124 	digit -= '0';
    125       else if ((digit - 'a') < 6)
    126 	digit -= 'a' - 10;
    127       else if ((digit - 'A') < 6)
    128 	digit -= 'A' - 10;
    129       else
    130 	break;
    131       val = (val << 4) | digit;
    132       ++num_digits;
    133       ++cp;
    134     }
    135   if (!num_digits)
    136     return NULL;
    137   *valp = val;
    138   return cp;
    139 }
    140 
    141 static inline char *
    142 scan_dec (char *cp, unsigned long *valp)
    143 {
    144   unsigned long num_digits = 0, digit, val = 0;
    145 
    146   if (!(cp = skip_whitespace (cp)))
    147     return NULL;
    148 
    149   while (1)
    150     {
    151       digit = *cp;
    152       if ((digit - '0') <= 9)
    153 	{
    154 	  digit -= '0';
    155 	  ++cp;
    156 	}
    157       else
    158 	break;
    159       val = (10 * val) + digit;
    160       ++num_digits;
    161     }
    162   if (!num_digits)
    163     return NULL;
    164   *valp = val;
    165   return cp;
    166 }
    167 
    168 static inline char *
    169 scan_char (char *cp, char *valp)
    170 {
    171   if (!cp)
    172     return NULL;
    173 
    174   *valp = *cp;
    175 
    176   /* don't step over NUL terminator */
    177   if (*cp)
    178     ++cp;
    179   return cp;
    180 }
    181 
    182 /* Scan a string delimited by white-space.  Fails on empty string or
    183    if string is doesn't fit in the specified buffer.  */
    184 static inline char *
    185 scan_string (char *cp, char *valp, size_t buf_size)
    186 {
    187   size_t i = 0;
    188 
    189   if (!(cp = skip_whitespace (cp)))
    190     return NULL;
    191 
    192   while (*cp != ' ' && *cp != '\t' && *cp != '\0')
    193     {
    194       if ((valp != NULL) && (i < buf_size - 1))
    195 	valp[i++] = *cp;
    196       ++cp;
    197     }
    198   if (i == 0 || i >= buf_size)
    199     return NULL;
    200   valp[i] = '\0';
    201   return cp;
    202 }
    203 
    204 static inline int
    205 maps_next (struct map_iterator *mi,
    206 	   unsigned long *low, unsigned long *high, unsigned long *offset,
    207 	   unsigned long *flags)
    208 {
    209   char perm[16], dash = 0, colon = 0, *cp;
    210   unsigned long major, minor, inum;
    211   ssize_t i, nread;
    212 
    213   if (mi->fd < 0)
    214     return 0;
    215 
    216   while (1)
    217     {
    218       ssize_t bytes_left = mi->buf_end - mi->buf;
    219       char *eol = NULL;
    220 
    221       for (i = 0; i < bytes_left; ++i)
    222 	{
    223 	  if (mi->buf[i] == '\n')
    224 	    {
    225 	      eol = mi->buf + i;
    226 	      break;
    227 	    }
    228 	  else if (mi->buf[i] == '\0')
    229 	    break;
    230 	}
    231       if (!eol)
    232 	{
    233 	  /* copy down the remaining bytes, if any */
    234 	  if (bytes_left > 0)
    235 	    memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
    236 
    237 	  mi->buf = mi->buf_end - mi->buf_size;
    238 	  nread = read (mi->fd, mi->buf + bytes_left,
    239 			mi->buf_size - bytes_left);
    240 	  if (nread <= 0)
    241 	    return 0;
    242 	  else if ((size_t) (nread + bytes_left) < mi->buf_size)
    243 	    {
    244 	      /* Move contents to the end of the buffer so we
    245 		 maintain the invariant that all bytes between
    246 		 mi->buf and mi->buf_end are valid.  */
    247 	      memmove (mi->buf_end - nread - bytes_left, mi->buf,
    248 		       nread + bytes_left);
    249 	      mi->buf = mi->buf_end - nread - bytes_left;
    250 	    }
    251 
    252 	  eol = mi->buf + bytes_left + nread - 1;
    253 
    254 	  for (i = bytes_left; i < bytes_left + nread; ++i)
    255 	    if (mi->buf[i] == '\n')
    256 	      {
    257 		eol = mi->buf + i;
    258 		break;
    259 	      }
    260 	}
    261       cp = mi->buf;
    262       mi->buf = eol + 1;
    263       *eol = '\0';
    264 
    265       /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
    266       cp = scan_hex (cp, low);
    267       cp = scan_char (cp, &dash);
    268       cp = scan_hex (cp, high);
    269       cp = scan_string (cp, perm, sizeof (perm));
    270       cp = scan_hex (cp, offset);
    271       cp = scan_hex (cp, &major);
    272       cp = scan_char (cp, &colon);
    273       cp = scan_hex (cp, &minor);
    274       cp = scan_dec (cp, &inum);
    275       cp = mi->path = skip_whitespace (cp);
    276       if (!cp)
    277 	continue;
    278       cp = scan_string (cp, NULL, 0);
    279       if (dash != '-' || colon != ':')
    280 	continue;	/* skip line with unknown or bad format */
    281       if (flags)
    282         {
    283           *flags = 0;
    284           if (perm[0] == 'r')
    285             {
    286               *flags |= PROT_READ;
    287             }
    288           if (perm[1] == 'w')
    289             {
    290               *flags |= PROT_WRITE;
    291             }
    292           if (perm[2] == 'x')
    293             {
    294               *flags |= PROT_EXEC;
    295             }
    296         }
    297       return 1;
    298     }
    299   return 0;
    300 }
    301 
    302 static inline void
    303 maps_close (struct map_iterator *mi)
    304 {
    305   if (mi->fd < 0)
    306     return;
    307   close (mi->fd);
    308   mi->fd = -1;
    309   if (mi->buf)
    310     {
    311       munmap (mi->buf_end - mi->buf_size, mi->buf_size);
    312       mi->buf = mi->buf_end = NULL;
    313     }
    314 }
    315 
    316 #endif /* os_linux_h */
    317