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