1 /* Iteration over virtual memory areas.
2 Copyright (C) 2011 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2011.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include <errno.h> /* errno */
24 #include <stdlib.h> /* size_t */
25 #include <fcntl.h> /* open, O_RDONLY */
26 #include <unistd.h> /* getpagesize, read, close */
28 #if defined __sgi || defined __osf__ /* IRIX, OSF/1 */
29 # include <string.h> /* memcpy */
30 # include <sys/types.h>
31 # include <sys/mman.h> /* mmap, munmap */
32 # include <sys/procfs.h> /* PIOC*, prmap_t */
35 #if defined __APPLE__ && defined __MACH__ /* MacOS X */
36 # include <mach/mach.h>
39 #if (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ /* Windows */
43 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
47 #if HAVE_MQUERY /* OpenBSD */
48 # include <sys/types.h>
49 # include <sys/mman.h> /* mquery */
53 /* Support for reading text files in the /proc file system. */
55 #if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ /* || defined __CYGWIN__ */
57 /* Buffered read-only streams.
58 We cannot use <stdio.h> here, because fopen() calls malloc(), and a malloc()
59 call may call mmap() and thus pre-allocate available memory. */
70 /* Open a read-only file stream. */
72 rof_open (struct rofile *rof, const char *filename)
74 int fd = open (filename, O_RDONLY);
84 /* Return the next byte from a read-only file stream without consuming it,
87 rof_peekchar (struct rofile *rof)
89 if (rof->position == rof->filled)
96 int n = read (rof->fd, rof->buffer, sizeof (rof->buffer));
98 if (n < 0 && errno == EINTR)
111 return (unsigned char) rof->buffer[rof->position];
114 /* Return the next byte from a read-only file stream, or -1 at EOF. */
116 rof_getchar (struct rofile *rof)
118 int c = rof_peekchar (rof);
124 /* Parse an unsigned hexadecimal number from a read-only file stream. */
126 rof_scanf_lx (struct rofile *rof, unsigned long *valuep)
128 unsigned long value = 0;
129 unsigned int numdigits = 0;
132 int c = rof_peekchar (rof);
133 if (c >= '0' && c <= '9')
134 value = (value << 4) + (c - '0');
135 else if (c >= 'A' && c <= 'F')
136 value = (value << 4) + (c - 'A' + 10);
137 else if (c >= 'a' && c <= 'f')
138 value = (value << 4) + (c - 'a' + 10);
150 /* Close a read-only file stream. */
152 rof_close (struct rofile *rof)
161 vma_iterate (vma_iterate_callback_fn callback, void *data)
163 #if defined __linux__ /* || defined __CYGWIN__ */
168 /* Open the current process' maps file. It describes one VMA per line. */
169 if (rof_open (&rof, "/proc/self/maps") < 0)
174 unsigned long start, end;
177 /* Parse one line. First start and end. */
178 if (!(rof_scanf_lx (&rof, &start) >= 0
179 && rof_getchar (&rof) == '-'
180 && rof_scanf_lx (&rof, &end) >= 0))
182 /* Then the flags. */
184 c = rof_getchar (&rof);
188 flags |= VMA_PROT_READ;
189 c = rof_getchar (&rof);
191 flags |= VMA_PROT_WRITE;
192 c = rof_getchar (&rof);
194 flags |= VMA_PROT_EXECUTE;
195 while (c = rof_getchar (&rof), c != -1 && c != '\n')
198 if (callback (data, start, end, flags))
203 #elif defined __FreeBSD__ || defined __NetBSD__
208 /* Open the current process' maps file. It describes one VMA per line. */
209 if (rof_open (&rof, "/proc/curproc/map") < 0)
214 unsigned long start, end;
217 /* Parse one line. First start. */
218 if (!(rof_getchar (&rof) == '0'
219 && rof_getchar (&rof) == 'x'
220 && rof_scanf_lx (&rof, &start) >= 0))
222 while (c = rof_peekchar (&rof), c == ' ' || c == '\t')
225 if (!(rof_getchar (&rof) == '0'
226 && rof_getchar (&rof) == 'x'
227 && rof_scanf_lx (&rof, &end) >= 0))
229 /* Then the flags. */
231 c = rof_getchar (&rof);
235 flags |= VMA_PROT_READ;
236 c = rof_getchar (&rof);
238 flags |= VMA_PROT_WRITE;
239 c = rof_getchar (&rof);
241 flags |= VMA_PROT_EXECUTE;
242 while (c = rof_getchar (&rof), c != -1 && c != '\n')
245 if (callback (data, start, end, flags))
250 #elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */
253 char fnamebuf[6+10+1];
258 # if HAVE_MAP_ANONYMOUS
260 # define map_flags MAP_ANONYMOUS
266 unsigned long auxmap_start;
267 unsigned long auxmap_end;
271 pagesize = getpagesize ();
273 /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()). */
274 fname = fnamebuf + sizeof (fnamebuf) - 1;
277 unsigned int value = getpid ();
279 *--fname = (value % 10) + '0';
280 while ((value = value / 10) > 0);
283 memcpy (fname, "/proc/", 6);
285 fd = open (fname, O_RDONLY);
289 if (ioctl (fd, PIOCNMAP, &nmaps) < 0)
292 memneed = (nmaps + 10) * sizeof (prmap_t);
293 /* Allocate memneed bytes of memory.
294 We cannot use alloca here, because not much stack space is guaranteed.
295 We also cannot use malloc here, because a malloc() call may call mmap()
296 and thus pre-allocate available memory.
297 So use mmap(), and ignore the resulting VMA. */
298 memneed = ((memneed - 1) / pagesize + 1) * pagesize;
299 # if !HAVE_MAP_ANONYMOUS
300 zero_fd = open ("/dev/zero", O_RDONLY, 0644);
304 auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
305 map_flags | MAP_PRIVATE, zero_fd, 0);
306 # if !HAVE_MAP_ANONYMOUS
309 if (auxmap == (void *) -1)
311 auxmap_start = (unsigned long) auxmap;
312 auxmap_end = auxmap_start + memneed;
313 maps = (prmap_t *) auxmap;
315 if (ioctl (fd, PIOCMAP, maps) < 0)
320 unsigned long start, end;
323 start = (unsigned long) mp->pr_vaddr;
324 end = start + mp->pr_size;
325 if (start == 0 && end == 0)
328 if (mp->pr_mflags & MA_READ)
329 flags |= VMA_PROT_READ;
330 if (mp->pr_mflags & MA_WRITE)
331 flags |= VMA_PROT_WRITE;
332 if (mp->pr_mflags & MA_EXEC)
333 flags |= VMA_PROT_EXECUTE;
335 if (start <= auxmap_start && auxmap_end - 1 <= end - 1)
337 /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1]
338 = [start,auxmap_start-1] u [auxmap_end,end-1]. */
339 if (start < auxmap_start)
340 if (callback (data, start, auxmap_start, flags))
342 if (auxmap_end - 1 < end - 1)
343 if (callback (data, auxmap_end, end, flags))
348 if (callback (data, start, end, flags))
352 munmap (auxmap, memneed);
357 munmap (auxmap, memneed);
362 #elif defined __APPLE__ && defined __MACH__ /* MacOS X */
364 task_t task = mach_task_self ();
365 vm_address_t address;
368 for (address = VM_MIN_ADDRESS;; address += size)
371 mach_port_t object_name;
373 /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
374 32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
375 mach_vm_address_t and mach_vm_size_t are always 64 bits large.
376 MacOS X 10.5 has three vm_region like methods:
377 - vm_region. It has arguments that depend on whether the current
378 process is 32-bit or 64-bit. When linking dynamically, this
379 function exists only in 32-bit processes. Therefore we use it only
381 - vm_region_64. It has arguments that depend on whether the current
382 process is 32-bit or 64-bit. It interprets a flavor
383 VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is
384 dangerous since 'struct vm_region_basic_info_64' is larger than
385 'struct vm_region_basic_info'; therefore let's write
386 VM_REGION_BASIC_INFO_64 explicitly.
387 - mach_vm_region. It has arguments that are 64-bit always. This
388 function is useful when you want to access the VM of a process
389 other than the current process.
390 In 64-bit processes, we could use vm_region_64 or mach_vm_region.
391 I choose vm_region_64 because it uses the same types as vm_region,
392 resulting in less conditional code. */
393 # if defined __ppc64__ || defined __x86_64__
394 struct vm_region_basic_info_64 info;
395 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
397 more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64,
398 (vm_region_info_t)&info, &info_count, &object_name)
401 struct vm_region_basic_info info;
402 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
404 more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
405 (vm_region_info_t)&info, &info_count, &object_name)
408 if (object_name != MACH_PORT_NULL)
409 mach_port_deallocate (mach_task_self (), object_name);
413 if (info.protection & VM_PROT_READ)
414 flags |= VMA_PROT_READ;
415 if (info.protection & VM_PROT_WRITE)
416 flags |= VMA_PROT_WRITE;
417 if (info.protection & VM_PROT_EXECUTE)
418 flags |= VMA_PROT_EXECUTE;
419 if (callback (data, address, address + size, flags))
423 #elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__
424 /* Windows platform. Use the native Windows API. */
426 MEMORY_BASIC_INFORMATION info;
427 unsigned long address = 0;
429 while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info))
431 if (info.State != MEM_FREE)
432 /* Ignore areas where info.State has the value MEM_RESERVE or,
433 equivalently, info.Protect has the undocumented value 0.
434 This is needed, so that on Cygwin, areas used by malloc() are
435 distinguished from areas reserved for future malloc(). */
436 if (info.State != MEM_RESERVE)
438 unsigned long start, end;
441 start = (unsigned long)info.BaseAddress;
442 end = start + info.RegionSize;
443 switch (info.Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
446 flags = VMA_PROT_READ;
450 flags = VMA_PROT_READ | VMA_PROT_WRITE;
453 flags = VMA_PROT_EXECUTE;
455 case PAGE_EXECUTE_READ:
456 flags = VMA_PROT_READ | VMA_PROT_EXECUTE;
458 case PAGE_EXECUTE_READWRITE:
459 case PAGE_EXECUTE_WRITECOPY:
460 flags = VMA_PROT_READ | VMA_PROT_WRITE | VMA_PROT_EXECUTE;
468 if (callback (data, start, end, flags))
471 address = (unsigned long)info.BaseAddress + info.RegionSize;
474 #elif defined __BEOS__ || defined __HAIKU__
475 /* Use the BeOS specific API. */
481 while (get_next_area_info (0, &cookie, &info) == B_OK)
483 unsigned long start, end;
486 start = (unsigned long) info.address;
487 end = start + info.size;
489 if (info.protection & B_READ_AREA)
490 flags |= VMA_PROT_READ | VMA_PROT_EXECUTE;
491 if (info.protection & B_WRITE_AREA)
492 flags |= VMA_PROT_WRITE;
494 if (callback (data, start, end, flags))
498 #elif HAVE_MQUERY /* OpenBSD */
502 int /*bool*/ address_known_mapped;
504 pagesize = getpagesize ();
505 /* Avoid calling mquery with a NULL first argument, because this argument
506 value has a specific meaning. We know the NULL page is unmapped. */
508 address_known_mapped = 0;
511 /* Test whether the page at address is mapped. */
512 if (address_known_mapped
513 || mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0)
516 /* The page at address is mapped.
517 This is the start of an interval. */
518 uintptr_t start = address;
521 /* Find the end of the interval. */
522 end = (uintptr_t) mquery ((void *) address, pagesize, 0, 0, -1, 0);
523 if (end == (uintptr_t) (void *) -1)
524 end = 0; /* wrap around */
527 /* It's too complicated to find out about the flags. Just pass 0. */
528 if (callback (data, start, end, 0))
531 if (address < pagesize) /* wrap around? */
534 /* Here we know that the page at address is unmapped. */
536 uintptr_t query_size = pagesize;
540 /* Query larger and larger blocks, to get through the unmapped address
541 range with few mquery() calls. */
544 if (2 * query_size > query_size)
545 query_size = 2 * query_size;
546 if (address + query_size - 1 < query_size) /* wrap around? */
548 address_known_mapped = 0;
551 if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
554 /* Not all the interval [address .. address + query_size - 1]
556 address_known_mapped = (query_size == pagesize);
559 /* The interval [address .. address + query_size - 1] is
561 address += query_size;
563 /* Reduce the query size again, to determine the precise size of the
564 unmapped interval that starts at address. */
565 while (query_size > pagesize)
567 query_size = query_size / 2;
568 if (address + query_size - 1 >= query_size)
570 if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
573 /* The interval [address .. address + query_size - 1] is
575 address += query_size;
576 address_known_mapped = 0;
579 address_known_mapped = (query_size == pagesize);
582 /* Here again query_size = pagesize, and
583 either address + pagesize - 1 < pagesize, or
584 mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0) fails.
585 So, the unmapped area ends at address. */
587 if (address + pagesize - 1 < pagesize) /* wrap around? */
599 /* Output the VMAs of the current process in a format similar to the Linux
600 /proc/$pid/maps file. */
603 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
606 printf ("%08lx-%08lx %c%c%c\n",
607 (unsigned long) start, (unsigned long) end,
608 flags & VMA_PROT_READ ? 'r' : '-',
609 flags & VMA_PROT_WRITE ? 'w' : '-',
610 flags & VMA_PROT_EXECUTE ? 'x' : '-');
617 vma_iterate (vma_iterate_callback, NULL);
619 /* Let the user interactively look at the /proc file system. */