bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
* new module 'sigsegv'
@ 2021-05-16 17:01 Bruno Haible
  2021-06-06 23:27 ` Dmitry V. Levin
  2021-06-19 12:02 ` new module 'sigsegv' Bruno Haible
  0 siblings, 2 replies; 24+ messages in thread
From: Bruno Haible @ 2021-05-16 17:01 UTC (permalink / raw)
  To: bug-gnulib; +Cc: bug-libsigsegv

[-- 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


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: new module 'sigsegv'
  2021-05-16 17:01 new module 'sigsegv' Bruno Haible
@ 2021-06-06 23:27 ` Dmitry V. Levin
  2021-06-07  0:49   ` Bruno Haible
  2021-06-19 12:02 ` new module 'sigsegv' Bruno Haible
  1 sibling, 1 reply; 24+ messages in thread
From: Dmitry V. Levin @ 2021-06-06 23:27 UTC (permalink / raw)
  To: Bruno Haible; +Cc: bug-gnulib

On Sun, May 16, 2021 at 07:01:45PM +0200, Bruno Haible wrote:
[...]
> To fix this problem, I'm adding a module 'sigsegv'.

I've tried to rebuild the latest GNU grep with the latest gnulib
and got a few build issues:

sigsegv.c: In function 'sigsegv_handler':
sigsegv.c:1059:36: error: declaration of 'sig' shadows a parameter [-Werror=shadow]
 1059 |           SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
      |                                    ^~~
sigsegv.c:738:11: note: in definition of macro 'SIGSEGV_FOR_ALL_SIGNALS'
  738 |     { int var; var = SIGSEGV; { body } }
      |           ^~~
sigsegv.c:64:45: note: shadowed declaration is here
   64 | # define SIGSEGV_FAULT_HANDLER_ARGLIST  int sig, siginfo_t *sip, void *ucp
      |                                         ~~~~^~~
sigsegv.c:915:18: note: in expansion of macro 'SIGSEGV_FAULT_HANDLER_ARGLIST'
  915 | sigsegv_handler (SIGSEGV_FAULT_HANDLER_ARGLIST)
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

test-sigsegv-catch-segv1.c:42:1: error: no previous prototype for 'handler' [-Werror=missing-prototypes]
   42 | handler (void *fault_address, int serious)
      | ^~~~~~~
test-sigsegv-catch-segv1.c:56:1: error: no previous prototype for 'crasher' [-Werror=missing-prototypes]
   56 | crasher (uintptr_t p)
      | ^~~~~~~

test-sigsegv-catch-segv2.c:61:1: error: no previous prototype for 'handler' [-Werror=missing-prototypes]
   61 | handler (void *fault_address, int serious)
      | ^~~~~~~
test-sigsegv-catch-segv2.c:76:1: error: no previous prototype for 'crasher' [-Werror=missing-prototypes]
   76 | crasher (uintptr_t p)
      | ^~~~~~~

test-sigsegv-catch-stackoverflow1.c:62:1: error: no previous prototype for 'stackoverflow_handler' [-Werror=missing-prototypes]
   62 | stackoverflow_handler (int emergency, stackoverflow_context_t scp)
      | ^~~~~~~~~~~~~~~~~~~~~
test-sigsegv-catch-stackoverflow1.c:76:1: error: no previous prototype for 'recurse_1' [-Werror=missing-prototypes]
   76 | recurse_1 (int n, volatile int *p)
      | ^~~~~~~~~
test-sigsegv-catch-stackoverflow1.c:84:1: error: no previous prototype for 'recurse' [-Werror=missing-prototypes]
   84 | recurse (volatile int n)
      | ^~~~~~~

test-sigsegv-catch-stackoverflow2.c:62:1: error: no previous prototype for 'stackoverflow_handler' [-Werror=missing-prototypes]
   62 | stackoverflow_handler (int emergency, stackoverflow_context_t scp)
      | ^~~~~~~~~~~~~~~~~~~~~
test-sigsegv-catch-stackoverflow2.c:78:1: error: no previous prototype for 'sigsegv_handler' [-Werror=missing-prototypes]
   78 | sigsegv_handler (void *address, int emergency)
      | ^~~~~~~~~~~~~~~
test-sigsegv-catch-stackoverflow2.c:98:1: error: no previous prototype for 'recurse_1' [-Werror=missing-prototypes]
   98 | recurse_1 (int n, volatile int *p)
      | ^~~~~~~~~
test-sigsegv-catch-stackoverflow2.c:106:1: error: no previous prototype for 'recurse' [-Werror=missing-prototypes]
  106 | recurse (volatile int n)
      | ^~~~~~~
test-sigsegv-catch-stackoverflow2.c: In function 'main':
test-sigsegv-catch-stackoverflow2.c:186:27: error: null pointer dereference [-Werror=null-dereference]
  186 |       *(volatile int *) 0 = 42;
      |       ~~~~~~~~~~~~~~~~~~~~^~~~

The following gnulib patch makes the compiler happy again:

diff --git a/lib/sigsegv.c b/lib/sigsegv.c
index 312f132b8..b0b406685 100644
--- a/lib/sigsegv.c
+++ b/lib/sigsegv.c
@@ -1056,7 +1056,7 @@ sigsegv_handler (SIGSEGV_FAULT_HANDLER_ARGLIST)
           /* Handler declined responsibility for real.  */
 
           /* Remove ourselves and dump core.  */
-          SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
+          SIGSEGV_FOR_ALL_SIGNALS (signo, signal (signo, SIG_DFL);)
         }
 
 # if HAVE_STACK_OVERFLOW_RECOVERY
diff --git a/tests/test-sigsegv-catch-segv1.c b/tests/test-sigsegv-catch-segv1.c
index 62eef69c8..741a37878 100644
--- a/tests/test-sigsegv-catch-segv1.c
+++ b/tests/test-sigsegv-catch-segv1.c
@@ -38,7 +38,7 @@ uintptr_t page;
 
 volatile int handler_called = 0;
 
-int
+static int
 handler (void *fault_address, int serious)
 {
   handler_called++;
@@ -52,7 +52,7 @@ handler (void *fault_address, int serious)
   return 0;
 }
 
-void
+static void
 crasher (uintptr_t p)
 {
   *(volatile int *) (p + 0x678) = 42;
diff --git a/tests/test-sigsegv-catch-segv2.c b/tests/test-sigsegv-catch-segv2.c
index dd28517f9..f28cb0a8e 100644
--- a/tests/test-sigsegv-catch-segv2.c
+++ b/tests/test-sigsegv-catch-segv2.c
@@ -57,7 +57,7 @@ handler_continuation (void *arg1, void *arg2, void *arg3)
   longjmp (mainloop, pass);
 }
 
-int
+static int
 handler (void *fault_address, int serious)
 {
   handler_called++;
@@ -72,7 +72,7 @@ handler (void *fault_address, int serious)
   return sigsegv_leave_handler (handler_continuation, NULL, NULL, NULL);
 }
 
-void
+static void
 crasher (uintptr_t p)
 {
   *(volatile int *) (p + 0x678 + 8 * pass) = 42;
diff --git a/tests/test-sigsegv-catch-stackoverflow1.c b/tests/test-sigsegv-catch-stackoverflow1.c
index c828ed282..bfa04ae8b 100644
--- a/tests/test-sigsegv-catch-stackoverflow1.c
+++ b/tests/test-sigsegv-catch-stackoverflow1.c
@@ -58,7 +58,7 @@ stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
   longjmp (mainloop, arg);
 }
 
-void
+static void
 stackoverflow_handler (int emergency, stackoverflow_context_t scp)
 {
   char dummy;
@@ -72,7 +72,7 @@ stackoverflow_handler (int emergency, stackoverflow_context_t scp)
                          (void *) (long) (emergency ? -1 : pass), NULL, NULL);
 }
 
-volatile int *
+static volatile int *
 recurse_1 (int n, volatile int *p)
 {
   if (n < INT_MAX)
@@ -80,7 +80,7 @@ recurse_1 (int n, volatile int *p)
   return p;
 }
 
-int
+static int
 recurse (volatile int n)
 {
   return *recurse_1 (n, &n);
diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c
index b94d1310b..f7fd191a6 100644
--- a/tests/test-sigsegv-catch-stackoverflow2.c
+++ b/tests/test-sigsegv-catch-stackoverflow2.c
@@ -58,7 +58,7 @@ stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
   longjmp (mainloop, arg);
 }
 
-void
+static void
 stackoverflow_handler (int emergency, stackoverflow_context_t scp)
 {
   pass++;
@@ -74,7 +74,7 @@ stackoverflow_handler (int emergency, stackoverflow_context_t scp)
                          (void *) (long) (emergency ? -1 : pass), NULL, NULL);
 }
 
-int
+static int
 sigsegv_handler (void *address, int emergency)
 {
   /* This test is necessary to distinguish stack overflow and SIGSEGV.  */
@@ -94,7 +94,7 @@ sigsegv_handler (void *address, int emergency)
                                 (void *) (long) pass, NULL, NULL);
 }
 
-volatile int *
+static volatile int *
 recurse_1 (int n, volatile int *p)
 {
   if (n < INT_MAX)
@@ -102,7 +102,7 @@ recurse_1 (int n, volatile int *p)
   return p;
 }
 
-int
+static int
 recurse (volatile int n)
 {
   return *recurse_1 (n, &n);
@@ -183,6 +183,9 @@ main ()
       *(volatile int *) (page + 0x678) = 42;
       break;
     case 3:
+#if 6 < __GNUC__
+# pragma GCC diagnostic ignored "-Wnull-dereference"
+#endif
       *(volatile int *) 0 = 42;
       break;
     case 4:

-- 
ldv


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: new module 'sigsegv'
  2021-06-06 23:27 ` Dmitry V. Levin
@ 2021-06-07  0:49   ` Bruno Haible
  2021-06-07 10:29     ` Dmitry V. Levin
  0 siblings, 1 reply; 24+ messages in thread
From: Bruno Haible @ 2021-06-07  0:49 UTC (permalink / raw)
  To: Dmitry V. Levin; +Cc: bug-gnulib

Hi Dmitry,

> I've tried to rebuild the latest GNU grep with the latest gnulib
> and got a few build issues:
> ...
> The following gnulib patch makes the compiler happy again:
> 
> diff --git a/lib/sigsegv.c b/lib/sigsegv.c
> index 312f132b8..b0b406685 100644
> --- a/lib/sigsegv.c
> +++ b/lib/sigsegv.c
> @@ -1056,7 +1056,7 @@ sigsegv_handler (SIGSEGV_FAULT_HANDLER_ARGLIST)
>            /* Handler declined responsibility for real.  */
>  
>            /* Remove ourselves and dump core.  */
> -          SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
> +          SIGSEGV_FOR_ALL_SIGNALS (signo, signal (signo, SIG_DFL);)
>          }
>  
>  # if HAVE_STACK_OVERFLOW_RECOVERY

Thanks for the report. This patch is good, but needs to be applied also to
the other sigsegv_handler, and also to libsigsegv (to be kept in sync when
possible). I'm doing that now.

>  
> -volatile int *
> +static volatile int *
>  recurse_1 (int n, volatile int *p)
>  {
>    if (n < INT_MAX)
> @@ -80,7 +80,7 @@ recurse_1 (int n, volatile int *p)
>    return p;
>  }
>  
> -int
> +static int
>  recurse (volatile int n)
>  {
>    return *recurse_1 (n, &n);

This part should better not be applied. It may enable compiler optimizations
(now or in the future) that, in the end, turn an endless recursion into an
endless loop.

> @@ -183,6 +183,9 @@ main ()
>        *(volatile int *) (page + 0x678) = 42;
>        break;
>      case 3:
> +#if 6 < __GNUC__
> +# pragma GCC diagnostic ignored "-Wnull-dereference"
> +#endif
>        *(volatile int *) 0 = 42;
>        break;
>      case 4:

We shouldn't spend time eliminating warnings from test code.

The goal is to have a good coverage of the lib/* code with unit tests.
That means, we need to
  - make it easy to write unit tests,
  - not make it time-consuming to maintain them.

Eliminating warnings from lib/* code is useful, to avoid bugs in the
programs. But eliminating warnings from tests/* code goes against the
goal of increasing test coverage.

I think the right fix would be that gnulib-tool's --import/--update
option, when creating a tests directory, adds a $(CFLAG_ALLOW_WARNING)
to tests/Makefile.am, where CFLAG_ALLOW_WARNING is defined as
  -Wno-error  when the compiler is GCC or clang,
  empty       otherwise
Will that work in GNU grep?

Bruno



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: new module 'sigsegv'
  2021-06-07  0:49   ` Bruno Haible
@ 2021-06-07 10:29     ` Dmitry V. Levin
  2021-06-08  1:45       ` Jim Meyering
  0 siblings, 1 reply; 24+ messages in thread
From: Dmitry V. Levin @ 2021-06-07 10:29 UTC (permalink / raw)
  To: Bruno Haible, Jim Meyering; +Cc: bug-gnulib

Hi,

On Mon, Jun 07, 2021 at 02:49:35AM +0200, Bruno Haible wrote:
[...]
> > -volatile int *
> > +static volatile int *
> >  recurse_1 (int n, volatile int *p)
> >  {
> >    if (n < INT_MAX)
> > @@ -80,7 +80,7 @@ recurse_1 (int n, volatile int *p)
> >    return p;
> >  }
> >  
> > -int
> > +static int
> >  recurse (volatile int n)
> >  {
> >    return *recurse_1 (n, &n);
> 
> This part should better not be applied. It may enable compiler optimizations
> (now or in the future) that, in the end, turn an endless recursion into an
> endless loop.

I'm not sure there is any difference left in the modern world of
lto-enabled compilers.  Anyway, in most cases warnings issued by
-Wmissing-prototypes are not related to functions with volatile return
types, so making them static shouldn't affect the result.

> > @@ -183,6 +183,9 @@ main ()
> >        *(volatile int *) (page + 0x678) = 42;
> >        break;
> >      case 3:
> > +#if 6 < __GNUC__
> > +# pragma GCC diagnostic ignored "-Wnull-dereference"
> > +#endif
> >        *(volatile int *) 0 = 42;
> >        break;
> >      case 4:
> 
> We shouldn't spend time eliminating warnings from test code.
> 
> The goal is to have a good coverage of the lib/* code with unit tests.
> That means, we need to
>   - make it easy to write unit tests,
>   - not make it time-consuming to maintain them.
> 
> Eliminating warnings from lib/* code is useful, to avoid bugs in the
> programs. But eliminating warnings from tests/* code goes against the
> goal of increasing test coverage.
> 
> I think the right fix would be that gnulib-tool's --import/--update
> option, when creating a tests directory, adds a $(CFLAG_ALLOW_WARNING)
> to tests/Makefile.am, where CFLAG_ALLOW_WARNING is defined as
>   -Wno-error  when the compiler is GCC or clang,
>   empty       otherwise
> Will that work in GNU grep?

GNU grep explicitly disables some warnings and enables -Werror for gnulib
tests [1][2], so I'd like to ask Jim what's the preferred way of handling
this in GNU grep.

[1] https://git.savannah.gnu.org/gitweb/?p=grep.git;a=commitdiff;h=v3.6-2-g623008c
[2] https://git.savannah.gnu.org/gitweb/?p=grep.git;a=commitdiff;h=v3.6-17-g74cda43

-- 
ldv


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: new module 'sigsegv'
  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
  0 siblings, 1 reply; 24+ messages in thread
From: Jim Meyering @ 2021-06-08  1:45 UTC (permalink / raw)
  To: Dmitry V. Levin; +Cc: bug-gnulib@gnu.org List, Bruno Haible

On Mon, Jun 7, 2021 at 3:29 AM Dmitry V. Levin <ldv@altlinux.org> wrote:
> Hi,
> On Mon, Jun 07, 2021 at 02:49:35AM +0200, Bruno Haible wrote:
> [...]
[...]
> > > @@ -183,6 +183,9 @@ main ()
> > >        *(volatile int *) (page + 0x678) = 42;
> > >        break;
> > >      case 3:
> > > +#if 6 < __GNUC__
> > > +# pragma GCC diagnostic ignored "-Wnull-dereference"
> > > +#endif
> > >        *(volatile int *) 0 = 42;
> > >        break;
> > >      case 4:
> >
> > We shouldn't spend time eliminating warnings from test code.
> >
> > The goal is to have a good coverage of the lib/* code with unit tests.
> > That means, we need to
> >   - make it easy to write unit tests,
> >   - not make it time-consuming to maintain them.
> >
> > Eliminating warnings from lib/* code is useful, to avoid bugs in the
> > programs. But eliminating warnings from tests/* code goes against the
> > goal of increasing test coverage.

Hi Bruno,
Isn't this code exceptional enough to merit three lines of warning suppression?
I would like to continue to use -Werror with most warning options even
in test code,
as long as the cost is low. This feels like a very low one-time cost.

> > I think the right fix would be that gnulib-tool's --import/--update
> > option, when creating a tests directory, adds a $(CFLAG_ALLOW_WARNING)
> > to tests/Makefile.am, where CFLAG_ALLOW_WARNING is defined as
> >   -Wno-error  when the compiler is GCC or clang,
> >   empty       otherwise
> > Will that work in GNU grep?

I would prefer not to exempt all of grep/gnulib's test code from
-Werror -Wnull-dereference just to accommodate this one unusual case.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* warnings in unit tests
  2021-06-08  1:45       ` Jim Meyering
@ 2021-06-08  2:40         ` Bruno Haible
  2021-06-08  5:55           ` Jim Meyering
  0 siblings, 1 reply; 24+ messages in thread
From: Bruno Haible @ 2021-06-08  2:40 UTC (permalink / raw)
  To: Jim Meyering; +Cc: bug-gnulib

Hi Jim,

> > > > @@ -183,6 +183,9 @@ main ()
> > > >        *(volatile int *) (page + 0x678) = 42;
> > > >        break;
> > > >      case 3:
> > > > +#if 6 < __GNUC__
> > > > +# pragma GCC diagnostic ignored "-Wnull-dereference"
> > > > +#endif
> > > >        *(volatile int *) 0 = 42;
> > > >        break;
> > > >      case 4:
> > >
> > > We shouldn't spend time eliminating warnings from test code.
> > >
> > > The goal is to have a good coverage of the lib/* code with unit tests.
> > > That means, we need to
> > >   - make it easy to write unit tests,
> > >   - not make it time-consuming to maintain them.
> > >
> > > Eliminating warnings from lib/* code is useful, to avoid bugs in the
> > > programs. But eliminating warnings from tests/* code goes against the
> > > goal of increasing test coverage.
> 
> Hi Bruno,
> Isn't this code exceptional enough to merit three lines of warning suppression?

No. Good unit tests do exceptional things, like calling close(-1), passing
NULL pointers to various functions, and other things that e.g. Coverity
warns about.

> I would like to continue to use -Werror with most warning options even
> in test code,
> as long as the cost is low. This feels like a very low one-time cost.

No, the cost is not low. Dmitry's patch also changed the linkage of 11
functions. He did so to silence warnings (-Wmissing-prototypes) which are
  1) not part of '-Wall',
  2) just pointless for tests [my opinion as maintainer of most of these
     tests].

I don't want the maintainers of packages that use Gnulib to push costs
onto Gnulib, when it is just for their personal preference.

The tests in Gnulib are there to evaluate the reliability of Gnulib's
modules on a particular platform. The more test coverage we have, the
better.

Warnings in the unit tests are not problems. Test *failures* are problems.

gnulib-tool has options --with-tests and --tests-base, that allow a
package to ship the Gnulib tests and thus help Gnulib (by having more
people run the unit tests and report possible test failures).

If a package maintainer insists on having warning-free builds, I kindly
ask them to not bundle the Gnulib tests any more — because requiring
warning-free tests from Gnulib is more of an impediment than of a help.

For those package maintainers who are OK to accept warnings in the
Gnulib tests directory, on the other hand, I repeat my offer to
automatically add -Wno-error in the tests/Makefile.am of that directory.

Bruno



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
       [not found]             ` <CAH8yC8kHTq5J9onJj+2jwy_DwzXrwujqFs9TEBxGh5k_KCu=kg@mail.gmail.com>
  0 siblings, 2 replies; 24+ messages in thread
From: Jim Meyering @ 2021-06-08  5:55 UTC (permalink / raw)
  To: Bruno Haible; +Cc: bug-gnulib@gnu.org List

On Mon, Jun 7, 2021 at 7:41 PM Bruno Haible <bruno@clisp.org> wrote:
> Hi Jim,
>
> > > > > @@ -183,6 +183,9 @@ main ()
> > > > >        *(volatile int *) (page + 0x678) = 42;
> > > > >        break;
> > > > >      case 3:
> > > > > +#if 6 < __GNUC__
> > > > > +# pragma GCC diagnostic ignored "-Wnull-dereference"
> > > > > +#endif
> > > > >        *(volatile int *) 0 = 42;
> > > > >        break;
> > > > >      case 4:
> > > >
> > > > We shouldn't spend time eliminating warnings from test code.
> > > >
> > > > The goal is to have a good coverage of the lib/* code with unit tests.
> > > > That means, we need to
> > > >   - make it easy to write unit tests,
> > > >   - not make it time-consuming to maintain them.
> > > >
> > > > Eliminating warnings from lib/* code is useful, to avoid bugs in the
> > > > programs. But eliminating warnings from tests/* code goes against the
> > > > goal of increasing test coverage.
> >
> > Hi Bruno,
> > Isn't this code exceptional enough to merit three lines of warning suppression?
>
> No. Good unit tests do exceptional things, like calling close(-1), passing
> NULL pointers to various functions, and other things that e.g. Coverity
> warns about.
>
> > I would like to continue to use -Werror with most warning options even
> > in test code,
> > as long as the cost is low. This feels like a very low one-time cost.
>
> No, the cost is not low. Dmitry's patch also changed the linkage of 11
> functions. He did so to silence warnings (-Wmissing-prototypes) which are
>   1) not part of '-Wall',
>   2) just pointless for tests [my opinion as maintainer of most of these
>      tests].
>
> I don't want the maintainers of packages that use Gnulib to push costs
> onto Gnulib, when it is just for their personal preference.
>
> The tests in Gnulib are there to evaluate the reliability of Gnulib's
> modules on a particular platform. The more test coverage we have, the
> better.
>
> Warnings in the unit tests are not problems. Test *failures* are problems.
>
> gnulib-tool has options --with-tests and --tests-base, that allow a
> package to ship the Gnulib tests and thus help Gnulib (by having more
> people run the unit tests and report possible test failures).
>
> If a package maintainer insists on having warning-free builds, I kindly
> ask them to not bundle the Gnulib tests any more — because requiring
> warning-free tests from Gnulib is more of an impediment than of a help.
>
> For those package maintainers who are OK to accept warnings in the
> Gnulib tests directory, on the other hand, I repeat my offer to
> automatically add -Wno-error in the tests/Makefile.am of that directory.

I can live without -Wmissing-prototypes in gnulib tests, but I still
remember times where using that option exposed a real bug.

My point about the cost/benefit was regarding that 3-line addition for
a single, deliberate NULL-deref.
That one really does not deserve to quash -Wnull-dereference for all tests.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  2021-06-08  5:55           ` Jim Meyering
@ 2021-06-08  8:56             ` Bruno Haible
  2021-06-09  0:41               ` Dmitry V. Levin
       [not found]             ` <CAH8yC8kHTq5J9onJj+2jwy_DwzXrwujqFs9TEBxGh5k_KCu=kg@mail.gmail.com>
  1 sibling, 1 reply; 24+ messages in thread
From: Bruno Haible @ 2021-06-08  8:56 UTC (permalink / raw)
  To: Jim Meyering; +Cc: bug-gnulib

Jim Meyering wrote:
> I can live without -Wmissing-prototypes in gnulib tests, but I still
> remember times where using that option exposed a real bug.

-Wmissing-prototypes typically exposes real bugs when a program is composed
of several compilation units. Unit tests are typically a single compilation
unit plus libtests.a, and libtests.a being built from modules with .h / .c
combinations it does not have the kind of bug that -Wmissing-prototypes can
detect.

Anyway, the main point is that I, as the author of 75% of the Gnulib tests
and maintainer of the Gnulib tests, trust a certain set of GCC warnings
(namely, '-Wall -ftrapv'), as they have proven useful for these tests. If
a maintainer of a different package trusts a different set of GCC warnings
or clang warnings or the warnings of some other tool, they are welcome to
report bugs that they found this way. But they are not welcome to force
their preferred set of warning options onto the Gnulib tests, because that
means additional maintenance costs, which goes against the goal of having
a high test coverage.

> My point about the cost/benefit was regarding that 3-line addition for
> a single, deliberate NULL-deref.
> That one really does not deserve to quash -Wnull-dereference for all tests.

Compilers are getting more and more knowledge about POSIX functions.
Over time, they will warn about more and more of the corner cases that
the Gnulib test suite exercises. So, it's not only about the deliberate
pointer access here.

Jeffrey Walton is doing sanitizer-enabled testing of Gnulib. This has
proven to be more useful than listening to large amounts of GCC or clang
warning options.

Bruno



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
       [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  7:23                 ` Bernhard Voelker
  0 siblings, 2 replies; 24+ messages in thread
From: Bruno Haible @ 2021-06-08 10:57 UTC (permalink / raw)
  To: noloader, bug-gnulib

Jeffrey Walton wrote:
> Apple's port of Clang enables missing prototypes by default. You will
> get the warnings whether you use -Wmissing-prototypes or not. You now
> have to actively disable the warning with -Wno-missing-prototypes.

Thanks for the heads-up. From my perspective, we can treat Apple cc
like MSVC, xlc, Sun cc, clang on Windows, and the other vendor compilers.
Namely, quickly glance over the build log to see whether there are
warnings that look relevant and dangerous, and other than that ignore
the warnings.

This holds for both lib/ and tests/.

Reminder: We never promised a warning-free build, neither of lib/ nor
tests/.

The difference between lib/ and tests/ is that code in lib/ goes into
the binaries delivered by the packages, and therefore if a package
maintainer makes an effort to silence a warning, we will consider
their patch. Whereas for tests, as I said, it is too much
of an effort/cost to do so.

Bruno



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  2021-06-08 10:57               ` Bruno Haible
@ 2021-06-08 16:42                 ` Paul Eggert
  2021-06-09 13:35                   ` Dmitry V. Levin
                                     ` (2 more replies)
  2021-06-09  7:23                 ` Bernhard Voelker
  1 sibling, 3 replies; 24+ messages in thread
From: Paul Eggert @ 2021-06-08 16:42 UTC (permalink / raw)
  To: Bruno Haible, noloader, bug-gnulib

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

For what it's worth I'm more with Bruno on this. For the tests, the cost 
of these warnings outweighs the benefit.

It'd be OK with me to disable the troublesome warnings globally for the 
tests subdirectory, using -Wno-missing-prototypes or whatever.

For the -Wnull-dereference issue it may be worthwhile to use a 
circumlocution that fools GCC into not issuing the warning. After all, a 
compiler smart enough to warn about '*(volatile int *) 0 = 42' might 
also be smart enough to see that it's undefined behavior and therefore 
omit the assignment's effect entirely. Perhaps something like the 
attached (untested) patch?

[-- Attachment #2: gnulib.diff --]
[-- Type: text/x-patch, Size: 675 bytes --]

diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c
index b94d1310b..bfd4617a3 100644
--- a/tests/test-sigsegv-catch-stackoverflow2.c
+++ b/tests/test-sigsegv-catch-stackoverflow2.c
@@ -50,6 +50,7 @@ sigset_t mainsigset;
 
 volatile int pass = 0;
 uintptr_t page;
+int volatile *null_pointer_to_volatile;
 
 static void
 stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
@@ -183,7 +184,7 @@ main ()
       *(volatile int *) (page + 0x678) = 42;
       break;
     case 3:
-      *(volatile int *) 0 = 42;
+      *null_pointer_to_volatile = 42;
       break;
     case 4:
       break;

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  2021-06-08  8:56             ` Bruno Haible
@ 2021-06-09  0:41               ` Dmitry V. Levin
  2021-06-10 20:05                 ` Bruno Haible
  0 siblings, 1 reply; 24+ messages in thread
From: Dmitry V. Levin @ 2021-06-09  0:41 UTC (permalink / raw)
  To: Bruno Haible; +Cc: bug-gnulib, Jim Meyering

On Tue, Jun 08, 2021 at 10:56:33AM +0200, Bruno Haible wrote:
> Jim Meyering wrote:
> > I can live without -Wmissing-prototypes in gnulib tests, but I still
> > remember times where using that option exposed a real bug.
> 
> -Wmissing-prototypes typically exposes real bugs when a program is composed
> of several compilation units. Unit tests are typically a single compilation
> unit plus libtests.a, and libtests.a being built from modules with .h / .c
> combinations it does not have the kind of bug that -Wmissing-prototypes can
> detect.

Unlike many other gcc warnings, -Wmissing-prototypes is especially useful
because it doesn't report false positives, so I don't see why one may want
to turn -Wmissing-prototypes off.

In case of recurse_1(), the function isn't declared static for a specific
reason that isn't obvious for casual readers.  In such cases it's usually
a good idea to add a comment explaining why this case is different from
the common pattern.


-- 
ldv


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  2021-06-08 10:57               ` Bruno Haible
  2021-06-08 16:42                 ` Paul Eggert
@ 2021-06-09  7:23                 ` Bernhard Voelker
  2021-06-09 14:17                   ` Bruno Haible
  1 sibling, 1 reply; 24+ messages in thread
From: Bernhard Voelker @ 2021-06-09  7:23 UTC (permalink / raw)
  To: Bruno Haible, noloader, bug-gnulib

On 6/8/21 12:57 PM, Bruno Haible wrote:
> The difference between lib/ and tests/ is that code in lib/ goes into
> the binaries delivered by the packages, and therefore if a package
> maintainer makes an effort to silence a warning, we will consider
> their patch. Whereas for tests, as I said, it is too much
> of an effort/cost to do so.

One little aspect of the tests code is that people might look (also) there to
learn how to use a certain gnulib module, and then copy/paste the code from
there into their projects.
That's at least true for the good test cases, surely not for those provoking errors.
This means it may be worthwhile to have at least the good test cases in a warning-
free shape (which I think it most often already is).

Have a nice day,
Berny


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  2 siblings, 0 replies; 24+ messages in thread
From: Dmitry V. Levin @ 2021-06-09 13:35 UTC (permalink / raw)
  To: Paul Eggert; +Cc: noloader, bug-gnulib, Bruno Haible

On Tue, Jun 08, 2021 at 09:42:55AM -0700, Paul Eggert wrote:
> For what it's worth I'm more with Bruno on this. For the tests, the cost 
> of these warnings outweighs the benefit.
> 
> It'd be OK with me to disable the troublesome warnings globally for the 
> tests subdirectory, using -Wno-missing-prototypes or whatever.
> 
> For the -Wnull-dereference issue it may be worthwhile to use a 
> circumlocution that fools GCC into not issuing the warning. After all, a 
> compiler smart enough to warn about '*(volatile int *) 0 = 42' might 
> also be smart enough to see that it's undefined behavior and therefore 
> omit the assignment's effect entirely. Perhaps something like the 
> attached (untested) patch?

> diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c
> index b94d1310b..bfd4617a3 100644
> --- a/tests/test-sigsegv-catch-stackoverflow2.c
> +++ b/tests/test-sigsegv-catch-stackoverflow2.c
> @@ -50,6 +50,7 @@ sigset_t mainsigset;
>  
>  volatile int pass = 0;
>  uintptr_t page;
> +int volatile *null_pointer_to_volatile;
>  
>  static void
>  stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
> @@ -183,7 +184,7 @@ main ()
>        *(volatile int *) (page + 0x678) = 42;
>        break;
>      case 3:
> -      *(volatile int *) 0 = 42;
> +      *null_pointer_to_volatile = 42;
>        break;
>      case 4:
>        break;

Thanks, this patch works, I'd definitely prefer applying it rather than
disabling -Wnull-dereference.


-- 
ldv


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  0 siblings, 1 reply; 24+ messages in thread
From: Bruno Haible @ 2021-06-09 14:17 UTC (permalink / raw)
  To: Bernhard Voelker; +Cc: noloader, bug-gnulib

Bernhard Voelker wrote:
> One little aspect of the tests code is that people might look (also) there to
> learn how to use a certain gnulib module, and then copy/paste the code from
> there into their projects.

Yes, a secondary value of the unit tests is to show how the APIs can be
correctly used. (For example, the unit tests of the 'list' and 'hamt' modules.)

> That's at least true for the good test cases, surely not for those provoking errors.
> This means it may be worthwhile to have at least the good test cases in a warning-
> free shape (which I think it most often already is).

Yes, the parts of the unit tests that exercise the normal use of an API should
normally compile without warnings with '-Wall'.

The parts that exercise corner cases (NULL pointer accesses, invalid arguments,
endless loops, endless recursions, etc.), on the other hand, can produce warnings,
from compilers and from static analysis tools.

That's one reason why we cannot tolerate '-Werror' on gnulib tests.

The other reason is that every package maintainer has their preferred set of
warnings — that's what the 'manywarnings' module is made for —, but it does
not make sense for package maintainers to enforce the absence of certain
warnings on code that 1) they don't maintain, 2) does not end up in the
binaries produced (installed) by their package.

Bruno



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  2 siblings, 0 replies; 24+ messages in thread
From: Bruno Haible @ 2021-06-09 19:38 UTC (permalink / raw)
  To: Paul Eggert; +Cc: noloader, bug-gnulib

Paul Eggert wrote:
> For the -Wnull-dereference issue it may be worthwhile to use a 
> circumlocution that fools GCC into not issuing the warning. After all, a 
> compiler smart enough to warn about '*(volatile int *) 0 = 42' might 
> also be smart enough to see that it's undefined behavior and therefore 
> omit the assignment's effect entirely.

Good point. Yes, when GCC "recognizes" undefined behaviour, it likes
to omit the entire code branch (basic block). Even if it does not do so
today, it might do so in the future. I've applied your patch like this
(here and in libsigsegv):


2021-06-09  Bruno Haible  <bruno@clisp.org>

	sigsegv tests: Hide a null pointer from the compiler's optimizations.
	Patch by Paul Eggert.
	* tests/test-sigsegv-catch-stackoverflow2.c
	(null_pointer_to_volatile_int): New variable.
	(main): Use it.

diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c
index b94d131..a491fd2 100644
--- a/tests/test-sigsegv-catch-stackoverflow2.c
+++ b/tests/test-sigsegv-catch-stackoverflow2.c
@@ -50,6 +50,7 @@ sigset_t mainsigset;
 
 volatile int pass = 0;
 uintptr_t page;
+volatile int *null_pointer_to_volatile_int /* = NULL */;
 
 static void
 stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
@@ -183,7 +184,7 @@ main ()
       *(volatile int *) (page + 0x678) = 42;
       break;
     case 3:
-      *(volatile int *) 0 = 42;
+      *null_pointer_to_volatile_int = 42;
       break;
     case 4:
       break;



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  0 siblings, 1 reply; 24+ messages in thread
From: Simon Josefsson via Gnulib discussion list @ 2021-06-10  8:13 UTC (permalink / raw)
  To: Bruno Haible; +Cc: Bernhard Voelker, noloader, bug-gnulib

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

Bruno Haible <bruno@clisp.org> writes:

> The other reason is that every package maintainer has their preferred set of
> warnings — that's what the 'manywarnings' module is made for —, but it does
> not make sense for package maintainers to enforce the absence of certain
> warnings on code that 1) they don't maintain, 2) does not end up in the
> binaries produced (installed) by their package.

I agree with that.  The unfortunate result, however, is that maintainers
are less likely to enable gnulib self-tests in their packages, since it
creates additional work.

I try to have gnulib tests enabled, but sometimes I disable them because
having them enabled leads to problems that are too time-consuming to
debug and fix.  Most of my projects have multiple gnulib instances in
them, which gnulib self-tests does not support.

I've seen over the years a number of cases where old releases of my
packages fail to 'make check' correctly only because of a gnulib
self-test that was 1) simply buggy, or 2) the gnulib replacement code
had bugs in them on some platform, or 3) the self-test tested a
corner-case that newer platforms (for valid reasons) chose to behave
differently for.

I think there is room for improvements in this field, with the goal of
making it easier for maintainers to always include all gnulib
self-tests, but I don't really know what it could be.

/Simon

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  2 siblings, 0 replies; 24+ messages in thread
From: Bruno Haible @ 2021-06-10 19:39 UTC (permalink / raw)
  To: Paul Eggert; +Cc: noloader, bug-gnulib

Paul Eggert wrote:
> For what it's worth I'm more with Bruno on this. For the tests, the cost 
> of these warnings outweighs the benefit.
> 
> It'd be OK with me to disable the troublesome warnings globally for the 
> tests subdirectory, using -Wno-missing-prototypes or whatever.

Thanks for your backing, Paul.

It's not the actual warnings that I want to disable. It's more the
expectation that the Gnulib tests "should be warning-free" that is
to be corrected. Implemented through this:

1) Add a '-Wno-error' option that disables the '-Werror' from [1]
   that has no raison d'être.

2) A message that indicates that warnings can be ignored in this
   directory.

[1] https://git.savannah.gnu.org/gitweb/?p=grep.git;a=blob;f=gnulib-tests/Makefile.am;h=3085f6357b7ece8e68c93650bf409a7609079d19;hb=HEAD


2021-06-10  Bruno Haible  <bruno@clisp.org>

	Clarify that compiler warnings in the Gnulib tests can be ignored.
	* gnulib-tool (func_emit_tests_Makefile_am): Emit overrides for CFLAGS
	and CXXFLAGS. Emit a dependency of 'all' on 'all-notice' that prints a
	notice.
	(func_emit_initmacro_start): Add a second argument. If it is true, emit
	code to require gl_CC_ALLOW_WARNINGS and gl_CXX_ALLOW_WARNINGS.
	(func_import, func_create_testdir): All callers updated.
	* m4/gnulib-common.m4 (gl_CC_ALLOW_WARNINGS, gl_CXX_ALLOW_WARNINGS): New
	macros.

diff --git a/gnulib-tool b/gnulib-tool
index 237693a..d41d58c 100755
--- a/gnulib-tool
+++ b/gnulib-tool
@@ -4201,6 +4201,25 @@ func_emit_tests_Makefile_am ()
     fi
   done
   echo
+  # Insert a '-Wno-error' option in the compilation commands emitted by
+  # Automake, between $(AM_CPPFLAGS) and before the reference to @CFLAGS@.
+  # Why?
+  # 1) Because parts of the Gnulib tests exercise corner cases (invalid
+  #    arguments, endless recursions, etc.) that a compiler may warn about,
+  #    even with just the normal '-Wall' option.
+  # 2) Because every package maintainer has their preferred set of warnings
+  #    that they may want to enforce in the main source code of their package.
+  #    But Gnulib tests are maintained in Gnulib and don't end up in binaries
+  #    that that package installs; therefore it does not make sense for
+  #    package maintainers to enforce the absence of warnings on these tests.
+  # Why before @CFLAGS@?
+  # - Because "the user is always right": If a user adds '-Werror' to their
+  #   CFLAGS, they have asked for errors, they will get errors. But they have
+  #   no right to complain about these errors, because Gnulib does not support
+  #   '-Werror'.
+  echo "CFLAGS = @GL_CFLAG_ALLOW_WARNINGS@ @CFLAGS@"
+  echo "CXXFLAGS = @GL_CXXFLAG_ALLOW_WARNINGS@ @CXXFLAGS@"
+  echo
   echo "AM_CPPFLAGS = \\"
   if $for_test; then
     echo "  -DGNULIB_STRICT_CHECKING=1 \\"
@@ -4248,6 +4267,14 @@ func_emit_tests_Makefile_am ()
   echo
   cat "$tmp"/main_snippets "$tmp"/longrunning_snippets \
     | sed -e 's|\$(top_srcdir)/build-aux/|$(top_srcdir)/'"$auxdir"'/|g'
+  # Arrange to print a message before compiling the files in this directory.
+  echo "all: all-notice"
+  echo "all-notice:"
+  echo "	@echo '## ---------------------------------------------------- ##'"
+  echo "	@echo '## ------------------- Gnulib tests ------------------- ##'"
+  echo "	@echo '## You can ignore compiler warnings in this directory.  ##'"
+  echo "	@echo '## ---------------------------------------------------- ##'"
+  echo
   echo "# Clean up after Solaris cc."
   echo "clean-local:"
   echo "	rm -rf SunWS_cache"
@@ -4262,9 +4289,11 @@ func_emit_tests_Makefile_am ()
   rm -f "$tmp"/main_snippets "$tmp"/longrunning_snippets
 }
 
-# func_emit_initmacro_start macro_prefix
+# func_emit_initmacro_start macro_prefix gentests
 # emits the first few statements of the gl_INIT macro to standard output.
 # - macro_prefix             prefix of gl_EARLY, gl_INIT macros to use
+# - gentests                 true if a tests Makefile.am is being generated,
+#                            false otherwise
 # - module_indicator_prefix  prefix of GNULIB_<modulename> variables to use
 func_emit_initmacro_start ()
 {
@@ -4298,6 +4327,10 @@ func_emit_initmacro_start ()
   # Scope the GNULIB_<modulename> variables.
   echo "  m4_pushdef([GL_MODULE_INDICATOR_PREFIX], [${module_indicator_prefix}])"
   echo "  gl_COMMON"
+  if "$2"; then
+    echo "  AC_REQUIRE([gl_CC_ALLOW_WARNINGS])"
+    echo "  AC_REQUIRE([gl_CXX_ALLOW_WARNINGS])"
+  fi
 }
 
 # func_emit_initmacro_end macro_prefix
@@ -5778,7 +5811,7 @@ s,//*$,/,'
       sed_replace_build_aux="$sed_noop"
     fi
     echo "  gl_m4_base='$m4base'"
-    func_emit_initmacro_start $macro_prefix
+    func_emit_initmacro_start $macro_prefix false
     echo "  gl_source_base='$sourcebase'"
     if test -n "$witness_c_macro"; then
       echo "  m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [$witness_c_macro])"
@@ -5791,7 +5824,7 @@ s,//*$,/,'
     func_emit_initmacro_end $macro_prefix
     echo "  gltests_libdeps="
     echo "  gltests_ltlibdeps="
-    func_emit_initmacro_start ${macro_prefix}tests
+    func_emit_initmacro_start ${macro_prefix}tests $gentests
     echo "  gl_source_base='$testsbase'"
     # Define a tests witness macro that depends on the package.
     # PACKAGE is defined by AM_INIT_AUTOMAKE, PACKAGE_TARNAME is defined by AC_INIT.
@@ -6436,7 +6469,7 @@ func_create_testdir ()
            ba
          }'
        echo "gl_m4_base='../$m4base'"
-       func_emit_initmacro_start $macro_prefix
+       func_emit_initmacro_start $macro_prefix true
        # We don't have explicit ordering constraints between the various
        # autoconf snippets. It's cleanest to put those of the library before
        # those of the tests.
@@ -6552,7 +6585,7 @@ func_create_testdir ()
      sed_replace_build_aux="$sed_noop"
    fi
    echo "gl_m4_base='$m4base'"
-   func_emit_initmacro_start $macro_prefix
+   func_emit_initmacro_start $macro_prefix false
    echo "gl_source_base='$sourcebase'"
    if $single_configure; then
      func_emit_autoconf_snippets "$main_modules" "$main_modules" func_verify_module true false false
@@ -6563,7 +6596,7 @@ func_create_testdir ()
    if $single_configure; then
      echo "  gltests_libdeps="
      echo "  gltests_ltlibdeps="
-     func_emit_initmacro_start ${macro_prefix}tests
+     func_emit_initmacro_start ${macro_prefix}tests true
      echo "  gl_source_base='$testsbase'"
      # Define a tests witness macro.
      echo "  ${macro_prefix}tests_WITNESS=IN_GNULIB_TESTS"
diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index 7db4be1..bfa1645 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -1,4 +1,4 @@
-# gnulib-common.m4 serial 65
+# gnulib-common.m4 serial 66
 dnl Copyright (C) 2007-2021 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -666,6 +666,72 @@ AC_DEFUN([gl_CACHE_VAL_SILENT],
   ])
 ])
 
+# gl_CC_ALLOW_WARNINGS
+# sets and substitutes a variable GL_CFLAG_ALLOW_WARNINGS, to a $(CC) option
+# that reverts a preceding '-Werror' option, if available.
+# This is expected to be '-Wno-error' on gcc, clang (except clang/MSVC), xlclang
+# and empty otherwise.
+AC_DEFUN([gl_CC_ALLOW_WARNINGS],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_CACHE_CHECK([for C compiler option to allow warnings],
+    [gl_cv_cc_wallow],
+    [rm -f conftest*
+     echo 'int dummy;' > conftest.c
+     AC_TRY_COMMAND([${CC-cc} $CFLAGS $CPPFLAGS -c conftest.c 2>conftest1.err]) >/dev/null
+     AC_TRY_COMMAND([${CC-cc} $CFLAGS $CPPFLAGS -Wno-error -c conftest.c 2>conftest2.err]) >/dev/null
+     dnl Test the number of error output lines, because AIX xlc accepts the
+     dnl option '-Wno-error', just to produce a warning
+     dnl "Option -Wno-error was incorrectly specified. The option will be ignored."
+     dnl afterwards.
+     if test $? = 0 && test `wc -l < conftest1.err` = `wc -l < conftest2.err`; then
+       gl_cv_cc_wallow='-Wno-error'
+     else
+       gl_cv_cc_wallow=none
+     fi
+     rm -f conftest*
+    ])
+  case "$gl_cv_cc_wallow" in
+    none) GL_CFLAG_ALLOW_WARNINGS='' ;;
+    *)    GL_CFLAG_ALLOW_WARNINGS="$gl_cv_cc_wallow" ;;
+  esac
+  AC_SUBST([GL_CFLAG_ALLOW_WARNINGS])
+])
+
+# gl_CXX_ALLOW_WARNINGS
+# sets and substitutes a variable GL_CXXFLAG_ALLOW_WARNINGS, to a $(CC) option
+# that reverts a preceding '-Werror' option, if available.
+AC_DEFUN([gl_CXX_ALLOW_WARNINGS],
+[
+  dnl Requires AC_PROG_CXX or gl_PROG_ANSI_CXX.
+  if test -n "$CXX" && test "$CXX" != no; then
+    AC_CACHE_CHECK([for C++ compiler option to allow warnings],
+      [gl_cv_cxx_wallow],
+      [rm -f conftest*
+       echo 'int dummy;' > conftest.cc
+       AC_TRY_COMMAND([${CXX-c++} $CXXFLAGS $CPPFLAGS -c conftest.cc 2>conftest1.err]) >/dev/null
+       AC_TRY_COMMAND([${CXX-c++} $CXXFLAGS $CPPFLAGS -Wno-error -c conftest.cc 2>conftest2.err]) >/dev/null
+       dnl Test the number of error output lines, because AIX xlC accepts the
+       dnl option '-Wno-error', just to produce a warning
+       dnl "Option -Wno-error was incorrectly specified. The option will be ignored."
+       dnl afterwards.
+       if test $? = 0 && test `wc -l < conftest1.err` = `wc -l < conftest2.err`; then
+         gl_cv_cxx_wallow='-Wno-error'
+       else
+         gl_cv_cxx_wallow=none
+       fi
+       rm -f conftest*
+      ])
+    case "$gl_cv_cxx_wallow" in
+      none) GL_CXXFLAG_ALLOW_WARNINGS='' ;;
+      *)    GL_CXXFLAG_ALLOW_WARNINGS="$gl_cv_cxx_wallow" ;;
+    esac
+  else
+    GL_CXXFLAG_ALLOW_WARNINGS=''
+  fi
+  AC_SUBST([GL_CXXFLAG_ALLOW_WARNINGS])
+])
+
 dnl Expands to some code for use in .c programs that, on native Windows, defines
 dnl the Microsoft deprecated alias function names to the underscore-prefixed
 dnl actual function names. With this macro, these function names are available



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  0 siblings, 2 replies; 24+ messages in thread
From: Bruno Haible @ 2021-06-10 19:51 UTC (permalink / raw)
  To: Simon Josefsson; +Cc: noloader, Bernhard Voelker, bug-gnulib

Simon Josefsson wrote:
> Most of my projects have multiple gnulib instances in
> them, which gnulib self-tests does not support.

Yes, this situation is not supported.

> I try to have gnulib tests enabled, but sometimes I disable them because
> having them enabled leads to problems that are too time-consuming to
> debug and fix.  ...
> 
> I've seen over the years a number of cases where old releases of my
> packages fail to 'make check' correctly only because of a gnulib
> self-test that was 1) simply buggy, or 2) the gnulib replacement code
> had bugs in them on some platform, or 3) the self-test tested a
> corner-case that newer platforms (for valid reasons) chose to behave
> differently for.

Failing tests can happen, yes. That's true for all test suites.

But it shouldn't be you as a package maintainer who debugs these tests.
(Only the tests for the modules that you contributed; i.e. wearing your
Gnulib contributor hat :-).

> I think there is room for improvements in this field

From what you are telling, the first improvement should be to clarify
where to report failing tests. Done as follows:


2021-06-10  Bruno Haible  <bruno@clisp.org>

	Clarify where to report test failures from Gnulib tests.
	* gnulib-tool (func_emit_tests_Makefile_am): Emit a dependency of
	'check-am' on 'check-notice' that prints a notice.

diff --git a/gnulib-tool b/gnulib-tool
index d41d58c..4f97e63 100755
--- a/gnulib-tool
+++ b/gnulib-tool
@@ -4275,6 +4275,14 @@ func_emit_tests_Makefile_am ()
   echo "	@echo '## You can ignore compiler warnings in this directory.  ##'"
   echo "	@echo '## ---------------------------------------------------- ##'"
   echo
+  # Arrange to print a message before executing the tests in this directory.
+  echo "check-am: check-notice"
+  echo "check-notice:"
+  echo "	@echo '## ---------------------------------------------------- ##'"
+  echo "	@echo '## ------------------- Gnulib tests ------------------- ##'"
+  echo "	@echo '## Please report test failures to <bug-gnulib@gnu.org>. ##'"
+  echo "	@echo '## ---------------------------------------------------- ##'"
+  echo
   echo "# Clean up after Solaris cc."
   echo "clean-local:"
   echo "	rm -rf SunWS_cache"



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  2021-06-09  0:41               ` Dmitry V. Levin
@ 2021-06-10 20:05                 ` Bruno Haible
  0 siblings, 0 replies; 24+ messages in thread
From: Bruno Haible @ 2021-06-10 20:05 UTC (permalink / raw)
  To: Dmitry V. Levin, bug-gnulib

Dmitry V. Levin wrote:
> Unlike many other gcc warnings, -Wmissing-prototypes is especially useful
> because it doesn't report false positives, so I don't see why one may want
> to turn -Wmissing-prototypes off.

Sometimes a function in a test is not used on some platforms. What are the
possible ways to deal with it?

(1) The test function could be put into a #if. This #if condition needs
    to be updated in some circumstances.
    => This approach (which I would use in lib/ code) is not zero-cost.

(2) The test function can be made 'static'; then we get a compiler warning
    about an unused function (already with '-Wall', IIRC).

(3) The test function can be made global; then we have no warning.
    But -Wmissing-prototypes makes it into a warning.

(4) Then we need to add a prototype to fix that warning.

You see my point? These are small considerations each time, but they
contribute to making test authoring+maintenance a hassle. And they have
no benefit (as I said, in a test that consists of a single compilation
unit, linked against one or more .a files).

Bruno




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  1 sibling, 0 replies; 24+ messages in thread
From: Simon Josefsson via Gnulib discussion list @ 2021-06-10 21:49 UTC (permalink / raw)
  To: Bruno Haible; +Cc: Bernhard Voelker, noloader, bug-gnulib

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

>> I think there is room for improvements in this field
> 
> From what you are telling, the first improvement should be to clarify
> where to report failing tests. Done as follows:

Great!  I think this will help, reporters rarely know anything about
the project and just report a build failure, and helping them find
where to report it may improve things.

/Simon


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  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
  1 sibling, 1 reply; 24+ messages in thread
From: Eric Blake @ 2021-06-11 12:21 UTC (permalink / raw)
  To: Bruno Haible; +Cc: Simon Josefsson, Bernhard Voelker, bug-gnulib, noloader

On Thu, Jun 10, 2021 at 09:51:05PM +0200, Bruno Haible wrote:
> +++ b/gnulib-tool
> @@ -4275,6 +4275,14 @@ func_emit_tests_Makefile_am ()
>    echo "	@echo '## You can ignore compiler warnings in this directory.  ##'"
>    echo "	@echo '## ---------------------------------------------------- ##'"
>    echo
> +  # Arrange to print a message before executing the tests in this directory.
> +  echo "check-am: check-notice"
> +  echo "check-notice:"
> +  echo "	@echo '## ---------------------------------------------------- ##'"
> +  echo "	@echo '## ------------------- Gnulib tests ------------------- ##'"
> +  echo "	@echo '## Please report test failures to <bug-gnulib@gnu.org>. ##'"

Is it worth the longer line for "test failures in this directory"?

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: warnings in unit tests
  2021-06-11 12:21                         ` Eric Blake
@ 2021-06-11 13:57                           ` Bruno Haible
  0 siblings, 0 replies; 24+ messages in thread
From: Bruno Haible @ 2021-06-11 13:57 UTC (permalink / raw)
  To: Eric Blake; +Cc: Simon Josefsson, Bernhard Voelker, bug-gnulib, noloader

Eric Blake wrote:
> > +++ b/gnulib-tool
> > @@ -4275,6 +4275,14 @@ func_emit_tests_Makefile_am ()
> >    echo "	@echo '## You can ignore compiler warnings in this directory.  ##'"
> >    echo "	@echo '## ---------------------------------------------------- ##'"
> >    echo
> > +  # Arrange to print a message before executing the tests in this directory.
> > +  echo "check-am: check-notice"
> > +  echo "check-notice:"
> > +  echo "	@echo '## ---------------------------------------------------- ##'"
> > +  echo "	@echo '## ------------------- Gnulib tests ------------------- ##'"
> > +  echo "	@echo '## Please report test failures to <bug-gnulib@gnu.org>. ##'"
> 
> Is it worth the longer line for "test failures in this directory"?

Yes, thanks for the suggestion. Done:


2021-06-11  Bruno Haible  <bruno@clisp.org>

	Make message in last commit more precise.
	Suggested by Eric Blake.
	* gnulib-tool (func_emit_tests_Makefile_am): Add more precision to
	'check-notice' message.

diff --git a/gnulib-tool b/gnulib-tool
index 4f97e63..63a875d 100755
--- a/gnulib-tool
+++ b/gnulib-tool
@@ -4278,10 +4278,10 @@ func_emit_tests_Makefile_am ()
   # Arrange to print a message before executing the tests in this directory.
   echo "check-am: check-notice"
   echo "check-notice:"
-  echo "	@echo '## ---------------------------------------------------- ##'"
-  echo "	@echo '## ------------------- Gnulib tests ------------------- ##'"
-  echo "	@echo '## Please report test failures to <bug-gnulib@gnu.org>. ##'"
-  echo "	@echo '## ---------------------------------------------------- ##'"
+  echo "	@echo '## ---------------------------------------------------------------------- ##'"
+  echo "	@echo '## ---------------------------- Gnulib tests ---------------------------- ##'"
+  echo "	@echo '## Please report test failures in this directory to <bug-gnulib@gnu.org>. ##'"
+  echo "	@echo '## ---------------------------------------------------------------------- ##'"
   echo
   echo "# Clean up after Solaris cc."
   echo "clean-local:"



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: new module 'sigsegv'
  2021-05-16 17:01 new module 'sigsegv' Bruno Haible
  2021-06-06 23:27 ` Dmitry V. Levin
@ 2021-06-19 12:02 ` Bruno Haible
  2021-06-21 18:22   ` [PATCH] sigsegv, sigsegv-tests: Assign my contributions to the FSF Eric Blake
  1 sibling, 1 reply; 24+ messages in thread
From: Bruno Haible @ 2021-06-19 12:02 UTC (permalink / raw)
  To: bug-gnulib

On 2021-05-16, I wrote:
> 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.

We have been reminded that
  - many GNU packages take files from Gnulib,
  - legal enforcement of the license is simpler when the copyright holder is
    a single entity, i.e. the FSF in this case.

I'm therefore assigning these contributions of mine to the FSF.
Eric and Paolo, I invite you to do the same.


2021-06-19  Bruno Haible  <bruno@clisp.org>

	sigsegv, sigsegv-tests: Assign my contributions to the FSF.
	* lib/sigsegv.in.h: Change copyright notice: Write "Copyright (C) FSF"
	instead of "Copyright (C) Bruno Haible".
	* lib/sigsegv.c: Likewise.
	* lib/stackvma.h: Likewise.
	* lib/stackvma.c: Likewise.
	* m4/sigaltstack.m4: Likewise.
	* m4/stack-direction.m4: Likewise.
	* tests/altstack-util.h: Likewise.
	* tests/mmap-anon-util.h: Likewise.
	* tests/test-sigsegv-catch-segv1.c: Likewise.
	* tests/test-sigsegv-catch-segv2.c: Likewise.
	* tests/test-sigsegv-catch-stackoverflow1.c: Likewise.
	* tests/test-sigsegv-catch-stackoverflow2.c: Likewise.

diff --git a/lib/sigsegv.c b/lib/sigsegv.c
index 865dce0..998c827 100644
--- a/lib/sigsegv.c
+++ b/lib/sigsegv.c
@@ -1,5 +1,5 @@
 /* Page fault handling library.
-   Copyright (C) 1993-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 1993-2021 Free Software Foundation, Inc.
    Copyright (C) 2018  Nylon Chen <nylon7@andestech.com>
 
    This program is free software: you can redistribute it and/or modify
@@ -15,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible and Nylon Chen.  */
+
 #include <config.h>
 
 /* Specification.  */
diff --git a/lib/sigsegv.in.h b/lib/sigsegv.in.h
index 9253766..17ad87e 100644
--- a/lib/sigsegv.in.h
+++ b/lib/sigsegv.in.h
@@ -1,5 +1,5 @@
 /* Page fault handling library.
-   Copyright (C) 1998-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 1998-2021 Free Software Foundation, Inc.
 
    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
@@ -14,6 +14,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible.  */
+
 #ifndef _SIGSEGV_H
 #define _SIGSEGV_H
 
diff --git a/lib/stackvma.c b/lib/stackvma.c
index cc032cf..a810afe 100644
--- a/lib/stackvma.c
+++ b/lib/stackvma.c
@@ -1,5 +1,5 @@
 /* Determine the virtual memory area of a given address.
-   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2002-2021 Free Software Foundation, Inc.
    Copyright (C) 2003-2006  Paolo Bonzini <bonzini@gnu.org>
 
    This program is free software: you can redistribute it and/or modify
@@ -15,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible and Paolo Bonzini.  */
+
 #include <config.h>
 
 /* On Solaris in 32-bit mode, when gnulib module 'largefile' is in use,
diff --git a/lib/stackvma.h b/lib/stackvma.h
index 7296801..1f214a4 100644
--- a/lib/stackvma.h
+++ b/lib/stackvma.h
@@ -1,5 +1,5 @@
 /* Determine the virtual memory area of a given address.
-   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2002-2021 Free Software Foundation, Inc.
    Copyright (C) 2003-2006  Paolo Bonzini <bonzini@gnu.org>
 
    This program is free software: you can redistribute it and/or modify
@@ -15,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible and Paolo Bonzini.  */
+
 #ifndef _STACKVMA_H
 #define _STACKVMA_H
 
diff --git a/m4/sigaltstack.m4 b/m4/sigaltstack.m4
index 47e9000..f8f4aa5 100644
--- a/m4/sigaltstack.m4
+++ b/m4/sigaltstack.m4
@@ -1,5 +1,5 @@
-# sigaltstack.m4 serial 14
-dnl Copyright (C) 2002-2021 Bruno Haible <bruno@clisp.org>
+# sigaltstack.m4 serial 15
+dnl Copyright (C) 2002-2021 Free Software Foundation, Inc.
 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
@@ -7,6 +7,8 @@ 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.
 
+dnl Written by Bruno Haible and Eric Blake.
+
 AC_DEFUN([SV_SIGALTSTACK],
 [
   AC_REQUIRE([AC_PROG_CC])
diff --git a/m4/stack-direction.m4 b/m4/stack-direction.m4
index c7a20a2..9328725 100644
--- a/m4/stack-direction.m4
+++ b/m4/stack-direction.m4
@@ -1,5 +1,4 @@
-# stack-direction.m4 serial 6
-dnl Copyright (C) 2002-2021 Bruno Haible <bruno@clisp.org>
+# stack-direction.m4 serial 7
 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
@@ -7,6 +6,8 @@ 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.
 
+dnl Written by Bruno Haible.
+
 # Determine the stack direction. Define the C macro STACK_DIRECTION.
 AC_DEFUN([SV_STACK_DIRECTION],
 [
diff --git a/tests/altstack-util.h b/tests/altstack-util.h
index f910726..ddbd9e5 100644
--- a/tests/altstack-util.h
+++ b/tests/altstack-util.h
@@ -1,6 +1,6 @@
 /* Some auxiliary stuff for defining an alternate stack.
+   Copyright (C) 2010-2021 Free Software Foundation, Inc.
    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
@@ -15,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Eric Blake and Bruno Haible.  */
+
 #include <stdint.h> /* uintptr_t */
 #include <string.h> /* for memset */
 
diff --git a/tests/mmap-anon-util.h b/tests/mmap-anon-util.h
index 6fb82ef..b526cb6 100644
--- a/tests/mmap-anon-util.h
+++ b/tests/mmap-anon-util.h
@@ -1,5 +1,5 @@
 /* Some auxiliary stuff for using mmap & friends.
-   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2002-2021 Free Software Foundation, Inc.
 
    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
@@ -14,6 +14,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible.  */
+
 #if defined _WIN32 && !defined __CYGWIN__
 
 /* ------------------------ Windows ------------------------ */
diff --git a/tests/test-sigsegv-catch-segv1.c b/tests/test-sigsegv-catch-segv1.c
index 62eef69..68f65c5 100644
--- a/tests/test-sigsegv-catch-segv1.c
+++ b/tests/test-sigsegv-catch-segv1.c
@@ -1,5 +1,5 @@
 /* Test that the handler is called, with the right fault address.
-   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2002-2021 Free Software Foundation, Inc.
 
    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
@@ -14,6 +14,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible.  */
+
 #include <config.h>
 
 /* Specification.  */
diff --git a/tests/test-sigsegv-catch-segv2.c b/tests/test-sigsegv-catch-segv2.c
index dd28517..b2a4804 100644
--- a/tests/test-sigsegv-catch-segv2.c
+++ b/tests/test-sigsegv-catch-segv2.c
@@ -1,5 +1,5 @@
 /* Test that the handler can be exited multiple times.
-   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2002-2021 Free Software Foundation, Inc.
 
    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
@@ -14,6 +14,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible.  */
+
 #include <config.h>
 
 /* Specification.  */
diff --git a/tests/test-sigsegv-catch-stackoverflow1.c b/tests/test-sigsegv-catch-stackoverflow1.c
index c828ed2..d5050d7 100644
--- a/tests/test-sigsegv-catch-stackoverflow1.c
+++ b/tests/test-sigsegv-catch-stackoverflow1.c
@@ -1,5 +1,5 @@
 /* Test the stack overflow handler.
-   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2002-2021 Free Software Foundation, Inc.
    Copyright (C) 2010 Eric Blake <eblake@redhat.com>
 
    This program is free software: you can redistribute it and/or modify
@@ -15,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible and Eric Blake.  */
+
 #include <config.h>
 
 /* Specification.  */
diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c
index a491fd2..63c0f39 100644
--- a/tests/test-sigsegv-catch-stackoverflow2.c
+++ b/tests/test-sigsegv-catch-stackoverflow2.c
@@ -1,5 +1,5 @@
 /* Test that stack overflow and SIGSEGV are correctly distinguished.
-   Copyright (C) 2002-2021  Bruno Haible <bruno@clisp.org>
+   Copyright (C) 2002-2021 Free Software Foundation, Inc.
    Copyright (C) 2010 Eric Blake <eblake@redhat.com>
 
    This program is free software: you can redistribute it and/or modify
@@ -15,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Written by Bruno Haible and Eric Blake.  */
+
 #include <config.h>
 
 /* Specification.  */



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH] sigsegv, sigsegv-tests: Assign my contributions to the FSF.
  2021-06-19 12:02 ` new module 'sigsegv' Bruno Haible
@ 2021-06-21 18:22   ` Eric Blake
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Blake @ 2021-06-21 18:22 UTC (permalink / raw)
  To: bug-gnulib

Following Bruno's lead, I'm also happy with this change.

* m4/sigaltstack.m4: Change copyright notice: Write "Copyright (C) FSF"
instead of "Copyright (C) Eric Blake".
* tests/altstack-util.h: Likewise.
* tests/test-sigsegv-catch-stackoverflow1.c: Likewise.
* tests/test-sigsegv-catch-stackoverflow2.c: Likewise.
---
 ChangeLog                                 | 9 +++++++++
 m4/sigaltstack.m4                         | 1 -
 tests/altstack-util.h                     | 1 -
 tests/test-sigsegv-catch-stackoverflow1.c | 1 -
 tests/test-sigsegv-catch-stackoverflow2.c | 1 -
 5 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index eee09cd522..bf51718de8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2021-06-21  Eric Blake  <eblake@redhat.com>
+
+	sigsegv, sigsegv-tests: Assign my contributions to the FSF.
+	* m4/sigaltstack.m4: Change copyright notice: Write "Copyright (C) FSF"
+	instead of "Copyright (C) Eric Blake".
+	* tests/altstack-util.h: Likewise.
+	* tests/test-sigsegv-catch-stackoverflow1.c: Likewise.
+	* tests/test-sigsegv-catch-stackoverflow2.c: Likewise.
+
 2021-06-20  Bruno Haible  <bruno@clisp.org>

 	unistd: Avoid compilation error in C++ mode on Solaris, HP-UX, mingw.
diff --git a/m4/sigaltstack.m4 b/m4/sigaltstack.m4
index f8f4aa524f..6dbd677981 100644
--- a/m4/sigaltstack.m4
+++ b/m4/sigaltstack.m4
@@ -1,6 +1,5 @@
 # sigaltstack.m4 serial 15
 dnl Copyright (C) 2002-2021 Free Software Foundation, Inc.
-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
diff --git a/tests/altstack-util.h b/tests/altstack-util.h
index ddbd9e54ef..fbd0d13f99 100644
--- a/tests/altstack-util.h
+++ b/tests/altstack-util.h
@@ -1,6 +1,5 @@
 /* Some auxiliary stuff for defining an alternate stack.
    Copyright (C) 2010-2021 Free Software Foundation, Inc.
-   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
diff --git a/tests/test-sigsegv-catch-stackoverflow1.c b/tests/test-sigsegv-catch-stackoverflow1.c
index d5050d7ca8..2f1e6f4879 100644
--- a/tests/test-sigsegv-catch-stackoverflow1.c
+++ b/tests/test-sigsegv-catch-stackoverflow1.c
@@ -1,6 +1,5 @@
 /* Test the stack overflow handler.
    Copyright (C) 2002-2021 Free Software Foundation, Inc.
-   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
diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c
index 63c0f39452..5914e32504 100644
--- a/tests/test-sigsegv-catch-stackoverflow2.c
+++ b/tests/test-sigsegv-catch-stackoverflow2.c
@@ -1,6 +1,5 @@
 /* Test that stack overflow and SIGSEGV are correctly distinguished.
    Copyright (C) 2002-2021 Free Software Foundation, Inc.
-   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
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2021-06-21 18:22 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-16 17:01 new module 'sigsegv' Bruno Haible
2021-06-06 23:27 ` 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

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).