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 30#include <sys/mman.h> 31 32struct map_iterator 33 { 34 off_t offset; 35 int fd; 36 size_t buf_size; 37 char *buf; 38 char *buf_end; 39 char *path; 40 }; 41 42static inline char * 43ltoa (char *buf, long val) 44{ 45 char *cp = buf, tmp; 46 ssize_t i, len; 47 48 do 49 { 50 *cp++ = '0' + (val % 10); 51 val /= 10; 52 } 53 while (val); 54 55 /* reverse the order of the digits: */ 56 len = cp - buf; 57 --cp; 58 for (i = 0; i < len / 2; ++i) 59 { 60 tmp = buf[i]; 61 buf[i] = cp[-i]; 62 cp[-i] = tmp; 63 } 64 return buf + len; 65} 66 67static inline int 68maps_init (struct map_iterator *mi, pid_t pid) 69{ 70 char path[sizeof ("/proc/0123456789/maps")], *cp; 71 72 memcpy (path, "/proc/", 6); 73 cp = ltoa (path + 6, pid); 74 assert (cp + 6 < path + sizeof (path)); 75 memcpy (cp, "/maps", 6); 76 77 mi->fd = open (path, O_RDONLY); 78 if (mi->fd >= 0) 79 { 80 /* Try to allocate a page-sized buffer. */ 81 mi->buf_size = getpagesize (); 82 cp = mmap (NULL, mi->buf_size, PROT_READ | PROT_WRITE, 83 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 84 if (cp == MAP_FAILED) 85 { 86 close(mi->fd); 87 mi->fd = -1; 88 return -1; 89 } 90 else 91 { 92 mi->offset = 0; 93 mi->buf = mi->buf_end = cp + mi->buf_size; 94 return 0; 95 } 96 } 97 return -1; 98} 99 100static inline char * 101skip_whitespace (char *cp) 102{ 103 if (!cp) 104 return NULL; 105 106 while (*cp == ' ' || *cp == '\t') 107 ++cp; 108 return cp; 109} 110 111static inline char * 112scan_hex (char *cp, unsigned long *valp) 113{ 114 unsigned long num_digits = 0, digit, val = 0; 115 116 cp = skip_whitespace (cp); 117 if (!cp) 118 return NULL; 119 120 while (1) 121 { 122 digit = *cp; 123 if ((digit - '0') <= 9) 124 digit -= '0'; 125 else if ((digit - 'a') < 6) 126 digit -= 'a' - 10; 127 else if ((digit - 'A') < 6) 128 digit -= 'A' - 10; 129 else 130 break; 131 val = (val << 4) | digit; 132 ++num_digits; 133 ++cp; 134 } 135 if (!num_digits) 136 return NULL; 137 *valp = val; 138 return cp; 139} 140 141static inline char * 142scan_dec (char *cp, unsigned long *valp) 143{ 144 unsigned long num_digits = 0, digit, val = 0; 145 146 if (!(cp = skip_whitespace (cp))) 147 return NULL; 148 149 while (1) 150 { 151 digit = *cp; 152 if ((digit - '0') <= 9) 153 { 154 digit -= '0'; 155 ++cp; 156 } 157 else 158 break; 159 val = (10 * val) + digit; 160 ++num_digits; 161 } 162 if (!num_digits) 163 return NULL; 164 *valp = val; 165 return cp; 166} 167 168static inline char * 169scan_char (char *cp, char *valp) 170{ 171 if (!cp) 172 return NULL; 173 174 *valp = *cp; 175 176 /* don't step over NUL terminator */ 177 if (*cp) 178 ++cp; 179 return cp; 180} 181 182/* Scan a string delimited by white-space. Fails on empty string or 183 if string is doesn't fit in the specified buffer. */ 184static inline char * 185scan_string (char *cp, char *valp, size_t buf_size) 186{ 187 size_t i = 0; 188 189 if (!(cp = skip_whitespace (cp))) 190 return NULL; 191 192 while (*cp != ' ' && *cp != '\t' && *cp != '\0') 193 { 194 if ((valp != NULL) && (i < buf_size - 1)) 195 valp[i++] = *cp; 196 ++cp; 197 } 198 if (i == 0 || i >= buf_size) 199 return NULL; 200 valp[i] = '\0'; 201 return cp; 202} 203 204static inline int 205maps_next (struct map_iterator *mi, 206 unsigned long *low, unsigned long *high, unsigned long *offset, 207 unsigned long *flags) 208{ 209 char perm[16], dash = 0, colon = 0, *cp; 210 unsigned long major, minor, inum; 211 ssize_t i, nread; 212 213 if (mi->fd < 0) 214 return 0; 215 216 while (1) 217 { 218 ssize_t bytes_left = mi->buf_end - mi->buf; 219 char *eol = NULL; 220 221 for (i = 0; i < bytes_left; ++i) 222 { 223 if (mi->buf[i] == '\n') 224 { 225 eol = mi->buf + i; 226 break; 227 } 228 else if (mi->buf[i] == '\0') 229 break; 230 } 231 if (!eol) 232 { 233 /* copy down the remaining bytes, if any */ 234 if (bytes_left > 0) 235 memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left); 236 237 mi->buf = mi->buf_end - mi->buf_size; 238 nread = read (mi->fd, mi->buf + bytes_left, 239 mi->buf_size - bytes_left); 240 if (nread <= 0) 241 return 0; 242 else if ((size_t) (nread + bytes_left) < mi->buf_size) 243 { 244 /* Move contents to the end of the buffer so we 245 maintain the invariant that all bytes between 246 mi->buf and mi->buf_end are valid. */ 247 memmove (mi->buf_end - nread - bytes_left, mi->buf, 248 nread + bytes_left); 249 mi->buf = mi->buf_end - nread - bytes_left; 250 } 251 252 eol = mi->buf + bytes_left + nread - 1; 253 254 for (i = bytes_left; i < bytes_left + nread; ++i) 255 if (mi->buf[i] == '\n') 256 { 257 eol = mi->buf + i; 258 break; 259 } 260 } 261 cp = mi->buf; 262 mi->buf = eol + 1; 263 *eol = '\0'; 264 265 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */ 266 cp = scan_hex (cp, low); 267 cp = scan_char (cp, &dash); 268 cp = scan_hex (cp, high); 269 cp = scan_string (cp, perm, sizeof (perm)); 270 cp = scan_hex (cp, offset); 271 cp = scan_hex (cp, &major); 272 cp = scan_char (cp, &colon); 273 cp = scan_hex (cp, &minor); 274 cp = scan_dec (cp, &inum); 275 cp = mi->path = skip_whitespace (cp); 276 if (!cp) 277 continue; 278 cp = scan_string (cp, NULL, 0); 279 if (dash != '-' || colon != ':') 280 continue; /* skip line with unknown or bad format */ 281 if (flags) 282 { 283 *flags = 0; 284 if (perm[0] == 'r') 285 { 286 *flags |= PROT_READ; 287 } 288 if (perm[1] == 'w') 289 { 290 *flags |= PROT_WRITE; 291 } 292 if (perm[2] == 'x') 293 { 294 *flags |= PROT_EXEC; 295 } 296 } 297 return 1; 298 } 299 return 0; 300} 301 302static inline void 303maps_close (struct map_iterator *mi) 304{ 305 if (mi->fd < 0) 306 return; 307 close (mi->fd); 308 mi->fd = -1; 309 if (mi->buf) 310 { 311 munmap (mi->buf_end - mi->buf_size, mi->buf_size); 312 mi->buf = mi->buf_end = NULL; 313 } 314} 315 316#endif /* os_linux_h */ 317