# HG changeset patch # User Bruno Haible # Date 1295837762 -3600 # Node ID 996391c07734e6cde42c17b40800aa0b611330ba # Parent eb791b56afa28691c706c7d54e6e377275e2502f New module 'vma-iter'. * lib/vma-iter.h: New file. * lib/vma-iter.c: New file, based on lib/get-rusage-as.c. * modules/vma-iter: New file. * lib/get-rusage-as.c: Include vma-iter.h. Don't include system headers for get_rusage_as_via_iterator. (vma_iterate_callback): New function. (get_rusage_as_via_iterator): Rewritten to use vma_iterate. * modules/get-rusage-as (Depends-on): Add vma-iter. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2011-01-23 Bruno Haible + + New module 'vma-iter'. + * lib/vma-iter.h: New file. + * lib/vma-iter.c: New file, based on lib/get-rusage-as.c. + * modules/vma-iter: New file. + * lib/get-rusage-as.c: Include vma-iter.h. Don't include system headers + for get_rusage_as_via_iterator. + (vma_iterate_callback): New function. + (get_rusage_as_via_iterator): Rewritten to use vma_iterate. + * modules/get-rusage-as (Depends-on): Add vma-iter. + 2011-01-23 Bruno Haible uninorm: Tweak includes. diff --git a/lib/get-rusage-as.c b/lib/get-rusage-as.c --- a/lib/get-rusage-as.c +++ b/lib/get-rusage-as.c @@ -141,23 +141,7 @@ /* System support for get_rusage_as_via_iterator(). */ -#if defined __sgi || defined __osf__ /* IRIX, OSF/1 */ -# include /* memcpy */ -# include -# include /* PIOC*, prmap_t */ -#endif - -#if defined __APPLE__ && defined __MACH__ /* MacOS X */ -# include -#endif - -#if (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ /* Windows */ -# include -#endif - -#if defined __BEOS__ /* BeOS */ -# include -#endif +#include "vma-iter.h" #if HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT @@ -327,368 +311,37 @@ #endif -/* Support for reading text files in the /proc file system. */ - -#if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ /* || defined __CYGWIN__ */ - -/* Buffered read-only streams. - We cannot use here, because fopen() calls malloc(), and a malloc() - call may call mmap() and thus pre-allocate available memory. */ +#if VMA_ITERATE_SUPPORTED -struct rofile - { - int fd; - size_t position; - size_t filled; - int eof_seen; - char buffer[1024]; - }; +static int +vma_iterate_callback (void *data, uintptr_t start, uintptr_t end, + unsigned int flags) +{ + uintptr_t *totalp = (uintptr_t *) data; -/* Open a read-only file stream. */ -static int -rof_open (struct rofile *rof, const char *filename) -{ - int fd = open (filename, O_RDONLY); - if (fd < 0) - return -1; - rof->fd = fd; - rof->position = 0; - rof->filled = 0; - rof->eof_seen = 0; + *totalp += end - start; return 0; } -/* Return the next byte from a read-only file stream without consuming it, - or -1 at EOF. */ -static int -rof_peekchar (struct rofile *rof) -{ - if (rof->position == rof->filled) - { - if (rof->eof_seen) - return -1; - else - for (;;) - { - int n = read (rof->fd, rof->buffer, sizeof (rof->buffer)); -# ifdef EINTR - if (n < 0 && errno == EINTR) - continue; -# endif - if (n <= 0) - { - rof->eof_seen = 1; - return -1; - } - rof->filled = n; - rof->position = 0; - break; - } - } - return (unsigned char) rof->buffer[rof->position]; -} - -/* Return the next byte from a read-only file stream, or -1 at EOF. */ -static int -rof_getchar (struct rofile *rof) -{ - int c = rof_peekchar (rof); - if (c >= 0) - rof->position++; - return c; -} - -/* Parse an unsigned hexadecimal number from a read-only file stream. */ -static int -rof_scanf_lx (struct rofile *rof, unsigned long *valuep) -{ - unsigned long value = 0; - unsigned int numdigits = 0; - for (;;) - { - int c = rof_peekchar (rof); - if (c >= '0' && c <= '9') - value = (value << 4) + (c - '0'); - else if (c >= 'A' && c <= 'F') - value = (value << 4) + (c - 'A' + 10); - else if (c >= 'a' && c <= 'f') - value = (value << 4) + (c - 'a' + 10); - else - break; - rof_getchar (rof); - numdigits++; - } - if (numdigits == 0) - return -1; - *valuep = value; - return 0; -} - -/* Close a read-only file stream. */ -static void -rof_close (struct rofile *rof) -{ - close (rof->fd); -} - -#endif - - static inline uintptr_t get_rusage_as_via_iterator (void) { -#if defined __linux__ /* || defined __CYGWIN__ */ - - struct rofile rof; - int c; - unsigned long total; - - /* Open the current process' maps file. It describes one VMA per line. */ - if (rof_open (&rof, "/proc/self/maps") < 0) - return 0; - - total = 0; - for (;;) - { - unsigned long start, end; - - if (!(rof_scanf_lx (&rof, &start) >= 0 - && rof_getchar (&rof) == '-' - && rof_scanf_lx (&rof, &end) >= 0)) - break; - while (c = rof_getchar (&rof), c != -1 && c != '\n') - ; - total += end - start; - } - rof_close (&rof); - return total; - -#elif defined __FreeBSD__ || defined __NetBSD__ - - struct rofile rof; - int c; - unsigned long total; - - /* Open the current process' maps file. It describes one VMA per line. */ - if (rof_open (&rof, "/proc/curproc/map") < 0) - return 0; - - total = 0; - for (;;) - { - unsigned long start, end; - - if (!(rof_getchar (&rof) == '0' - && rof_getchar (&rof) == 'x' - && rof_scanf_lx (&rof, &start) >= 0)) - break; - while (c = rof_peekchar (&rof), c == ' ' || c == '\t') - rof_getchar (&rof); - if (!(rof_getchar (&rof) == '0' - && rof_getchar (&rof) == 'x' - && rof_scanf_lx (&rof, &end) >= 0)) - break; - while (c = rof_getchar (&rof), c != -1 && c != '\n') - continue; - total += end - start; - } - rof_close (&rof); - return total; - -#elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */ - - size_t pagesize; - char fnamebuf[6+10+1]; - char *fname; - int fd; - int nmaps; - size_t memneed; -# if HAVE_MAP_ANONYMOUS -# define zero_fd -1 -# define map_flags MAP_ANONYMOUS -# else - int zero_fd; -# define map_flags 0 -# endif - void *auxmap; - unsigned long auxmap_start; - unsigned long auxmap_end; - prmap_t* maps; - prmap_t* mp; - unsigned long total; - - pagesize = getpagesize (); - - /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()). */ - fname = fnamebuf + sizeof (fnamebuf) - 1; - *fname = '\0'; - { - unsigned int value = getpid (); - do - *--fname = (value % 10) + '0'; - while ((value = value / 10) > 0); - } - fname -= 6; - memcpy (fname, "/proc/", 6); - - fd = open (fname, O_RDONLY); - if (fd < 0) - return 0; - - if (ioctl (fd, PIOCNMAP, &nmaps) < 0) - goto fail2; + uintptr_t total = 0; - memneed = (nmaps + 10) * sizeof (prmap_t); - /* Allocate memneed bytes of memory. - We cannot use alloca here, because not much stack space is guaranteed. - We also cannot use malloc here, because a malloc() call may call mmap() - and thus pre-allocate available memory. - So use mmap(), and ignore the resulting VMA. */ - memneed = ((memneed - 1) / pagesize + 1) * pagesize; -# if !HAVE_MAP_ANONYMOUS - zero_fd = open ("/dev/zero", O_RDONLY, 0644); - if (zero_fd < 0) - goto fail2; -# endif - auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, - map_flags | MAP_PRIVATE, zero_fd, 0); -# if !HAVE_MAP_ANONYMOUS - close (zero_fd); -# endif - if (auxmap == (void *) -1) - goto fail2; - auxmap_start = (unsigned long) auxmap; - auxmap_end = auxmap_start + memneed; - maps = (prmap_t *) auxmap; - - if (ioctl (fd, PIOCMAP, maps) < 0) - goto fail1; - - total = 0; - for (mp = maps;;) - { - unsigned long start, end; - - start = (unsigned long) mp->pr_vaddr; - end = start + mp->pr_size; - if (start == 0 && end == 0) - break; - mp++; - if (start <= auxmap_start && auxmap_end - 1 <= end - 1) - /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] - = [start,auxmap_start-1] u [auxmap_end,end-1]. */ - total += (end - start) - memneed; - else - total += end - start; - } - munmap (auxmap, memneed); - close (fd); - return total; - - fail1: - munmap (auxmap, memneed); - fail2: - close (fd); - return 0; - -#elif defined __APPLE__ && defined __MACH__ /* MacOS X */ - - task_t task = mach_task_self (); - vm_address_t address; - vm_size_t size; - vm_address_t total = 0; + vma_iterate (vma_iterate_callback, &total); - for (address = VM_MIN_ADDRESS;; address += size) - { - int more; - mach_port_t object_name; - /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have - 32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas - mach_vm_address_t and mach_vm_size_t are always 64 bits large. - MacOS X 10.5 has three vm_region like methods: - - vm_region. It has arguments that depend on whether the current - process is 32-bit or 64-bit. When linking dynamically, this - function exists only in 32-bit processes. Therefore we use it only - in 32-bit processes. - - vm_region_64. It has arguments that depend on whether the current - process is 32-bit or 64-bit. It interprets a flavor - VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is - dangerous since 'struct vm_region_basic_info_64' is larger than - 'struct vm_region_basic_info'; therefore let's write - VM_REGION_BASIC_INFO_64 explicitly. - - mach_vm_region. It has arguments that are 64-bit always. This - function is useful when you want to access the VM of a process - other than the current process. - In 64-bit processes, we could use vm_region_64 or mach_vm_region. - I choose vm_region_64 because it uses the same types as vm_region, - resulting in less conditional code. */ -# if defined __ppc64__ || defined __x86_64__ - struct vm_region_basic_info_64 info; - mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; - - more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64, - (vm_region_info_t)&info, &info_count, &object_name) - == KERN_SUCCESS); -# else - struct vm_region_basic_info info; - mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; - - more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO, - (vm_region_info_t)&info, &info_count, &object_name) - == KERN_SUCCESS); -# endif - if (object_name != MACH_PORT_NULL) - mach_port_deallocate (mach_task_self (), object_name); - if (!more) - break; - total += size; - } return total; - -#elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ - /* Windows platform. Use the native Windows API. */ - - MEMORY_BASIC_INFORMATION info; - unsigned long address = 0; - unsigned long total = 0; - - while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info)) - { - if (info.State != MEM_FREE) - /* Ignore areas where info.Protect has the undocumented value 0. - This is needed, so that on Cygwin, areas used by malloc() are - distinguished from areas reserved for future malloc(). */ - if (info.Protect != 0) - total += info.RegionSize; - address = (unsigned long)info.BaseAddress + info.RegionSize; - } - return total; - -#elif defined __BEOS__ - /* Use the BeOS specific API. */ - - area_info info; - int32 cookie; - unsigned long total = 0; - - cookie = 0; - while (get_next_area_info (0, &cookie, &info) == B_OK) - { - unsigned long start, end; - - start = (unsigned long) info.address; - end = start + info.size; - - total += end - start; - } - return total; +} #else +static inline uintptr_t +get_rusage_as_via_iterator (void) +{ return 0; +} #endif -} uintptr_t diff --git a/lib/vma-iter.c b/lib/vma-iter.c new file mode 100644 --- /dev/null +++ b/lib/vma-iter.c @@ -0,0 +1,525 @@ +/* Iteration over virtual memory areas. + Copyright (C) 2011 Free Software Foundation, Inc. + Written by Bruno Haible , 2011. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +/* Specification. */ +#include "vma-iter.h" + +#include /* errno */ +#include /* size_t */ +#include /* open, O_RDONLY */ +#include /* getpagesize, read, close */ + +#if defined __sgi || defined __osf__ /* IRIX, OSF/1 */ +# include /* memcpy */ +# include +# include /* mmap, munmap */ +# include /* PIOC*, prmap_t */ +#endif + +#if defined __APPLE__ && defined __MACH__ /* MacOS X */ +# include +#endif + +#if (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ /* Windows */ +# include +#endif + +#if defined __BEOS__ /* BeOS */ +# include +#endif + + +/* Support for reading text files in the /proc file system. */ + +#if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ /* || defined __CYGWIN__ */ + +/* Buffered read-only streams. + We cannot use here, because fopen() calls malloc(), and a malloc() + call may call mmap() and thus pre-allocate available memory. */ + +struct rofile + { + int fd; + size_t position; + size_t filled; + int eof_seen; + char buffer[1024]; + }; + +/* Open a read-only file stream. */ +static int +rof_open (struct rofile *rof, const char *filename) +{ + int fd = open (filename, O_RDONLY); + if (fd < 0) + return -1; + rof->fd = fd; + rof->position = 0; + rof->filled = 0; + rof->eof_seen = 0; + return 0; +} + +/* Return the next byte from a read-only file stream without consuming it, + or -1 at EOF. */ +static int +rof_peekchar (struct rofile *rof) +{ + if (rof->position == rof->filled) + { + if (rof->eof_seen) + return -1; + else + for (;;) + { + int n = read (rof->fd, rof->buffer, sizeof (rof->buffer)); +# ifdef EINTR + if (n < 0 && errno == EINTR) + continue; +# endif + if (n <= 0) + { + rof->eof_seen = 1; + return -1; + } + rof->filled = n; + rof->position = 0; + break; + } + } + return (unsigned char) rof->buffer[rof->position]; +} + +/* Return the next byte from a read-only file stream, or -1 at EOF. */ +static int +rof_getchar (struct rofile *rof) +{ + int c = rof_peekchar (rof); + if (c >= 0) + rof->position++; + return c; +} + +/* Parse an unsigned hexadecimal number from a read-only file stream. */ +static int +rof_scanf_lx (struct rofile *rof, unsigned long *valuep) +{ + unsigned long value = 0; + unsigned int numdigits = 0; + for (;;) + { + int c = rof_peekchar (rof); + if (c >= '0' && c <= '9') + value = (value << 4) + (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (value << 4) + (c - 'A' + 10); + else if (c >= 'a' && c <= 'f') + value = (value << 4) + (c - 'a' + 10); + else + break; + rof_getchar (rof); + numdigits++; + } + if (numdigits == 0) + return -1; + *valuep = value; + return 0; +} + +/* Close a read-only file stream. */ +static void +rof_close (struct rofile *rof) +{ + close (rof->fd); +} + +#endif + + +void +vma_iterate (vma_iterate_callback_fn callback, void *data) +{ +#if defined __linux__ /* || defined __CYGWIN__ */ + + struct rofile rof; + int c; + + /* Open the current process' maps file. It describes one VMA per line. */ + if (rof_open (&rof, "/proc/self/maps") < 0) + return; + + for (;;) + { + unsigned long start, end; + unsigned int flags; + + /* Parse one line. First start and end. */ + if (!(rof_scanf_lx (&rof, &start) >= 0 + && rof_getchar (&rof) == '-' + && rof_scanf_lx (&rof, &end) >= 0)) + break; + /* Then the flags. */ + do + c = rof_getchar (&rof); + while (c == ' '); + flags = 0; + if (c == 'r') + flags |= VMA_PROT_READ; + c = rof_getchar (&rof); + if (c == 'w') + flags |= VMA_PROT_WRITE; + c = rof_getchar (&rof); + if (c == 'x') + flags |= VMA_PROT_EXECUTE; + while (c = rof_getchar (&rof), c != -1 && c != '\n') + ; + + if (callback (data, start, end, flags)) + break; + } + rof_close (&rof); + +#elif defined __FreeBSD__ || defined __NetBSD__ + + struct rofile rof; + int c; + + /* Open the current process' maps file. It describes one VMA per line. */ + if (rof_open (&rof, "/proc/curproc/map") < 0) + return; + + for (;;) + { + unsigned long start, end; + unsigned int flags; + + /* Parse one line. First start. */ + if (!(rof_getchar (&rof) == '0' + && rof_getchar (&rof) == 'x' + && rof_scanf_lx (&rof, &start) >= 0)) + break; + while (c = rof_peekchar (&rof), c == ' ' || c == '\t') + rof_getchar (&rof); + /* Then end. */ + if (!(rof_getchar (&rof) == '0' + && rof_getchar (&rof) == 'x' + && rof_scanf_lx (&rof, &end) >= 0)) + break; + /* Then the flags. */ + do + c = rof_getchar (&rof); + while (c == ' '); + flags = 0; + if (c == 'r') + flags |= VMA_PROT_READ; + c = rof_getchar (&rof); + if (c == 'w') + flags |= VMA_PROT_WRITE; + c = rof_getchar (&rof); + if (c == 'x') + flags |= VMA_PROT_EXECUTE; + while (c = rof_getchar (&rof), c != -1 && c != '\n') + ; + + if (callback (data, start, end, flags)) + break; + } + rof_close (&rof); + +#elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */ + + size_t pagesize; + char fnamebuf[6+10+1]; + char *fname; + int fd; + int nmaps; + size_t memneed; +# if HAVE_MAP_ANONYMOUS +# define zero_fd -1 +# define map_flags MAP_ANONYMOUS +# else + int zero_fd; +# define map_flags 0 +# endif + void *auxmap; + unsigned long auxmap_start; + unsigned long auxmap_end; + prmap_t* maps; + prmap_t* mp; + + pagesize = getpagesize (); + + /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()). */ + fname = fnamebuf + sizeof (fnamebuf) - 1; + *fname = '\0'; + { + unsigned int value = getpid (); + do + *--fname = (value % 10) + '0'; + while ((value = value / 10) > 0); + } + fname -= 6; + memcpy (fname, "/proc/", 6); + + fd = open (fname, O_RDONLY); + if (fd < 0) + return; + + if (ioctl (fd, PIOCNMAP, &nmaps) < 0) + goto fail2; + + memneed = (nmaps + 10) * sizeof (prmap_t); + /* Allocate memneed bytes of memory. + We cannot use alloca here, because not much stack space is guaranteed. + We also cannot use malloc here, because a malloc() call may call mmap() + and thus pre-allocate available memory. + So use mmap(), and ignore the resulting VMA. */ + memneed = ((memneed - 1) / pagesize + 1) * pagesize; +# if !HAVE_MAP_ANONYMOUS + zero_fd = open ("/dev/zero", O_RDONLY, 0644); + if (zero_fd < 0) + goto fail2; +# endif + auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, + map_flags | MAP_PRIVATE, zero_fd, 0); +# if !HAVE_MAP_ANONYMOUS + close (zero_fd); +# endif + if (auxmap == (void *) -1) + goto fail2; + auxmap_start = (unsigned long) auxmap; + auxmap_end = auxmap_start + memneed; + maps = (prmap_t *) auxmap; + + if (ioctl (fd, PIOCMAP, maps) < 0) + goto fail1; + + for (mp = maps;;) + { + unsigned long start, end; + unsigned int flags; + + start = (unsigned long) mp->pr_vaddr; + end = start + mp->pr_size; + if (start == 0 && end == 0) + break; + flags = 0; + if (mp->pr_mflags & MA_READ) + flags |= VMA_PROT_READ; + if (mp->pr_mflags & MA_WRITE) + flags |= VMA_PROT_WRITE; + if (mp->pr_mflags & MA_EXEC) + flags |= VMA_PROT_EXECUTE; + mp++; + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (callback (data, start, auxmap_start, flags)) + break; + if (callback (data, auxmap_end, end, flags)) + break; + } + else + { + if (callback (data, start, end, flags)) + break; + } + } + munmap (auxmap, memneed); + close (fd); + return; + + fail1: + munmap (auxmap, memneed); + fail2: + close (fd); + return; + +#elif defined __APPLE__ && defined __MACH__ /* MacOS X */ + + task_t task = mach_task_self (); + vm_address_t address; + vm_size_t size; + + for (address = VM_MIN_ADDRESS;; address += size) + { + int more; + mach_port_t object_name; + unsigned int flags; + /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have + 32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas + mach_vm_address_t and mach_vm_size_t are always 64 bits large. + MacOS X 10.5 has three vm_region like methods: + - vm_region. It has arguments that depend on whether the current + process is 32-bit or 64-bit. When linking dynamically, this + function exists only in 32-bit processes. Therefore we use it only + in 32-bit processes. + - vm_region_64. It has arguments that depend on whether the current + process is 32-bit or 64-bit. It interprets a flavor + VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is + dangerous since 'struct vm_region_basic_info_64' is larger than + 'struct vm_region_basic_info'; therefore let's write + VM_REGION_BASIC_INFO_64 explicitly. + - mach_vm_region. It has arguments that are 64-bit always. This + function is useful when you want to access the VM of a process + other than the current process. + In 64-bit processes, we could use vm_region_64 or mach_vm_region. + I choose vm_region_64 because it uses the same types as vm_region, + resulting in less conditional code. */ +# if defined __ppc64__ || defined __x86_64__ + struct vm_region_basic_info_64 info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + + more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, &info_count, &object_name) + == KERN_SUCCESS); +# else + struct vm_region_basic_info info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; + + more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO, + (vm_region_info_t)&info, &info_count, &object_name) + == KERN_SUCCESS); +# endif + if (object_name != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), object_name); + if (!more) + break; + flags = 0; + if (info.protection & VM_PROT_READ) + flags |= VMA_PROT_READ; + if (info.protection & VM_PROT_WRITE) + flags |= VMA_PROT_WRITE; + if (info.protection & VM_PROT_EXECUTE) + flags |= VMA_PROT_EXECUTE; + if (callback (data, address, address + size, flags)) + break; + } + +#elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ + /* Windows platform. Use the native Windows API. */ + + MEMORY_BASIC_INFORMATION info; + unsigned long address = 0; + + while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info)) + { + if (info.State != MEM_FREE) + /* Ignore areas where info.State has the value MEM_RESERVE or, + equivalently, info.Protect has the undocumented value 0. + This is needed, so that on Cygwin, areas used by malloc() are + distinguished from areas reserved for future malloc(). */ + if (info.State != MEM_RESERVE) + { + unsigned long start, end; + unsigned int flags; + + start = (unsigned long)info.BaseAddress; + end = start + info.RegionSize; + switch (info.Protect & ~(PAGE_GUARD|PAGE_NOCACHE)) + { + case PAGE_READONLY: + flags = VMA_PROT_READ; + break; + case PAGE_READWRITE: + case PAGE_WRITECOPY: + flags = VMA_PROT_READ | VMA_PROT_WRITE; + break; + case PAGE_EXECUTE: + flags = VMA_PROT_EXECUTE; + break; + case PAGE_EXECUTE_READ: + flags = VMA_PROT_READ | VMA_PROT_EXECUTE; + break; + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + flags = VMA_PROT_READ | VMA_PROT_WRITE | VMA_PROT_EXECUTE; + break; + case PAGE_NOACCESS: + default: + flags = 0; + break; + } + + if (callback (data, start, end, flags)) + break; + } + address = (unsigned long)info.BaseAddress + info.RegionSize; + } + +#elif defined __BEOS__ + /* Use the BeOS specific API. */ + + area_info info; + int32 cookie; + + cookie = 0; + while (get_next_area_info (0, &cookie, &info) == B_OK) + { + unsigned long start, end; + unsigned int flags; + + start = (unsigned long) info.address; + end = start + info.size; + flags = 0; + if (info.protection & B_READ_AREA) + flags |= VMA_PROT_READ | VMA_PROT_EXECUTE; + if (info.protection & B_WRITE_AREA) + flags |= VMA_PROT_WRITE; + + if (callback (data, start, end, flags)) + break; + } + +#endif +} + + +#ifdef TEST + +#include + +/* Output the VMAs of the current process in a format similar to the Linux + /proc/$pid/maps file. */ + +static int +vma_iterate_callback (void *data, uintptr_t start, uintptr_t end, + unsigned int flags) +{ + printf ("%08lx-%08lx %c%c%c\n", + (unsigned long) start, (unsigned long) end, + flags & VMA_PROT_READ ? 'r' : '-', + flags & VMA_PROT_WRITE ? 'w' : '-', + flags & VMA_PROT_EXECUTE ? 'x' : '-'); + return 0; +} + +int +main () +{ + vma_iterate (vma_iterate_callback, NULL); + + /* Let the user interactively look at the /proc file system. */ + sleep (10); + + return 0; +} + +#endif /* TEST */ diff --git a/lib/vma-iter.h b/lib/vma-iter.h new file mode 100644 --- /dev/null +++ b/lib/vma-iter.h @@ -0,0 +1,63 @@ +/* Iteration over virtual memory areas. + Copyright (C) 2011 Free Software Foundation, Inc. + Written by Bruno Haible , 2011. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _VMA_ITER_H +#define _VMA_ITER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Bit mask for the FLAGS parameter of a vma_iterate callback function. */ +#define VMA_PROT_READ (1<<0) +#define VMA_PROT_WRITE (1<<1) +#define VMA_PROT_EXECUTE (1<<2) + +typedef int (*vma_iterate_callback_fn) (void *data, + uintptr_t start, uintptr_t end, + unsigned int flags); + +/* Iterate over the virtual memory areas of the current process. + If such iteration is supported, the callback is called once for every + virtual memory area, in ascending order, with the following arguments: + - DATA is the same argument as passed to vma_iterate. + - START is the address of the first byte in the area, page-aligned. + - END is the address of the last byte in the area plus 1, page-aligned. + Note that it may be 0 for the last area in the address space. + - FLAGS is a combination of the VMA_* bits. + If the callback returns 0, the iteration continues. If it returns 1, + the iteration terminates prematurely. + This function may open file descriptors, but does not call malloc(). */ +extern void vma_iterate (vma_iterate_callback_fn callback, void *data); + +/* The macro VMA_ITERATE_SUPPORTED indicates that vma_iterate is supported on + this platform. + Note that even when this macro is defined, vma_iterate() may still fail to + find any virtual memory area, for example if /proc is not mounted. */ +#if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ || defined __sgi || defined __osf__ || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ +# define VMA_ITERATE_SUPPORTED 1 +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _VMA_ITER_H */ diff --git a/modules/get-rusage-as b/modules/get-rusage-as --- a/modules/get-rusage-as +++ b/modules/get-rusage-as @@ -11,6 +11,7 @@ unistd extensions getpagesize +vma-iter configure.ac: AC_REQUIRE([AC_C_INLINE]) diff --git a/modules/vma-iter b/modules/vma-iter new file mode 100644 --- /dev/null +++ b/modules/vma-iter @@ -0,0 +1,28 @@ +Description: +Iteration over virtual memory areas. + +Files: +lib/vma-iter.h +lib/vma-iter.c +m4/mmap-anon.m4 + +Depends-on: +stdint +unistd +extensions +getpagesize + +configure.ac: +gl_FUNC_MMAP_ANON + +Makefile.am: +lib_SOURCES += vma-iter.c + +Include: +"vma-iter.h" + +License: +GPL + +Maintainer: +Bruno Haible