bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
From: Bruno Haible <bruno@clisp.org>
To: bug-gnulib@gnu.org
Cc: bug-libsigsegv@gnu.org
Subject: new module 'sigsegv'
Date: Sun, 16 May 2021 19:01:45 +0200	[thread overview]
Message-ID: <15897619.AQZsorJ5YP@omega> (raw)

[-- Attachment #1: Type: text/plain, Size: 4261 bytes --]

For a long time, I've been dissatisfied with the 'c-stack' module:

  * It supports only Solaris, and even the Solaris support costs maintenance
    effort (seen this week: Paul needed to borrow a trick from GNU libsigsegv,
    in order to support Solaris 11/SPARC).

  * For the few platforms that it supports, it needs a significant amount of
    complexity in m4/c-stack.m4 and lib/c-stack.c.

  * Without additional library, it supports proprietary platforms better than
    glibc systems. This is something that we should not do, RMS emphasized
    on gnu-prog-discuss on 2021-01-03 (message "Features that are not supported
    on GNU wrap-up").

So, GNU libsigsegv should be the natural replacement. But I'm dissatisfied
with GNU libsigsegv as well:

  * It is an extra library. Although it produce less than 10 KB of compiled
    code, the maintainer of a package needs to pay the price of an
    external library (in terms of build system cost).

  * It is an extra library, although it is merely a "normal" application of the
    sigaltstack() system call.

  * It has way too many configure tests. This introduces bugs: E.g. When I
    did the port to OpenIndiana, OpenIndiana was not treated like Solaris 11
    due to a mistake in the autoconf test, and the result was a suboptimal
    port. A compilation error would have been better. In other words,
    sometimes writing explicit #ifdefs for platforms is better, because it
    clarifies the expectations.

  * Part of its API (the dispatcher) is not multithread-safe [so far].

RMS's guidance is to make things easy for applications on glibc systems.
GNU libsigsegv exists as a separate library mostly because of Windows platforms
and older platforms.

To fix this problem, I'm adding a module 'sigsegv'.

It's a simplified variant of GNU libsigsegv, as a Gnulib module.

It implements the most important features of GNU libsigsegv: catching SIGSEGV
and catching stack overflow. It does *not* implement the 'sigsegv_dispatcher'
type (which is not multithread-safe, see above).

It supports all modern Unix-like platforms: Linux, Hurd, FreeBSD, NetBSD,
OpenBSD, macOS, AIX, Solaris, Cygwin, Haiku, even IRIX. It does *not* support
HP-UX, Minix, native Windows; on these platforms the module compiles and
provides a <sigsegv.h> header file, but it does not define HAVE_SIGSEGV_RECOVERY
and HAVE_STACK_OVERFLOW_RECOVERY.

Unlike GNU libsigsegv, which consists of many .h and .c files, this module
compiles to just two object files, rather than a library. I bet that two
.c files, with total size of 100 KB, is more acceptable to the Gnulib users
than a wealth of 70 include files.

It's kind of a "mini-libsigsegv". (I considered calling the module
'mini-libsigsegv', but the .h file is called <sigsegv.h>, and I think the
module name should better be consistent with that.)

The module is analogous to the many "mini-libunistring" modules (uni*/*) and
the "mini-gmp" that was added a year ago.

The copyright is currently still mine with a few contributors (notably
Eric Blake and Paolo Bonzini). We have other files not copyrighted by the
FSF in Gnulib: atanl.c, filevercmp.c, logl.c. If you think the stuff should
better be (C) FSF, I have no problem assigning my copyrights on that, as
I do with all other Gnulib contributions.

The module accepts an option '--with-libsigsegv', so as to force the use
of an installed GNU libsigsegv. This is needed e.g. on old Mac OS X 10.5
in 64-bit mode (because the new module uses sigaltstack(), whereas GNU
libsigsegv still uses the Mach API.)

Bruno


2021-05-16  Bruno Haible  <bruno@clisp.org>

	sigsegv: Add tests.
	* tests/test-sigsegv-catch-segv1.c: New file, from GNU libsigsegv with
	modifications.
	* tests/test-sigsegv-catch-segv2.c: Likewise.
	* tests/test-sigsegv-catch-stackoverflow1.c: Likewise.
	* tests/test-sigsegv-catch-stackoverflow2.c: Likewise.
	* tests/altstack-util.h: Likewise.
	* tests/mmap-anon-util.h: Likewise.
	* modules/sigsegv-tests: New file.

	sigsegv: New module.
	* lib/sigsegv.in.h: New file, from GNU libsigsegv with modifications.
	* lib/sigsegv.c: Likewise.
	* lib/stackvma.h: Likewise.
	* lib/stackvma.c: Likewise.
	* m4/sigaltstack.m4: Likewise.
	* m4/stack-direction.m4: Likewise.
	* modules/sigsegv: New file.


[-- Attachment #2: 0001-sigsegv-New-module.patch --]
[-- Type: text/x-patch, Size: 140858 bytes --]

From e54aa6196947ed22ff66bcd714e4fc7bd0c5c3b4 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Sun, 16 May 2021 15:20:12 +0200
Subject: [PATCH 1/2] sigsegv: New module.

* lib/sigsegv.in.h: New file, from GNU libsigsegv with modifications.
* lib/sigsegv.c: Likewise.
* lib/stackvma.h: Likewise.
* lib/stackvma.c: Likewise.
* m4/sigaltstack.m4: Likewise.
* m4/stack-direction.m4: Likewise.
* modules/sigsegv: New file.
---
 ChangeLog             |   11 +
 lib/sigsegv.c         | 1372 ++++++++++++++++++++++++++++++++
 lib/sigsegv.in.h      |  233 ++++++
 lib/stackvma.c        | 2064 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/stackvma.h        |   60 ++
 m4/sigaltstack.m4     |  198 +++++
 m4/stack-direction.m4 |  104 +++
 modules/sigsegv       |  101 +++
 8 files changed, 4143 insertions(+)
 create mode 100644 lib/sigsegv.c
 create mode 100644 lib/sigsegv.in.h
 create mode 100644 lib/stackvma.c
 create mode 100644 lib/stackvma.h
 create mode 100644 m4/sigaltstack.m4
 create mode 100644 m4/stack-direction.m4
 create mode 100644 modules/sigsegv

diff --git a/ChangeLog b/ChangeLog
index 30663cb..c939655 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2021-05-16  Bruno Haible  <bruno@clisp.org>
+
+	sigsegv: New module.
+	* lib/sigsegv.in.h: New file, from GNU libsigsegv with modifications.
+	* lib/sigsegv.c: Likewise.
+	* lib/stackvma.h: Likewise.
+	* lib/stackvma.c: Likewise.
+	* m4/sigaltstack.m4: Likewise.
+	* m4/stack-direction.m4: Likewise.
+	* modules/sigsegv: New file.
+
 2021-05-15  Pádraig Brady  <P@draigBrady.com>
 
 	realloc-gnu: avoid glibc MALLOC_CHECK_ issue
diff --git a/lib/sigsegv.c b/lib/sigsegv.c
new file mode 100644
index 0000000..312f132
--- /dev/null
+++ b/lib/sigsegv.c
@@ -0,0 +1,1372 @@
+/* Page fault handling library.
+   Copyright (C) 1993-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2018  Nylon Chen <nylon7@andestech.com>
+
+   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 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigsegv.h"
+
+#include <errno.h>
+#include <stdio.h> /* declares perror */
+#include <stdint.h> /* defines uintptr_t */
+#include <stdlib.h>
+#include <signal.h>
+#if HAVE_GETRLIMIT
+# include <sys/resource.h> /* declares struct rlimit */
+#endif
+
+#ifdef __OpenBSD__
+# include <sys/param.h> /* defines macro OpenBSD */
+#endif
+
+
+/* Version number.  */
+int libsigsegv_version = LIBSIGSEGV_VERSION;
+
+
+/* ======================= Fault handler information ======================= */
+
+/* Define:
+
+     SIGSEGV_FAULT_HANDLER_ARGLIST
+          is the argument list for the actual fault handler.
+
+   and if available (optional):
+
+     SIGSEGV_FAULT_ADDRESS
+          is a macro for fetching the fault address.
+
+     SIGSEGV_FAULT_CONTEXT
+          is a macro giving a pointer to the entire fault context (i.e.
+          the register set etc.).
+
+     SIGSEGV_FAULT_STACKPOINTER
+          is a macro for fetching the stackpointer at the moment the fault
+          occurred.
+ */
+
+#if defined __linux__ || defined __ANDROID__ /* Linux */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+# if defined __alpha__
+
+/* See glibc/sysdeps/unix/sysv/linux/alpha/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/alpha/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/alpha/sys/ucontext.h
+   and the 'struct sigcontext' defined in <asm/sigcontext.h>
+   are actually the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.sc_regs[30]
+
+# elif defined __arm64__ /* 64-bit */
+
+/* See glibc/sysdeps/unix/sysv/linux/aarch64/sys/ucontext.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/aarch64/sys/ucontext.h
+   and the 'struct sigcontext' defined in <asm/sigcontext.h>
+   are actually the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.sp
+
+# elif defined __arm__ || defined __armhf__ /* 32-bit */
+
+/* See glibc/sysdeps/unix/sysv/linux/arm/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/arm/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/arm/sys/ucontext.h
+   and the 'struct sigcontext' defined in <asm/sigcontext.h>
+   are actually the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.arm_sp
+
+# elif defined __cris__
+
+/* See glibc-ports/sysdeps/unix/sysv/linux/cris/sys/ucontext.h.
+   Note that the 'mcontext_t' defined in
+   glibc-ports/sysdeps/unix/sysv/linux/cris/sys/ucontext.h
+   and the 'struct sigcontext' defined in <asm/sigcontext.h>
+   are actually the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.usp
+
+# elif defined __hppa__
+
+/* See glibc/sysdeps/unix/sysv/linux/hppa/sys/ucontext.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/hppa/sys/ucontext.h
+   and the 'struct sigcontext' defined in <asm/sigcontext.h>
+   are actually the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.sc_gr[30]
+
+# elif defined __x86_64__ /* 64 bit registers */
+
+/* See glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/x86_64/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
+   and the 'struct sigcontext' defined in
+   glibc/sysdeps/unix/sysv/linux/x86/bits/sigcontext.h
+   (see also <asm/sigcontext.h>)
+   are effectively the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_RSP]
+
+# elif defined __i386__ /* 32 bit registers */
+
+/* See glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/i386/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
+   and the 'struct sigcontext_ia32' defined in <asm/sigcontext32.h>
+   are effectively the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_ESP]
+                     /* same value as ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_UESP] */
+
+# elif defined __ia64__
+
+/* See glibc/sysdeps/unix/sysv/linux/ia64/sys/ucontext.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/ia64/sys/ucontext.h
+   and the 'struct sigcontext' defined in
+   glibc/sysdeps/unix/sysv/linux/ia64/bits/sigcontext.h
+   (see also <asm/sigcontext.h>)
+   are actually the same.  */
+
+/* IA-64 has two stack pointers, one that grows down, called $r12, and one
+   that grows up, called $bsp/$bspstore.  */
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.sc_gr[12]
+
+/* It would be better to access $bspstore instead of $bsp but I don't know
+   where to find it in 'struct sigcontext'.  Anyway, it doesn't matter
+   because $bsp and $bspstore never differ by more than ca. 1 KB.  */
+#  define SIGSEGV_FAULT_BSP_POINTER  ((ucontext_t *) ucp)->uc_mcontext.sc_ar_bsp
+
+# elif defined __m68k__
+
+/* See glibc/sysdeps/unix/sysv/linux/m68k/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/m68k/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/m68k/sys/ucontext.h
+   and the 'struct sigcontext' defined in <asm/sigcontext.h>
+   are quite different types.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[R_SP]
+
+# elif defined __mips__ || defined __mipsn32__ || defined __mips64__
+
+/* See glibc/sysdeps/unix/sysv/linux/mips/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/mips/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/mips/sys/ucontext.h
+   and the 'struct sigcontext' defined in
+   glibc/sysdeps/unix/sysv/linux/mips/bits/sigcontext.h
+   (see also <asm/sigcontext.h>)
+   are effectively the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[29]
+
+# elif defined __nds32__
+
+/* See glibc/sysdeps/unix/sysv/linux/nds32/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/nds32/sigcontextinfo.h.
+   Both are found in <https://patches-gcc.linaro.org/cover/4409/> part 08/11
+   <https://sourceware.org/ml/libc-alpha/2018-05/msg00125.html>.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.nds32_sp
+
+# elif defined __powerpc__ || defined __powerpc64__ || defined __powerpc64_elfv2__
+
+/* See glibc/sysdeps/unix/sysv/linux/powerpc/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/powerpc/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/powerpc/sys/ucontext.h,
+   the 'struct sigcontext' defined in <asm/sigcontext.h>,
+   and the 'struct pt_regs' defined in <asm/ptrace.h>
+   are quite different types.  */
+
+#  if defined __powerpc64__ || defined __powerpc64_elfv2__ /* 64-bit */
+#   define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gp_regs[1]
+#  else /* 32-bit */
+/* both should be equivalent */
+#   if 0
+#    define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.regs->gpr[1]
+#   else
+#    define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.uc_regs->gregs[1]
+#   endif
+#  endif
+
+# elif defined __riscv32__ || __riscv64__
+
+/* See glibc/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/riscv/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h
+   and the 'struct sigcontext' defined in
+   glibc/sysdeps/unix/sysv/linux/riscv/bits/sigcontext.h
+   start with the same block of 32 general-purpose registers.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.__gregs[REG_SP]
+
+# elif defined __s390__ || defined __s390x__
+
+/* See glibc/sysdeps/unix/sysv/linux/s390/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/s390/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/s390/sys/ucontext.h
+   and the '_sigregs' type, indirect part of 'struct sigcontext', defined
+   in <asm/sigcontext.h>, are effectively the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[15]
+
+# elif defined __sh__
+
+/* See glibc/sysdeps/unix/sysv/linux/sh/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/sh/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/sh/sys/ucontext.h
+   and the 'struct sigcontext' defined in <asm/sigcontext.h>
+   are effectively the same.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[15]
+
+# elif defined __sparc__ || defined __sparc64__
+
+/* See glibc/sysdeps/unix/sysv/linux/sparc/sys/ucontext.h
+   and the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/sparc/{sparc32,sparc64}/sigcontextinfo.h.
+   Note that the 'mcontext_t' defined in
+   glibc/sysdeps/unix/sysv/linux/sparc/sys/ucontext.h
+   and the 'struct sigcontext' defined in
+   glibc/sysdeps/unix/sysv/linux/sparc/bits/sigcontext.h
+   (see also <asm/sigcontext.h>)
+   are quite different types.  */
+
+#  if defined __sparc64__/* 64-bit */
+/* From linux-4.8.1/arch/sparc/kernel/signal_64.c, function setup_rt_frame, we
+   see that ucp is not an 'ucontext_t *' but rather a 'struct sigcontext *'
+   that happens to have the same value as sip (which is possible because a
+   'struct sigcontext' starts with 128 bytes room for the siginfo_t).  */
+#   define SIGSEGV_FAULT_STACKPOINTER  (((struct sigcontext *) ucp)->sigc_regs.u_regs[14] + 2047)
+#  else /* 32-bit */
+/* From linux-4.8.1/arch/sparc/kernel/signal_32.c, function setup_rt_frame,
+   and linux-4.8.1/arch/sparc/kernel/signal32.c, function setup_rt_frame32, we
+   see that ucp is a 'struct pt_regs *' or 'struct pt_regs32 *', respectively.
+   In userland, this is a 'struct sigcontext *'.  */
+#   define SIGSEGV_FAULT_STACKPOINTER  ((struct sigcontext *) ucp)->si_regs.u_regs[14]
+#  endif
+
+/* The sip->si_addr field is correct for a normal fault, but unusable in case
+   of a stack overflow. What I observe (when running
+   tests/test-sigsegv-catch-stackoverflow1, with a printf right at the beginning
+   of sigsegv_handler) is that sip->si_addr is near 0:
+     - in 64-bit mode: sip->si_addr = 0x000000000000030F, and gdb shows me that
+       the fault occurs in an instruction 'stx %o3,[%fp+0x30f]' and %fp is 0.
+       In fact, all registers %l0..%l7 and %i0..%i7 are 0.
+     - in 32-bit mode: sip->si_addr = 0xFFFFFA64, and gdb shows me that
+       the fault occurs in an instruction 'st %g2,[%fp-1436]' and %fp is 0.
+       In fact, all registers %l0..%l7 and %i0..%i7 are 0.
+   Apparently when the stack overflow occurred, some trap has tried to move the
+   contents of the registers %l0..%l7 and %i0..%i7 (a "window" in SPARC
+   terminology) to the stack, did not succeed in doing this, replaced all these
+   register values with 0, and resumed execution at the fault location. This
+   time, due to %fp = 0, a different fault was triggered. Now it is impossible
+   to determine the real (previous) fault address because, even if know the
+   faulting instruction, the previous register values have been lost.  */
+#  define BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW
+
+# else
+
+/* When adding support for other CPUs here:  */
+
+/* For SIGSEGV_FAULT_HANDLER_ARGLIST, see the definition of SIGCONTEXT in
+   glibc/sysdeps/unix/sysv/linux/<cpu>/sigcontextinfo.h.  */
+
+/* For SIGSEGV_FAULT_STACKPOINTER, see the definition of GET_STACK in
+   glibc/sysdeps/unix/sysv/linux/<cpu>/sigcontextinfo.h.  */
+
+# endif
+
+#endif
+
+#if defined __GNU__ /* Hurd */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, int code, struct sigcontext *scp
+# define SIGSEGV_FAULT_ADDRESS  (unsigned long) code
+# define SIGSEGV_FAULT_CONTEXT  scp
+
+# if defined __i386__
+
+/* scp points to a 'struct sigcontext' (defined in
+   glibc/sysdeps/mach/hurd/i386/bits/sigcontext.h).
+   The registers of this struct get pushed on the stack through
+   gnumach/i386/i386/locore.S:trapall.  */
+/* Both sc_esp and sc_uesp appear to have the same value.
+   It appears more reliable to use sc_uesp because it is labelled as
+   "old esp, if trapped from user".  */
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_uesp
+
+# endif
+
+#endif
+
+#if defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ /* GNU/kFreeBSD, FreeBSD */
+
+# if defined __arm__ || defined __armhf__ || defined __arm64__
+
+#  define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+#  define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+#  define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+
+#  if defined __arm64__ /* 64-bit */
+
+/* See sys/arm64/include/ucontext.h.  */
+
+#   define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.mc_gpregs.gp_sp
+
+#  elif defined __arm__ || defined __armhf__ /* 32-bit */
+
+/* See sys/arm/include/ucontext.h.  */
+
+#   define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.__gregs[_REG_SP]
+
+#  endif
+
+# else
+
+/* On FreeBSD 12, both of these approaches work.  On FreeBSD derivatives, the
+   first one has more chances to work.  */
+#  if 1
+/* Use signal handlers without SA_SIGINFO.  */
+
+#   define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, int code, struct sigcontext *scp, void *addr
+#   define SIGSEGV_FAULT_ADDRESS  addr
+#   define SIGSEGV_FAULT_CONTEXT  scp
+
+/* See sys/x86/include/signal.h.  */
+
+#   if defined __x86_64__
+/* 64 bit registers */
+
+#    define SIGSEGV_FAULT_STACKPOINTER  scp->sc_rsp
+
+#   elif defined __i386__
+/* 32 bit registers */
+
+#    define SIGSEGV_FAULT_STACKPOINTER  scp->sc_esp
+
+#   endif
+
+#  else
+/* Use signal handlers with SA_SIGINFO.  */
+
+#   define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *scp
+#   define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+#   define SIGSEGV_FAULT_CONTEXT  ((struct sigcontext *) scp)
+#   define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+/* See sys/x86/include/signal.h.  */
+
+#   if defined __x86_64__
+/* 64 bit registers */
+
+#    define SIGSEGV_FAULT_STACKPOINTER  ((struct sigcontext *) scp)->sc_rsp
+
+#   elif defined __i386__
+/* 32 bit registers */
+
+#    define SIGSEGV_FAULT_STACKPOINTER  ((struct sigcontext *) scp)->sc_esp
+
+#   endif
+
+#  endif
+
+# endif
+
+#endif
+
+#if defined __NetBSD__ /* NetBSD */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+/* _UC_MACHINE_SP is a platform independent macro.
+   Defined in <machine/mcontext.h>, see
+     http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/arch/$arch/include/mcontext.h
+   Supported on alpha, amd64, i386, ia64, m68k, mips, powerpc, sparc since
+   NetBSD 2.0.
+   On i386, _UC_MACHINE_SP is the same as ->uc_mcontext.__gregs[_REG_UESP],
+   and apparently the same value as       ->uc_mcontext.__gregs[_REG_ESP]. */
+# ifdef _UC_MACHINE_SP
+#  define SIGSEGV_FAULT_STACKPOINTER  _UC_MACHINE_SP ((ucontext_t *) ucp)
+# endif
+
+#endif
+
+#if defined __OpenBSD__ /* OpenBSD */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, struct sigcontext *scp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  scp
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+# if defined __alpha__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/alpha/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_regs[30]
+
+# elif defined __arm__ || defined __armhf__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/arm/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_usr_sp
+
+# elif defined __hppa__ || defined __hppa64__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/hppa/include/signal.h
+   and
+   openbsd-src/sys/arch/hppa64/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_regs[30]
+
+# elif defined __x86_64__
+/* 64 bit registers */
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/amd64/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_rsp
+
+# elif defined __i386__
+/* 32 bit registers */
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/i386/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_esp
+
+# elif defined __m68k__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/m68k/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_sp
+
+# elif defined __m88k__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/m88k/include/signal.h
+   and the definition of 'struct reg' in
+   openbsd-src/sys/arch/m88k/include/reg.h.  */
+
+#  if OpenBSD >= 201211 /* OpenBSD version >= 5.2 */
+#   define SIGSEGV_FAULT_STACKPOINTER  scp->sc_regs[31]
+#  else
+#   define SIGSEGV_FAULT_STACKPOINTER  scp->sc_regs.r[31]
+#  endif
+
+# elif defined __mips__ || defined __mipsn32__ || defined __mips64__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/mips64/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_regs[29]
+
+# elif defined __powerpc__ || defined __powerpc64__
+
+/* See the definition of 'struct sigcontext' and 'struct trapframe' in
+   openbsd-src/sys/arch/powerpc/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_frame.fixreg[1]
+
+# elif defined __sh__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/sh/include/signal.h
+   and the definition of 'struct reg' in
+   openbsd-src/sys/arch/sh/include/reg.h.  */
+
+#  if OpenBSD >= 201211 /* OpenBSD version >= 5.2 */
+#   define SIGSEGV_FAULT_STACKPOINTER  scp->sc_reg[20-15]
+#  else
+#   define SIGSEGV_FAULT_STACKPOINTER  scp->sc_reg.r_r15
+#  endif
+
+# elif defined __sparc__ || defined __sparc64__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/sparc/include/signal.h
+   and
+   openbsd-src/sys/arch/sparc64/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_sp
+
+# elif defined __vax__
+
+/* See the definition of 'struct sigcontext' in
+   openbsd-src/sys/arch/vax/include/signal.h.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_sp
+
+# endif
+
+#endif
+
+#if (defined __APPLE__ && defined __MACH__) /* macOS */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+# if defined __x86_64__
+
+/* See the definitions of
+     - 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_types/_ucontext.h>,
+     - 'struct __darwin_mcontext64' in <i386/_mcontext.h>, and
+     - 'struct __darwin_x86_thread_state64' in <mach/i386/_structs.h>.  */
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext->__ss.__rsp
+
+# elif defined __i386__
+
+/* See the definitions of
+     - 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_types/_ucontext.h>,
+     - 'struct __darwin_mcontext32' in <i386/_mcontext.h>, and
+     - 'struct __darwin_i386_thread_state' in <mach/i386/_structs.h>.  */
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext->__ss.__esp
+
+# elif defined __arm64__
+
+/* See the definitions of
+     - 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_types/_ucontext.h>,
+     - 'struct __darwin_mcontext64' in <arm/_mcontext.h>, and
+     - 'struct __darwin_arm_thread_state64' in <mach/arm/_structs.h>.  */
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext->__ss.__sp
+
+# elif defined __powerpc__
+
+/* See the definitions of
+     - 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_structs.h>,
+     - 'struct __darwin_mcontext' in <ppc/_structs.h>, and
+     - 'struct __darwin_ppc_thread_state' in <mach/ppc/_structs.h>.  */
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext->__ss.__r1
+
+# endif
+
+#endif
+
+#if defined _AIX /* AIX */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+# if defined __powerpc__ || defined __powerpc64__
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.jmp_context.gpr[1]
+# endif
+
+#endif
+
+#if defined __sgi /* IRIX */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, int code, struct sigcontext *scp
+# define SIGSEGV_FAULT_ADDRESS  (unsigned long) scp->sc_badvaddr
+# define SIGSEGV_FAULT_CONTEXT  scp
+
+# if defined __mips__ || defined __mipsn32__ || defined __mips64__
+#  define SIGSEGV_FAULT_STACKPOINTER  scp->sc_regs[29]
+# endif
+
+#endif
+
+#if defined __sun /* Solaris */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+# if defined __x86_64__
+/* 64 bit registers */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_RSP]
+
+# elif defined __i386__
+/* 32 bit registers */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[ESP]
+
+# elif defined __sparc__ || defined __sparc64__
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_O6]
+
+#  if SOLARIS11
+
+/* On Solaris 11.3/SPARC, both in 32-bit and 64-bit mode, when catching
+   stack overflow, the fault address is correct the first time, but is zero
+   or near zero the second time.
+   'truss tests/test-sigsegv-catch-stackoverflow1' shows it:
+
+   In 32-bit mode:
+
+    Incurred fault #6, FLTBOUNDS  %pc = 0x000116E8
+      siginfo: SIGSEGV SEGV_MAPERR addr=0xFFB00000
+    Received signal #11, SIGSEGV [caught]
+      siginfo: SIGSEGV SEGV_MAPERR addr=0xFFB00000
+   then
+    Incurred fault #6, FLTBOUNDS  %pc = 0x000116E8
+      siginfo: SIGSEGV SEGV_MAPERR addr=0x00000008
+    Received signal #11, SIGSEGV [caught]
+      siginfo: SIGSEGV SEGV_MAPERR addr=0x00000008
+
+   In 64-bit mode:
+
+    Incurred fault #6, FLTBOUNDS  %pc = 0x100001C58
+      siginfo: SIGSEGV SEGV_MAPERR addr=0xFFFFFFFF7FF00000
+    Received signal #11, SIGSEGV [caught]
+      siginfo: SIGSEGV SEGV_MAPERR addr=0xFFFFFFFF7FF00000
+   then
+    Incurred fault #6, FLTBOUNDS  %pc = 0x100001C58
+      siginfo: SIGSEGV SEGV_MAPERR addr=0x00000000
+    Received signal #11, SIGSEGV [caught]
+      siginfo: SIGSEGV SEGV_MAPERR addr=0x00000000
+ */
+#   define BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW
+
+#  endif
+
+# endif
+
+#endif
+
+#if defined __CYGWIN__ /* Cygwin */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+/* See the definition of 'ucontext_t' in <sys/ucontext.h> and
+   of 'struct __mcontext' in <cygwin/signal.h>.  */
+# if defined __x86_64__
+/* 64 bit registers */
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.rsp
+# elif defined __i386__
+/* 32 bit registers */
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.esp
+# endif
+
+#endif
+
+#if defined __HAIKU__ /* Haiku */
+
+# define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
+# define SIGSEGV_FAULT_ADDRESS  sip->si_addr
+# define SIGSEGV_FAULT_CONTEXT  ((ucontext_t *) ucp)
+# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+
+# if defined __x86_64__
+/* 64 bit registers */
+
+/* See the definition of 'ucontext_t' in <signal.h> and
+   of 'struct vregs' in <arch/x86_64/signal.h>.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.rsp
+
+# elif defined __i386__
+/* 32 bit registers */
+
+/* See the definition of 'ucontext_t' in <signal.h> and
+   of 'struct vregs' in <arch/x86/signal.h>.  */
+
+#  define SIGSEGV_FAULT_STACKPOINTER  ((ucontext_t *) ucp)->uc_mcontext.esp
+
+# endif
+
+#endif
+
+/* ========================================================================== */
+
+/* List of signals that are sent when an invalid virtual memory address
+   is accessed, or when the stack overflows.  */
+#if defined __GNU__ \
+    || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
+    || defined __NetBSD__ || defined __OpenBSD__ \
+    || (defined __APPLE__ && defined __MACH__)
+# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \
+    { int var; var = SIGSEGV; { body } var = SIGBUS; { body } }
+#else
+# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \
+    { int var; var = SIGSEGV; { body } }
+#endif
+
+/* ========================================================================== */
+
+/* Determine the virtual memory area of a given address.  */
+#include "stackvma.h"
+
+/* ========================================================================== */
+
+/* On the average Unix platform, we define
+
+   HAVE_SIGSEGV_RECOVERY
+       if there is a fault-*.h include file which defines
+       SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_ADDRESS.
+
+   HAVE_STACK_OVERFLOW_RECOVERY
+       if HAVE_SIGALTSTACK is set and
+       at least two of the following are true:
+       A) There is a fault-*.h include file which defines
+          SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_ADDRESS.
+       B) There is a fault-*.h include file which defines
+          SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_STACKPOINTER.
+       C) There is a stackvma-*.c, other than stackvma-none.c, which
+          defines sigsegv_get_vma.
+
+   Why? Obviously, to catch stack overflow, we need an alternate signal
+   stack; this requires kernel support. But we also need to distinguish
+   (with a reasonable confidence) a stack overflow from a regular SIGSEGV.
+   If we have A) and B), we use the
+     Heuristic AB: If the fault address is near the stack pointer, it's a
+     stack overflow.
+   If we have A) and C), we use the
+     Heuristic AC: If the fault address is near and beyond the bottom of
+     the stack's virtual memory area, it's a stack overflow.
+   If we have B) and C), we use the
+     Heuristic BC: If the stack pointer is near the bottom of the stack's
+     virtual memory area, it's a stack overflow.
+     This heuristic comes in two flavours: On OSes which let the stack's
+     VMA grow continuously, we determine the bottom by use of getrlimit().
+     On OSes which preallocate the stack's VMA with its maximum size
+     (like BeOS), we use the stack's VMA directly.
+ */
+
+#if HAVE_SIGSEGV_RECOVERY \
+     && !(defined SIGSEGV_FAULT_HANDLER_ARGLIST && defined SIGSEGV_FAULT_ADDRESS)
+# error "You need to define SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_ADDRESS before you can define HAVE_SIGSEGV_RECOVERY."
+#endif
+#if !HAVE_SIGSEGV_RECOVERY \
+    && (defined SIGSEGV_FAULT_HANDLER_ARGLIST && defined SIGSEGV_FAULT_ADDRESS) \
+    && !(defined __FreeBSD__ && (defined __sparc__ || defined __sparc64__))
+# if __GNUC__ || (__clang_major__ >= 4)
+#  warning "You can define HAVE_SIGSEGV_RECOVERY on this platform."
+# else
+#  error "You can define HAVE_SIGSEGV_RECOVERY on this platform."
+# endif
+#endif
+
+#if HAVE_STACK_OVERFLOW_RECOVERY \
+    && !(defined SIGSEGV_FAULT_ADDRESS + defined SIGSEGV_FAULT_STACKPOINTER + HAVE_STACKVMA >= 2)
+# error "You need to define two of SIGSEGV_FAULT_ADDRESS, SIGSEGV_FAULT_STACKPOINTER, HAVE_STACKVMA, before you can define HAVE_STACK_OVERFLOW_RECOVERY."
+#endif
+#if !HAVE_STACK_OVERFLOW_RECOVERY \
+    && (defined SIGSEGV_FAULT_ADDRESS + defined SIGSEGV_FAULT_STACKPOINTER + HAVE_STACKVMA >= 2) \
+    && !(defined __FreeBSD__ && (defined __sparc__ || defined __sparc64__)) \
+    && !(defined __NetBSD__ && (defined __sparc__ || defined __sparc64__))
+# if __GNUC__ || (__clang_major__ >= 4)
+#  warning "You can define HAVE_STACK_OVERFLOW_RECOVERY on this platform."
+# else
+#  error "You can define HAVE_STACK_OVERFLOW_RECOVERY on this platform."
+# endif
+#endif
+
+/* ========================================================================== */
+
+#if HAVE_STACK_OVERFLOW_RECOVERY
+
+/* ======= Leaving a signal handler executing on the alternate stack ======= */
+
+/* Platform dependent:
+   Leaving a signal handler executing on the alternate stack.  */
+static void sigsegv_reset_onstack_flag (void);
+
+/* -------------------------- leave-sigaltstack.c -------------------------- */
+
+# if defined __GNU__ \
+     || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
+     || defined __NetBSD__ || defined __OpenBSD__
+
+static void
+sigsegv_reset_onstack_flag (void)
+{
+  stack_t ss;
+
+  if (sigaltstack (NULL, &ss) >= 0)
+    {
+      ss.ss_flags &= ~SS_ONSTACK;
+      sigaltstack (&ss, NULL);
+    }
+}
+
+/* --------------------------- leave-setcontext.c --------------------------- */
+
+# elif defined __sgi || defined __sun /* IRIX, Solaris */
+
+#  include <ucontext.h>
+
+static void
+sigsegv_reset_onstack_flag (void)
+{
+  ucontext_t uc;
+
+  if (getcontext (&uc) >= 0)
+    /* getcontext returns twice.  We are interested in the returned context
+       only the first time, i.e. when the SS_ONSTACK bit is set.  */
+    if (uc.uc_stack.ss_flags & SS_ONSTACK)
+      {
+        uc.uc_stack.ss_flags &= ~SS_ONSTACK;
+        /* Note that setcontext() does not refill uc.  Therefore if
+           setcontext() keeps SS_ONSTACK set in the kernel, either
+           setcontext() will return -1 or getcontext() will return a
+           second time, with the SS_ONSTACK bit being cleared.  */
+        setcontext (&uc);
+      }
+}
+
+/* ------------------------------ leave-nop.c ------------------------------ */
+
+# else
+
+static void
+sigsegv_reset_onstack_flag (void)
+{
+  /* Nothing to do. sigaltstack() simply looks at the stack pointer,
+     therefore SS_ONSTACK is not sticky.  */
+}
+
+# endif
+
+/* ========================================================================== */
+
+# if HAVE_STACKVMA
+
+/* Address of the last byte belonging to the stack vma.  */
+static uintptr_t stack_top = 0;
+
+/* Needs to be called once only.  */
+static void
+remember_stack_top (void *some_variable_on_stack)
+{
+  struct vma_struct vma;
+
+  if (sigsegv_get_vma ((uintptr_t) some_variable_on_stack, &vma) >= 0)
+    stack_top = vma.end - 1;
+}
+
+# endif /* HAVE_STACKVMA */
+
+static stackoverflow_handler_t stk_user_handler = (stackoverflow_handler_t)NULL;
+static uintptr_t stk_extra_stack;
+static size_t stk_extra_stack_size;
+
+#endif /* HAVE_STACK_OVERFLOW_RECOVERY */
+
+#if HAVE_SIGSEGV_RECOVERY
+
+/* User's SIGSEGV handler.  */
+static sigsegv_handler_t user_handler = (sigsegv_handler_t)NULL;
+
+#endif /* HAVE_SIGSEGV_RECOVERY */
+
+
+/* Our SIGSEGV handler, with OS dependent argument list.  */
+
+#if HAVE_SIGSEGV_RECOVERY
+
+static void
+sigsegv_handler (SIGSEGV_FAULT_HANDLER_ARGLIST)
+{
+  void *address = (void *) (SIGSEGV_FAULT_ADDRESS);
+
+# if HAVE_STACK_OVERFLOW_RECOVERY
+#  if !(HAVE_STACKVMA || defined SIGSEGV_FAULT_STACKPOINTER)
+#error "Insufficient heuristics for detecting a stack overflow.  Either define CFG_STACKVMA and HAVE_STACKVMA correctly, or define SIGSEGV_FAULT_STACKPOINTER correctly, or undefine HAVE_STACK_OVERFLOW_RECOVERY!"
+#  endif
+
+  /* Call user's handler.  */
+  if (user_handler && (*user_handler) (address, 0))
+    {
+      /* Handler successful.  */
+    }
+  else
+    {
+      /* Handler declined responsibility.  */
+
+      /* Did the user install a stack overflow handler?  */
+      if (stk_user_handler)
+        {
+          /* See whether it was a stack overflow. If so, longjump away.  */
+#  ifdef SIGSEGV_FAULT_STACKPOINTER
+          uintptr_t old_sp = (uintptr_t) (SIGSEGV_FAULT_STACKPOINTER);
+#   ifdef __ia64
+          uintptr_t old_bsp = (uintptr_t) (SIGSEGV_FAULT_BSP_POINTER);
+#   endif
+#  endif
+
+#  if HAVE_STACKVMA
+          /* Were we able to determine the stack top?  */
+          if (stack_top)
+            {
+              /* Determine stack bounds.  */
+              int saved_errno;
+              struct vma_struct vma;
+              int ret;
+
+              saved_errno = errno;
+              ret = sigsegv_get_vma (stack_top, &vma);
+              errno = saved_errno;
+              if (ret >= 0)
+                {
+#   ifndef BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW
+                  /* Heuristic AC: If the fault_address is nearer to the stack
+                     segment's [start,end] than to the previous segment, we
+                     consider it a stack overflow.
+                     In the case of IA-64, we know that the previous segment
+                     is the up-growing bsp segment, and either of the two
+                     stacks can overflow.  */
+                  uintptr_t addr = (uintptr_t) address;
+
+#    ifdef __ia64
+                  if (addr >= vma.prev_end && addr <= vma.end - 1)
+#    else
+#     if STACK_DIRECTION < 0
+                  if (addr >= vma.start
+                      ? (addr <= vma.end - 1)
+                      : vma.is_near_this (addr, &vma))
+#     else
+                  if (addr <= vma.end - 1
+                      ? (addr >= vma.start)
+                      : vma.is_near_this (addr, &vma))
+#     endif
+#    endif
+                    {
+#   else /* BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW */
+#    if HAVE_GETRLIMIT && defined RLIMIT_STACK
+                  /* Heuristic BC: If the stack size has reached its maximal size,
+                     and old_sp is near the low end, we consider it a stack
+                     overflow.  */
+                  struct rlimit rl;
+
+                  saved_errno = errno;
+                  ret = getrlimit (RLIMIT_STACK, &rl);
+                  errno = saved_errno;
+                  if (ret >= 0)
+                    {
+                      uintptr_t current_stack_size = vma.end - vma.start;
+                      uintptr_t max_stack_size = rl.rlim_cur;
+                      if (current_stack_size <= max_stack_size + 4096
+                          && max_stack_size <= current_stack_size + 4096
+#    else
+                    {
+                      if (1
+#    endif
+#    ifdef SIGSEGV_FAULT_STACKPOINTER
+                          /* Heuristic BC: If we know old_sp, and it is neither
+                             near the low end, nor in the alternate stack, then
+                             it's probably not a stack overflow.  */
+                          && ((old_sp >= stk_extra_stack
+                               && old_sp <= stk_extra_stack + stk_extra_stack_size)
+#     if STACK_DIRECTION < 0
+                              || (old_sp <= vma.start + 4096
+                                  && vma.start <= old_sp + 4096))
+#     else
+                              || (old_sp <= vma.end + 4096
+                                  && vma.end <= old_sp + 4096))
+#     endif
+#    endif
+                         )
+#   endif /* BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW */
+#  else /* !HAVE_STACKVMA */
+          /* Heuristic AB: If the fault address is near the stack pointer,
+             it's a stack overflow.  */
+          uintptr_t addr = (uintptr_t) address;
+
+          if ((addr <= old_sp + 4096 && old_sp <= addr + 4096)
+#   ifdef __ia64
+              || (addr <= old_bsp + 4096 && old_bsp <= addr + 4096)
+#   endif
+             )
+            {
+                {
+                    {
+#  endif /* !HAVE_STACKVMA */
+                        {
+#  ifdef SIGSEGV_FAULT_STACKPOINTER
+                          int emergency =
+                            (old_sp >= stk_extra_stack
+                             && old_sp <= stk_extra_stack + stk_extra_stack_size);
+                          stackoverflow_context_t context = (SIGSEGV_FAULT_CONTEXT);
+#  else
+                          int emergency = 0;
+                          stackoverflow_context_t context = (void *) 0;
+#  endif
+                          /* Call user's handler.  */
+                          (*stk_user_handler) (emergency, context);
+                        }
+                    }
+                }
+            }
+        }
+# endif /* HAVE_STACK_OVERFLOW_RECOVERY */
+
+      if (user_handler && (*user_handler) (address, 1))
+        {
+          /* Handler successful.  */
+        }
+      else
+        {
+          /* Handler declined responsibility for real.  */
+
+          /* Remove ourselves and dump core.  */
+          SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
+        }
+
+# if HAVE_STACK_OVERFLOW_RECOVERY
+    }
+# endif /* HAVE_STACK_OVERFLOW_RECOVERY */
+}
+
+#elif HAVE_STACK_OVERFLOW_RECOVERY
+
+static void
+# ifdef SIGSEGV_FAULT_STACKPOINTER
+sigsegv_handler (SIGSEGV_FAULT_HANDLER_ARGLIST)
+# else
+sigsegv_handler (int sig)
+# endif
+{
+# if !((HAVE_GETRLIMIT && defined RLIMIT_STACK) || defined SIGSEGV_FAULT_STACKPOINTER)
+#  error "Insufficient heuristics for detecting a stack overflow.  Either define SIGSEGV_FAULT_STACKPOINTER correctly, or undefine HAVE_STACK_OVERFLOW_RECOVERY!"
+# endif
+
+  /* Did the user install a handler?  */
+  if (stk_user_handler)
+    {
+      /* See whether it was a stack overflow.  If so, longjump away.  */
+# ifdef SIGSEGV_FAULT_STACKPOINTER
+      uintptr_t old_sp = (uintptr_t) (SIGSEGV_FAULT_STACKPOINTER);
+# endif
+
+      /* Were we able to determine the stack top?  */
+      if (stack_top)
+        {
+          /* Determine stack bounds.  */
+          int saved_errno;
+          struct vma_struct vma;
+          int ret;
+
+          saved_errno = errno;
+          ret = sigsegv_get_vma (stack_top, &vma);
+          errno = saved_errno;
+          if (ret >= 0)
+            {
+# if HAVE_GETRLIMIT && defined RLIMIT_STACK
+              /* Heuristic BC: If the stack size has reached its maximal size,
+                 and old_sp is near the low end, we consider it a stack
+                 overflow.  */
+              struct rlimit rl;
+
+              saved_errno = errno;
+              ret = getrlimit (RLIMIT_STACK, &rl);
+              errno = saved_errno;
+              if (ret >= 0)
+                {
+                  uintptr_t current_stack_size = vma.end - vma.start;
+                  uintptr_t max_stack_size = rl.rlim_cur;
+                  if (current_stack_size <= max_stack_size + 4096
+                      && max_stack_size <= current_stack_size + 4096
+# else
+                {
+                  if (1
+# endif
+# ifdef SIGSEGV_FAULT_STACKPOINTER
+                      /* Heuristic BC: If we know old_sp, and it is neither
+                         near the low end, nor in the alternate stack, then
+                         it's probably not a stack overflow.  */
+                      && ((old_sp >= stk_extra_stack
+                           && old_sp <= stk_extra_stack + stk_extra_stack_size)
+#  if STACK_DIRECTION < 0
+                          || (old_sp <= vma.start + 4096
+                              && vma.start <= old_sp + 4096))
+#  else
+                          || (old_sp <= vma.end + 4096
+                              && vma.end <= old_sp + 4096))
+#  endif
+# endif
+                     )
+                    {
+# ifdef SIGSEGV_FAULT_STACKPOINTER
+                      int emergency =
+                        (old_sp >= stk_extra_stack
+                         && old_sp <= stk_extra_stack + stk_extra_stack_size);
+                      stackoverflow_context_t context = (SIGSEGV_FAULT_CONTEXT);
+# else
+                      int emergency = 0;
+                      stackoverflow_context_t context = (void *) 0;
+# endif
+                      /* Call user's handler.  */
+                      (*stk_user_handler)(emergency,context);
+                    }
+                }
+            }
+        }
+    }
+
+  /* Remove ourselves and dump core.  */
+  SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
+}
+
+#endif
+
+
+#if HAVE_SIGSEGV_RECOVERY || HAVE_STACK_OVERFLOW_RECOVERY
+
+static void
+install_for (int sig)
+{
+  struct sigaction action;
+
+# ifdef SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+  action.sa_sigaction = &sigsegv_handler;
+# else
+  action.sa_handler = (void (*) (int)) &sigsegv_handler;
+# endif
+  /* Block most signals while SIGSEGV is being handled.  */
+  /* Signals SIGKILL, SIGSTOP cannot be blocked.  */
+  /* Signals SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU are not blocked because
+     dealing with these signals seems dangerous.  */
+  /* Signals SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGTRAP, SIGIOT, SIGEMT, SIGBUS,
+     SIGSYS, SIGSTKFLT are not blocked because these are synchronous signals,
+     which may require immediate intervention, otherwise the process may
+     starve.  */
+  sigemptyset (&action.sa_mask);
+# ifdef SIGHUP
+  sigaddset (&action.sa_mask,SIGHUP);
+# endif
+# ifdef SIGINT
+  sigaddset (&action.sa_mask,SIGINT);
+# endif
+# ifdef SIGQUIT
+  sigaddset (&action.sa_mask,SIGQUIT);
+# endif
+# ifdef SIGPIPE
+  sigaddset (&action.sa_mask,SIGPIPE);
+# endif
+# ifdef SIGALRM
+  sigaddset (&action.sa_mask,SIGALRM);
+# endif
+# ifdef SIGTERM
+  sigaddset (&action.sa_mask,SIGTERM);
+# endif
+# ifdef SIGUSR1
+  sigaddset (&action.sa_mask,SIGUSR1);
+# endif
+# ifdef SIGUSR2
+  sigaddset (&action.sa_mask,SIGUSR2);
+# endif
+# ifdef SIGCHLD
+  sigaddset (&action.sa_mask,SIGCHLD);
+# endif
+# ifdef SIGCLD
+  sigaddset (&action.sa_mask,SIGCLD);
+# endif
+# ifdef SIGURG
+  sigaddset (&action.sa_mask,SIGURG);
+# endif
+# ifdef SIGIO
+  sigaddset (&action.sa_mask,SIGIO);
+# endif
+# ifdef SIGPOLL
+  sigaddset (&action.sa_mask,SIGPOLL);
+# endif
+# ifdef SIGXCPU
+  sigaddset (&action.sa_mask,SIGXCPU);
+# endif
+# ifdef SIGXFSZ
+  sigaddset (&action.sa_mask,SIGXFSZ);
+# endif
+# ifdef SIGVTALRM
+  sigaddset (&action.sa_mask,SIGVTALRM);
+# endif
+# ifdef SIGPROF
+  sigaddset (&action.sa_mask,SIGPROF);
+# endif
+# ifdef SIGPWR
+  sigaddset (&action.sa_mask,SIGPWR);
+# endif
+# ifdef SIGLOST
+  sigaddset (&action.sa_mask,SIGLOST);
+# endif
+# ifdef SIGWINCH
+  sigaddset (&action.sa_mask,SIGWINCH);
+# endif
+  /* Note that sigaction() implicitly adds sig itself to action.sa_mask.  */
+  /* Ask the OS to provide a structure siginfo_t to the handler.  */
+# ifdef SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
+  action.sa_flags = SA_SIGINFO;
+# else
+  action.sa_flags = 0;
+# endif
+# if HAVE_STACK_OVERFLOW_RECOVERY && HAVE_SIGALTSTACK /* not BeOS */
+  /* Work around Linux 2.2.5 bug: If SA_ONSTACK is specified but sigaltstack()
+     has not been called, the kernel will busy loop, eating CPU time.  So
+     avoid setting SA_ONSTACK until the user has requested stack overflow
+     handling.  */
+  if (stk_user_handler)
+    action.sa_flags |= SA_ONSTACK;
+# endif
+  sigaction (sig, &action, (struct sigaction *) NULL);
+}
+
+#endif /* HAVE_SIGSEGV_RECOVERY || HAVE_STACK_OVERFLOW_RECOVERY */
+
+int
+sigsegv_install_handler (sigsegv_handler_t handler)
+{
+#if HAVE_SIGSEGV_RECOVERY
+  user_handler = handler;
+
+  SIGSEGV_FOR_ALL_SIGNALS (sig, install_for (sig);)
+
+  return 0;
+#else
+  return -1;
+#endif
+}
+
+void
+sigsegv_deinstall_handler (void)
+{
+#if HAVE_SIGSEGV_RECOVERY
+  user_handler = (sigsegv_handler_t)NULL;
+
+# if HAVE_STACK_OVERFLOW_RECOVERY
+  if (!stk_user_handler)
+# endif
+    {
+      SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
+    }
+#endif
+}
+
+int
+sigsegv_leave_handler (void (*continuation) (void*, void*, void*),
+                       void* cont_arg1, void* cont_arg2, void* cont_arg3)
+{
+#if HAVE_STACK_OVERFLOW_RECOVERY
+  /*
+   * Reset the system's knowledge that we are executing on the alternate
+   * stack. If we didn't do that, siglongjmp would be needed instead of
+   * longjmp to leave the signal handler.
+   */
+  sigsegv_reset_onstack_flag ();
+#endif
+  (*continuation) (cont_arg1, cont_arg2, cont_arg3);
+  return 1;
+}
+
+int
+stackoverflow_install_handler (stackoverflow_handler_t handler,
+                               void *extra_stack, size_t extra_stack_size)
+{
+#if HAVE_STACK_OVERFLOW_RECOVERY
+# if HAVE_STACKVMA
+  if (!stack_top)
+    {
+      int dummy;
+      remember_stack_top (&dummy);
+      if (!stack_top)
+        return -1;
+    }
+# endif
+
+  stk_user_handler = handler;
+  stk_extra_stack = (uintptr_t) extra_stack;
+  stk_extra_stack_size = extra_stack_size;
+  {
+    stack_t ss;
+# if SIGALTSTACK_SS_REVERSED
+    ss.ss_sp = (char *) extra_stack + extra_stack_size - sizeof (void *);
+    ss.ss_size = extra_stack_size - sizeof (void *);
+# else
+    ss.ss_sp = extra_stack;
+    ss.ss_size = extra_stack_size;
+# endif
+    ss.ss_flags = 0; /* no SS_DISABLE */
+    if (sigaltstack (&ss, (stack_t*)0) < 0)
+      return -1;
+  }
+
+  /* Install the signal handlers with SA_ONSTACK.  */
+  SIGSEGV_FOR_ALL_SIGNALS (sig, install_for (sig);)
+  return 0;
+#else
+  return -1;
+#endif
+}
+
+void
+stackoverflow_deinstall_handler (void)
+{
+#if HAVE_STACK_OVERFLOW_RECOVERY
+  stk_user_handler = (stackoverflow_handler_t) NULL;
+
+# if HAVE_SIGSEGV_RECOVERY
+  if (user_handler)
+    {
+      /* Reinstall the signal handlers without SA_ONSTACK, to avoid Linux
+         bug.  */
+      SIGSEGV_FOR_ALL_SIGNALS (sig, install_for (sig);)
+    }
+  else
+# endif
+    {
+      SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
+    }
+
+  {
+    stack_t ss;
+    ss.ss_flags = SS_DISABLE;
+    if (sigaltstack (&ss, (stack_t *) 0) < 0)
+      perror ("gnulib sigsegv (stackoverflow_deinstall_handler)");
+  }
+#endif
+}
diff --git a/lib/sigsegv.in.h b/lib/sigsegv.in.h
new file mode 100644
index 0000000..1c87acc
--- /dev/null
+++ b/lib/sigsegv.in.h
@@ -0,0 +1,233 @@
+/* Page fault handling library.
+   Copyright (C) 1998-2021  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 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 <https://www.gnu.org/licenses/>.  */
+
+#ifndef _SIGSEGV_H
+#define _SIGSEGV_H
+
+/* Get size_t.  */
+#include <stddef.h>
+
+/* Define the fault context structure.  */
+#if defined __linux__ || defined __ANDROID__ \
+    || (defined __FreeBSD__ && (defined __arm__ || defined __armhf__ || defined __arm64__)) \
+    || defined __NetBSD__ \
+    || defined _AIX || defined __sun \
+    || defined __CYGWIN__
+/* Linux, FreeBSD, NetBSD, AIX, Solaris, Cygwin */
+# include <ucontext.h>
+#elif (defined __APPLE__ && defined __MACH__)
+/* macOS */
+# include <sys/ucontext.h>
+#elif defined __HAIKU__
+/* Haiku */
+# include <signal.h>
+#endif
+
+/* Correct the value of SIGSTKSZ on some systems.
+   AIX 64-bit: original value 4096 is too small.
+   HP-UX: original value 8192 is too small.
+   Solaris 11/x86_64: original value 8192 is too small.  */
+#if defined _AIX && defined _ARCH_PPC64
+# include <signal.h>
+# undef SIGSTKSZ
+# define SIGSTKSZ 8192
+#endif
+#if defined __hpux || (defined __sun && (defined __x86_64__ || defined __amd64__))
+# include <signal.h>
+# undef SIGSTKSZ
+# define SIGSTKSZ 16384
+#endif
+
+/* HAVE_SIGSEGV_RECOVERY
+   is defined if the system supports catching SIGSEGV.  */
+#if defined __linux__ || defined __ANDROID__ || defined __GNU__ \
+    || defined __FreeBSD_kernel__ || (defined __FreeBSD__ && !(defined __sparc__ || defined __sparc64__)) || defined __DragonFly__ \
+    || defined __NetBSD__ \
+    || defined __OpenBSD__ \
+    || (defined __APPLE__ && defined __MACH__) \
+    || defined _AIX || defined __sgi || defined __sun \
+    || defined __CYGWIN__ || defined __HAIKU__
+/* Linux, Hurd, GNU/kFreeBSD, FreeBSD, NetBSD, OpenBSD, macOS, AIX, IRIX, Solaris, Cygwin, Haiku */
+# define HAVE_SIGSEGV_RECOVERY 1
+#endif
+
+/* HAVE_STACK_OVERFLOW_RECOVERY
+   is defined if stack overflow can be caught.  */
+#if defined __linux__ || defined __ANDROID__ || defined __GNU__ \
+    || defined __FreeBSD_kernel__ || (defined __FreeBSD__ && !(defined __sparc__ || defined __sparc64__)) || defined __DragonFly__ \
+    || (defined __NetBSD__ && !(defined __sparc__ || defined __sparc64__)) \
+    || defined __OpenBSD__ \
+    || (defined __APPLE__ && defined __MACH__) \
+    || defined _AIX || defined __sgi || defined __sun \
+    || defined __CYGWIN__ || defined __HAIKU__
+/* Linux, Hurd, GNU/kFreeBSD, FreeBSD, NetBSD, OpenBSD, macOS, AIX, IRIX, Solaris, Cygwin, Haiku */
+# define HAVE_STACK_OVERFLOW_RECOVERY 1
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBSIGSEGV_VERSION 0x020D    /* version number: (major<<8) + minor */
+extern int libsigsegv_version;       /* Likewise */
+
+/* -------------------------------------------------------------------------- */
+
+#if 1 /* really only HAVE_SIGSEGV_RECOVERY */
+
+/*
+ * The mask of bits that are set to zero in a fault address that gets passed
+ * to a global SIGSEGV handler.
+ * On some platforms, the precise fault address is not known, only the memory
+ * page into which the fault address falls. This is apparently allowed by POSIX:
+ * <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html>
+ * says: "For some implementations, the value of si_addr may be inaccurate."
+ * In this case, the returned fault address is rounded down to a multiple of
+ * getpagesize() = sysconf(_SC_PAGESIZE).
+ * On such platforms, we define SIGSEGV_FAULT_ADDRESS_ALIGNMENT to be an upper
+ * bound for getpagesize() (and, like getpagesize(), also a power of 2).
+ * On the platforms where the returned fault address is the precise one, we
+ * define SIGSEGV_FAULT_ADDRESS_ALIGNMENT to 1.
+ */
+# if defined __NetBSD__ && (defined __sparc__ || defined __sparc64__)
+  /* getpagesize () is 0x1000 or 0x2000, depending on hardware.  */
+#  define SIGSEGV_FAULT_ADDRESS_ALIGNMENT 0x2000UL
+# elif defined __linux__ && (defined __s390__ || defined __s390x__)
+  /* getpagesize () is 0x1000.  */
+#  define SIGSEGV_FAULT_ADDRESS_ALIGNMENT 0x1000UL
+# else
+#  define SIGSEGV_FAULT_ADDRESS_ALIGNMENT 1UL
+# endif
+
+/*
+ * The type of a global SIGSEGV handler.
+ * The fault address, with the bits (SIGSEGV_FAULT_ADDRESS_ALIGNMENT - 1)
+ * cleared, is passed as argument.
+ * The access type (read access or write access) is not passed; your handler
+ * has to know itself how to distinguish these two cases.
+ * The second argument is 0, meaning it could also be a stack overflow, or 1,
+ * meaning the handler should seriously try to fix the fault.
+ * The return value should be nonzero if the handler has done its job
+ * and no other handler should be called, or 0 if the handler declines
+ * responsibility for the given address.
+ *
+ * The handler is run at a moment when nothing about the global state of the
+ * program is known. Therefore it cannot use facilities that manipulate global
+ * variables or locks. In particular, it cannot use malloc(); use mmap()
+ * instead. It cannot use fopen(); use open() instead. Etc. All global
+ * variables that are accessed by the handler should be marked 'volatile'.
+ */
+typedef int (*sigsegv_handler_t) (void* fault_address, int serious);
+
+/*
+ * Installs a global SIGSEGV handler.
+ * This should be called once only, and it ignores any previously installed
+ * SIGSEGV handler.
+ * Returns 0 on success, or -1 if the system doesn't support catching SIGSEGV.
+ */
+extern int sigsegv_install_handler (sigsegv_handler_t handler);
+
+/*
+ * Deinstalls the global SIGSEGV handler.
+ * This goes back to the state where no SIGSEGV handler is installed.
+ */
+extern void sigsegv_deinstall_handler (void);
+
+/*
+ * Prepares leaving a SIGSEGV handler (through longjmp or similar means).
+ * Control is transferred by calling CONTINUATION with CONT_ARG1, CONT_ARG2,
+ * CONT_ARG3 as arguments.
+ * CONTINUATION must not return.
+ * The sigsegv_leave_handler function may return if called from a SIGSEGV
+ * handler; its return value should be used as the handler's return value.
+ * The sigsegv_leave_handler function does not return if called from a
+ * stack overflow handler.
+ */
+extern int sigsegv_leave_handler (void (*continuation) (void*, void*, void*), void* cont_arg1, void* cont_arg2, void* cont_arg3);
+
+#endif /* HAVE_SIGSEGV_RECOVERY */
+
+#if 1 /* really only HAVE_STACK_OVERFLOW_RECOVERY */
+
+/*
+ * The type of a context passed to a stack overflow handler.
+ * This type is system dependent; on some platforms it is an 'ucontext_t *',
+ * on some platforms it is a 'struct sigcontext *', on others merely an
+ * opaque 'void *'.
+ */
+# if defined __linux__ || defined __ANDROID__ \
+     || (defined __FreeBSD__ && (defined __arm__ || defined __armhf__ || defined __arm64__)) \
+     || defined __NetBSD__ \
+     || (defined __APPLE__ && defined __MACH__) \
+     || defined _AIX || defined __sun \
+     || defined __CYGWIN__ || defined __HAIKU__
+typedef ucontext_t *stackoverflow_context_t;
+# elif defined __GNU__ \
+       || defined __FreeBSD_kernel__ || (defined __FreeBSD__ && !(defined __sparc__ || defined __sparc64__)) \
+       || defined __OpenBSD__ || defined __sgi
+typedef struct sigcontext *stackoverflow_context_t;
+# else
+typedef void *stackoverflow_context_t;
+# endif
+
+/*
+ * The type of a stack overflow handler.
+ * Such a handler should perform a longjmp call in order to reduce the amount
+ * of stack needed. It must not return.
+ * The emergency argument is 0 when the stack could be repared, or 1 if the
+ * application should better save its state and exit now.
+ *
+ * The handler is run at a moment when nothing about the global state of the
+ * program is known. Therefore it cannot use facilities that manipulate global
+ * variables or locks. In particular, it cannot use malloc(); use mmap()
+ * instead. It cannot use fopen(); use open() instead. Etc. All global
+ * variables that are accessed by the handler should be marked 'volatile'.
+ */
+typedef void (*stackoverflow_handler_t) (int emergency, stackoverflow_context_t scp);
+
+/*
+ * Installs a stack overflow handler.
+ * The extra_stack argument is a pointer to a pre-allocated area used as a
+ * stack for executing the handler. It typically comes from a static variable
+ * or from heap-allocated memoty; placing it on the main stack may fail on
+ * some operating systems.
+ * Its size, passed in extra_stack_size, should be sufficiently large.  The
+ * following code determines an appropriate size:
+ *   #include <signal.h>
+ *   #ifndef SIGSTKSZ         / * glibc defines SIGSTKSZ for this purpose * /
+ *   # define SIGSTKSZ 16384  / * on most platforms, 16 KB are sufficient * /
+ *   #endif
+ * Returns 0 on success, or -1 if the system doesn't support catching stack
+ * overflow.
+ */
+extern int stackoverflow_install_handler (stackoverflow_handler_t handler,
+                                          void* extra_stack, size_t extra_stack_size);
+
+/*
+ * Deinstalls the stack overflow handler.
+ */
+extern void stackoverflow_deinstall_handler (void);
+
+#endif /* HAVE_STACK_OVERFLOW_RECOVERY */
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SIGSEGV_H */
diff --git a/lib/stackvma.c b/lib/stackvma.c
new file mode 100644
index 0000000..faa9923
--- /dev/null
+++ b/lib/stackvma.c
@@ -0,0 +1,2064 @@
+/* Determine the virtual memory area of a given address.
+   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2003-2006  Paolo Bonzini <bonzini@gnu.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 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "stackvma.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* =========================== stackvma-simple.c =========================== */
+
+#if defined __linux__ || defined __ANDROID__ \
+    || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
+    || defined __NetBSD__ \
+    || (defined __APPLE__ && defined __MACH__) \
+    || defined __sgi || defined __sun \
+    || defined __CYGWIN__ || defined __HAIKU__
+
+/* This file contains the proximity test function for the simple cases, where
+   the OS has an API for enumerating the mapped ranges of virtual memory.  */
+
+# if STACK_DIRECTION < 0
+
+/* Info about the gap between this VMA and the previous one.
+   addr must be < vma->start.  */
+static int
+simple_is_near_this (uintptr_t addr, struct vma_struct *vma)
+{
+  return (vma->start - addr <= (vma->start - vma->prev_end) / 2);
+}
+
+# endif
+# if STACK_DIRECTION > 0
+
+/* Info about the gap between this VMA and the next one.
+   addr must be > vma->end - 1.  */
+static int
+simple_is_near_this (uintptr_t addr, struct vma_struct *vma)
+{
+  return (addr - vma->end < (vma->next_start - vma->end) / 2);
+}
+
+# endif
+
+#endif
+
+/* =========================== stackvma-rofile.c =========================== */
+/* Buffered read-only streams.  */
+
+#if defined __linux__ || defined __ANDROID__ \
+    || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
+    || defined __NetBSD__ \
+    || defined __CYGWIN__
+
+# include <errno.h> /* errno, EINTR */
+# include <fcntl.h> /* open, O_RDONLY */
+# include <stddef.h> /* size_t */
+# include <unistd.h> /* getpagesize, lseek, read, close */
+# include <sys/types.h>
+# include <sys/mman.h> /* mmap, munmap */
+
+# if defined __linux__ || defined __ANDROID__
+#  include <limits.h> /* PATH_MAX */
+# endif
+
+/* Buffered read-only streams.
+   We cannot use <stdio.h> here, because fopen() calls malloc(), and a malloc()
+   call may have been interrupted.
+   Also, we cannot use multiple read() calls, because if the buffer size is
+   smaller than the file's contents:
+     - On NetBSD, the second read() call would return 0, thus making the file
+       appear truncated.
+     - On DragonFly BSD, the first read() call would fail with errno = EFBIG.
+     - On all platforms, if some other thread is doing memory allocations or
+       deallocations between two read() calls, there is a high risk that the
+       result of these two read() calls don't fit together, and as a
+       consequence we will parse gargage and either omit some VMAs or return
+       VMAs with nonsensical addresses.
+   So use mmap(), and ignore the resulting VMA.
+   The stack-allocated buffer cannot be too large, because this can be called
+   when we are in the context of an alternate stack of just SIGSTKSZ bytes.  */
+
+# if defined __linux__ || defined __ANDROID__
+  /* On Linux, if the file does not entirely fit into the buffer, the read()
+     function stops before the line that would come out truncated.  The
+     maximum size of such a line is 73 + PATH_MAX bytes.  To be sure that we
+     have read everything, we must verify that at least that many bytes are
+     left when read() returned.  */
+#  define MIN_LEFTOVER (73 + PATH_MAX)
+# else
+#  define MIN_LEFTOVER 1
+# endif
+
+# if MIN_LEFTOVER < 1024
+#  define STACK_ALLOCATED_BUFFER_SIZE 1024
+# else
+  /* There is no point in using a stack-allocated buffer if it is too small
+     anyway.  */
+#  define STACK_ALLOCATED_BUFFER_SIZE 1
+# endif
+
+struct rofile
+  {
+    size_t position;
+    size_t filled;
+    int eof_seen;
+    /* These fields deal with allocation of the buffer.  */
+    char *buffer;
+    char *auxmap;
+    size_t auxmap_length;
+    uintptr_t auxmap_start;
+    uintptr_t auxmap_end;
+    char stack_allocated_buffer[STACK_ALLOCATED_BUFFER_SIZE];
+  };
+
+/* Open a read-only file stream.  */
+static int
+rof_open (struct rofile *rof, const char *filename)
+{
+  int fd;
+  uintptr_t pagesize;
+  size_t size;
+
+  fd = open (filename, O_RDONLY);
+  if (fd < 0)
+    return -1;
+  rof->position = 0;
+  rof->eof_seen = 0;
+  /* Try the static buffer first.  */
+  pagesize = 0;
+  rof->buffer = rof->stack_allocated_buffer;
+  size = sizeof (rof->stack_allocated_buffer);
+  rof->auxmap = NULL;
+  rof->auxmap_start = 0;
+  rof->auxmap_end = 0;
+  for (;;)
+    {
+      /* Attempt to read the contents in a single system call.  */
+      if (size > MIN_LEFTOVER)
+        {
+          int n = read (fd, rof->buffer, size);
+          if (n < 0 && errno == EINTR)
+            goto retry;
+# if defined __DragonFly__
+          if (!(n < 0 && errno == EFBIG))
+# endif
+            {
+              if (n <= 0)
+                /* Empty file.  */
+                goto fail1;
+              if (n + MIN_LEFTOVER <= size)
+                {
+                  /* The buffer was sufficiently large.  */
+                  rof->filled = n;
+# if defined __linux__ || defined __ANDROID__
+                  /* On Linux, the read() call may stop even if the buffer was
+                     large enough.  We need the equivalent of full_read().  */
+                  for (;;)
+                    {
+                      n = read (fd, rof->buffer + rof->filled, size - rof->filled);
+                      if (n < 0 && errno == EINTR)
+                        goto retry;
+                      if (n < 0)
+                        /* Some error.  */
+                        goto fail1;
+                      if (n + MIN_LEFTOVER > size - rof->filled)
+                        /* Allocate a larger buffer.  */
+                        break;
+                      if (n == 0)
+                        {
+                          /* Reached the end of file.  */
+                          close (fd);
+                          return 0;
+                        }
+                      rof->filled += n;
+                    }
+# else
+                  close (fd);
+                  return 0;
+# endif
+                }
+            }
+        }
+      /* Allocate a larger buffer.  */
+      if (pagesize == 0)
+        {
+          pagesize = getpagesize ();
+          size = pagesize;
+          while (size <= MIN_LEFTOVER)
+            size = 2 * size;
+        }
+      else
+        {
+          size = 2 * size;
+          if (size == 0)
+            /* Wraparound.  */
+            goto fail1;
+          if (rof->auxmap != NULL)
+            munmap (rof->auxmap, rof->auxmap_length);
+        }
+      rof->auxmap = (void *) mmap ((void *) 0, size, PROT_READ | PROT_WRITE,
+                                   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+      if (rof->auxmap == (void *) -1)
+        {
+          close (fd);
+          return -1;
+        }
+      rof->auxmap_length = size;
+      rof->auxmap_start = (uintptr_t) rof->auxmap;
+      rof->auxmap_end = rof->auxmap_start + size;
+      rof->buffer = (char *) rof->auxmap;
+     retry:
+      /* Restart.  */
+      if (lseek (fd, 0, SEEK_SET) < 0)
+        {
+          close (fd);
+          fd = open (filename, O_RDONLY);
+          if (fd < 0)
+            goto fail2;
+        }
+    }
+ fail1:
+  close (fd);
+ fail2:
+  if (rof->auxmap != NULL)
+    munmap (rof->auxmap, rof->auxmap_length);
+  return -1;
+}
+
+/* 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)
+    {
+      rof->eof_seen = 1;
+      return -1;
+    }
+  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, uintptr_t *valuep)
+{
+  uintptr_t 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)
+{
+  if (rof->auxmap != NULL)
+    munmap (rof->auxmap, rof->auxmap_length);
+}
+
+#endif
+
+/* ========================== stackvma-vma-iter.c ========================== */
+/* Iterate through the virtual memory areas of the current process,
+   by reading from the /proc file system.  */
+
+/* This code is a simplied copy (no handling of protection flags) of the
+   code in gnulib's lib/vma-iter.c.  */
+
+#if defined __linux__ || defined __ANDROID__ \
+    || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
+    || defined __NetBSD__ \
+    || defined __CYGWIN__
+
+/* Forward declarations.  */
+struct callback_locals;
+static int callback (struct callback_locals *locals, uintptr_t start, uintptr_t end);
+
+# if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __CYGWIN__
+/* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc
+   file system.  */
+
+static int
+vma_iterate_proc (struct callback_locals *locals)
+{
+  struct rofile rof;
+
+  /* Open the current process' maps file.  It describes one VMA per line.  */
+  if (rof_open (&rof, "/proc/self/maps") >= 0)
+    {
+      uintptr_t auxmap_start = rof.auxmap_start;
+      uintptr_t auxmap_end = rof.auxmap_end;
+
+      for (;;)
+        {
+          uintptr_t start, end;
+          int c;
+
+          /* Parse one line.  First start and 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')
+            ;
+
+          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 (locals, start, auxmap_start))
+                  break;
+              if (auxmap_end - 1 < end - 1)
+                if (callback (locals, auxmap_end, end))
+                  break;
+            }
+          else
+            {
+              if (callback (locals, start, end))
+                break;
+            }
+        }
+      rof_close (&rof);
+      return 0;
+    }
+
+  return -1;
+}
+
+# elif defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__
+
+static int
+vma_iterate_proc (struct callback_locals *locals)
+{
+  struct rofile rof;
+
+  /* Open the current process' maps file.  It describes one VMA per line.
+     On FreeBSD:
+       Cf. <https://www.freebsd.org/cgi/cvsweb.cgi/src/sys/fs/procfs/procfs_map.c?annotate=HEAD>
+     On NetBSD, there are two such files:
+       - /proc/curproc/map in near-FreeBSD syntax,
+       - /proc/curproc/maps in Linux syntax.
+       Cf. <http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/miscfs/procfs/procfs_map.c?rev=HEAD> */
+  if (rof_open (&rof, "/proc/curproc/map") >= 0)
+    {
+      uintptr_t auxmap_start = rof.auxmap_start;
+      uintptr_t auxmap_end = rof.auxmap_end;
+
+      for (;;)
+        {
+          uintptr_t start, end;
+          int c;
+
+          /* 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;
+          while (c = rof_getchar (&rof), c != -1 && c != '\n')
+            ;
+
+          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 (locals, start, auxmap_start))
+                  break;
+              if (auxmap_end - 1 < end - 1)
+                if (callback (locals, auxmap_end, end))
+                  break;
+            }
+          else
+            {
+              if (callback (locals, start, end))
+                break;
+            }
+        }
+      rof_close (&rof);
+      return 0;
+    }
+
+  return -1;
+}
+
+# endif
+
+# if (defined __FreeBSD_kernel__ || defined __FreeBSD__) && defined KERN_PROC_VMMAP /* FreeBSD >= 7.1 */
+
+#  include <sys/user.h> /* struct kinfo_vmentry */
+#  include <sys/sysctl.h> /* sysctl */
+
+static int
+vma_iterate_bsd (struct callback_locals *locals)
+{
+  /* Documentation: https://www.freebsd.org/cgi/man.cgi?sysctl(3)  */
+  int info_path[] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid () };
+  size_t len;
+  size_t pagesize;
+  size_t memneed;
+  void *auxmap;
+  unsigned long auxmap_start;
+  unsigned long auxmap_end;
+  char *mem;
+  char *p;
+  char *p_end;
+
+  len = 0;
+  if (sysctl (info_path, 4, NULL, &len, NULL, 0) < 0)
+    return -1;
+  /* Allow for small variations over time.  In a multithreaded program
+     new VMAs can be allocated at any moment.  */
+  len = 2 * len + 200;
+  /* 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.  */
+  pagesize = getpagesize ();
+  memneed = len;
+  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)
+    return -1;
+  auxmap_start = (unsigned long) auxmap;
+  auxmap_end = auxmap_start + memneed;
+  mem = (char *) auxmap;
+  if (sysctl (info_path, 4, mem, &len, NULL, 0) < 0)
+    {
+      munmap (auxmap, memneed);
+      return -1;
+    }
+  p = mem;
+  p_end = mem + len;
+  while (p < p_end)
+    {
+      struct kinfo_vmentry *kve = (struct kinfo_vmentry *) p;
+      unsigned long start = kve->kve_start;
+      unsigned long end = kve->kve_end;
+      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 (locals, start, auxmap_start))
+              break;
+          if (auxmap_end - 1 < end - 1)
+            if (callback (locals, auxmap_end, end))
+              break;
+        }
+      else
+        {
+          if (callback (locals, start, end))
+            break;
+        }
+      p += kve->kve_structsize;
+    }
+  munmap (auxmap, memneed);
+  return 0;
+}
+
+# else
+
+#  define vma_iterate_bsd(locals) (-1)
+
+# endif
+
+
+/* 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:
+     - LOCALS 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.
+   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().
+   Return 0 if all went well, or -1 in case of error.  */
+static int
+vma_iterate (struct callback_locals *locals)
+{
+# if defined __FreeBSD__
+  /* On FreeBSD with procfs (but not GNU/kFreeBSD, which uses linprocfs), the
+     function vma_iterate_proc does not return the virtual memory areas that
+     were created by anonymous mmap.  See
+     <https://svnweb.freebsd.org/base/head/sys/fs/procfs/procfs_map.c?view=markup>
+     So use vma_iterate_proc only as a fallback.  */
+  int retval = vma_iterate_bsd (locals);
+  if (retval == 0)
+      return 0;
+
+  return vma_iterate_proc (locals);
+# else
+  /* On the other platforms, try the /proc approach first, and the sysctl()
+     as a fallback.  */
+  int retval = vma_iterate_proc (locals);
+  if (retval == 0)
+      return 0;
+
+  return vma_iterate_bsd (locals);
+# endif
+}
+
+#endif
+
+/* =========================== stackvma-mincore.c =========================== */
+
+/* mincore() is a system call that allows to inquire the status of a
+   range of pages of virtual memory.  In particular, it allows to inquire
+   whether a page is mapped at all (except on Mac OS X, where mincore
+   returns 0 even for unmapped addresses).
+   As of 2006, mincore() is supported by:        possible bits:
+     - Linux,   since Linux 2.4 and glibc 2.2,   1
+     - Solaris, since Solaris 9,                 1
+     - MacOS X, since MacOS X 10.3 (at least),   1
+     - FreeBSD, since FreeBSD 6.0,               MINCORE_{INCORE,REFERENCED,MODIFIED}
+     - NetBSD,  since NetBSD 3.0 (at least),     1
+     - OpenBSD, since OpenBSD 2.6 (at least),    1
+     - AIX,     since AIX 5.3,                   1
+   As of 2019, also on
+     - Hurd.
+   However, while the API allows to easily determine the bounds of mapped
+   virtual memory, it does not make it easy to find the bounds of _unmapped_
+   virtual memory ranges.  We try to work around this, but it may still be
+   slow.  */
+
+#if defined __linux__ || defined __ANDROID__ \
+    || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
+    || defined __NetBSD__ /* || defined __OpenBSD__ */ \
+    /* || (defined __APPLE__ && defined __MACH__) */ \
+    || defined _AIX || defined __sun
+
+# include <unistd.h> /* getpagesize, mincore */
+# include <sys/types.h>
+# include <sys/mman.h> /* mincore */
+
+/* The AIX declaration of mincore() uses 'caddr_t', whereas the other platforms
+   use 'void *'. */
+# ifdef _AIX
+typedef caddr_t MINCORE_ADDR_T;
+# else
+typedef void* MINCORE_ADDR_T;
+# endif
+
+/* The glibc and musl declaration of mincore() uses 'unsigned char *', whereas
+   the BSD declaration uses 'char *'.  */
+# if __GLIBC__ >= 2 || defined __linux__ || defined __ANDROID__
+typedef unsigned char pageinfo_t;
+# else
+typedef char pageinfo_t;
+# endif
+
+/* Cache for getpagesize().  */
+static uintptr_t pagesize;
+
+/* Initialize pagesize.  */
+static void
+init_pagesize (void)
+{
+  pagesize = getpagesize ();
+}
+
+/* Test whether the page starting at ADDR is among the address range.
+   ADDR must be a multiple of pagesize.  */
+static int
+is_mapped (uintptr_t addr)
+{
+  pageinfo_t vec[1];
+  return mincore ((MINCORE_ADDR_T) addr, pagesize, vec) >= 0;
+}
+
+/* Assuming that the page starting at ADDR is among the address range,
+   return the start of its virtual memory range.
+   ADDR must be a multiple of pagesize.  */
+static uintptr_t
+mapped_range_start (uintptr_t addr)
+{
+  /* Use a moderately sized VEC here, small enough that it fits on the stack
+     (without requiring malloc).  */
+  pageinfo_t vec[1024];
+  uintptr_t stepsize = sizeof (vec);
+
+  for (;;)
+    {
+      uintptr_t max_remaining;
+
+      if (addr == 0)
+        return addr;
+
+      max_remaining = addr / pagesize;
+      if (stepsize > max_remaining)
+        stepsize = max_remaining;
+      if (mincore ((MINCORE_ADDR_T) (addr - stepsize * pagesize),
+                   stepsize * pagesize, vec) < 0)
+        /* Time to search in smaller steps.  */
+        break;
+      /* The entire range exists.  Continue searching in large steps.  */
+      addr -= stepsize * pagesize;
+    }
+  for (;;)
+    {
+      uintptr_t halfstepsize1;
+      uintptr_t halfstepsize2;
+
+      if (stepsize == 1)
+        return addr;
+
+      /* Here we know that less than stepsize pages exist starting at addr.  */
+      halfstepsize1 = (stepsize + 1) / 2;
+      halfstepsize2 = stepsize / 2;
+      /* halfstepsize1 + halfstepsize2 = stepsize.  */
+
+      if (mincore ((MINCORE_ADDR_T) (addr - halfstepsize1 * pagesize),
+                   halfstepsize1 * pagesize, vec) < 0)
+        stepsize = halfstepsize1;
+      else
+        {
+          addr -= halfstepsize1 * pagesize;
+          stepsize = halfstepsize2;
+        }
+    }
+}
+
+/* Assuming that the page starting at ADDR is among the address range,
+   return the end of its virtual memory range + 1.
+   ADDR must be a multiple of pagesize.  */
+static uintptr_t
+mapped_range_end (uintptr_t addr)
+{
+  /* Use a moderately sized VEC here, small enough that it fits on the stack
+     (without requiring malloc).  */
+  pageinfo_t vec[1024];
+  uintptr_t stepsize = sizeof (vec);
+
+  addr += pagesize;
+  for (;;)
+    {
+      uintptr_t max_remaining;
+
+      if (addr == 0) /* wrapped around? */
+        return addr;
+
+      max_remaining = (- addr) / pagesize;
+      if (stepsize > max_remaining)
+        stepsize = max_remaining;
+      if (mincore ((MINCORE_ADDR_T) addr, stepsize * pagesize, vec) < 0)
+        /* Time to search in smaller steps.  */
+        break;
+      /* The entire range exists.  Continue searching in large steps.  */
+      addr += stepsize * pagesize;
+    }
+  for (;;)
+    {
+      uintptr_t halfstepsize1;
+      uintptr_t halfstepsize2;
+
+      if (stepsize == 1)
+        return addr;
+
+      /* Here we know that less than stepsize pages exist starting at addr.  */
+      halfstepsize1 = (stepsize + 1) / 2;
+      halfstepsize2 = stepsize / 2;
+      /* halfstepsize1 + halfstepsize2 = stepsize.  */
+
+      if (mincore ((MINCORE_ADDR_T) addr, halfstepsize1 * pagesize, vec) < 0)
+        stepsize = halfstepsize1;
+      else
+        {
+          addr += halfstepsize1 * pagesize;
+          stepsize = halfstepsize2;
+        }
+    }
+}
+
+/* Determine whether an address range [ADDR1..ADDR2] is completely unmapped.
+   ADDR1 must be <= ADDR2.  */
+static int
+is_unmapped (uintptr_t addr1, uintptr_t addr2)
+{
+  uintptr_t count;
+  uintptr_t stepsize;
+
+  /* Round addr1 down.  */
+  addr1 = (addr1 / pagesize) * pagesize;
+  /* Round addr2 up and turn it into an exclusive bound.  */
+  addr2 = ((addr2 / pagesize) + 1) * pagesize;
+
+  /* This is slow: mincore() does not provide a way to determine the bounds
+     of the gaps directly.  So we have to use mincore() on individual pages
+     over and over again.  Only after we've verified that all pages are
+     unmapped, we know that the range is completely unmapped.
+     If we were to traverse the pages from bottom to top or from top to bottom,
+     it would be slow even in the average case.  To speed up the search, we
+     exploit the fact that mapped memory ranges are larger than one page on
+     average, therefore we have good chances of hitting a mapped area if we
+     traverse only every second, or only fourth page, etc.  This doesn't
+     decrease the worst-case runtime, only the average runtime.  */
+  count = (addr2 - addr1) / pagesize;
+  /* We have to test is_mapped (addr1 + i * pagesize) for 0 <= i < count.  */
+  for (stepsize = 1; stepsize < count; )
+    stepsize = 2 * stepsize;
+  for (;;)
+    {
+      uintptr_t addr_stepsize;
+      uintptr_t i;
+      uintptr_t addr;
+
+      stepsize = stepsize / 2;
+      if (stepsize == 0)
+        break;
+      addr_stepsize = stepsize * pagesize;
+      for (i = stepsize, addr = addr1 + addr_stepsize;
+           i < count;
+           i += 2 * stepsize, addr += 2 * addr_stepsize)
+        /* Here addr = addr1 + i * pagesize.  */
+        if (is_mapped (addr))
+          return 0;
+    }
+  return 1;
+}
+
+# if STACK_DIRECTION < 0
+
+/* Info about the gap between this VMA and the previous one.
+   addr must be < vma->start.  */
+static int
+mincore_is_near_this (uintptr_t addr, struct vma_struct *vma)
+{
+  /*   vma->start - addr <= (vma->start - vma->prev_end) / 2
+     is mathematically equivalent to
+       vma->prev_end <= 2 * addr - vma->start
+     <==> is_unmapped (2 * addr - vma->start, vma->start - 1).
+     But be careful about overflow: if 2 * addr - vma->start is negative,
+     we consider a tiny "guard page" mapping [0, 0] to be present around
+     NULL; it intersects the range (2 * addr - vma->start, vma->start - 1),
+     therefore return false.  */
+  uintptr_t testaddr = addr - (vma->start - addr);
+  if (testaddr > addr) /* overflow? */
+    return 0;
+  /* Here testaddr <= addr < vma->start.  */
+  return is_unmapped (testaddr, vma->start - 1);
+}
+
+# endif
+# if STACK_DIRECTION > 0
+
+/* Info about the gap between this VMA and the next one.
+   addr must be > vma->end - 1.  */
+static int
+mincore_is_near_this (uintptr_t addr, struct vma_struct *vma)
+{
+  /*   addr - vma->end < (vma->next_start - vma->end) / 2
+     is mathematically equivalent to
+       vma->next_start > 2 * addr - vma->end
+     <==> is_unmapped (vma->end, 2 * addr - vma->end).
+     But be careful about overflow: if 2 * addr - vma->end is > ~0UL,
+     we consider a tiny "guard page" mapping [0, 0] to be present around
+     NULL; it intersects the range (vma->end, 2 * addr - vma->end),
+     therefore return false.  */
+  uintptr_t testaddr = addr + (addr - vma->end);
+  if (testaddr < addr) /* overflow? */
+    return 0;
+  /* Here vma->end - 1 < addr <= testaddr.  */
+  return is_unmapped (vma->end, testaddr);
+}
+
+# endif
+
+static int
+mincore_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  if (pagesize == 0)
+    init_pagesize ();
+  address = (address / pagesize) * pagesize;
+  vma->start = mapped_range_start (address);
+  vma->end = mapped_range_end (address);
+  vma->is_near_this = mincore_is_near_this;
+  return 0;
+}
+
+#endif
+
+/* ========================================================================== */
+
+/* ---------------------------- stackvma-linux.c ---------------------------- */
+
+#if defined __linux__ || defined __ANDROID__ /* Linux */
+
+struct callback_locals
+{
+  uintptr_t address;
+  struct vma_struct *vma;
+# if STACK_DIRECTION < 0
+  uintptr_t prev;
+# else
+  int stop_at_next_vma;
+# endif
+  int retval;
+};
+
+static int
+callback (struct callback_locals *locals, uintptr_t start, uintptr_t end)
+{
+# if STACK_DIRECTION < 0
+  if (locals->address >= start && locals->address <= end - 1)
+    {
+      locals->vma->start = start;
+      locals->vma->end = end;
+      locals->vma->prev_end = locals->prev;
+      locals->retval = 0;
+      return 1;
+    }
+  locals->prev = end;
+# else
+  if (locals->stop_at_next_vma)
+    {
+      locals->vma->next_start = start;
+      locals->stop_at_next_vma = 0;
+      return 1;
+    }
+  if (locals->address >= start && locals->address <= end - 1)
+    {
+      locals->vma->start = start;
+      locals->vma->end = end;
+      locals->retval = 0;
+      locals->stop_at_next_vma = 1;
+      return 0;
+    }
+# endif
+  return 0;
+}
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  struct callback_locals locals;
+  locals.address = address;
+  locals.vma = vma;
+# if STACK_DIRECTION < 0
+  locals.prev = 0;
+# else
+  locals.stop_at_next_vma = 0;
+# endif
+  locals.retval = -1;
+
+  vma_iterate (&locals);
+  if (locals.retval == 0)
+    {
+# if !(STACK_DIRECTION < 0)
+      if (locals.stop_at_next_vma)
+        vma->next_start = 0;
+# endif
+      vma->is_near_this = simple_is_near_this;
+      return 0;
+    }
+
+  return mincore_get_vma (address, vma);
+}
+
+/* --------------------------- stackvma-freebsd.c --------------------------- */
+
+#elif defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ /* GNU/kFreeBSD, FreeBSD */
+
+struct callback_locals
+{
+  uintptr_t address;
+  struct vma_struct *vma;
+  /* The stack appears as multiple adjacents segments, therefore we
+     merge adjacent segments.  */
+  uintptr_t curr_start, curr_end;
+# if STACK_DIRECTION < 0
+  uintptr_t prev_end;
+# else
+  int stop_at_next_vma;
+# endif
+  int retval;
+};
+
+static int
+callback (struct callback_locals *locals, uintptr_t start, uintptr_t end)
+{
+  if (start == locals->curr_end)
+    {
+      /* Merge adjacent segments.  */
+      locals->curr_end = end;
+      return 0;
+    }
+# if STACK_DIRECTION < 0
+  if (locals->curr_start < locals->curr_end
+      && locals->address >= locals->curr_start
+      && locals->address <= locals->curr_end - 1)
+    {
+      locals->vma->start = locals->curr_start;
+      locals->vma->end = locals->curr_end;
+      locals->vma->prev_end = locals->prev_end;
+      locals->retval = 0;
+      return 1;
+    }
+  locals->prev_end = locals->curr_end;
+# else
+  if (locals->stop_at_next_vma)
+    {
+      locals->vma->next_start = locals->curr_start;
+      locals->stop_at_next_vma = 0;
+      return 1;
+    }
+  if (locals->curr_start < locals->curr_end
+      && locals->address >= locals->curr_start
+      && locals->address <= locals->curr_end - 1)
+    {
+      locals->vma->start = locals->curr_start;
+      locals->vma->end = locals->curr_end;
+      locals->retval = 0;
+      locals->stop_at_next_vma = 1;
+      return 0;
+    }
+# endif
+  locals->curr_start = start; locals->curr_end = end;
+  return 0;
+}
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  struct callback_locals locals;
+  locals.address = address;
+  locals.vma = vma;
+  locals.curr_start = 0;
+  locals.curr_end = 0;
+# if STACK_DIRECTION < 0
+  locals.prev_end = 0;
+# else
+  locals.stop_at_next_vma = 0;
+# endif
+  locals.retval = -1;
+
+  vma_iterate (&locals);
+  if (locals.retval < 0)
+    {
+      if (locals.curr_start < locals.curr_end
+          && address >= locals.curr_start && address <= locals.curr_end - 1)
+        {
+          vma->start = locals.curr_start;
+          vma->end = locals.curr_end;
+# if STACK_DIRECTION < 0
+          vma->prev_end = locals.prev_end;
+# else
+          vma->next_start = 0;
+# endif
+          locals.retval = 0;
+        }
+    }
+  if (locals.retval == 0)
+    {
+# if !(STACK_DIRECTION < 0)
+      if (locals.stop_at_next_vma)
+        vma->next_start = 0;
+# endif
+      vma->is_near_this = simple_is_near_this;
+      return 0;
+    }
+
+  /* FreeBSD 6.[01] doesn't allow to distinguish unmapped pages from
+     mapped but swapped-out pages.  See whether it's fixed.  */
+  if (!is_mapped (0))
+    /* OK, mincore() appears to work as expected.  */
+    return mincore_get_vma (address, vma);
+  return -1;
+}
+
+/* --------------------------- stackvma-netbsd.c --------------------------- */
+
+#elif defined __NetBSD__ /* NetBSD */
+
+struct callback_locals
+{
+  uintptr_t address;
+  struct vma_struct *vma;
+  /* The stack appears as multiple adjacents segments, therefore we
+     merge adjacent segments.  */
+  uintptr_t curr_start, curr_end;
+# if STACK_DIRECTION < 0
+  uintptr_t prev_end;
+# else
+  int stop_at_next_vma;
+# endif
+  int retval;
+};
+
+static int
+callback (struct callback_locals *locals, uintptr_t start, uintptr_t end)
+{
+  if (start == locals->curr_end)
+    {
+      /* Merge adjacent segments.  */
+      locals->curr_end = end;
+      return 0;
+    }
+# if STACK_DIRECTION < 0
+  if (locals->curr_start < locals->curr_end
+      && locals->address >= locals->curr_start
+      && locals->address <= locals->curr_end - 1)
+    {
+      locals->vma->start = locals->curr_start;
+      locals->vma->end = locals->curr_end;
+      locals->vma->prev_end = locals->prev_end;
+      locals->retval = 0;
+      return 1;
+    }
+  locals->prev_end = locals->curr_end;
+# else
+  if (locals->stop_at_next_vma)
+    {
+      locals->vma->next_start = locals->curr_start;
+      locals->stop_at_next_vma = 0;
+      return 1;
+    }
+  if (locals->curr_start < locals->curr_end
+      && locals->address >= locals->curr_start
+      && locals->address <= locals->curr_end - 1)
+    {
+      locals->vma->start = locals->curr_start;
+      locals->vma->end = locals->curr_end;
+      locals->retval = 0;
+      locals->stop_at_next_vma = 1;
+      return 0;
+    }
+# endif
+  locals->curr_start = start; locals->curr_end = end;
+  return 0;
+}
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  struct callback_locals locals;
+  locals.address = address;
+  locals.vma = vma;
+  locals.curr_start = 0;
+  locals.curr_end = 0;
+# if STACK_DIRECTION < 0
+  locals.prev_end = 0;
+# else
+  locals.stop_at_next_vma = 0;
+# endif
+  locals.retval = -1;
+
+  vma_iterate (&locals);
+  if (locals.retval < 0)
+    {
+      if (locals.curr_start < locals.curr_end
+          && address >= locals.curr_start && address <= locals.curr_end - 1)
+        {
+          vma->start = locals.curr_start;
+          vma->end = locals.curr_end;
+# if STACK_DIRECTION < 0
+          vma->prev_end = locals.prev_end;
+# else
+          vma->next_start = 0;
+# endif
+          locals.retval = 0;
+        }
+    }
+  if (locals.retval == 0)
+    {
+# if !(STACK_DIRECTION < 0)
+      if (locals.stop_at_next_vma)
+        vma->next_start = 0;
+# endif
+      vma->is_near_this = simple_is_near_this;
+      return 0;
+    }
+
+  return mincore_get_vma (address, vma);
+}
+
+/* --------------------------- stackvma-mquery.c --------------------------- */
+
+/* mquery() is a system call that allows to inquire the status of a
+   range of pages of virtual memory.  In particular, it allows to inquire
+   whether a page is mapped at all, and where is the next unmapped page
+   after a given address.
+   As of 2021, mquery() is supported by:
+     - OpenBSD, since OpenBSD 3.4.
+   Note that this file can give different results.  For example, on
+   OpenBSD 4.4 / i386 the stack segment (which starts around 0xcdbfe000)
+   ends at 0xcfbfdfff according to mincore, but at 0xffffffff according to
+   mquery.  */
+
+#elif defined __OpenBSD__ /* OpenBSD */
+
+# include <unistd.h> /* getpagesize, mincore */
+# include <sys/types.h>
+# include <sys/mman.h> /* mincore */
+
+/* Cache for getpagesize().  */
+static uintptr_t pagesize;
+
+/* Initialize pagesize.  */
+static void
+init_pagesize (void)
+{
+  pagesize = getpagesize ();
+}
+
+/* Test whether the page starting at ADDR is among the address range.
+   ADDR must be a multiple of pagesize.  */
+static int
+is_mapped (uintptr_t addr)
+{
+  /* Avoid calling mquery with a NULL first argument, because this argument
+     value has a specific meaning.  We know the NULL page is unmapped.  */
+  if (addr == 0)
+    return 0;
+  return mquery ((void *) addr, pagesize, 0, MAP_FIXED, -1, 0) == (void *) -1;
+}
+
+/* Assuming that the page starting at ADDR is among the address range,
+   return the start of its virtual memory range.
+   ADDR must be a multiple of pagesize.  */
+static uintptr_t
+mapped_range_start (uintptr_t addr)
+{
+  uintptr_t stepsize;
+  uintptr_t known_unmapped_page;
+
+  /* Look at smaller addresses, in larger and larger steps, to minimize the
+     number of mquery() calls.  */
+  stepsize = pagesize;
+  for (;;)
+    {
+      uintptr_t hole;
+
+      if (addr == 0)
+        abort ();
+
+      if (addr <= stepsize)
+        {
+          known_unmapped_page = 0;
+          break;
+        }
+
+      hole = (uintptr_t) mquery ((void *) (addr - stepsize), pagesize,
+                                     0, 0, -1, 0);
+      if (!(hole == (uintptr_t) (void *) -1 || hole >= addr))
+        {
+          /* Some part of [addr - stepsize, addr - 1] is unmapped.  */
+          known_unmapped_page = hole;
+          break;
+        }
+
+      /* The entire range [addr - stepsize, addr - 1] is mapped.  */
+      addr -= stepsize;
+
+      if (2 * stepsize > stepsize && 2 * stepsize < addr)
+        stepsize = 2 * stepsize;
+    }
+
+  /* Now reduce the step size again.
+     We know that the page at known_unmapped_page is unmapped and that
+     0 < addr - known_unmapped_page <= stepsize.  */
+  while (stepsize > pagesize && stepsize / 2 >= addr - known_unmapped_page)
+    stepsize = stepsize / 2;
+  /* Still 0 < addr - known_unmapped_page <= stepsize.  */
+  while (stepsize > pagesize)
+    {
+      uintptr_t hole;
+
+      stepsize = stepsize / 2;
+      hole = (uintptr_t) mquery ((void *) (addr - stepsize), pagesize,
+                                     0, 0, -1, 0);
+      if (!(hole == (uintptr_t) (void *) -1 || hole >= addr))
+        /* Some part of [addr - stepsize, addr - 1] is unmapped.  */
+        known_unmapped_page = hole;
+      else
+        /* The entire range [addr - stepsize, addr - 1] is mapped.  */
+        addr -= stepsize;
+      /* Still 0 < addr - known_unmapped_page <= stepsize.  */
+    }
+
+  return addr;
+}
+
+/* Assuming that the page starting at ADDR is among the address range,
+   return the end of its virtual memory range + 1.
+   ADDR must be a multiple of pagesize.  */
+static uintptr_t
+mapped_range_end (uintptr_t addr)
+{
+  uintptr_t end;
+
+  if (addr == 0)
+    abort ();
+
+  end = (uintptr_t) mquery ((void *) addr, pagesize, 0, 0, -1, 0);
+  if (end == (uintptr_t) (void *) -1)
+    end = 0; /* wrap around */
+  return end;
+}
+
+/* Determine whether an address range [ADDR1..ADDR2] is completely unmapped.
+   ADDR1 must be <= ADDR2.  */
+static int
+is_unmapped (uintptr_t addr1, uintptr_t addr2)
+{
+  /* Round addr1 down.  */
+  addr1 = (addr1 / pagesize) * pagesize;
+  /* Round addr2 up and turn it into an exclusive bound.  */
+  addr2 = ((addr2 / pagesize) + 1) * pagesize;
+
+  /* Avoid calling mquery with a NULL first argument, because this argument
+     value has a specific meaning.  We know the NULL page is unmapped.  */
+  if (addr1 == 0)
+    addr1 = pagesize;
+
+  if (addr1 < addr2)
+    {
+      if (mquery ((void *) addr1, addr2 - addr1, 0, MAP_FIXED, -1, 0)
+          == (void *) -1)
+        /* Not all the interval [addr1 .. addr2 - 1] is unmapped.  */
+        return 0;
+      else
+        /* The interval [addr1 .. addr2 - 1] is unmapped.  */
+        return 1;
+    }
+  return 1;
+}
+
+# if STACK_DIRECTION < 0
+
+/* Info about the gap between this VMA and the previous one.
+   addr must be < vma->start.  */
+static int
+mquery_is_near_this (uintptr_t addr, struct vma_struct *vma)
+{
+  /*   vma->start - addr <= (vma->start - vma->prev_end) / 2
+     is mathematically equivalent to
+       vma->prev_end <= 2 * addr - vma->start
+     <==> is_unmapped (2 * addr - vma->start, vma->start - 1).
+     But be careful about overflow: if 2 * addr - vma->start is negative,
+     we consider a tiny "guard page" mapping [0, 0] to be present around
+     NULL; it intersects the range (2 * addr - vma->start, vma->start - 1),
+     therefore return false.  */
+  uintptr_t testaddr = addr - (vma->start - addr);
+  if (testaddr > addr) /* overflow? */
+    return 0;
+  /* Here testaddr <= addr < vma->start.  */
+  return is_unmapped (testaddr, vma->start - 1);
+}
+
+# endif
+# if STACK_DIRECTION > 0
+
+/* Info about the gap between this VMA and the next one.
+   addr must be > vma->end - 1.  */
+static int
+mquery_is_near_this (uintptr_t addr, struct vma_struct *vma)
+{
+  /*   addr - vma->end < (vma->next_start - vma->end) / 2
+     is mathematically equivalent to
+       vma->next_start > 2 * addr - vma->end
+     <==> is_unmapped (vma->end, 2 * addr - vma->end).
+     But be careful about overflow: if 2 * addr - vma->end is > ~0UL,
+     we consider a tiny "guard page" mapping [0, 0] to be present around
+     NULL; it intersects the range (vma->end, 2 * addr - vma->end),
+     therefore return false.  */
+  uintptr_t testaddr = addr + (addr - vma->end);
+  if (testaddr < addr) /* overflow? */
+    return 0;
+  /* Here vma->end - 1 < addr <= testaddr.  */
+  return is_unmapped (vma->end, testaddr);
+}
+
+# endif
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  if (pagesize == 0)
+    init_pagesize ();
+  address = (address / pagesize) * pagesize;
+  vma->start = mapped_range_start (address);
+  vma->end = mapped_range_end (address);
+  vma->is_near_this = mquery_is_near_this;
+  return 0;
+}
+
+/* ---------------------------- stackvma-mach.c ---------------------------- */
+
+#elif (defined __APPLE__ && defined __MACH__) /* macOS */
+
+#include <libc.h>
+#include <nlist.h>
+#include <mach/mach.h>
+#include <mach/machine/vm_param.h>
+
+int
+sigsegv_get_vma (uintptr_t req_address, struct vma_struct *vma)
+{
+  uintptr_t prev_address = 0, prev_size = 0;
+  uintptr_t join_address = 0, join_size = 0;
+  int more = 1;
+  vm_address_t address;
+  vm_size_t size;
+  task_t task = mach_task_self ();
+
+  for (address = VM_MIN_ADDRESS; more; address += size)
+    {
+      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 __aarch64__ || 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 (!more)
+        {
+          address = join_address + join_size;
+          size = 0;
+        }
+
+      if ((uintptr_t) address == join_address + join_size)
+        join_size += size;
+      else
+        {
+          prev_address = join_address;
+          prev_size = join_size;
+          join_address = (uintptr_t) address;
+          join_size = size;
+        }
+
+      if (object_name != MACH_PORT_NULL)
+        mach_port_deallocate (mach_task_self (), object_name);
+
+# if STACK_DIRECTION < 0
+      if (join_address <= req_address && join_address + join_size > req_address)
+        {
+          vma->start = join_address;
+          vma->end = join_address + join_size;
+          vma->prev_end = prev_address + prev_size;
+          vma->is_near_this = simple_is_near_this;
+          return 0;
+        }
+# else
+      if (prev_address <= req_address && prev_address + prev_size > req_address)
+        {
+          vma->start = prev_address;
+          vma->end = prev_address + prev_size;
+          vma->next_start = join_address;
+          vma->is_near_this = simple_is_near_this;
+          return 0;
+        }
+# endif
+    }
+
+# if STACK_DIRECTION > 0
+  if (join_address <= req_address && join_address + size > req_address)
+    {
+      vma->start = prev_address;
+      vma->end = prev_address + prev_size;
+      vma->next_start = ~0UL;
+      vma->is_near_this = simple_is_near_this;
+      return 0;
+    }
+# endif
+
+  return -1;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#elif defined _AIX /* AIX */
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  return mincore_get_vma (address, vma);
+}
+
+/* --------------------------- stackvma-procfs.h --------------------------- */
+
+#elif defined __sgi || defined __sun /* IRIX, Solaris */
+
+# include <errno.h> /* errno, EINTR */
+# include <fcntl.h> /* open, O_RDONLY */
+# include <stddef.h> /* size_t */
+# include <unistd.h> /* getpagesize, getpid, read, close */
+# include <sys/types.h>
+# include <sys/mman.h> /* mmap, munmap */
+# include <sys/stat.h> /* fstat */
+# include <string.h> /* memcpy */
+
+/* Try to use the newer ("structured") /proc filesystem API, if supported.  */
+# define _STRUCTURED_PROC 1
+# include <sys/procfs.h> /* prmap_t, optionally PIOC* */
+
+# if !defined __sun
+
+/* Cache for getpagesize().  */
+static uintptr_t pagesize;
+
+/* Initialize pagesize.  */
+static void
+init_pagesize (void)
+{
+  pagesize = getpagesize ();
+}
+
+# endif
+
+struct callback_locals
+{
+  uintptr_t address;
+  struct vma_struct *vma;
+# if STACK_DIRECTION < 0
+  uintptr_t prev;
+# else
+  int stop_at_next_vma;
+# endif
+  int retval;
+};
+
+static int
+callback (struct callback_locals *locals, uintptr_t start, uintptr_t end)
+{
+# if STACK_DIRECTION < 0
+  if (locals->address >= start && locals->address <= end - 1)
+    {
+      locals->vma->start = start;
+      locals->vma->end = end;
+      locals->vma->prev_end = locals->prev;
+      locals->retval = 0;
+      return 1;
+    }
+  locals->prev = end;
+# else
+  if (locals->stop_at_next_vma)
+    {
+      locals->vma->next_start = start;
+      locals->stop_at_next_vma = 0;
+      return 1;
+    }
+  if (locals->address >= start && locals->address <= end - 1)
+    {
+      locals->vma->start = start;
+      locals->vma->end = end;
+      locals->retval = 0;
+      locals->stop_at_next_vma = 1;
+      return 0;
+    }
+# endif
+  return 0;
+}
+
+/* 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:
+     - LOCALS 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.
+   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().
+   Return 0 if all went well, or -1 in case of error.  */
+/* This code is a simplied copy (no handling of protection flags) of the
+   code in gnulib's lib/vma-iter.c.  */
+static int
+vma_iterate (struct callback_locals *locals)
+{
+  /* Note: Solaris <sys/procfs.h> 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.  */
+
+  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 /* !HAVE_MAP_ANONYMOUS */
+  int zero_fd;
+#   define map_flags 0
+#  endif
+  void *auxmap;
+  uintptr_t auxmap_start;
+  uintptr_t auxmap_end;
+  prmap_t* maps;
+  prmap_t* mp;
+
+  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)
+    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;
+#  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 = (uintptr_t) auxmap;
+  auxmap_end = auxmap_start + memneed;
+  maps = (prmap_t *) auxmap;
+
+  if (ioctl (fd, PIOCMAP, maps) < 0)
+    goto fail1;
+
+  for (mp = maps;;)
+    {
+      uintptr_t start, end;
+
+      start = (uintptr_t) 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 (callback (locals, start, auxmap_start))
+              break;
+          if (auxmap_end - 1 < end - 1)
+            if (callback (locals, auxmap_end, end))
+              break;
+        }
+      else
+        {
+          if (callback (locals, start, end))
+            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/<pid>/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.  */
+
+  char fnamebuf[6+10+4+1];
+  char *fname;
+  int fd;
+  int nmaps;
+  size_t memneed;
+#  if HAVE_MAP_ANONYMOUS
+#   define zero_fd -1
+#   define map_flags MAP_ANONYMOUS
+#  else /* !HAVE_MAP_ANONYMOUS */
+  int zero_fd;
+#   define map_flags 0
+#  endif
+  void *auxmap;
+  uintptr_t auxmap_start;
+  uintptr_t auxmap_end;
+  prmap_t* maps;
+  prmap_t* maps_end;
+  prmap_t* mp;
+
+  if (pagesize == 0)
+    init_pagesize ();
+
+  /* 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;
+#  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 = (uintptr_t) 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++)
+    {
+      uintptr_t start, end;
+
+      start = (uintptr_t) mp->pr_vaddr;
+      end = start + mp->pr_size;
+      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 (locals, start, auxmap_start))
+              break;
+          if (auxmap_end - 1 < end - 1)
+            if (callback (locals, auxmap_end, end))
+              break;
+        }
+      else
+        {
+          if (callback (locals, start, end))
+            break;
+        }
+    }
+  munmap (auxmap, memneed);
+  close (fd);
+  return 0;
+
+ fail1:
+  munmap (auxmap, memneed);
+ fail2:
+  close (fd);
+  return -1;
+
+# endif
+}
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  struct callback_locals locals;
+  locals.address = address;
+  locals.vma = vma;
+# if STACK_DIRECTION < 0
+  locals.prev = 0;
+# else
+  locals.stop_at_next_vma = 0;
+# endif
+  locals.retval = -1;
+
+  vma_iterate (&locals);
+  if (locals.retval == 0)
+    {
+# if !(STACK_DIRECTION < 0)
+      if (locals.stop_at_next_vma)
+        vma->next_start = 0;
+# endif
+      vma->is_near_this = simple_is_near_this;
+      return 0;
+    }
+
+# if defined __sun
+  return mincore_get_vma (address, vma);
+# else
+  return -1;
+# endif
+}
+
+/* -------------------------------------------------------------------------- */
+
+#elif defined __CYGWIN__ /* Cygwin */
+
+struct callback_locals
+{
+  uintptr_t address;
+  struct vma_struct *vma;
+  /* The stack appears as three adjacents segments, therefore we
+     merge adjacent segments.  */
+  uintptr_t curr_start, curr_end;
+# if STACK_DIRECTION < 0
+  uintptr_t prev_end;
+# else
+  int stop_at_next_vma;
+# endif
+  int retval;
+};
+
+static int
+callback (struct callback_locals *locals, uintptr_t start, uintptr_t end)
+{
+  if (start == locals->curr_end)
+    {
+      /* Merge adjacent segments.  */
+      locals->curr_end = end;
+      return 0;
+    }
+# if STACK_DIRECTION < 0
+  if (locals->curr_start < locals->curr_end
+      && locals->address >= locals->curr_start
+      && locals->address <= locals->curr_end - 1)
+    {
+      locals->vma->start = locals->curr_start;
+      locals->vma->end = locals->curr_end;
+      locals->vma->prev_end = locals->prev_end;
+      locals->retval = 0;
+      return 1;
+    }
+  locals->prev_end = locals->curr_end;
+# else
+  if (locals->stop_at_next_vma)
+    {
+      locals->vma->next_start = locals->curr_start;
+      locals->stop_at_next_vma = 0;
+      return 1;
+    }
+  if (locals->curr_start < locals->curr_end
+      && locals->address >= locals->curr_start
+      && locals->address <= locals->curr_end - 1)
+    {
+      locals->vma->start = locals->curr_start;
+      locals->vma->end = locals->curr_end;
+      locals->retval = 0;
+      locals->stop_at_next_vma = 1;
+      return 0;
+    }
+# endif
+  locals->curr_start = start; locals->curr_end = end;
+  return 0;
+}
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  struct callback_locals locals;
+  locals.address = address;
+  locals.vma = vma;
+  locals.curr_start = 0;
+  locals.curr_end = 0;
+# if STACK_DIRECTION < 0
+  locals.prev_end = 0;
+# else
+  locals.stop_at_next_vma = 0;
+# endif
+  locals.retval = -1;
+
+  vma_iterate (&locals);
+  if (locals.retval < 0)
+    {
+      if (locals.curr_start < locals.curr_end
+          && address >= locals.curr_start && address <= locals.curr_end - 1)
+        {
+          vma->start = locals.curr_start;
+          vma->end = locals.curr_end;
+# if STACK_DIRECTION < 0
+          vma->prev_end = locals.prev_end;
+# else
+          vma->next_start = 0;
+# endif
+          locals.retval = 0;
+        }
+    }
+  if (locals.retval == 0)
+    {
+# if !(STACK_DIRECTION < 0)
+      if (locals.stop_at_next_vma)
+        vma->next_start = 0;
+# endif
+      vma->is_near_this = simple_is_near_this;
+      return 0;
+    }
+
+  return -1;
+}
+
+/* ---------------------------- stackvma-beos.h ---------------------------- */
+
+#elif defined __HAIKU__ /* Haiku */
+
+# include <OS.h> /* get_next_area_info */
+
+struct callback_locals
+{
+  uintptr_t address;
+  struct vma_struct *vma;
+# if STACK_DIRECTION < 0
+  uintptr_t prev;
+# else
+  int stop_at_next_vma;
+# endif
+  int retval;
+};
+
+static int
+callback (struct callback_locals *locals, uintptr_t start, uintptr_t end)
+{
+# if STACK_DIRECTION < 0
+  if (locals->address >= start && locals->address <= end - 1)
+    {
+      locals->vma->start = start;
+      locals->vma->end = end;
+      locals->vma->prev_end = locals->prev;
+      locals->retval = 0;
+      return 1;
+    }
+  locals->prev = end;
+# else
+  if (locals->stop_at_next_vma)
+    {
+      locals->vma->next_start = start;
+      locals->stop_at_next_vma = 0;
+      return 1;
+    }
+  if (locals->address >= start && locals->address <= end - 1)
+    {
+      locals->vma->start = start;
+      locals->vma->end = end;
+      locals->retval = 0;
+      locals->stop_at_next_vma = 1;
+      return 0;
+    }
+# endif
+  return 0;
+}
+
+/* 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:
+     - LOCALS 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.
+   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().
+   Return 0 if all went well, or -1 in case of error.  */
+/* This code is a simplied copy (no handling of protection flags) of the
+   code in gnulib's lib/vma-iter.c.  */
+static int
+vma_iterate (struct callback_locals *locals)
+{
+  area_info info;
+  ssize_t cookie;
+
+  cookie = 0;
+  while (get_next_area_info (0, &cookie, &info) == B_OK)
+    {
+      uintptr_t start, end;
+
+      start = (uintptr_t) info.address;
+      end = start + info.size;
+
+      if (callback (locals, start, end))
+        break;
+    }
+  return 0;
+}
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  struct callback_locals locals;
+  locals.address = address;
+  locals.vma = vma;
+# if STACK_DIRECTION < 0
+  locals.prev = 0;
+# else
+  locals.stop_at_next_vma = 0;
+# endif
+  locals.retval = -1;
+
+  vma_iterate (&locals);
+  if (locals.retval == 0)
+    {
+# if !(STACK_DIRECTION < 0)
+      if (locals.stop_at_next_vma)
+        vma->next_start = 0;
+# endif
+      vma->is_near_this = simple_is_near_this;
+      return 0;
+    }
+  return -1;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#else /* Hurd, Minix, ... */
+
+int
+sigsegv_get_vma (uintptr_t address, struct vma_struct *vma)
+{
+  /* No way.  */
+  return -1;
+}
+
+#endif
diff --git a/lib/stackvma.h b/lib/stackvma.h
new file mode 100644
index 0000000..7296801
--- /dev/null
+++ b/lib/stackvma.h
@@ -0,0 +1,60 @@
+/* Determine the virtual memory area of a given address.
+   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2003-2006  Paolo Bonzini <bonzini@gnu.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 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 <https://www.gnu.org/licenses/>.  */
+
+#ifndef _STACKVMA_H
+#define _STACKVMA_H
+
+#include <stdint.h>
+
+/* Describes a virtual memory area, with some info about the gap between
+   it and the next or previous virtual memory area.  */
+struct vma_struct
+{
+  uintptr_t start;
+  uintptr_t end;
+#if STACK_DIRECTION < 0
+  /* Info about the gap between this VMA and the previous one.
+     addr must be < vma->start.  */
+  int (*is_near_this) (uintptr_t addr, struct vma_struct *vma);
+  /* Private field, not provided by all sigsegv_get_vma implementations.  */
+  uintptr_t prev_end;
+#endif
+#if STACK_DIRECTION > 0
+  /* Info about the gap between this VMA and the next one.
+     addr must be > vma->end - 1.  */
+  int (*is_near_this) (uintptr_t addr, struct vma_struct *vma);
+  /* Private field, not provided by all sigsegv_get_vma implementations.  */
+  uintptr_t next_start;
+#endif
+};
+
+/* Determines the virtual memory area to which a given address belongs,
+   and returns 0.  Returns -1 if it cannot be determined.
+   This function is used to determine the stack extent when a fault occurs.  */
+extern int sigsegv_get_vma (uintptr_t address, struct vma_struct *vma);
+
+/* Defined if sigsegv_get_vma actually works (i.e. does not always fail).  */
+#if defined __linux__ || defined __ANDROID__ \
+    || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
+    || defined __NetBSD__ || defined __OpenBSD__ \
+    || (defined __APPLE__ && defined __MACH__) \
+    || defined _AIX || defined __sgi || defined __sun \
+    || defined __CYGWIN__ || defined __HAIKU__
+# define HAVE_STACKVMA 1
+#endif
+
+#endif /* _STACKVMA_H */
diff --git a/m4/sigaltstack.m4 b/m4/sigaltstack.m4
new file mode 100644
index 0000000..212e9d3
--- /dev/null
+++ b/m4/sigaltstack.m4
@@ -0,0 +1,198 @@
+# sigaltstack.m4 serial 12
+dnl Copyright (C) 2002-2021 Bruno Haible <bruno@clisp.org>
+dnl Copyright (C) 2008 Eric Blake <ebb9@byu.net>
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+AC_DEFUN([SV_SIGALTSTACK],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  AC_CHECK_FUNCS_ONCE([sigaltstack setrlimit])
+
+  if test "$ac_cv_func_sigaltstack" = yes; then
+    AC_CHECK_TYPE([stack_t], ,
+      [AC_DEFINE(stack_t, [struct sigaltstack],
+         [Define to 'struct sigaltstack' if that's the type of the argument to sigaltstack])
+      ],
+      [
+#include <signal.h>
+#if HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+      ])
+  fi
+
+  AC_CACHE_CHECK([for working sigaltstack], [sv_cv_sigaltstack], [
+    if test "$ac_cv_func_sigaltstack" = yes; then
+      case "$host_os" in
+        macos* | darwin[[6-9]]* | darwin[[1-9]][[0-9]]*)
+          # On MacOS X 10.2 or newer, just assume that if it compiles, it will
+          # work. If we were to perform the real test, 1 Crash Report dialog
+          # window would pop up.
+          AC_LINK_IFELSE([
+            AC_LANG_PROGRAM([[#include <signal.h>]],
+              [[int x = SA_ONSTACK; stack_t ss; sigaltstack ((stack_t*)0, &ss);]])],
+            [sv_cv_sigaltstack="guessing yes"],
+            [sv_cv_sigaltstack=no])
+          ;;
+        *)
+          AC_RUN_IFELSE([
+            AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <signal.h>
+#if HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#if HAVE_SETRLIMIT
+# include <sys/types.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+#ifndef SIGSTKSZ
+# define SIGSTKSZ 16384
+#endif
+void stackoverflow_handler (int sig)
+{
+  /* If we get here, the stack overflow was caught.  */
+  exit (0);
+}
+volatile int * recurse_1 (volatile int n, volatile int *p)
+{
+  if (n >= 0)
+    *recurse_1 (n + 1, p) += n;
+  return p;
+}
+int recurse (volatile int n)
+{
+  int sum = 0;
+  return *recurse_1 (n, &sum);
+}
+char mystack[2 * SIGSTKSZ];
+int main ()
+{
+  stack_t altstack;
+  struct sigaction action;
+#if defined HAVE_SETRLIMIT && defined RLIMIT_STACK
+  /* Before starting the endless recursion, try to be friendly to the user's
+     machine.  On some Linux 2.2.x systems, there is no stack limit for user
+     processes at all.  We don't want to kill such systems.  */
+  struct rlimit rl;
+  rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
+  setrlimit (RLIMIT_STACK, &rl);
+#endif
+  /* Install the alternate stack.  Use the midpoint of mystack, to guard
+     against a buggy interpretation of ss_sp on IRIX.  */
+  altstack.ss_sp = mystack + SIGSTKSZ;
+  altstack.ss_size = SIGSTKSZ;
+  altstack.ss_flags = 0; /* no SS_DISABLE */
+  if (sigaltstack (&altstack, NULL) < 0)
+    exit (1);
+  /* Install the SIGSEGV handler.  */
+  sigemptyset (&action.sa_mask);
+  action.sa_handler = &stackoverflow_handler;
+  action.sa_flags = SA_ONSTACK;
+  sigaction (SIGSEGV, &action, (struct sigaction *) NULL);
+  sigaction (SIGBUS, &action, (struct sigaction *) NULL);
+  /* Provoke a stack overflow.  */
+  recurse (0);
+  exit (2);
+}]])],
+            [sv_cv_sigaltstack=yes],
+            [sv_cv_sigaltstack=no],
+            [
+              dnl FIXME: Put in some more known values here.
+              case "$host_os" in
+                *)
+                  AC_LINK_IFELSE([
+                    AC_LANG_PROGRAM([[#include <signal.h>]],
+                      [[int x = SA_ONSTACK; stack_t ss; sigaltstack ((stack_t*)0, &ss);]])],
+                    [sv_cv_sigaltstack="guessing yes"],
+                    [sv_cv_sigaltstack=no])
+                  ;;
+              esac
+            ])
+          ;;
+      esac
+    else
+      sv_cv_sigaltstack=no
+    fi
+  ])
+  if test "$sv_cv_sigaltstack" != no; then
+    AC_DEFINE([HAVE_WORKING_SIGALTSTACK], [1],
+      [Define if you have the sigaltstack() function and it works.])
+
+    dnl The ss_sp field of a stack_t is, according to POSIX, the lowest address
+    dnl of the memory block designated as an alternate stack. But IRIX 5.3
+    dnl interprets it as the highest address!
+    AC_CACHE_CHECK([for correct stack_t interpretation],
+      [sv_cv_sigaltstack_low_base], [
+      AC_RUN_IFELSE([
+        AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <signal.h>
+#if HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifndef SIGSTKSZ
+# define SIGSTKSZ 16384
+#endif
+volatile char *stack_lower_bound;
+volatile char *stack_upper_bound;
+static void check_stack_location (volatile char *addr)
+{
+  if (addr >= stack_lower_bound && addr <= stack_upper_bound)
+    exit (0);
+  else
+    exit (1);
+}
+static void stackoverflow_handler (int sig)
+{
+  char dummy;
+  check_stack_location (&dummy);
+}
+int main ()
+{
+  char mystack[2 * SIGSTKSZ];
+  stack_t altstack;
+  struct sigaction action;
+  /* Install the alternate stack.  */
+  altstack.ss_sp = mystack + SIGSTKSZ;
+  altstack.ss_size = SIGSTKSZ;
+  stack_lower_bound = (char *) altstack.ss_sp;
+  stack_upper_bound = (char *) altstack.ss_sp + altstack.ss_size - 1;
+  altstack.ss_flags = 0; /* no SS_DISABLE */
+  if (sigaltstack (&altstack, NULL) < 0)
+    exit (2);
+  /* Install the SIGSEGV handler.  */
+  sigemptyset (&action.sa_mask);
+  action.sa_handler = &stackoverflow_handler;
+  action.sa_flags = SA_ONSTACK;
+  if (sigaction (SIGSEGV, &action, (struct sigaction *) NULL) < 0)
+    exit(3);
+  /* Provoke a SIGSEGV.  */
+  raise (SIGSEGV);
+  exit (3);
+}]])],
+      [sv_cv_sigaltstack_low_base=yes],
+      [sv_cv_sigaltstack_low_base=no],
+      [
+        dnl FIXME: Put in some more known values here.
+        case "$host_os" in
+          irix5*) sv_cv_sigaltstack_low_base="no" ;;
+          *)      sv_cv_sigaltstack_low_base="guessing yes" ;;
+        esac
+      ])
+    ])
+    if test "$sv_cv_sigaltstack_low_base" = no; then
+      AC_DEFINE([SIGALTSTACK_SS_REVERSED], [1],
+        [Define if sigaltstack() interprets the stack_t.ss_sp field incorrectly,
+         as the highest address of the alternate stack range rather than as the
+         lowest address.])
+    fi
+  fi
+])
diff --git a/m4/stack-direction.m4 b/m4/stack-direction.m4
new file mode 100644
index 0000000..c7a20a2
--- /dev/null
+++ b/m4/stack-direction.m4
@@ -0,0 +1,104 @@
+# stack-direction.m4 serial 6
+dnl Copyright (C) 2002-2021 Bruno Haible <bruno@clisp.org>
+dnl Copyright (C) 2002-2021 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+# Determine the stack direction. Define the C macro STACK_DIRECTION.
+AC_DEFUN([SV_STACK_DIRECTION],
+[
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_CACHE_CHECK([for stack direction], [sv_cv_stack_direction_msg], [
+    case "$host_cpu" in
+      dnl See the #define STACK_GROWS_DOWNWARD in gcc-3.1/gcc/config/*/*.h.
+      a29k | \
+      aarch64* | \
+      alpha* | \
+      arc | \
+      arm* | strongarm* | xscale* | \
+      avr | avr32 | \
+      bfin | \
+      c1 | c2 | c32 | c34 | c38 | \
+      clipper | \
+      cris | \
+      d30v | \
+      elxsi | \
+      fr30 | \
+      h8300 | \
+      i?86 | x86_64 | \
+      i860 | \
+      ia64 | \
+      m32r | \
+      m68* | \
+      m88k | \
+      mcore | \
+      microblaze | \
+      mips* | \
+      mmix | \
+      mn10200 | \
+      mn10300 | \
+      nios2 | \
+      nds32* | \
+      ns32k | \
+      pdp11 | \
+      pj* | \
+      powerpc* | rs6000 | \
+      riscv* | \
+      romp | \
+      s390* | \
+      sh* | \
+      sparc* | \
+      v850 | \
+      vax | \
+      xtensa)
+        sv_cv_stack_direction=-1 ;;
+      c4x | \
+      dsp16xx | \
+      i960 | \
+      hppa* | parisc* | \
+      stormy16 | \
+      we32k)
+        sv_cv_stack_direction=1 ;;
+      *)
+        if test $cross_compiling = no; then
+          cat > conftest.c <<EOF
+#include <stdio.h>
+int
+find_stack_direction (int *addr, int depth)
+{
+  int dir, dummy = 0;
+  if (! addr)
+    addr = &dummy;
+  *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1;
+  dir = depth ? find_stack_direction (addr, depth - 1) : 0;
+  return dir + dummy;
+}
+int
+main (int argc, char *argv[])
+{
+  printf ("%d\n", find_stack_direction (NULL, argc + 20));
+  return 0;
+}
+EOF
+          AC_TRY_EVAL([ac_link])
+          sv_cv_stack_direction=`./conftest`
+        else
+          sv_cv_stack_direction=0
+        fi
+        ;;
+    esac
+    case $sv_cv_stack_direction in
+      1)  sv_cv_stack_direction_msg="grows up";;
+      -1) sv_cv_stack_direction_msg="grows down";;
+      *)  sv_cv_stack_direction_msg="unknown";;
+    esac
+  ])
+  AC_DEFINE_UNQUOTED([STACK_DIRECTION], [$sv_cv_stack_direction],
+    [Define as the direction of stack growth for your system.
+     STACK_DIRECTION > 0 => grows toward higher addresses
+     STACK_DIRECTION < 0 => grows toward lower addresses
+     STACK_DIRECTION = 0 => spaghetti stack.])
+])
diff --git a/modules/sigsegv b/modules/sigsegv
new file mode 100644
index 0000000..2ef8433
--- /dev/null
+++ b/modules/sigsegv
@@ -0,0 +1,101 @@
+Description:
+A simplified variant of GNU libsigsegv.
+It implements the most important features of GNU libsigsegv: catching SIGSEGV
+and catching stack overflow. It does *not* implement the 'sigsegv_dispatcher'
+type (which is not multithread-safe).
+It supports all modern Unix-like platforms: Linux, Hurd, FreeBSD, NetBSD,
+OpenBSD, macOS, AIX, Solaris, Cygwin, Haiku, even IRIX. It does *not* support
+HP-UX, Minix, native Windows; on these platforms the module compiles and
+provides a <sigsegv.h> header file, but it does not define HAVE_SIGSEGV_RECOVERY
+and HAVE_STACK_OVERFLOW_RECOVERY.
+Unlike GNU libsigsegv, which consists of many .h and .c files, this module
+compiles to just two object files, rather than a library.
+
+Files:
+lib/sigsegv.in.h
+lib/sigsegv.c
+lib/stackvma.h
+lib/stackvma.c
+m4/mmap-anon.m4
+m4/sigaltstack.m4
+m4/stack-direction.m4
+m4/libsigsegv.m4
+
+Depends-on:
+havelib
+host-cpu-c-abi
+stdint
+getpagesize
+
+configure.ac:
+AC_ARG_WITH([libsigsegv],
+  [AS_HELP_STRING([--with-libsigsegv],
+     [use the GNU libsigsegv library, when present, instead of the gnulib module 'sigsegv'])])
+SIGSEGV_H=sigsegv.h
+if test "$with_libsigsegv" = yes; then
+  gl_LIBSIGSEGV
+  if test "$gl_cv_lib_sigsegv" = yes; then
+    SIGSEGV_H=
+  fi
+fi
+AC_SUBST([SIGSEGV_H])
+AM_CONDITIONAL([GL_GENERATE_SIGSEGV_H], [test -n "$SIGSEGV_H"])
+if test -n "$SIGSEGV_H"; then
+  dnl Persuade glibc <sys/ucontext.h> to declare macros designating register
+  dnl indices: REG_RSP on x86_64, REG_ESP on i386.
+  dnl Persuade Solaris OpenIndiana <ucontext.h> to include <sys/regset.h>,
+  dnl which declares macros designating register indices, such as ESP on i386.
+  dnl Persuade Solaris OpenIndiana <unistd.h> to declare mincore().
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  case "$host_os" in
+    solaris2.11)
+      AC_DEFINE([SOLARIS11], [1], [Define on Solaris 11 and its derivates.])
+      ;;
+  esac
+
+  gl_FUNC_MMAP_ANON
+
+  dnl Stack direction.
+  SV_STACK_DIRECTION
+
+  dnl Catching stack overflow requires an alternate signal stack.
+  dnl The old "install a guard page" trick would be unreliable, because
+  dnl we don't know where exactly to place the guard page.
+  SV_SIGALTSTACK
+
+  AC_CHECK_FUNCS_ONCE([getrlimit])
+fi
+
+Makefile.am:
+BUILT_SOURCES += $(SIGSEGV_H)
+
+if GL_GENERATE_SIGSEGV_H
+sigsegv.h: sigsegv.in.h $(top_builddir)/config.status
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  cat $(srcdir)/sigsegv.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+else
+sigsegv.h: $(top_builddir)/config.status
+	rm -f $@
+endif
+MOSTLYCLEANFILES += sigsegv.h sigsegv.h-t
+
+if GL_GENERATE_SIGSEGV_H
+lib_SOURCES += sigsegv.c stackvma.c
+endif
+
+Include:
+<sigsegv.h>
+
+Link:
+$(LTLIBSIGSEGV) when linking with libtool, $(LIBSIGSEGV) otherwise
+
+License:
+GPLv2+
+
+Maintainer:
+Bruno Haible
-- 
2.7.4


[-- Attachment #3: 0002-sigsegv-Add-tests.patch --]
[-- Type: text/x-patch, Size: 25387 bytes --]

From e3592af1366003104ee4fc4d800ace2d899f06cd Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Sun, 16 May 2021 18:29:48 +0200
Subject: [PATCH 2/2] sigsegv: Add tests.

* tests/test-sigsegv-catch-segv1.c: New file, from GNU libsigsegv with
modifications.
* tests/test-sigsegv-catch-segv2.c: Likewise.
* tests/test-sigsegv-catch-stackoverflow1.c: Likewise.
* tests/test-sigsegv-catch-stackoverflow2.c: Likewise.
* tests/altstack-util.h: Likewise.
* tests/mmap-anon-util.h: Likewise.
* modules/sigsegv-tests: New file.
---
 ChangeLog                                 |  10 ++
 modules/sigsegv-tests                     |  31 +++++
 tests/altstack-util.h                     |  59 +++++++++
 tests/mmap-anon-util.h                    |  97 ++++++++++++++
 tests/test-sigsegv-catch-segv1.c          | 128 ++++++++++++++++++
 tests/test-sigsegv-catch-segv2.c          | 151 +++++++++++++++++++++
 tests/test-sigsegv-catch-stackoverflow1.c | 148 +++++++++++++++++++++
 tests/test-sigsegv-catch-stackoverflow2.c | 209 ++++++++++++++++++++++++++++++
 8 files changed, 833 insertions(+)
 create mode 100644 modules/sigsegv-tests
 create mode 100644 tests/altstack-util.h
 create mode 100644 tests/mmap-anon-util.h
 create mode 100644 tests/test-sigsegv-catch-segv1.c
 create mode 100644 tests/test-sigsegv-catch-segv2.c
 create mode 100644 tests/test-sigsegv-catch-stackoverflow1.c
 create mode 100644 tests/test-sigsegv-catch-stackoverflow2.c

diff --git a/ChangeLog b/ChangeLog
index c939655..88f7944 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2021-05-16  Bruno Haible  <bruno@clisp.org>
 
+	sigsegv: Add tests.
+	* tests/test-sigsegv-catch-segv1.c: New file, from GNU libsigsegv with
+	modifications.
+	* tests/test-sigsegv-catch-segv2.c: Likewise.
+	* tests/test-sigsegv-catch-stackoverflow1.c: Likewise.
+	* tests/test-sigsegv-catch-stackoverflow2.c: Likewise.
+	* tests/altstack-util.h: Likewise.
+	* tests/mmap-anon-util.h: Likewise.
+	* modules/sigsegv-tests: New file.
+
 	sigsegv: New module.
 	* lib/sigsegv.in.h: New file, from GNU libsigsegv with modifications.
 	* lib/sigsegv.c: Likewise.
diff --git a/modules/sigsegv-tests b/modules/sigsegv-tests
new file mode 100644
index 0000000..ed7ed94
--- /dev/null
+++ b/modules/sigsegv-tests
@@ -0,0 +1,31 @@
+Files:
+tests/test-sigsegv-catch-segv1.c
+tests/test-sigsegv-catch-segv2.c
+tests/test-sigsegv-catch-stackoverflow1.c
+tests/test-sigsegv-catch-stackoverflow2.c
+tests/altstack-util.h
+tests/mmap-anon-util.h
+m4/mmap-anon.m4
+
+Depends-on:
+stdint
+
+configure.ac:
+AC_CHECK_FUNCS_ONCE([setrlimit])
+gl_FUNC_MMAP_ANON
+
+Makefile.am:
+TESTS += \
+  test-sigsegv-catch-segv1 \
+  test-sigsegv-catch-segv2 \
+  test-sigsegv-catch-stackoverflow1 \
+  test-sigsegv-catch-stackoverflow2
+check_PROGRAMS += \
+  test-sigsegv-catch-segv1 \
+  test-sigsegv-catch-segv2 \
+  test-sigsegv-catch-stackoverflow1 \
+  test-sigsegv-catch-stackoverflow2
+test_sigsegv_catch_segv1_LDADD = $(LDADD) $(LIBSIGSEGV)
+test_sigsegv_catch_segv2_LDADD = $(LDADD) $(LIBSIGSEGV)
+test_sigsegv_catch_stackoverflow1_LDADD = $(LDADD) $(LIBSIGSEGV)
+test_sigsegv_catch_stackoverflow2_LDADD = $(LDADD) $(LIBSIGSEGV)
diff --git a/tests/altstack-util.h b/tests/altstack-util.h
new file mode 100644
index 0000000..5130645
--- /dev/null
+++ b/tests/altstack-util.h
@@ -0,0 +1,59 @@
+/* Some auxiliary stuff for defining an alternate stack.
+   Copyright (C) 2010  Eric Blake <eblake@redhat.com>
+   Copyright (C) 2010-2021  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 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 <https://www.gnu.org/licenses/>.  */
+
+#include <stdint.h> /* uintptr_t */
+#include <string.h> /* for memset */
+
+#ifndef SIGSTKSZ
+# define SIGSTKSZ 16384
+#endif
+
+/* glibc says: Users should use SIGSTKSZ as the size of user-supplied
+   buffers.  We want to detect stack overflow of the alternate stack
+   in a nicer manner than just crashing, so we overallocate in
+   comparison to what we hand libsigsegv.  Also, we intentionally hand
+   an unaligned pointer, to ensure the alternate stack still ends up
+   aligned.  */
+#define MYSTACK_CRUMPLE_ZONE 8192
+char mystack_storage[SIGSTKSZ + 2 * MYSTACK_CRUMPLE_ZONE + 31];
+char *mystack; /* SIGSTKSZ bytes in the middle of storage. */
+
+static void
+prepare_alternate_stack (void)
+{
+  memset (mystack_storage, 's', sizeof mystack_storage);
+  mystack = (char *) ((uintptr_t) (mystack_storage + MYSTACK_CRUMPLE_ZONE) | 31);
+}
+
+static void
+check_alternate_stack_no_overflow (void)
+{
+  unsigned int i;
+
+  for (i = MYSTACK_CRUMPLE_ZONE; i > 0; i--)
+    if (*(mystack - i) != 's')
+      {
+        printf ("Alternate stack was exceeded by %u bytes!!\n", i);
+        exit (1);
+      }
+  for (i = MYSTACK_CRUMPLE_ZONE; i > 0; i--)
+    if (*(mystack + SIGSTKSZ - 1 + i) != 's')
+      {
+        printf ("Alternate stack was exceeded by %u bytes!!\n", i);
+        exit (1);
+      }
+}
diff --git a/tests/mmap-anon-util.h b/tests/mmap-anon-util.h
new file mode 100644
index 0000000..6fb82ef
--- /dev/null
+++ b/tests/mmap-anon-util.h
@@ -0,0 +1,97 @@
+/* Some auxiliary stuff for using mmap & friends.
+   Copyright (C) 2002-2021  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 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 <https://www.gnu.org/licenses/>.  */
+
+#if defined _WIN32 && !defined __CYGWIN__
+
+/* ------------------------ Windows ------------------------ */
+
+# define WIN32_LEAN_AND_MEAN /* avoid including junk */
+# include <windows.h>
+# include <winerror.h>
+# define PROT_NONE PAGE_NOACCESS
+# define PROT_READ PAGE_READONLY
+# define PROT_READ_WRITE PAGE_READWRITE
+
+static void *
+mmap_zeromap (void *map_addr_hint, size_t map_len)
+{
+  if (VirtualAlloc ((void *)((uintptr_t) map_addr_hint & -0x10000),
+                    (((uintptr_t) map_addr_hint + map_len - 1) | 0xffff) + 1
+                    - ((uintptr_t) map_addr_hint & -0x10000),
+                    MEM_RESERVE, PAGE_NOACCESS)
+      && VirtualAlloc (map_addr_hint, map_len, MEM_COMMIT, PAGE_READWRITE))
+    return map_addr_hint;
+  else
+    return (void *)(-1);
+}
+
+int
+munmap (void *addr, size_t len)
+{
+  if (VirtualFree (addr, len, MEM_DECOMMIT))
+    return 0;
+  else
+    return -1;
+}
+
+int
+mprotect (void *addr, size_t len, int prot)
+{
+  DWORD oldprot;
+
+  if (VirtualProtect (addr, len, prot, &oldprot))
+    return 0;
+  else
+    return -1;
+}
+
+#else
+
+/* ------------------------ Unix ------------------------ */
+
+# include <sys/types.h>
+# include <sys/mman.h>
+# include <fcntl.h>
+
+# ifndef PROT_NONE
+#  define PROT_NONE 0
+# endif
+# define PROT_READ_WRITE  (PROT_READ|PROT_WRITE)
+
+# if HAVE_MAP_ANONYMOUS
+#  define zero_fd -1
+#  define map_flags MAP_ANONYMOUS | MAP_PRIVATE
+# else
+#  ifndef MAP_FILE
+#   define MAP_FILE 0
+#  endif
+static int zero_fd;
+#  define map_flags MAP_FILE | MAP_PRIVATE
+# endif
+
+static void *
+mmap_zeromap (void *map_addr_hint, size_t map_len)
+{
+# ifdef __hpux
+  /* HP-UX 10 mmap() often fails when given a hint.  So give the OS complete
+     freedom about the address range.  */
+  return (void *) mmap ((void *) 0,    map_len, PROT_READ_WRITE, map_flags, zero_fd, 0);
+# else
+  return (void *) mmap (map_addr_hint, map_len, PROT_READ_WRITE, map_flags, zero_fd, 0);
+# endif
+}
+
+#endif
diff --git a/tests/test-sigsegv-catch-segv1.c b/tests/test-sigsegv-catch-segv1.c
new file mode 100644
index 0000000..62eef69
--- /dev/null
+++ b/tests/test-sigsegv-catch-segv1.c
@@ -0,0 +1,128 @@
+/* Test that the handler is called, with the right fault address.
+   Copyright (C) 2002-2021  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 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigsegv.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+#if HAVE_SIGSEGV_RECOVERY
+
+# include "mmap-anon-util.h"
+# include <stdlib.h>
+
+# if SIGSEGV_FAULT_ADDRESS_ALIGNMENT > 1UL
+#  include <unistd.h>
+#  define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS (getpagesize () - 1)
+# else
+#  define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS 0
+# endif
+
+uintptr_t page;
+
+volatile int handler_called = 0;
+
+int
+handler (void *fault_address, int serious)
+{
+  handler_called++;
+  if (handler_called > 10)
+    abort ();
+  if (fault_address
+      != (void *)((page + 0x678) & ~SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS))
+    abort ();
+  if (mprotect ((void *) page, 0x4000, PROT_READ_WRITE) == 0)
+    return 1;
+  return 0;
+}
+
+void
+crasher (uintptr_t p)
+{
+  *(volatile int *) (p + 0x678) = 42;
+}
+
+int
+main ()
+{
+  int prot_unwritable;
+  void *p;
+
+  /* Preparations.  */
+# if !HAVE_MAP_ANONYMOUS
+  zero_fd = open ("/dev/zero", O_RDONLY, 0644);
+# endif
+
+# if defined __linux__ && defined __sparc__
+  /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as
+     PROT_READ | PROT_WRITE.  */
+  prot_unwritable = PROT_NONE;
+# else
+  prot_unwritable = PROT_READ;
+# endif
+
+  /* Setup some mmaped memory.  */
+  p = mmap_zeromap ((void *) 0x12340000, 0x4000);
+  if (p == (void *)(-1))
+    {
+      fprintf (stderr, "mmap_zeromap failed.\n");
+      exit (2);
+    }
+  page = (uintptr_t) p;
+
+  /* Make it read-only.  */
+  if (mprotect ((void *) page, 0x4000, prot_unwritable) < 0)
+    {
+      fprintf (stderr, "mprotect failed.\n");
+      exit (2);
+    }
+  /* Test whether it's possible to make it read-write after it was read-only.
+     This is not possible on Cygwin.  */
+  if (mprotect ((void *) page, 0x4000, PROT_READ_WRITE) < 0
+      || mprotect ((void *) page, 0x4000, prot_unwritable) < 0)
+    {
+      fprintf (stderr, "mprotect failed.\n");
+      exit (2);
+    }
+
+  /* Install the SIGSEGV handler.  */
+  sigsegv_install_handler (&handler);
+
+  /* The first write access should invoke the handler and then complete.  */
+  crasher (page);
+  /* The second write access should not invoke the handler.  */
+  crasher (page);
+
+  /* Check that the handler was called only once.  */
+  if (handler_called != 1)
+    exit (1);
+  /* Test passed!  */
+  printf ("Test passed.\n");
+  return 0;
+}
+
+#else
+
+int
+main ()
+{
+  return 77;
+}
+
+#endif
diff --git a/tests/test-sigsegv-catch-segv2.c b/tests/test-sigsegv-catch-segv2.c
new file mode 100644
index 0000000..dd28517
--- /dev/null
+++ b/tests/test-sigsegv-catch-segv2.c
@@ -0,0 +1,151 @@
+/* Test that the handler can be exited multiple times.
+   Copyright (C) 2002-2021  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 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigsegv.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+#if HAVE_SIGSEGV_RECOVERY
+
+# if defined _WIN32 && !defined __CYGWIN__
+  /* Windows doesn't have sigset_t.  */
+  typedef int sigset_t;
+#  define sigemptyset(set)
+#  define sigprocmask(how,set,oldset)
+# endif
+
+# include "mmap-anon-util.h"
+# include <stdlib.h> /* for abort, exit */
+# include <signal.h>
+# include <setjmp.h>
+
+# if SIGSEGV_FAULT_ADDRESS_ALIGNMENT > 1UL
+#  include <unistd.h>
+#  define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS (getpagesize () - 1)
+# else
+#  define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS 0
+# endif
+
+jmp_buf mainloop;
+sigset_t mainsigset;
+
+volatile int pass = 0;
+uintptr_t page;
+
+volatile int handler_called = 0;
+
+static void
+handler_continuation (void *arg1, void *arg2, void *arg3)
+{
+  longjmp (mainloop, pass);
+}
+
+int
+handler (void *fault_address, int serious)
+{
+  handler_called++;
+  if (handler_called > 10)
+    abort ();
+  if (fault_address
+      != (void *)((page + 0x678 + 8 * pass) & ~SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS))
+    abort ();
+  pass++;
+  printf ("Fault %d caught.\n", pass);
+  sigprocmask (SIG_SETMASK, &mainsigset, NULL);
+  return sigsegv_leave_handler (handler_continuation, NULL, NULL, NULL);
+}
+
+void
+crasher (uintptr_t p)
+{
+  *(volatile int *) (p + 0x678 + 8 * pass) = 42;
+}
+
+int
+main ()
+{
+  int prot_unwritable;
+  void *p;
+  sigset_t emptyset;
+
+  /* Preparations.  */
+# if !HAVE_MAP_ANONYMOUS
+  zero_fd = open ("/dev/zero", O_RDONLY, 0644);
+# endif
+
+# if defined __linux__ && defined __sparc__
+  /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as
+     PROT_READ | PROT_WRITE.  */
+  prot_unwritable = PROT_NONE;
+# else
+  prot_unwritable = PROT_READ;
+# endif
+
+  /* Setup some mmaped memory.  */
+  p = mmap_zeromap ((void *) 0x12340000, 0x4000);
+  if (p == (void *)(-1))
+    {
+      fprintf (stderr, "mmap_zeromap failed.\n");
+      exit (2);
+    }
+  page = (uintptr_t) p;
+
+  /* Make it read-only.  */
+  if (mprotect ((void *) page, 0x4000, prot_unwritable) < 0)
+    {
+      fprintf (stderr, "mprotect failed.\n");
+      exit (2);
+    }
+
+  /* Install the SIGSEGV handler.  */
+  if (sigsegv_install_handler (&handler) < 0)
+    exit (2);
+
+  /* Save the current signal mask.  */
+  sigemptyset (&emptyset);
+  sigprocmask (SIG_BLOCK, &emptyset, &mainsigset);
+
+  /* Provoke two SIGSEGVs in a row.  */
+  switch (setjmp (mainloop))
+    {
+    case 0: case 1:
+      printf ("Doing SIGSEGV pass %d.\n", pass + 1);
+      crasher (page);
+      printf ("no SIGSEGV?!\n"); exit (1);
+    case 2:
+      break;
+    default:
+      abort ();
+    }
+
+  /* Test passed!  */
+  printf ("Test passed.\n");
+  return 0;
+}
+
+#else
+
+int
+main ()
+{
+  return 77;
+}
+
+#endif
diff --git a/tests/test-sigsegv-catch-stackoverflow1.c b/tests/test-sigsegv-catch-stackoverflow1.c
new file mode 100644
index 0000000..141dfd4
--- /dev/null
+++ b/tests/test-sigsegv-catch-stackoverflow1.c
@@ -0,0 +1,148 @@
+/* Test the stack overflow handler.
+   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2010 Eric Blake <eblake@redhat.com>
+
+   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 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigsegv.h"
+
+#include <stdio.h>
+#include <limits.h>
+
+#if HAVE_STACK_OVERFLOW_RECOVERY
+
+# if defined _WIN32 && !defined __CYGWIN__
+  /* Windows doesn't have sigset_t.  */
+  typedef int sigset_t;
+#  define sigemptyset(set)
+#  define sigprocmask(how,set,oldset)
+# endif
+
+# include <stddef.h> /* needed for NULL on SunOS4 */
+# include <stdlib.h> /* for abort, exit */
+# include <signal.h>
+# include <setjmp.h>
+# if HAVE_SETRLIMIT
+#  include <sys/types.h>
+#  include <sys/time.h>
+#  include <sys/resource.h>
+# endif
+# include "altstack-util.h"
+
+jmp_buf mainloop;
+sigset_t mainsigset;
+
+volatile int pass = 0;
+
+volatile char *stack_lower_bound;
+volatile char *stack_upper_bound;
+
+static void
+stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
+{
+  int arg = (int) (long) arg1;
+  longjmp (mainloop, arg);
+}
+
+void
+stackoverflow_handler (int emergency, stackoverflow_context_t scp)
+{
+  char dummy;
+  volatile char *addr = &dummy;
+  if (!(addr >= stack_lower_bound && addr <= stack_upper_bound))
+    abort ();
+  pass++;
+  printf ("Stack overflow %d caught.\n", pass);
+  sigprocmask (SIG_SETMASK, &mainsigset, NULL);
+  sigsegv_leave_handler (stackoverflow_handler_continuation,
+                         (void *) (long) (emergency ? -1 : pass), NULL, NULL);
+}
+
+volatile int *
+recurse_1 (int n, volatile int *p)
+{
+  if (n < INT_MAX)
+    *recurse_1 (n + 1, p) += n;
+  return p;
+}
+
+int
+recurse (volatile int n)
+{
+  return *recurse_1 (n, &n);
+}
+
+int
+main ()
+{
+  sigset_t emptyset;
+
+# if HAVE_SETRLIMIT && defined RLIMIT_STACK
+  /* Before starting the endless recursion, try to be friendly to the user's
+     machine.  On some Linux 2.2.x systems, there is no stack limit for user
+     processes at all.  We don't want to kill such systems.  */
+  struct rlimit rl;
+  rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
+  setrlimit (RLIMIT_STACK, &rl);
+# endif
+
+  /* Prepare the storage for the alternate stack.  */
+  prepare_alternate_stack ();
+
+  /* Install the stack overflow handler.  */
+  if (stackoverflow_install_handler (&stackoverflow_handler,
+                                     mystack, SIGSTKSZ)
+      < 0)
+    exit (2);
+  stack_lower_bound = mystack;
+  stack_upper_bound = mystack + SIGSTKSZ - 1;
+
+  /* Save the current signal mask.  */
+  sigemptyset (&emptyset);
+  sigprocmask (SIG_BLOCK, &emptyset, &mainsigset);
+
+  /* Provoke two stack overflows in a row.  */
+  switch (setjmp (mainloop))
+    {
+    case -1:
+      printf ("emergency exit\n"); exit (1);
+    case 0: case 1:
+      printf ("Starting recursion pass %d.\n", pass + 1);
+      recurse (0);
+      printf ("no endless recursion?!\n"); exit (1);
+    case 2:
+      break;
+    default:
+      abort ();
+    }
+
+  /* Validate that the alternate stack did not overflow.  */
+  check_alternate_stack_no_overflow ();
+
+  printf ("Test passed.\n");
+  exit (0);
+}
+
+#else
+
+int
+main ()
+{
+  return 77;
+}
+
+#endif
diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c
new file mode 100644
index 0000000..415b8a4
--- /dev/null
+++ b/tests/test-sigsegv-catch-stackoverflow2.c
@@ -0,0 +1,209 @@
+/* Test that stack overflow and SIGSEGV are correctly distinguished.
+   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2010 Eric Blake <eblake@redhat.com>
+
+   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 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigsegv.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <limits.h>
+
+#if HAVE_STACK_OVERFLOW_RECOVERY && HAVE_SIGSEGV_RECOVERY
+
+# if defined _WIN32 && !defined __CYGWIN__
+  /* Windows doesn't have sigset_t.  */
+  typedef int sigset_t;
+#  define sigemptyset(set)
+#  define sigprocmask(how,set,oldset)
+# endif
+
+# include "mmap-anon-util.h"
+# include <stddef.h> /* needed for NULL on SunOS4 */
+# include <stdlib.h> /* for abort, exit */
+# include <signal.h>
+# include <setjmp.h>
+# if HAVE_SETRLIMIT
+#  include <sys/types.h>
+#  include <sys/time.h>
+#  include <sys/resource.h>
+# endif
+# include "altstack-util.h"
+
+jmp_buf mainloop;
+sigset_t mainsigset;
+
+volatile int pass = 0;
+uintptr_t page;
+
+static void
+stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
+{
+  int arg = (int) (long) arg1;
+  longjmp (mainloop, arg);
+}
+
+void
+stackoverflow_handler (int emergency, stackoverflow_context_t scp)
+{
+  pass++;
+  if (pass <= 2)
+    printf ("Stack overflow %d caught.\n", pass);
+  else
+    {
+      printf ("Segmentation violation misdetected as stack overflow.\n");
+      exit (1);
+    }
+  sigprocmask (SIG_SETMASK, &mainsigset, NULL);
+  sigsegv_leave_handler (stackoverflow_handler_continuation,
+                         (void *) (long) (emergency ? -1 : pass), NULL, NULL);
+}
+
+int
+sigsegv_handler (void *address, int emergency)
+{
+  /* This test is necessary to distinguish stack overflow and SIGSEGV.  */
+  if (!emergency)
+    return 0;
+
+  pass++;
+  if (pass <= 2)
+    {
+      printf ("Stack overflow %d missed.\n", pass);
+      exit (1);
+    }
+  else
+    printf ("Segmentation violation correctly detected.\n");
+  sigprocmask (SIG_SETMASK, &mainsigset, NULL);
+  return sigsegv_leave_handler (stackoverflow_handler_continuation,
+                                (void *) (long) pass, NULL, NULL);
+}
+
+volatile int *
+recurse_1 (int n, volatile int *p)
+{  
+  if (n < INT_MAX)
+    *recurse_1 (n + 1, p) += n;
+  return p;
+}
+
+int
+recurse (volatile int n)
+{
+  return *recurse_1 (n, &n);
+}
+
+int
+main ()
+{
+  int prot_unwritable;
+  void *p;
+  sigset_t emptyset;
+
+# if HAVE_SETRLIMIT && defined RLIMIT_STACK
+  /* Before starting the endless recursion, try to be friendly to the user's
+     machine.  On some Linux 2.2.x systems, there is no stack limit for user
+     processes at all.  We don't want to kill such systems.  */
+  struct rlimit rl;
+  rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
+  setrlimit (RLIMIT_STACK, &rl);
+# endif
+
+  /* Prepare the storage for the alternate stack.  */
+  prepare_alternate_stack ();
+
+  /* Install the stack overflow handler.  */
+  if (stackoverflow_install_handler (&stackoverflow_handler,
+                                     mystack, SIGSTKSZ)
+      < 0)
+    exit (2);
+
+  /* Preparations.  */
+# if !HAVE_MAP_ANONYMOUS
+  zero_fd = open ("/dev/zero", O_RDONLY, 0644);
+# endif
+
+# if defined __linux__ && defined __sparc__
+  /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as
+     PROT_READ | PROT_WRITE.  */
+  prot_unwritable = PROT_NONE;
+# else
+  prot_unwritable = PROT_READ;
+# endif
+
+  /* Setup some mmaped memory.  */
+  p = mmap_zeromap ((void *) 0x12340000, 0x4000);
+  if (p == (void *)(-1))
+    {
+      fprintf (stderr, "mmap_zeromap failed.\n");
+      exit (2);
+    }
+  page = (uintptr_t) p;
+
+  /* Make it read-only.  */
+  if (mprotect ((void *) page, 0x4000, prot_unwritable) < 0)
+    {
+      fprintf (stderr, "mprotect failed.\n");
+      exit (2);
+    }
+
+  /* Install the SIGSEGV handler.  */
+  if (sigsegv_install_handler (&sigsegv_handler) < 0)
+    exit (2);
+
+  /* Save the current signal mask.  */
+  sigemptyset (&emptyset);
+  sigprocmask (SIG_BLOCK, &emptyset, &mainsigset);
+
+  /* Provoke two stack overflows in a row.  */
+  switch (setjmp (mainloop))
+    {
+    case -1:
+      printf ("emergency exit\n"); exit (1);
+    case 0: case 1:
+      printf ("Starting recursion pass %d.\n", pass + 1);
+      recurse (0);
+      printf ("no endless recursion?!\n"); exit (1);
+    case 2:
+      *(volatile int *) (page + 0x678) = 42;
+      break;
+    case 3:
+      *(volatile int *) 0 = 42;
+      break;
+    case 4:
+      break;
+    default:
+      abort ();
+    }
+
+  /* Validate that the alternate stack did not overflow.  */
+  check_alternate_stack_no_overflow ();
+
+  printf ("Test passed.\n");
+  exit (0);
+}
+
+#else
+
+int
+main ()
+{
+  return 77;
+}
+
+#endif
-- 
2.7.4


             reply	other threads:[~2021-05-16 17:02 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-16 17:01 Bruno Haible [this message]
2021-06-06 23:27 ` new module 'sigsegv' Dmitry V. Levin
2021-06-07  0:49   ` Bruno Haible
2021-06-07 10:29     ` Dmitry V. Levin
2021-06-08  1:45       ` Jim Meyering
2021-06-08  2:40         ` warnings in unit tests Bruno Haible
2021-06-08  5:55           ` Jim Meyering
2021-06-08  8:56             ` Bruno Haible
2021-06-09  0:41               ` Dmitry V. Levin
2021-06-10 20:05                 ` Bruno Haible
     [not found]             ` <CAH8yC8kHTq5J9onJj+2jwy_DwzXrwujqFs9TEBxGh5k_KCu=kg@mail.gmail.com>
2021-06-08 10:57               ` Bruno Haible
2021-06-08 16:42                 ` Paul Eggert
2021-06-09 13:35                   ` Dmitry V. Levin
2021-06-09 19:38                   ` Bruno Haible
2021-06-10 19:39                   ` Bruno Haible
2021-06-09  7:23                 ` Bernhard Voelker
2021-06-09 14:17                   ` Bruno Haible
2021-06-10  8:13                     ` Simon Josefsson via Gnulib discussion list
2021-06-10 19:51                       ` Bruno Haible
2021-06-10 21:49                         ` Simon Josefsson via Gnulib discussion list
2021-06-11 12:21                         ` Eric Blake
2021-06-11 13:57                           ` Bruno Haible
2021-06-19 12:02 ` new module 'sigsegv' Bruno Haible
2021-06-21 18:22   ` [PATCH] sigsegv, sigsegv-tests: Assign my contributions to the FSF Eric Blake

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://lists.gnu.org/mailman/listinfo/bug-gnulib

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=15897619.AQZsorJ5YP@omega \
    --to=bruno@clisp.org \
    --cc=bug-gnulib@gnu.org \
    --cc=bug-libsigsegv@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).