blob: f01f7a5021e777203de7d1b23166ac8ab22ccd72 [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
mostang.com!davidm824d6612003-02-08 10:10:59 +00003 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
25
26#ifndef os_linux_h
27#define os_linux_h
28
29struct map_iterator
30 {
mostang.com!davidm64c702c2004-03-31 07:38:06 +000031 off_t offset;
32 int fd;
mostang.com!davidm5de4b352004-04-21 03:56:06 +000033 size_t buf_size;
34 char *buf;
35 char *buf_end;
mostang.com!davidm824d6612003-02-08 10:10:59 +000036 };
37
mostang.com!davidm64c702c2004-03-31 07:38:06 +000038static inline char *
39ltoa (char *buf, long val)
40{
41 char *cp = buf, tmp;
42 ssize_t i, len;
43
44 do
45 {
46 *cp++ = '0' + (val % 10);
47 val /= 10;
48 }
49 while (val);
50
51 /* reverse the order of the digits: */
52 len = cp - buf;
53 --cp;
54 for (i = 0; i < len / 2; ++i)
55 {
56 tmp = buf[i];
57 buf[i] = cp[-i];
58 cp[-i] = tmp;
59 }
60 return buf + len;
61}
62
mostang.com!davidm824d6612003-02-08 10:10:59 +000063static inline void
64maps_init (struct map_iterator *mi, pid_t pid)
65{
mostang.com!davidm64c702c2004-03-31 07:38:06 +000066 char path[PATH_MAX], *cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +000067
mostang.com!davidm64c702c2004-03-31 07:38:06 +000068 memcpy (path, "/proc/", 6);
69 cp = ltoa (path + 6, pid);
70 memcpy (cp, "/maps", 6);
71
72 mi->fd = open (path, O_RDONLY);
73 mi->offset = 0;
mostang.com!davidm5de4b352004-04-21 03:56:06 +000074
75 /* Try to allocate a page-sized buffer. If that fails, we'll fall
76 back on reading one line at a time. */
77 mi->buf_size = getpagesize ();
78 cp = mmap (0, mi->buf_size, PROT_READ | PROT_WRITE,
79 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
80 if (cp == MAP_FAILED)
81 cp = NULL;
82 mi->buf = mi->buf_end = cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +000083}
84
85static inline char *
86skip_whitespace (char *cp)
87{
88 if (!cp)
89 return NULL;
90
91 while (*cp == ' ' || *cp == '\t')
92 ++cp;
93 return cp;
94}
95
96static inline char *
97scan_hex (char *cp, unsigned long *valp)
98{
99 unsigned long num_digits = 0, digit, val = 0;
100
101 cp = skip_whitespace (cp);
102 if (!cp)
103 return NULL;
104
105 while (1)
106 {
107 digit = *cp;
108 if ((digit - '0') <= 9)
109 digit -= '0';
110 else if ((digit - 'a') < 6)
111 digit -= 'a' - 10;
112 else if ((digit - 'A') < 6)
113 digit -= 'A' - 10;
114 else
115 break;
116 val = (val << 4) | digit;
117 ++num_digits;
118 ++cp;
119 }
120 if (!num_digits)
121 return NULL;
122 *valp = val;
123 return cp;
124}
125
126static inline char *
127scan_dec (char *cp, unsigned long *valp)
128{
129 unsigned long num_digits = 0, digit, val = 0;
130
131 if (!(cp = skip_whitespace (cp)))
132 return NULL;
133
134 while (1)
135 {
136 digit = *cp++;
137 if ((digit - '0') <= 9)
138 digit -= '0';
139 else
140 break;
141 val = (10 * val) + digit;
142 ++num_digits;
143 }
144 if (!num_digits)
145 return NULL;
146 *valp = val;
147 return cp;
148}
149
150static inline char *
151scan_char (char *cp, char *valp)
152{
153 if (!cp)
154 return NULL;
155
156 *valp = *cp++;
157 return cp;
158}
159
160/* Scan a string delimited by white-space. Fails on empty string or
161 if string is doesn't fit in the specified buffer. */
162static inline char *
163scan_string (char *cp, char *valp, size_t buf_size)
164{
165 size_t i = 0;
166
167 if (!(cp = skip_whitespace (cp)))
168 return NULL;
169
170 while (*cp != ' ' && *cp != '\t' && *cp != '\0')
171 {
172 if (i < buf_size - 1)
173 valp[i++] = *cp;
174 ++cp;
175 }
176 if (i == 0 || i >= buf_size)
177 return NULL;
178 valp[i] = '\0';
179 return cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000180}
181
182static inline int
183maps_next (struct map_iterator *mi,
184 unsigned long *low, unsigned long *high, unsigned long *offset,
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000185 char *path, size_t path_size)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000186{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000187 char line[256 + PATH_MAX], perm[16], dash, colon, *cp;
188 unsigned long major, minor, inum;
189 size_t to_read = 256; /* most lines fit in 256 characters easy */
190 ssize_t i, nread;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000191
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000192 if (mi->fd < 0)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000193 return 0;
194
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000195 while (1)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000196 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000197 if (mi->buf)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000198 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000199 ssize_t bytes_left = mi->buf_end - mi->buf;
200 char *eol = NULL;
201
202 for (i = 0; i < bytes_left; ++i)
203 {
204 if (mi->buf[i] == '\n')
205 {
206 eol = mi->buf + i;
207 break;
208 }
209 else if (mi->buf[i] == '\0')
210 break;
211 }
212 if (!eol)
213 {
214 /* copy down the remaining bytes, if any */
215 if (bytes_left > 0)
216 memcpy (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
217
218 mi->buf = mi->buf_end - mi->buf_size;
219 nread = read (mi->fd, mi->buf + bytes_left,
220 mi->buf_size - bytes_left);
221 if (nread <= 0)
222 return 0;
223
224 eol = mi->buf + bytes_left + nread - 1;
225
226 for (i = bytes_left; i < bytes_left + nread; ++i)
227 if (mi->buf[i] == '\n')
228 {
229 eol = mi->buf + i;
230 break;
231 }
232 }
233 cp = mi->buf;
234 mi->buf = eol + 1;
235 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000236 }
237 else
238 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000239 /* maps_init() wasn't able to allocate a buffer; do it the
240 slow way. */
241 lseek (mi->fd, mi->offset, SEEK_SET);
242
243 if ((nread = read (mi->fd, line, to_read)) <= 0)
244 return 0;
245 for (i = 0; i < nread && line[i] != '\n'; ++i)
246 /* skip */;
247 if (i < nread)
248 {
249 line[i] = '\0';
250 mi->offset += i + 1;
251 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000252 else
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000253 {
254 if (to_read < sizeof (line))
255 to_read = sizeof (line) - 1;
256 else
257 mi->offset += nread; /* not supposed to happen... */
258 continue; /* duh, no newline found */
259 }
260 cp = line;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000261 }
262
263 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000264 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000265 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 = scan_string (cp, path, path_size);
274 if (!cp || dash != '-' || colon != ':')
275 continue; /* skip line with unknown or bad format */
276 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000277 }
278 return 0;
279}
280
281static inline void
282maps_close (struct map_iterator *mi)
283{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000284 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000285 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000286 close (mi->fd);
287 mi->fd = -1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000288}
289
290#endif /* os_linux_h */