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