1/* libunwind - a platform-independent unwind library 2 Copyright (C) 2003-2004 Hewlett-Packard Co 3 Copyright (C) 2007 David Mosberger-Tang 4 Contributed by David Mosberger-Tang <dmosberger@gmail.com> 5 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 { 32 off_t offset; 33 int fd; 34 size_t buf_size; 35 char *buf; 36 char *buf_end; 37 char *path; 38 }; 39 40static 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 65static inline int 66maps_init (struct map_iterator *mi, pid_t pid) 67{ 68 char path[sizeof ("/proc/0123456789/maps")], *cp; 69 70 memcpy (path, "/proc/", 6); 71 cp = ltoa (path + 6, pid); 72 assert (cp + 6 < path + sizeof (path)); 73 memcpy (cp, "/maps", 6); 74 75 mi->fd = open (path, O_RDONLY); 76 if (mi->fd >= 0) 77 { 78 /* Try to allocate a page-sized buffer. */ 79 mi->buf_size = getpagesize (); 80 cp = mmap (NULL, mi->buf_size, PROT_READ | PROT_WRITE, 81 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 82 if (cp == MAP_FAILED) 83 { 84 close(mi->fd); 85 mi->fd = -1; 86 return -1; 87 } 88 else 89 { 90 mi->offset = 0; 91 mi->buf = mi->buf_end = cp + mi->buf_size; 92 return 0; 93 } 94 } 95 return -1; 96} 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 { 149 digit = *cp; 150 if ((digit - '0') <= 9) 151 { 152 digit -= '0'; 153 ++cp; 154 } 155 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 172 *valp = *cp; 173 174 /* don't step over NUL terminator */ 175 if (*cp) 176 ++cp; 177 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 { 192 if ((valp != NULL) && (i < buf_size - 1)) 193 valp[i++] = *cp; 194 ++cp; 195 } 196 if (i == 0 || i >= buf_size) 197 return NULL; 198 valp[i] = '\0'; 199 return cp; 200} 201 202static inline int 203maps_next (struct map_iterator *mi, 204 unsigned long *low, unsigned long *high, unsigned long *offset) 205{ 206 char perm[16], dash = 0, colon = 0, *cp; 207 unsigned long major, minor, inum; 208 ssize_t i, nread; 209 210 if (mi->fd < 0) 211 return 0; 212 213 while (1) 214 { 215 ssize_t bytes_left = mi->buf_end - mi->buf; 216 char *eol = NULL; 217 218 for (i = 0; i < bytes_left; ++i) 219 { 220 if (mi->buf[i] == '\n') 221 { 222 eol = mi->buf + i; 223 break; 224 } 225 else if (mi->buf[i] == '\0') 226 break; 227 } 228 if (!eol) 229 { 230 /* copy down the remaining bytes, if any */ 231 if (bytes_left > 0) 232 memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left); 233 234 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) 238 return 0; 239 else if ((size_t) (nread + bytes_left) < mi->buf_size) 240 { 241 /* 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; 247 } 248 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 } 257 } 258 cp = mi->buf; 259 mi->buf = eol + 1; 260 *eol = '\0'; 261 262 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */ 263 cp = scan_hex (cp, low); 264 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); 272 cp = mi->path = skip_whitespace (cp); 273 if (!cp) 274 continue; 275 cp = scan_string (cp, NULL, 0); 276 if (dash != '-' || colon != ':') 277 continue; /* skip line with unknown or bad format */ 278 return 1; 279 } 280 return 0; 281} 282 283static inline void 284maps_close (struct map_iterator *mi) 285{ 286 if (mi->fd < 0) 287 return; 288 close (mi->fd); 289 mi->fd = -1; 290 if (mi->buf) 291 { 292 munmap (mi->buf_end - mi->buf_size, mi->buf_size); 293 mi->buf = mi->buf_end = NULL; 294 } 295} 296 297#endif /* os_linux_h */ 298