>From fef4a135e413afbe21d8522e4d6a80a55cb8eec0 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 19 Mar 2017 15:45:26 +0100 Subject: [PATCH 2/2] vma-iter: Add support for Solaris. * lib/vma-iter.c (vma_iterate): On Solaris, use the /proc filesystem approach. * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on Solaris. * lib/get-rusage-as.c: Update comment about Solaris. * lib/get-rusage-data.c: Likewise. --- ChangeLog | 9 ++ lib/get-rusage-as.c | 4 +- lib/get-rusage-data.c | 5 +- lib/vma-iter.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++- lib/vma-iter.h | 2 +- 5 files changed, 269 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 311d45c..ee9e0d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2017-03-19 Bruno Haible + vma-iter: Add support for Solaris. + * lib/vma-iter.c (vma_iterate): On Solaris, use the /proc filesystem + approach. + * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on Solaris. + * lib/get-rusage-as.c: Update comment about Solaris. + * lib/get-rusage-data.c: Likewise. + +2017-03-19 Bruno Haible + vma-iter: Prefer HP-UX specific API on HP-UX. * lib/vma-iter.c (vma_iterate): Move HP-UX specific implementation up. * lib/vma-iter.h: Update. diff --git a/lib/get-rusage-as.c b/lib/get-rusage-as.c index fb9d218..3d9915d 100644 --- a/lib/get-rusage-as.c +++ b/lib/get-rusage-as.c @@ -95,7 +95,9 @@ Solaris: a) setrlimit with RLIMIT_AS works. - b) No VMA iteration API exists. + b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP, and the + /proc/self/maps file contains a list of the virtual memory areas. + Both methods agree, Cygwin: a) setrlimit with RLIMIT_AS always fails when the limit is < 0x80000000. diff --git a/lib/get-rusage-data.c b/lib/get-rusage-data.c index 4745ad6..62293ad 100644 --- a/lib/get-rusage-data.c +++ b/lib/get-rusage-data.c @@ -101,7 +101,10 @@ Solaris: a) setrlimit with RLIMIT_DATA works. - b) No VMA iteration API exists. + b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP, and the + /proc/self/maps file contains a list of the virtual memory areas. + get_rusage_data_via_setrlimit() ignores the data segment of the executable, + whereas get_rusage_data_via_iterator() includes it. Cygwin: a) setrlimit with RLIMIT_DATA always fails. diff --git a/lib/vma-iter.c b/lib/vma-iter.c index 2a12449..7356188 100644 --- a/lib/vma-iter.c +++ b/lib/vma-iter.c @@ -23,7 +23,7 @@ #include /* errno */ #include /* size_t */ #include /* open, O_RDONLY */ -#include /* getpagesize, read, close */ +#include /* getpagesize, read, close, getpid */ #if defined __sgi || defined __osf__ /* IRIX, OSF/1 */ # include /* memcpy */ @@ -32,6 +32,15 @@ # include /* PIOC*, prmap_t */ #endif +#if defined __sun /* Solaris */ +# include /* memcpy */ +# include +# include /* mmap, munmap */ +/* Try to use the newer ("structured") /proc filesystem API, if supported. */ +# define _STRUCTURED_PROC 1 +# include /* prmap_t, optionally PIOC* */ +#endif + #if HAVE_PSTAT_GETPROCVM /* HP-UX */ # include /* pstat_getprocvm */ #endif @@ -369,6 +378,248 @@ vma_iterate (vma_iterate_callback_fn callback, void *data) close (fd); return -1; +#elif defined __sun /* Solaris */ + + /* Note: Solaris defines a different type prmap_t with + _STRUCTURED_PROC than without! Here's a table of sizeof(prmap_t): + 32-bit 64-bit + _STRUCTURED_PROC = 0 32 56 + _STRUCTURED_PROC = 1 96 104 + Therefore, if the include files provide the newer API, prmap_t has + the bigger size, and thus you MUST use the newer API. And if the + include files provide the older API, prmap_t has the smaller size, + and thus you MUST use the older API. */ + +# if defined PIOCNMAP && defined PIOCMAP + /* We must use the older /proc interface. */ + + size_t pagesize; + char fnamebuf[6+10+1]; + char *fname; + int fd; + int nmaps; + size_t memneed; + 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 -1; + + 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; + auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + 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 (start < auxmap_start) + if (callback (data, start, auxmap_start, flags)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (data, auxmap_end, end, flags)) + break; + } + else + { + if (callback (data, start, end, flags)) + break; + } + } + munmap (auxmap, memneed); + close (fd); + return 0; + + fail1: + munmap (auxmap, memneed); + fail2: + close (fd); + return -1; + +# else + /* We must use the newer /proc interface. + Documentation: + https://docs.oracle.com/cd/E23824_01/html/821-1473/proc-4.html + The contents of /proc//map consists of records of type + prmap_t. These are different in 32-bit and 64-bit processes, + but here we are fortunately accessing only the current process. */ + + size_t pagesize; + char fnamebuf[6+10+4+1]; + char *fname; + int fd; + int nmaps; + size_t memneed; + void *auxmap; + unsigned long auxmap_start; + unsigned long auxmap_end; + prmap_t* maps; + prmap_t* maps_end; + prmap_t* mp; + + pagesize = getpagesize (); + + /* Construct fname = sprintf (fnamebuf+i, "/proc/%u/map", getpid ()). */ + fname = fnamebuf + sizeof (fnamebuf) - 1 - 4; + memcpy (fname, "/map", 4 + 1); + { + 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 -1; + + { + struct stat statbuf; + if (fstat (fd, &statbuf) < 0) + goto fail2; + nmaps = statbuf.st_size / sizeof (prmap_t); + } + + 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; + auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (auxmap == (void *) -1) + goto fail2; + auxmap_start = (unsigned long) auxmap; + auxmap_end = auxmap_start + memneed; + maps = (prmap_t *) auxmap; + + /* Read up to memneed bytes from fd into maps. */ + { + size_t remaining = memneed; + size_t total_read = 0; + char *ptr = (char *) maps; + + do + { + size_t nread = read (fd, ptr, remaining); + if (nread == (size_t)-1) + { + if (errno == EINTR) + continue; + goto fail1; + } + if (nread == 0) + /* EOF */ + break; + total_read += nread; + ptr += nread; + remaining -= nread; + } + while (remaining > 0); + + nmaps = (memneed - remaining) / sizeof (prmap_t); + maps_end = maps + nmaps; + } + + for (mp = maps; mp < maps_end; mp++) + { + unsigned long start, end; + unsigned int flags; + + start = (unsigned long) mp->pr_vaddr; + end = start + mp->pr_size; + 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; + 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 (start < auxmap_start) + if (callback (data, start, auxmap_start, flags)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (data, auxmap_end, end, flags)) + break; + } + else + { + if (callback (data, start, end, flags)) + break; + } + } + munmap (auxmap, memneed); + close (fd); + return 0; + + fail1: + munmap (auxmap, memneed); + fail2: + close (fd); + return -1; + +# endif + #elif HAVE_PSTAT_GETPROCVM /* HP-UX */ unsigned long pagesize = getpagesize (); diff --git a/lib/vma-iter.h b/lib/vma-iter.h index b1acaa2..bb1f3c1 100644 --- a/lib/vma-iter.h +++ b/lib/vma-iter.h @@ -52,7 +52,7 @@ extern int vma_iterate (vma_iterate_callback_fn callback, void *data); 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__ || HAVE_PSTAT_GETPROCVM || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY +#if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ || defined __sgi || defined __osf__ || defined __sun || HAVE_PSTAT_GETPROCVM || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY # define VMA_ITERATE_SUPPORTED 1 #endif -- 2.7.4