blob: b2c9a8be55c56caa2d1ac7dd047570bbe5e0c153 [file] [log] [blame]
mostang.com!davidm824d6612003-02-08 10:10:59 +00001/* libunwind - a platform-independent unwind library
mostang.com!davidm64c702c2004-03-31 07:38:06 +00002 Copyright (C) 2003-2004 Hewlett-Packard Co
David Mosberger-Tang03e05b42007-08-22 12:57:49 -06003 Copyright (C) 2007 David Mosberger-Tang
4 Contributed by David Mosberger-Tang <dmosberger@gmail.com>
mostang.com!davidm824d6612003-02-08 10:10:59 +00005
6This file is part of libunwind.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
26
27#ifndef os_linux_h
28#define os_linux_h
29
30struct map_iterator
31 {
mostang.com!davidm64c702c2004-03-31 07:38:06 +000032 off_t offset;
33 int fd;
mostang.com!davidm5de4b352004-04-21 03:56:06 +000034 size_t buf_size;
35 char *buf;
36 char *buf_end;
mostang.com!davidm824d6612003-02-08 10:10:59 +000037 };
38
mostang.com!davidm64c702c2004-03-31 07:38:06 +000039static inline char *
40ltoa (char *buf, long val)
41{
42 char *cp = buf, tmp;
43 ssize_t i, len;
44
45 do
46 {
47 *cp++ = '0' + (val % 10);
48 val /= 10;
49 }
50 while (val);
51
52 /* reverse the order of the digits: */
53 len = cp - buf;
54 --cp;
55 for (i = 0; i < len / 2; ++i)
56 {
57 tmp = buf[i];
58 buf[i] = cp[-i];
59 cp[-i] = tmp;
60 }
61 return buf + len;
62}
63
mostang.com!davidm824d6612003-02-08 10:10:59 +000064static inline void
65maps_init (struct map_iterator *mi, pid_t pid)
66{
mostang.com!davidm64c702c2004-03-31 07:38:06 +000067 char path[PATH_MAX], *cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +000068
mostang.com!davidm64c702c2004-03-31 07:38:06 +000069 memcpy (path, "/proc/", 6);
70 cp = ltoa (path + 6, pid);
71 memcpy (cp, "/maps", 6);
72
73 mi->fd = open (path, O_RDONLY);
74 mi->offset = 0;
David Mosberger-Tang03e05b42007-08-22 12:57:49 -060075 mi->buf_size = 0;
mostang.com!davidm5de4b352004-04-21 03:56:06 +000076
mostang.com!davidm2f210752004-04-21 07:24:35 +000077 cp = NULL;
78 if (mi->fd >= 0)
79 {
80 /* Try to allocate a page-sized buffer. If that fails, we'll
81 fall back on reading one line at a time. */
82 mi->buf_size = getpagesize ();
83 cp = mmap (0, mi->buf_size, PROT_READ | PROT_WRITE,
84 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
85 if (cp == MAP_FAILED)
86 cp = NULL;
87 else
88 cp += mi->buf_size;
89 }
mostang.com!davidm5de4b352004-04-21 03:56:06 +000090 mi->buf = mi->buf_end = cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +000091}
92
93static inline char *
94skip_whitespace (char *cp)
95{
96 if (!cp)
97 return NULL;
98
99 while (*cp == ' ' || *cp == '\t')
100 ++cp;
101 return cp;
102}
103
104static inline char *
105scan_hex (char *cp, unsigned long *valp)
106{
107 unsigned long num_digits = 0, digit, val = 0;
108
109 cp = skip_whitespace (cp);
110 if (!cp)
111 return NULL;
112
113 while (1)
114 {
115 digit = *cp;
116 if ((digit - '0') <= 9)
117 digit -= '0';
118 else if ((digit - 'a') < 6)
119 digit -= 'a' - 10;
120 else if ((digit - 'A') < 6)
121 digit -= 'A' - 10;
122 else
123 break;
124 val = (val << 4) | digit;
125 ++num_digits;
126 ++cp;
127 }
128 if (!num_digits)
129 return NULL;
130 *valp = val;
131 return cp;
132}
133
134static inline char *
135scan_dec (char *cp, unsigned long *valp)
136{
137 unsigned long num_digits = 0, digit, val = 0;
138
139 if (!(cp = skip_whitespace (cp)))
140 return NULL;
141
142 while (1)
143 {
hp.com!davidm6c612882004-06-29 05:59:35 +0000144 digit = *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000145 if ((digit - '0') <= 9)
hp.com!davidm6c612882004-06-29 05:59:35 +0000146 {
147 digit -= '0';
148 ++cp;
149 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000150 else
151 break;
152 val = (10 * val) + digit;
153 ++num_digits;
154 }
155 if (!num_digits)
156 return NULL;
157 *valp = val;
158 return cp;
159}
160
161static inline char *
162scan_char (char *cp, char *valp)
163{
164 if (!cp)
165 return NULL;
166
hp.com!davidm6c612882004-06-29 05:59:35 +0000167 *valp = *cp;
168
169 /* don't step over NUL terminator */
170 if (*cp)
171 ++cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000172 return cp;
173}
174
175/* Scan a string delimited by white-space. Fails on empty string or
176 if string is doesn't fit in the specified buffer. */
177static inline char *
178scan_string (char *cp, char *valp, size_t buf_size)
179{
180 size_t i = 0;
181
182 if (!(cp = skip_whitespace (cp)))
183 return NULL;
184
185 while (*cp != ' ' && *cp != '\t' && *cp != '\0')
186 {
187 if (i < buf_size - 1)
188 valp[i++] = *cp;
189 ++cp;
190 }
191 if (i == 0 || i >= buf_size)
192 return NULL;
193 valp[i] = '\0';
194 return cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000195}
196
197static inline int
198maps_next (struct map_iterator *mi,
199 unsigned long *low, unsigned long *high, unsigned long *offset,
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000200 char *path, size_t path_size)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000201{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000202 char line[256 + PATH_MAX], perm[16], dash, colon, *cp;
203 unsigned long major, minor, inum;
204 size_t to_read = 256; /* most lines fit in 256 characters easy */
205 ssize_t i, nread;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000206
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000207 if (mi->fd < 0)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000208 return 0;
209
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000210 while (1)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000211 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000212 if (mi->buf)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000213 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000214 ssize_t bytes_left = mi->buf_end - mi->buf;
215 char *eol = NULL;
216
217 for (i = 0; i < bytes_left; ++i)
218 {
219 if (mi->buf[i] == '\n')
220 {
221 eol = mi->buf + i;
222 break;
223 }
224 else if (mi->buf[i] == '\0')
225 break;
226 }
227 if (!eol)
228 {
229 /* copy down the remaining bytes, if any */
230 if (bytes_left > 0)
Curt Wohlgemuth253f3e52007-10-18 10:45:55 -0600231 memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000232
233 mi->buf = mi->buf_end - mi->buf_size;
234 nread = read (mi->fd, mi->buf + bytes_left,
235 mi->buf_size - bytes_left);
236 if (nread <= 0)
237 return 0;
David Mosberger-Tang03e05b42007-08-22 12:57:49 -0600238 else if ((size_t) (nread + bytes_left) < mi->buf_size)
David Mosberger-Tangaf2503e2006-07-25 21:35:30 -0600239 {
240 /* Move contents to the end of the buffer so we
241 maintain the invariant that all bytes between
242 mi->buf and mi->buf_end are valid. */
Curt Wohlgemuth253f3e52007-10-18 10:45:55 -0600243 memmove (mi->buf_end - nread - bytes_left, mi->buf,
David Mosberger-Tangaf2503e2006-07-25 21:35:30 -0600244 nread + bytes_left);
245 mi->buf = mi->buf_end - nread - bytes_left;
246 }
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000247
248 eol = mi->buf + bytes_left + nread - 1;
249
250 for (i = bytes_left; i < bytes_left + nread; ++i)
251 if (mi->buf[i] == '\n')
252 {
253 eol = mi->buf + i;
254 break;
255 }
256 }
257 cp = mi->buf;
258 mi->buf = eol + 1;
259 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000260 }
261 else
262 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000263 /* maps_init() wasn't able to allocate a buffer; do it the
264 slow way. */
265 lseek (mi->fd, mi->offset, SEEK_SET);
266
267 if ((nread = read (mi->fd, line, to_read)) <= 0)
268 return 0;
269 for (i = 0; i < nread && line[i] != '\n'; ++i)
270 /* skip */;
271 if (i < nread)
272 {
273 line[i] = '\0';
274 mi->offset += i + 1;
275 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000276 else
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000277 {
278 if (to_read < sizeof (line))
279 to_read = sizeof (line) - 1;
280 else
281 mi->offset += nread; /* not supposed to happen... */
282 continue; /* duh, no newline found */
283 }
284 cp = line;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000285 }
286
287 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000288 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000289 cp = scan_char (cp, &dash);
290 cp = scan_hex (cp, high);
291 cp = scan_string (cp, perm, sizeof (perm));
292 cp = scan_hex (cp, offset);
293 cp = scan_hex (cp, &major);
294 cp = scan_char (cp, &colon);
295 cp = scan_hex (cp, &minor);
296 cp = scan_dec (cp, &inum);
297 cp = scan_string (cp, path, path_size);
298 if (!cp || dash != '-' || colon != ':')
299 continue; /* skip line with unknown or bad format */
300 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000301 }
302 return 0;
303}
304
305static inline void
306maps_close (struct map_iterator *mi)
307{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000308 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000309 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000310 close (mi->fd);
311 mi->fd = -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000312 if (mi->buf)
313 {
314 munmap (mi->buf_end - mi->buf_size, mi->buf_size);
315 mi->buf = mi->buf_end = 0;
316 }
mostang.com!davidm824d6612003-02-08 10:10:59 +0000317}
318
319#endif /* os_linux_h */