aboutsummaryrefslogtreecommitdiffstats
path: root/libsigsegv/src/stackvma-procfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsigsegv/src/stackvma-procfs.c')
-rw-r--r--libsigsegv/src/stackvma-procfs.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/libsigsegv/src/stackvma-procfs.c b/libsigsegv/src/stackvma-procfs.c
new file mode 100644
index 00000000..24c90a1a
--- /dev/null
+++ b/libsigsegv/src/stackvma-procfs.c
@@ -0,0 +1,188 @@
+/* Determine the virtual memory area of a given address.
+ Copyright (C) 2002, 2006, 2008 Bruno Haible <bruno@clisp.org>
+
+ 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 2, 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, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include "stackvma.h"
+#include <unistd.h> /* open, close */
+#include <fcntl.h> /* open */
+#include <sys/types.h>
+#include <sys/mman.h> /* mmap, munmap */
+#include <sys/procfs.h> /* PIOC*, prmap_t */
+
+#include "stackvma-simple.c"
+
+#if HAVE_MINCORE
+# define sigsegv_get_vma mincore_get_vma
+# define STATIC static
+# include "stackvma-mincore.c"
+# undef sigsegv_get_vma
+#else
+/* Cache for getpagesize(). */
+static unsigned long pagesize;
+/* Initialize pagesize. */
+static void
+init_pagesize (void)
+{
+ pagesize = getpagesize ();
+}
+#endif
+
+int
+sigsegv_get_vma (unsigned long address, struct vma_struct *vma)
+{
+ char fnamebuf[6+10+1];
+ char *fname;
+ int fd;
+ int nmaps;
+ size_t memneed;
+#if HAVE_MMAP_ANON
+# define zero_fd -1
+# define map_flags MAP_ANON
+#elif HAVE_MMAP_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 start, end;
+#if STACK_DIRECTION < 0
+ unsigned long prev;
+#endif
+
+ if (pagesize == 0)
+ init_pagesize ();
+
+ /* 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)
+ goto failed;
+
+ 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 we are low on stack space.
+ We also cannot use malloc here, because a malloc() call may have been
+ interrupted.
+ So use mmap(), and ignore the resulting VMA. */
+ memneed = ((memneed - 1) / pagesize + 1) * pagesize;
+#if !(HAVE_MMAP_ANON || HAVE_MMAP_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_MMAP_ANON || HAVE_MMAP_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;
+
+#if STACK_DIRECTION < 0
+ prev = 0;
+#endif
+ for (mp = maps;;)
+ {
+ 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]. */
+ if (start != auxmap_start)
+ {
+ if (address >= start && address <= auxmap_start - 1)
+ {
+ end = auxmap_start;
+ goto found;
+ }
+#if STACK_DIRECTION < 0
+ prev = auxmap_start;
+#endif
+ }
+ if (end != auxmap_end)
+ {
+ if (address >= auxmap_end && address <= end - 1)
+ {
+ start = auxmap_end;
+ goto found;
+ }
+#if STACK_DIRECTION < 0
+ prev = end;
+#endif
+ }
+ }
+ else
+ {
+ if (address >= start && address <= end - 1)
+ goto found;
+#if STACK_DIRECTION < 0
+ prev = end;
+#endif
+ }
+ }
+
+ fail1:
+ munmap (auxmap, memneed);
+ fail2:
+ close (fd);
+ failed:
+#if HAVE_MINCORE
+ return mincore_get_vma (address, vma);
+#else
+ return -1;
+#endif
+
+ found:
+ vma->start = start;
+ vma->end = end;
+#if STACK_DIRECTION < 0
+ vma->prev_end = prev;
+#else
+ vma->next_start = (unsigned long) mp->pr_vaddr;
+#endif
+ munmap (auxmap, memneed);
+ close (fd);
+ vma->is_near_this = simple_is_near_this;
+ return 0;
+}