blob: 5b7295b01f4990519b124a5accda2c70a75f5264 [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)
Zachary T Welch6a072982011-03-02 17:40:10 +010083 {
84 close(mi->fd);
85 mi->fd = -1;
86 return -1;
87 }
mostang.com!davidm2f210752004-04-21 07:24:35 +000088 else
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070089 {
90 mi->offset = 0;
91 mi->buf = mi->buf_end = cp + mi->buf_size;
92 return 0;
93 }
mostang.com!davidm2f210752004-04-21 07:24:35 +000094 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -070095 return -1;
mostang.com!davidm64c702c2004-03-31 07:38:06 +000096}
97
98static inline char *
99skip_whitespace (char *cp)
100{
101 if (!cp)
102 return NULL;
103
104 while (*cp == ' ' || *cp == '\t')
105 ++cp;
106 return cp;
107}
108
109static inline char *
110scan_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
139static inline char *
140scan_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 {
hp.com!davidm6c612882004-06-29 05:59:35 +0000149 digit = *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000150 if ((digit - '0') <= 9)
hp.com!davidm6c612882004-06-29 05:59:35 +0000151 {
152 digit -= '0';
153 ++cp;
154 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000155 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
166static inline char *
167scan_char (char *cp, char *valp)
168{
169 if (!cp)
170 return NULL;
171
hp.com!davidm6c612882004-06-29 05:59:35 +0000172 *valp = *cp;
173
174 /* don't step over NUL terminator */
175 if (*cp)
176 ++cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000177 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. */
182static inline char *
183scan_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 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700192 if ((valp != NULL) && (i < buf_size - 1))
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000193 valp[i++] = *cp;
194 ++cp;
195 }
196 if (i == 0 || i >= buf_size)
197 return NULL;
198 valp[i] = '\0';
199 return cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000200}
201
202static inline int
203maps_next (struct map_iterator *mi,
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700204 unsigned long *low, unsigned long *high, unsigned long *offset)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000205{
Arun Sharma851f1422011-10-29 17:11:29 -0700206 char perm[16], dash = 0, colon = 0, *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000207 unsigned long major, minor, inum;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000208 ssize_t i, nread;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000209
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000210 if (mi->fd < 0)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000211 return 0;
212
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000213 while (1)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000214 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700215 ssize_t bytes_left = mi->buf_end - mi->buf;
216 char *eol = NULL;
217
218 for (i = 0; i < bytes_left; ++i)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000219 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700220 if (mi->buf[i] == '\n')
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000221 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700222 eol = mi->buf + i;
223 break;
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000224 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700225 else if (mi->buf[i] == '\0')
226 break;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000227 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700228 if (!eol)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000229 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700230 /* copy down the remaining bytes, if any */
231 if (bytes_left > 0)
232 memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000233
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700234 mi->buf = mi->buf_end - mi->buf_size;
235 nread = read (mi->fd, mi->buf + bytes_left,
236 mi->buf_size - bytes_left);
237 if (nread <= 0)
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000238 return 0;
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700239 else if ((size_t) (nread + bytes_left) < mi->buf_size)
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000240 {
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700241 /* Move contents to the end of the buffer so we
242 maintain the invariant that all bytes between
243 mi->buf and mi->buf_end are valid. */
244 memmove (mi->buf_end - nread - bytes_left, mi->buf,
245 nread + bytes_left);
246 mi->buf = mi->buf_end - nread - bytes_left;
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000247 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700248
249 eol = mi->buf + bytes_left + nread - 1;
250
251 for (i = bytes_left; i < bytes_left + nread; ++i)
252 if (mi->buf[i] == '\n')
253 {
254 eol = mi->buf + i;
255 break;
256 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000257 }
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700258 cp = mi->buf;
259 mi->buf = eol + 1;
260 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000261
262 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000263 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000264 cp = scan_char (cp, &dash);
265 cp = scan_hex (cp, high);
266 cp = scan_string (cp, perm, sizeof (perm));
267 cp = scan_hex (cp, offset);
268 cp = scan_hex (cp, &major);
269 cp = scan_char (cp, &colon);
270 cp = scan_hex (cp, &minor);
271 cp = scan_dec (cp, &inum);
Paul Pluzhnikovb56375e2009-10-07 12:51:03 -0700272 cp = mi->path = skip_whitespace (cp);
273 if (!cp)
274 continue;
275 cp = scan_string (cp, NULL, 0);
Arun Sharma5a1d3c62010-02-21 22:35:26 -0800276 if (dash != '-' || colon != ':')
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000277 continue; /* skip line with unknown or bad format */
278 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000279 }
280 return 0;
281}
282
283static inline void
284maps_close (struct map_iterator *mi)
285{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000286 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000287 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000288 close (mi->fd);
289 mi->fd = -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000290 if (mi->buf)
291 {
292 munmap (mi->buf_end - mi->buf_size, mi->buf_size);
293 mi->buf = mi->buf_end = 0;
294 }
mostang.com!davidm824d6612003-02-08 10:10:59 +0000295}
296
297#endif /* os_linux_h */