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 (at) gmail.com> 5 6 This file is part of libunwind. 7 8 Permission is hereby granted, free of charge, to any person obtaining 9 a copy of this software and associated documentation files (the 10 "Software"), to deal in the Software without restriction, including 11 without limitation the rights to use, copy, modify, merge, publish, 12 distribute, sublicense, and/or sell copies of the Software, and to 13 permit persons to whom the Software is furnished to do so, subject to 14 the following conditions: 15 16 The above copyright notice and this permission notice shall be 17 included in all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 WITH 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 32 struct 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 42 static inline char * 43 ltoa (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 67 static inline int 68 maps_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 100 static inline char * 101 skip_whitespace (char *cp) 102 { 103 if (!cp) 104 return NULL; 105 106 while (*cp == ' ' || *cp == '\t') 107 ++cp; 108 return cp; 109 } 110 111 static inline char * 112 scan_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 141 static inline char * 142 scan_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 168 static inline char * 169 scan_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. */ 184 static inline char * 185 scan_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 204 static inline int 205 maps_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 302 static inline void 303 maps_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