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 = 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 get_maps_count(mi->fd);
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