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 struct map_iterator
31   {
32     off_t offset;
33     int fd;
34     size_t buf_size;
35     char *buf;
36     char *buf_end;
37     char *path;
38   };
39 
40 static inline char *
ltoa(char * buf,long val)41 ltoa (char *buf, long val)
42 {
43   char *cp = buf, tmp;
44   ssize_t i, len;
45 
46   do
47     {
48       *cp++ = '0' + (val % 10);
49       val /= 10;
50     }
51   while (val);
52 
53   /* reverse the order of the digits: */
54   len = cp - buf;
55   --cp;
56   for (i = 0; i < len / 2; ++i)
57     {
58       tmp = buf[i];
59       buf[i] = cp[-i];
60       cp[-i] = tmp;
61     }
62   return buf + len;
63 }
64 
65 static inline int
maps_init(struct map_iterator * mi,pid_t pid)66 maps_init (struct map_iterator *mi, pid_t pid)
67 {
68   char path[sizeof ("/proc/0123456789/maps")], *cp;
69 
70   memcpy (path, "/proc/", 6);
71   cp = ltoa (path + 6, pid);
72   assert (cp + 6 < path + sizeof (path));
73   memcpy (cp, "/maps", 6);
74 
75   mi->fd = open (path, O_RDONLY);
76   if (mi->fd >= 0)
77     {
78       /* Try to allocate a page-sized buffer.  */
79       mi->buf_size = getpagesize ();
80       cp = mmap (NULL, mi->buf_size, PROT_READ | PROT_WRITE,
81 		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
82       if (cp == MAP_FAILED)
83 	{
84 	  close(mi->fd);
85 	  mi->fd = -1;
86 	  return -1;
87 	}
88       else
89 	{
90 	  mi->offset = 0;
91 	  mi->buf = mi->buf_end = cp + mi->buf_size;
92 	  return 0;
93 	}
94     }
95   return -1;
96 }
97 
98 static inline char *
skip_whitespace(char * cp)99 skip_whitespace (char *cp)
100 {
101   if (!cp)
102     return NULL;
103 
104   while (*cp == ' ' || *cp == '\t')
105     ++cp;
106   return cp;
107 }
108 
109 static inline char *
scan_hex(char * cp,unsigned long * valp)110 scan_hex (char *cp, unsigned long *valp)
111 {
112   unsigned long num_digits = 0, digit, val = 0;
113 
114   cp = skip_whitespace (cp);
115   if (!cp)
116     return NULL;
117 
118   while (1)
119     {
120       digit = *cp;
121       if ((digit - '0') <= 9)
122 	digit -= '0';
123       else if ((digit - 'a') < 6)
124 	digit -= 'a' - 10;
125       else if ((digit - 'A') < 6)
126 	digit -= 'A' - 10;
127       else
128 	break;
129       val = (val << 4) | digit;
130       ++num_digits;
131       ++cp;
132     }
133   if (!num_digits)
134     return NULL;
135   *valp = val;
136   return cp;
137 }
138 
139 static inline char *
scan_dec(char * cp,unsigned long * valp)140 scan_dec (char *cp, unsigned long *valp)
141 {
142   unsigned long num_digits = 0, digit, val = 0;
143 
144   if (!(cp = skip_whitespace (cp)))
145     return NULL;
146 
147   while (1)
148     {
149       digit = *cp;
150       if ((digit - '0') <= 9)
151 	{
152 	  digit -= '0';
153 	  ++cp;
154 	}
155       else
156 	break;
157       val = (10 * val) + digit;
158       ++num_digits;
159     }
160   if (!num_digits)
161     return NULL;
162   *valp = val;
163   return cp;
164 }
165 
166 static inline char *
scan_char(char * cp,char * valp)167 scan_char (char *cp, char *valp)
168 {
169   if (!cp)
170     return NULL;
171 
172   *valp = *cp;
173 
174   /* don't step over NUL terminator */
175   if (*cp)
176     ++cp;
177   return cp;
178 }
179 
180 /* Scan a string delimited by white-space.  Fails on empty string or
181    if string is doesn't fit in the specified buffer.  */
182 static inline char *
scan_string(char * cp,char * valp,size_t buf_size)183 scan_string (char *cp, char *valp, size_t buf_size)
184 {
185   size_t i = 0;
186 
187   if (!(cp = skip_whitespace (cp)))
188     return NULL;
189 
190   while (*cp != ' ' && *cp != '\t' && *cp != '\0')
191     {
192       if ((valp != NULL) && (i < buf_size - 1))
193 	valp[i++] = *cp;
194       ++cp;
195     }
196   if (i == 0 || i >= buf_size)
197     return NULL;
198   valp[i] = '\0';
199   return cp;
200 }
201 
202 static inline int
maps_next(struct map_iterator * mi,unsigned long * low,unsigned long * high,unsigned long * offset)203 maps_next (struct map_iterator *mi,
204 	   unsigned long *low, unsigned long *high, unsigned long *offset)
205 {
206   char perm[16], dash = 0, colon = 0, *cp;
207   unsigned long major, minor, inum;
208   ssize_t i, nread;
209 
210   if (mi->fd < 0)
211     return 0;
212 
213   while (1)
214     {
215       ssize_t bytes_left = mi->buf_end - mi->buf;
216       char *eol = NULL;
217 
218       for (i = 0; i < bytes_left; ++i)
219 	{
220 	  if (mi->buf[i] == '\n')
221 	    {
222 	      eol = mi->buf + i;
223 	      break;
224 	    }
225 	  else if (mi->buf[i] == '\0')
226 	    break;
227 	}
228       if (!eol)
229 	{
230 	  /* copy down the remaining bytes, if any */
231 	  if (bytes_left > 0)
232 	    memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
233 
234 	  mi->buf = mi->buf_end - mi->buf_size;
235 	  nread = read (mi->fd, mi->buf + bytes_left,
236 			mi->buf_size - bytes_left);
237 	  if (nread <= 0)
238 	    return 0;
239 	  else if ((size_t) (nread + bytes_left) < mi->buf_size)
240 	    {
241 	      /* Move contents to the end of the buffer so we
242 		 maintain the invariant that all bytes between
243 		 mi->buf and mi->buf_end are valid.  */
244 	      memmove (mi->buf_end - nread - bytes_left, mi->buf,
245 		       nread + bytes_left);
246 	      mi->buf = mi->buf_end - nread - bytes_left;
247 	    }
248 
249 	  eol = mi->buf + bytes_left + nread - 1;
250 
251 	  for (i = bytes_left; i < bytes_left + nread; ++i)
252 	    if (mi->buf[i] == '\n')
253 	      {
254 		eol = mi->buf + i;
255 		break;
256 	      }
257 	}
258       cp = mi->buf;
259       mi->buf = eol + 1;
260       *eol = '\0';
261 
262       /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
263       cp = scan_hex (cp, low);
264       cp = scan_char (cp, &dash);
265       cp = scan_hex (cp, high);
266       cp = scan_string (cp, perm, sizeof (perm));
267       cp = scan_hex (cp, offset);
268       cp = scan_hex (cp, &major);
269       cp = scan_char (cp, &colon);
270       cp = scan_hex (cp, &minor);
271       cp = scan_dec (cp, &inum);
272       cp = mi->path = skip_whitespace (cp);
273       if (!cp)
274 	continue;
275       cp = scan_string (cp, NULL, 0);
276       if (dash != '-' || colon != ':')
277 	continue;	/* skip line with unknown or bad format */
278       return 1;
279     }
280   return 0;
281 }
282 
283 static inline void
maps_close(struct map_iterator * mi)284 maps_close (struct map_iterator *mi)
285 {
286   if (mi->fd < 0)
287     return;
288   close (mi->fd);
289   mi->fd = -1;
290   if (mi->buf)
291     {
292       munmap (mi->buf_end - mi->buf_size, mi->buf_size);
293       mi->buf = mi->buf_end = NULL;
294     }
295 }
296 
297 #endif /* os_linux_h */
298