blob: 7e11dae5c1656a745e6e53f1f18ea38aea4e6851 [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
mostang.com!davidm2f210752004-04-21 07:24:35 +000075 cp = NULL;
76 if (mi->fd >= 0)
77 {
78 /* Try to allocate a page-sized buffer. If that fails, we'll
79 fall back on reading one line at a time. */
80 mi->buf_size = getpagesize ();
81 cp = mmap (0, mi->buf_size, PROT_READ | PROT_WRITE,
82 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
83 if (cp == MAP_FAILED)
84 cp = NULL;
85 else
86 cp += mi->buf_size;
87 }
mostang.com!davidm5de4b352004-04-21 03:56:06 +000088 mi->buf = mi->buf_end = cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +000089}
90
91static inline char *
92skip_whitespace (char *cp)
93{
94 if (!cp)
95 return NULL;
96
97 while (*cp == ' ' || *cp == '\t')
98 ++cp;
99 return cp;
100}
101
102static inline char *
103scan_hex (char *cp, unsigned long *valp)
104{
105 unsigned long num_digits = 0, digit, val = 0;
106
107 cp = skip_whitespace (cp);
108 if (!cp)
109 return NULL;
110
111 while (1)
112 {
113 digit = *cp;
114 if ((digit - '0') <= 9)
115 digit -= '0';
116 else if ((digit - 'a') < 6)
117 digit -= 'a' - 10;
118 else if ((digit - 'A') < 6)
119 digit -= 'A' - 10;
120 else
121 break;
122 val = (val << 4) | digit;
123 ++num_digits;
124 ++cp;
125 }
126 if (!num_digits)
127 return NULL;
128 *valp = val;
129 return cp;
130}
131
132static inline char *
133scan_dec (char *cp, unsigned long *valp)
134{
135 unsigned long num_digits = 0, digit, val = 0;
136
137 if (!(cp = skip_whitespace (cp)))
138 return NULL;
139
140 while (1)
141 {
142 digit = *cp++;
143 if ((digit - '0') <= 9)
144 digit -= '0';
145 else
146 break;
147 val = (10 * val) + digit;
148 ++num_digits;
149 }
150 if (!num_digits)
151 return NULL;
152 *valp = val;
153 return cp;
154}
155
156static inline char *
157scan_char (char *cp, char *valp)
158{
159 if (!cp)
160 return NULL;
161
162 *valp = *cp++;
163 return cp;
164}
165
166/* Scan a string delimited by white-space. Fails on empty string or
167 if string is doesn't fit in the specified buffer. */
168static inline char *
169scan_string (char *cp, char *valp, size_t buf_size)
170{
171 size_t i = 0;
172
173 if (!(cp = skip_whitespace (cp)))
174 return NULL;
175
176 while (*cp != ' ' && *cp != '\t' && *cp != '\0')
177 {
178 if (i < buf_size - 1)
179 valp[i++] = *cp;
180 ++cp;
181 }
182 if (i == 0 || i >= buf_size)
183 return NULL;
184 valp[i] = '\0';
185 return cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000186}
187
188static inline int
189maps_next (struct map_iterator *mi,
190 unsigned long *low, unsigned long *high, unsigned long *offset,
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000191 char *path, size_t path_size)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000192{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000193 char line[256 + PATH_MAX], perm[16], dash, colon, *cp;
194 unsigned long major, minor, inum;
195 size_t to_read = 256; /* most lines fit in 256 characters easy */
196 ssize_t i, nread;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000197
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000198 if (mi->fd < 0)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000199 return 0;
200
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000201 while (1)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000202 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000203 if (mi->buf)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000204 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000205 ssize_t bytes_left = mi->buf_end - mi->buf;
206 char *eol = NULL;
207
208 for (i = 0; i < bytes_left; ++i)
209 {
210 if (mi->buf[i] == '\n')
211 {
212 eol = mi->buf + i;
213 break;
214 }
215 else if (mi->buf[i] == '\0')
216 break;
217 }
218 if (!eol)
219 {
220 /* copy down the remaining bytes, if any */
221 if (bytes_left > 0)
222 memcpy (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
223
224 mi->buf = mi->buf_end - mi->buf_size;
225 nread = read (mi->fd, mi->buf + bytes_left,
226 mi->buf_size - bytes_left);
227 if (nread <= 0)
228 return 0;
229
230 eol = mi->buf + bytes_left + nread - 1;
231
232 for (i = bytes_left; i < bytes_left + nread; ++i)
233 if (mi->buf[i] == '\n')
234 {
235 eol = mi->buf + i;
236 break;
237 }
238 }
239 cp = mi->buf;
240 mi->buf = eol + 1;
241 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000242 }
243 else
244 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000245 /* maps_init() wasn't able to allocate a buffer; do it the
246 slow way. */
247 lseek (mi->fd, mi->offset, SEEK_SET);
248
249 if ((nread = read (mi->fd, line, to_read)) <= 0)
250 return 0;
251 for (i = 0; i < nread && line[i] != '\n'; ++i)
252 /* skip */;
253 if (i < nread)
254 {
255 line[i] = '\0';
256 mi->offset += i + 1;
257 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000258 else
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000259 {
260 if (to_read < sizeof (line))
261 to_read = sizeof (line) - 1;
262 else
263 mi->offset += nread; /* not supposed to happen... */
264 continue; /* duh, no newline found */
265 }
266 cp = line;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000267 }
268
269 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000270 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000271 cp = scan_char (cp, &dash);
272 cp = scan_hex (cp, high);
273 cp = scan_string (cp, perm, sizeof (perm));
274 cp = scan_hex (cp, offset);
275 cp = scan_hex (cp, &major);
276 cp = scan_char (cp, &colon);
277 cp = scan_hex (cp, &minor);
278 cp = scan_dec (cp, &inum);
279 cp = scan_string (cp, path, path_size);
280 if (!cp || dash != '-' || colon != ':')
281 continue; /* skip line with unknown or bad format */
282 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000283 }
284 return 0;
285}
286
287static inline void
288maps_close (struct map_iterator *mi)
289{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000290 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000291 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000292 close (mi->fd);
293 mi->fd = -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000294 if (mi->buf)
295 {
296 munmap (mi->buf_end - mi->buf_size, mi->buf_size);
297 mi->buf = mi->buf_end = 0;
298 }
mostang.com!davidm824d6612003-02-08 10:10:59 +0000299}
300
301#endif /* os_linux_h */