blob: af301ce0d713093ac1bb4375a0f18df9a8ae4dc4 [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;
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070037 char *path;
mostang.com!davidm824d6612003-02-08 10:10:59 +000038 };
39
mostang.com!davidm64c702c2004-03-31 07:38:06 +000040static inline char *
41ltoa (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
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070065static inline int
mostang.com!davidm824d6612003-02-08 10:10:59 +000066maps_init (struct map_iterator *mi, pid_t pid)
67{
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070068 char path[sizeof ("/proc/0123456789/maps")], *cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +000069
mostang.com!davidm64c702c2004-03-31 07:38:06 +000070 memcpy (path, "/proc/", 6);
71 cp = ltoa (path + 6, pid);
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070072 assert (cp + 6 < path + sizeof (path));
mostang.com!davidm64c702c2004-03-31 07:38:06 +000073 memcpy (cp, "/maps", 6);
74
75 mi->fd = open (path, O_RDONLY);
mostang.com!davidm2f210752004-04-21 07:24:35 +000076 if (mi->fd >= 0)
77 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070078 /* Try to allocate a page-sized buffer. */
mostang.com!davidm2f210752004-04-21 07:24:35 +000079 mi->buf_size = getpagesize ();
80 cp = mmap (0, mi->buf_size, PROT_READ | PROT_WRITE,
81 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
82 if (cp == MAP_FAILED)
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070083 return -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +000084 else
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070085 {
86 mi->offset = 0;
87 mi->buf = mi->buf_end = cp + mi->buf_size;
88 return 0;
89 }
mostang.com!davidm2f210752004-04-21 07:24:35 +000090 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070091 return -1;
mostang.com!davidm64c702c2004-03-31 07:38:06 +000092}
93
94static inline char *
95skip_whitespace (char *cp)
96{
97 if (!cp)
98 return NULL;
99
100 while (*cp == ' ' || *cp == '\t')
101 ++cp;
102 return cp;
103}
104
105static inline char *
106scan_hex (char *cp, unsigned long *valp)
107{
108 unsigned long num_digits = 0, digit, val = 0;
109
110 cp = skip_whitespace (cp);
111 if (!cp)
112 return NULL;
113
114 while (1)
115 {
116 digit = *cp;
117 if ((digit - '0') <= 9)
118 digit -= '0';
119 else if ((digit - 'a') < 6)
120 digit -= 'a' - 10;
121 else if ((digit - 'A') < 6)
122 digit -= 'A' - 10;
123 else
124 break;
125 val = (val << 4) | digit;
126 ++num_digits;
127 ++cp;
128 }
129 if (!num_digits)
130 return NULL;
131 *valp = val;
132 return cp;
133}
134
135static inline char *
136scan_dec (char *cp, unsigned long *valp)
137{
138 unsigned long num_digits = 0, digit, val = 0;
139
140 if (!(cp = skip_whitespace (cp)))
141 return NULL;
142
143 while (1)
144 {
hp.com!davidm6c612882004-06-29 05:59:35 +0000145 digit = *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000146 if ((digit - '0') <= 9)
hp.com!davidm6c612882004-06-29 05:59:35 +0000147 {
148 digit -= '0';
149 ++cp;
150 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000151 else
152 break;
153 val = (10 * val) + digit;
154 ++num_digits;
155 }
156 if (!num_digits)
157 return NULL;
158 *valp = val;
159 return cp;
160}
161
162static inline char *
163scan_char (char *cp, char *valp)
164{
165 if (!cp)
166 return NULL;
167
hp.com!davidm6c612882004-06-29 05:59:35 +0000168 *valp = *cp;
169
170 /* don't step over NUL terminator */
171 if (*cp)
172 ++cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000173 return cp;
174}
175
176/* Scan a string delimited by white-space. Fails on empty string or
177 if string is doesn't fit in the specified buffer. */
178static inline char *
179scan_string (char *cp, char *valp, size_t buf_size)
180{
181 size_t i = 0;
182
183 if (!(cp = skip_whitespace (cp)))
184 return NULL;
185
186 while (*cp != ' ' && *cp != '\t' && *cp != '\0')
187 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700188 if ((valp != NULL) && (i < buf_size - 1))
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000189 valp[i++] = *cp;
190 ++cp;
191 }
192 if (i == 0 || i >= buf_size)
193 return NULL;
194 valp[i] = '\0';
195 return cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000196}
197
198static inline int
199maps_next (struct map_iterator *mi,
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700200 unsigned long *low, unsigned long *high, unsigned long *offset)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000201{
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700202 char perm[16], dash, colon, *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000203 unsigned long major, minor, inum;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000204 ssize_t i, nread;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000205
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000206 if (mi->fd < 0)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000207 return 0;
208
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000209 while (1)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000210 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700211 ssize_t bytes_left = mi->buf_end - mi->buf;
212 char *eol = NULL;
213
214 for (i = 0; i < bytes_left; ++i)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000215 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700216 if (mi->buf[i] == '\n')
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000217 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700218 eol = mi->buf + i;
219 break;
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000220 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700221 else if (mi->buf[i] == '\0')
222 break;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000223 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700224 if (!eol)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000225 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700226 /* copy down the remaining bytes, if any */
227 if (bytes_left > 0)
228 memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000229
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700230 mi->buf = mi->buf_end - mi->buf_size;
231 nread = read (mi->fd, mi->buf + bytes_left,
232 mi->buf_size - bytes_left);
233 if (nread <= 0)
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000234 return 0;
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700235 else if ((size_t) (nread + bytes_left) < mi->buf_size)
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000236 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700237 /* Move contents to the end of the buffer so we
238 maintain the invariant that all bytes between
239 mi->buf and mi->buf_end are valid. */
240 memmove (mi->buf_end - nread - bytes_left, mi->buf,
241 nread + bytes_left);
242 mi->buf = mi->buf_end - nread - bytes_left;
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000243 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700244
245 eol = mi->buf + bytes_left + nread - 1;
246
247 for (i = bytes_left; i < bytes_left + nread; ++i)
248 if (mi->buf[i] == '\n')
249 {
250 eol = mi->buf + i;
251 break;
252 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000253 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700254 cp = mi->buf;
255 mi->buf = eol + 1;
256 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000257
258 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000259 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000260 cp = scan_char (cp, &dash);
261 cp = scan_hex (cp, high);
262 cp = scan_string (cp, perm, sizeof (perm));
263 cp = scan_hex (cp, offset);
264 cp = scan_hex (cp, &major);
265 cp = scan_char (cp, &colon);
266 cp = scan_hex (cp, &minor);
267 cp = scan_dec (cp, &inum);
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700268 cp = mi->path = skip_whitespace (cp);
269 if (!cp)
270 continue;
271 cp = scan_string (cp, NULL, 0);
Arun Sharma5a1d3c62010-02-21 22:35:26 -0800272 if (dash != '-' || colon != ':')
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000273 continue; /* skip line with unknown or bad format */
274 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000275 }
276 return 0;
277}
278
279static inline void
280maps_close (struct map_iterator *mi)
281{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000282 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000283 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000284 close (mi->fd);
285 mi->fd = -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000286 if (mi->buf)
287 {
288 munmap (mi->buf_end - mi->buf_size, mi->buf_size);
289 mi->buf = mi->buf_end = 0;
290 }
mostang.com!davidm824d6612003-02-08 10:10:59 +0000291}
292
293#endif /* os_linux_h */