blob: 70e007ca67588bc54e22715827b757e68fb6a4d4 [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
Christopher Ferris7d46a212013-11-14 14:03:33 -080030#include <sys/mman.h>
31
mostang.com!davidm824d6612003-02-08 10:10:59 +000032struct map_iterator
33 {
mostang.com!davidm64c702c2004-03-31 07:38:06 +000034 off_t offset;
35 int fd;
mostang.com!davidm5de4b352004-04-21 03:56:06 +000036 size_t buf_size;
37 char *buf;
38 char *buf_end;
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070039 char *path;
mostang.com!davidm824d6612003-02-08 10:10:59 +000040 };
41
mostang.com!davidm64c702c2004-03-31 07:38:06 +000042static inline char *
43ltoa (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
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070067static inline int
mostang.com!davidm824d6612003-02-08 10:10:59 +000068maps_init (struct map_iterator *mi, pid_t pid)
69{
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070070 char path[sizeof ("/proc/0123456789/maps")], *cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +000071
mostang.com!davidm64c702c2004-03-31 07:38:06 +000072 memcpy (path, "/proc/", 6);
73 cp = ltoa (path + 6, pid);
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070074 assert (cp + 6 < path + sizeof (path));
mostang.com!davidm64c702c2004-03-31 07:38:06 +000075 memcpy (cp, "/maps", 6);
76
77 mi->fd = open (path, O_RDONLY);
mostang.com!davidm2f210752004-04-21 07:24:35 +000078 if (mi->fd >= 0)
79 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070080 /* Try to allocate a page-sized buffer. */
mostang.com!davidm2f210752004-04-21 07:24:35 +000081 mi->buf_size = getpagesize ();
Tommi Rantala890e23e2012-09-21 08:38:52 +030082 cp = mmap (NULL, mi->buf_size, PROT_READ | PROT_WRITE,
mostang.com!davidm2f210752004-04-21 07:24:35 +000083 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
84 if (cp == MAP_FAILED)
Zachary T Welch6a072982011-03-02 17:40:10 +010085 {
86 close(mi->fd);
87 mi->fd = -1;
88 return -1;
89 }
mostang.com!davidm2f210752004-04-21 07:24:35 +000090 else
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070091 {
92 mi->offset = 0;
93 mi->buf = mi->buf_end = cp + mi->buf_size;
94 return 0;
95 }
mostang.com!davidm2f210752004-04-21 07:24:35 +000096 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070097 return -1;
mostang.com!davidm64c702c2004-03-31 07:38:06 +000098}
99
100static inline char *
101skip_whitespace (char *cp)
102{
103 if (!cp)
104 return NULL;
105
106 while (*cp == ' ' || *cp == '\t')
107 ++cp;
108 return cp;
109}
110
111static inline char *
112scan_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
141static inline char *
142scan_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 {
hp.com!davidm6c612882004-06-29 05:59:35 +0000151 digit = *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000152 if ((digit - '0') <= 9)
hp.com!davidm6c612882004-06-29 05:59:35 +0000153 {
154 digit -= '0';
155 ++cp;
156 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000157 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
168static inline char *
169scan_char (char *cp, char *valp)
170{
171 if (!cp)
172 return NULL;
173
hp.com!davidm6c612882004-06-29 05:59:35 +0000174 *valp = *cp;
175
176 /* don't step over NUL terminator */
177 if (*cp)
178 ++cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000179 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. */
184static inline char *
185scan_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 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700194 if ((valp != NULL) && (i < buf_size - 1))
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000195 valp[i++] = *cp;
196 ++cp;
197 }
198 if (i == 0 || i >= buf_size)
199 return NULL;
200 valp[i] = '\0';
201 return cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000202}
203
204static inline int
205maps_next (struct map_iterator *mi,
Christopher Ferris7d46a212013-11-14 14:03:33 -0800206 unsigned long *low, unsigned long *high, unsigned long *offset,
207 unsigned long *flags)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000208{
Arun Sharma851f1422011-10-29 17:11:29 -0700209 char perm[16], dash = 0, colon = 0, *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000210 unsigned long major, minor, inum;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000211 ssize_t i, nread;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000212
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000213 if (mi->fd < 0)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000214 return 0;
215
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000216 while (1)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000217 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700218 ssize_t bytes_left = mi->buf_end - mi->buf;
219 char *eol = NULL;
220
221 for (i = 0; i < bytes_left; ++i)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000222 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700223 if (mi->buf[i] == '\n')
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000224 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700225 eol = mi->buf + i;
226 break;
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000227 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700228 else if (mi->buf[i] == '\0')
229 break;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000230 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700231 if (!eol)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000232 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700233 /* copy down the remaining bytes, if any */
234 if (bytes_left > 0)
235 memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000236
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700237 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)
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000241 return 0;
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700242 else if ((size_t) (nread + bytes_left) < mi->buf_size)
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000243 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700244 /* 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;
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000250 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700251
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 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000260 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700261 cp = mi->buf;
262 mi->buf = eol + 1;
263 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000264
265 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000266 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000267 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);
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700275 cp = mi->path = skip_whitespace (cp);
276 if (!cp)
277 continue;
278 cp = scan_string (cp, NULL, 0);
Arun Sharma5a1d3c62010-02-21 22:35:26 -0800279 if (dash != '-' || colon != ':')
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000280 continue; /* skip line with unknown or bad format */
Christopher Ferris7d46a212013-11-14 14:03:33 -0800281 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 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000297 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000298 }
299 return 0;
300}
301
302static inline void
303maps_close (struct map_iterator *mi)
304{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000305 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000306 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000307 close (mi->fd);
308 mi->fd = -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000309 if (mi->buf)
310 {
311 munmap (mi->buf_end - mi->buf_size, mi->buf_size);
Tommi Rantala890e23e2012-09-21 08:38:52 +0300312 mi->buf = mi->buf_end = NULL;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000313 }
mostang.com!davidm824d6612003-02-08 10:10:59 +0000314}
315
316#endif /* os_linux_h */