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