• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
unw_ltoa(char * buf,long val)41 unw_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 = unw_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,unsigned long * flags)203 maps_next (struct map_iterator *mi,
204            unsigned long *low, unsigned long *high, unsigned long *offset,
205            unsigned long *flags)
206 {
207   char perm[16], dash = 0, colon = 0, *cp;
208   unsigned long major, minor, inum;
209   ssize_t i, nread;
210 
211   if (mi->fd < 0)
212     return 0;
213 
214   while (1)
215     {
216       ssize_t bytes_left = mi->buf_end - mi->buf;
217       char *eol = NULL;
218 
219       for (i = 0; i < bytes_left; ++i)
220         {
221           if (mi->buf[i] == '\n')
222             {
223               eol = mi->buf + i;
224               break;
225             }
226           else if (mi->buf[i] == '\0')
227             break;
228         }
229       if (!eol)
230         {
231           /* copy down the remaining bytes, if any */
232           if (bytes_left > 0)
233             memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
234 
235           mi->buf = mi->buf_end - mi->buf_size;
236           nread = read (mi->fd, mi->buf + bytes_left,
237                         mi->buf_size - bytes_left);
238           if (nread <= 0)
239             return 0;
240           else if ((size_t) (nread + bytes_left) < mi->buf_size)
241             {
242               /* Move contents to the end of the buffer so we
243                  maintain the invariant that all bytes between
244                  mi->buf and mi->buf_end are valid.  */
245               memmove (mi->buf_end - nread - bytes_left, mi->buf,
246                        nread + bytes_left);
247               mi->buf = mi->buf_end - nread - bytes_left;
248             }
249 
250           eol = mi->buf + bytes_left + nread - 1;
251 
252           for (i = bytes_left; i < bytes_left + nread; ++i)
253             if (mi->buf[i] == '\n')
254               {
255                 eol = mi->buf + i;
256                 break;
257               }
258         }
259       cp = mi->buf;
260       mi->buf = eol + 1;
261       *eol = '\0';
262 
263       /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
264       cp = scan_hex (cp, low);
265       cp = scan_char (cp, &dash);
266       cp = scan_hex (cp, high);
267       cp = scan_string (cp, perm, sizeof (perm));
268       cp = scan_hex (cp, offset);
269       cp = scan_hex (cp, &major);
270       cp = scan_char (cp, &colon);
271       cp = scan_hex (cp, &minor);
272       cp = scan_dec (cp, &inum);
273       cp = mi->path = skip_whitespace (cp);
274       if (!cp)
275         continue;
276       cp = scan_string (cp, NULL, 0);
277       if (dash != '-' || colon != ':')
278         continue;       /* skip line with unknown or bad format */
279       if (flags)
280         {
281           *flags = 0;
282           if (perm[0] == 'r')
283             {
284               *flags |= PROT_READ;
285             }
286           if (perm[1] == 'w')
287             {
288               *flags |= PROT_WRITE;
289             }
290           if (perm[2] == 'x')
291             {
292               *flags |= PROT_EXEC;
293             }
294         }
295       return 1;
296     }
297   return 0;
298 }
299 
300 static inline void
maps_close(struct map_iterator * mi)301 maps_close (struct map_iterator *mi)
302 {
303   if (mi->fd < 0)
304     return;
305   close (mi->fd);
306   mi->fd = -1;
307   if (mi->buf)
308     {
309       munmap (mi->buf_end - mi->buf_size, mi->buf_size);
310       mi->buf = mi->buf_end = NULL;
311     }
312 }
313 
314 #endif /* os_linux_h */
315