Fix an incorrect hex parser
[platform/upstream/libunwind.git] / src / os-linux.h
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 *
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
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 *
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 *
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 *
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 *
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 *
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
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
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 */