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