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@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 *
ltoa(char * buf,long val)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
maps_init(struct map_iterator * mi,pid_t pid)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 *
skip_whitespace(char * cp)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 *
scan_hex(char * cp,unsigned long * valp)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 *
scan_dec(char * cp,unsigned long * valp)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 *
scan_char(char * cp,char * valp)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 *
scan_string(char * cp,char * valp,size_t buf_size)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
maps_next(struct map_iterator * mi,unsigned long * low,unsigned long * high,unsigned long * offset,unsigned long * flags)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
maps_close(struct map_iterator * mi)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