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