Re: [PATCH] linux: Inline syscall cancellation to keep wrapper frames observable (BZ 34338)

Adhemerval Zanella Netto <[email protected]> Tue, 4 Aug 2026 11:27:18 -0300
Newsgroups gmane.comp.lib.glibc.alpha
Organization Linaro
Message-ID <[email protected]>

On 30/06/26 16:09, Adhemerval Zanella wrote:
> The cancellable syscall wrappers end with a tail call to __syscall_cancel,
> ithe wrapper frame is then elided, so when the syscall executes the wrapper
> is no longer present on the stack.  Tools that unwind from CFI alone, such
> as valgrind, perf and sampling profilers, cannot observe it. On gdb, it
> only recovers it from DWARF call site information, which reduced-debuginfo
> libc builds usually omit.
> 
> The behaviour is target dependent: for a shared (PIC) the tail call is
> emitted on aarch64, arc, loongarch and riscv.  It is not emitte on i386,
> x86_64, arm, s390x, sparc and alpha, where the seventh argument is passed
> on the stack or fewer argument registers are available, nor on powerpc
> and mips, where the TOC/GOT pointer must be restored after the call.
> This is why the problem was originally reported as aarch64 specific while
> x86_64 was unaffected.
> 
> Rather than only inhibiting the tail call [1] (which keeps the wrapper frame
> but still leaves the __syscall_cancel and __internal_syscall_cancel
> frames), move the cancellation logic back into the wrappers.  In the
> single-threaded case the syscall is now issued directly from the wrapper;
> only the multi-threaded path still calls the out-of-line __syscall_cancel_arch.
> 
> This keeps the wrapper observable and removes the extra frames, mimicking
> how cancellation was handled before 89b53077d2a58f00e7debdfe58afabe953dac60d.
> 
> The result is a small libc.so .text increase (size, first column):
> 
>   ABI            master      patched      diff    increase
>   aarch64       1635880     1647424      11544      0.71%
>   x86_64        1981081     1992257      11176      0.56%
>   powerpc64le   2364336     2376964      12628      0.53%
>   riscv64       1368386     1376704       8318      0.61%
>   loongarch64   1741385     1755601      14216      0.82%
> 
> The tst-backtrace5 was suppose to track this issue, but due wrong
> loop variable check it does take this in account.  This patch also fixes it.
> 
> Checked on aarch64-linux-gnu, x86_64-linux-gnu, i686-linux-gnu,
> arm-linux-gnueabihf, and powerpc64le-linux-gnu.
> 
> [1] https://sourceware.org/pipermail/libc-alpha/2025-March/165395.html

Ping.

> ---
>  debug/tst-backtrace5.c                  | 32 ++++++----
>  elf/Makefile                            |  4 +-
>  nptl/cancellation.c                     | 60 ------------------
>  nptl/futex-internal.c                   |  2 +-
>  nptl/sem_waitcommon.c                   |  2 +-
>  sysdeps/unix/sysdep.h                   | 81 +++++++++++--------------
>  sysdeps/unix/sysv/linux/epoll_pwait2.c  |  2 +-
>  sysdeps/unix/sysv/linux/recvmmsg.c      |  2 +-
>  sysdeps/unix/sysv/linux/sigtimedwait.c  |  2 +-
>  sysdeps/unix/sysv/linux/sysdep-cancel.h | 61 +++++++++++++++++++
>  10 files changed, 125 insertions(+), 123 deletions(-)
> 
> diff --git a/debug/tst-backtrace5.c b/debug/tst-backtrace5.c
> index 4c5784c060a..6538da9ab59 100644
> --- a/debug/tst-backtrace5.c
> +++ b/debug/tst-backtrace5.c
> @@ -36,10 +36,15 @@
>     trampoline, read, 3 * fn, and do_test.  */
>  #define NUM_FUNCTIONS 7
>  
> +/* Avoid the read wrapper frame truncation on targets that add extra frames
> +   between it and handle_signal (the cancellable syscall wrappers
> +   __syscall_cancel*, or the i686 __kernel_vsyscall entry).  */
> +#define MAX_FUNCTIONS 64
> +
>  void
>  handle_signal (int signum)
>  {
> -  void *addresses[NUM_FUNCTIONS];
> +  void *addresses[MAX_FUNCTIONS];
>    char **symbols;
>    int n;
>    int i;
> @@ -70,23 +75,28 @@ handle_signal (int signum)
>        return;
>      }
>  
> -  /* Do not check name for signal trampoline or cancellable syscall
> -     wrappers (__syscall_cancel*).  */
> -  for (; i < n - 1; i++)
> +  /* Skip the signal trampoline and any cancellable syscall wrapper frames
> +     (__syscall_cancel*) and require the read syscall wrapper to be
> +     present.  */
> +  for (i = 1; i < n; i++)
>      if (match (symbols[i], "read"))
>        break;
> -  if (i == n - 1)
> +  if (i == n)
>      {
>        FAIL ();
>        return;
>      }
>  
> -  for (; i < n - 1; i++)
> -    if (!match (symbols[i], "fn"))
> -      {
> -	FAIL ();
> -	return;
> -      }
> +  /* The read wrapper must be followed by the three fn recursion frames.  */
> +  for (int j = 0; j < 3; j++)
> +    {
> +      i++;
> +      if (i == n || !match (symbols[i], "fn"))
> +	{
> +	  FAIL ();
> +	  return;
> +	}
> +    }
>    /* Symbol names are not available for static functions, so we do not
>       check do_test.  */
>  
> diff --git a/elf/Makefile b/elf/Makefile
> index 65144282941..02c335ffb90 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -1550,7 +1550,9 @@ $(objpfx)dl-allobjs.os: $(all-rtld-routines:%=$(objpfx)%.os)
>  # when compiled for libc.
>  rtld-stubbed-symbols = \
>    __libc_assert_fail \
> -  __syscall_cancel \
> +  __libc_single_threaded_internal \
> +  __syscall_cancel_arch \
> +  __syscall_do_cancel \
>    calloc \
>    free \
>    malloc \
> diff --git a/nptl/cancellation.c b/nptl/cancellation.c
> index 4368cb72319..63f09840ffe 100644
> --- a/nptl/cancellation.c
> +++ b/nptl/cancellation.c
> @@ -19,66 +19,6 @@
>  #include <stdlib.h>
>  #include "pthreadP.h"
>  
> -/* Called by the INTERNAL_SYSCALL_CANCEL macro, check for cancellation and
> -   returns the syscall value or its negative error code.  */
> -long int
> -__internal_syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2,
> -			   __syscall_arg_t a3, __syscall_arg_t a4,
> -			   __syscall_arg_t a5, __syscall_arg_t a6,
> -			   __SYSCALL_CANCEL7_ARG_DEF
> -			   __syscall_arg_t nr)
> -{
> -  long int result;
> -  struct pthread *pd = THREAD_SELF;
> -
> -  /* If cancellation is not enabled, call the syscall directly and also
> -     for thread terminatation to avoid call __syscall_do_cancel while
> -     executing cleanup handlers.  */
> -  int ch = atomic_load_relaxed (&pd->cancelhandling);
> -  if (SINGLE_THREAD_P || !cancel_enabled (ch) || cancel_exiting (ch))
> -    {
> -      result = INTERNAL_SYSCALL_NCS_CALL (nr, a1, a2, a3, a4, a5, a6
> -					  __SYSCALL_CANCEL7_ARCH_ARG7);
> -      if (INTERNAL_SYSCALL_ERROR_P (result))
> -	return -INTERNAL_SYSCALL_ERRNO (result);
> -      return result;
> -    }
> -
> -  /* Call the arch-specific entry points that contains the globals markers
> -     to be checked by SIGCANCEL handler.  */
> -  result = __syscall_cancel_arch (&pd->cancelhandling, nr, a1, a2, a3, a4, a5,
> -			          a6 __SYSCALL_CANCEL7_ARCH_ARG7);
> -
> -  /* If the cancellable syscall was interrupted by SIGCANCEL and it has no
> -     side-effect, cancel the thread if cancellation is enabled.  */
> -  ch = atomic_load_relaxed (&pd->cancelhandling);
> -  /* The behaviour here assumes that EINTR is returned only if there are no
> -     visible side effects.  POSIX Issue 7 has not yet provided any stronger
> -     language for close, and in theory the close syscall could return EINTR
> -     and leave the file descriptor open (conforming and leaks).  It expects
> -     that no such kernel is used with glibc.  */
> -  if (result == -EINTR && cancel_enabled_and_canceled (ch))
> -    __syscall_do_cancel ();
> -
> -  return result;
> -}
> -
> -/* Called by the SYSCALL_CANCEL macro, check for cancellation and return the
> -   syscall expected success value (usually 0) or, in case of failure, -1 and
> -   sets errno to syscall return value.  */
> -long int
> -__syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2,
> -		  __syscall_arg_t a3, __syscall_arg_t a4,
> -		  __syscall_arg_t a5, __syscall_arg_t a6,
> -		  __SYSCALL_CANCEL7_ARG_DEF __syscall_arg_t nr)
> -{
> -  long int r = __internal_syscall_cancel (a1, a2, a3, a4, a5, a6,
> -					  __SYSCALL_CANCEL7_ARG nr);
> -  return __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (r))
> -	 ? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (r))
> -	 : r;
> -}
> -
>  /* Called by __syscall_cancel_arch or function above start the thread
>     cancellation.  */
>  _Noreturn void
> diff --git a/nptl/futex-internal.c b/nptl/futex-internal.c
> index fc6b11c8ada..07f1462abeb 100644
> --- a/nptl/futex-internal.c
> +++ b/nptl/futex-internal.c
> @@ -17,7 +17,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <errno.h>
> -#include <sysdep.h>
> +#include <sysdep-cancel.h>
>  #include <time.h>
>  #include <futex-internal.h>
>  #include <kernel-features.h>
> diff --git a/nptl/sem_waitcommon.c b/nptl/sem_waitcommon.c
> index b0cbe5cebf7..82c686f0201 100644
> --- a/nptl/sem_waitcommon.c
> +++ b/nptl/sem_waitcommon.c
> @@ -18,7 +18,7 @@
>  
>  #include <kernel-features.h>
>  #include <errno.h>
> -#include <sysdep.h>
> +#include <sysdep-cancel.h>
>  #include <futex-internal.h>
>  #include <internaltypes.h>
>  #include <semaphore.h>
> diff --git a/sysdeps/unix/sysdep.h b/sysdeps/unix/sysdep.h
> index a6ce5348ec3..f0f271ec021 100644
> --- a/sysdeps/unix/sysdep.h
> +++ b/sysdeps/unix/sysdep.h
> @@ -154,42 +154,31 @@
>  # define __SYSCALL_CANCEL7_ARG7
>  # define __SYSCALL_CANCEL7_ARCH_ARG7
>  #endif
> -long int __internal_syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2,
> -				    __syscall_arg_t a3, __syscall_arg_t a4,
> -				    __syscall_arg_t a5, __syscall_arg_t a6,
> -				    __SYSCALL_CANCEL7_ARG_DEF
> -				    __syscall_arg_t nr) attribute_hidden;
> -
> -long int __syscall_cancel (__syscall_arg_t arg1, __syscall_arg_t arg2,
> -			   __syscall_arg_t arg3, __syscall_arg_t arg4,
> -			   __syscall_arg_t arg5, __syscall_arg_t arg6,
> -			   __SYSCALL_CANCEL7_ARG_DEF
> -			   __syscall_arg_t nr) attribute_hidden;
>  
>  #define __SYSCALL_CANCEL0(name)						\
> -  __syscall_cancel (0, 0, 0, 0, 0, 0, __SYSCALL_CANCEL7_ARG __NR_##name)
> +  syscall_cancel (0, 0, 0, 0, 0, 0, __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __SYSCALL_CANCEL1(name, a1)					\
> -  __syscall_cancel (__SSC (a1), 0, 0, 0, 0, 0,				\
> -		    __SYSCALL_CANCEL7_ARG __NR_##name)
> +  syscall_cancel (__SSC (a1), 0, 0, 0, 0, 0,				\
> +		  __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __SYSCALL_CANCEL2(name, a1, a2) \
> -  __syscall_cancel (__SSC (a1), __SSC (a2), 0, 0, 0, 0,			\
> -		    __SYSCALL_CANCEL7_ARG __NR_##name)
> +  syscall_cancel (__SSC (a1), __SSC (a2), 0, 0, 0, 0,			\
> +		  __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __SYSCALL_CANCEL3(name, a1, a2, a3) \
> -  __syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), 0, 0, 0,	\
> -		    __SYSCALL_CANCEL7_ARG __NR_##name)
> +  syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), 0, 0, 0,	\
> +		  __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __SYSCALL_CANCEL4(name, a1, a2, a3, a4) \
> -  __syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),			\
> -		    __SSC(a4), 0, 0, __SYSCALL_CANCEL7_ARG __NR_##name)
> +  syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),			\
> +		  __SSC(a4), 0, 0, __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __SYSCALL_CANCEL5(name, a1, a2, a3, a4, a5) \
> -  __syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), __SSC(a4),	\
> -		    __SSC (a5), 0, __SYSCALL_CANCEL7_ARG __NR_##name)
> +  syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), __SSC(a4),	\
> +		  __SSC (a5), 0, __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __SYSCALL_CANCEL6(name, a1, a2, a3, a4, a5, a6) \
> -  __syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), __SSC (a4),	\
> -		    __SSC (a5), __SSC (a6), __SYSCALL_CANCEL7_ARG	\
> -		    __NR_##name)
> +  syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), __SSC (a4),	\
> +		  __SSC (a5), __SSC (a6), __SYSCALL_CANCEL7_ARG	\
> +		  __NR_##name)
>  #define __SYSCALL_CANCEL7(name, a1, a2, a3, a4, a5, a6, a7)		\
> -  __syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), __SSC (a4),	\
> -		    __SSC (a5), __SSC (a6), __SSC (a7), __NR_##name)
> +  syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), __SSC (a4),	\
> +		  __SSC (a5), __SSC (a6), __SSC (a7), __NR_##name)
>  
>  #define __SYSCALL_CANCEL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n
>  #define __SYSCALL_CANCEL_NARGS(...) \
> @@ -206,33 +195,33 @@ long int __syscall_cancel (__syscall_arg_t arg1, __syscall_arg_t arg2,
>    __SYSCALL_CANCEL_DISP (__SYSCALL_CANCEL, __VA_ARGS__)
>  
>  #define __INTERNAL_SYSCALL_CANCEL0(name)				\
> -  __internal_syscall_cancel (0, 0, 0, 0, 0, 0, __SYSCALL_CANCEL7_ARG	\
> +  internal_syscall_cancel (0, 0, 0, 0, 0, 0, __SYSCALL_CANCEL7_ARG	\
>  			     __NR_##name)
>  #define __INTERNAL_SYSCALL_CANCEL1(name, a1)				\
> -  __internal_syscall_cancel (__SSC (a1), 0, 0, 0, 0, 0,			\
> -			     __SYSCALL_CANCEL7_ARG __NR_##name)
> +  internal_syscall_cancel (__SSC (a1), 0, 0, 0, 0, 0,			\
> +			   __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __INTERNAL_SYSCALL_CANCEL2(name, a1, a2)			\
> -  __internal_syscall_cancel (__SSC (a1), __SSC (a2), 0, 0, 0, 0,	\
> -			     __SYSCALL_CANCEL7_ARG __NR_##name)
> +  internal_syscall_cancel (__SSC (a1), __SSC (a2), 0, 0, 0, 0,	\
> +			   __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __INTERNAL_SYSCALL_CANCEL3(name, a1, a2, a3)			\
> -  __internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), 0,	\
> -			     0, 0, __SYSCALL_CANCEL7_ARG __NR_##name)
> +  internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3), 0,	\
> +			   0, 0, __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __INTERNAL_SYSCALL_CANCEL4(name, a1, a2, a3, a4)		\
> -  __internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),	\
> -			     __SSC(a4), 0, 0,				\
> -			     __SYSCALL_CANCEL7_ARG __NR_##name)
> +  internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),	\
> +			   __SSC(a4), 0, 0,				\
> +			   __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __INTERNAL_SYSCALL_CANCEL5(name, a1, a2, a3, a4, a5)		\
> -  __internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),	\
> -			     __SSC(a4), __SSC (a5), 0,			\
> -			     __SYSCALL_CANCEL7_ARG __NR_##name)
> +  internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),	\
> +			   __SSC(a4), __SSC (a5), 0,			\
> +			   __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __INTERNAL_SYSCALL_CANCEL6(name, a1, a2, a3, a4, a5, a6)	\
> -  __internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),	\
> -			     __SSC (a4), __SSC (a5), __SSC (a6),	\
> -			     __SYSCALL_CANCEL7_ARG __NR_##name)
> +  internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),	\
> +			   __SSC (a4), __SSC (a5), __SSC (a6),	\
> +			   __SYSCALL_CANCEL7_ARG __NR_##name)
>  #define __INTERNAL_SYSCALL_CANCEL7(name, a1, a2, a3, a4, a5, a6, a7) \
> -  __internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),     \
> -			     __SSC (a4), __SSC (a5), __SSC (a6),     \
> -			     __SSC (a7), __NR_##name)
> +  internal_syscall_cancel (__SSC (a1), __SSC (a2), __SSC (a3),     \
> +			   __SSC (a4), __SSC (a5), __SSC (a6),     \
> +			   __SSC (a7), __NR_##name)
>  
>  /* Issue a cancellable syscall defined by syscall number NAME plus any other
>     argument required.  If an error occurs its value is returned as an negative
> diff --git a/sysdeps/unix/sysv/linux/epoll_pwait2.c b/sysdeps/unix/sysv/linux/epoll_pwait2.c
> index 4da03e3e69f..76ec1f8a71a 100644
> --- a/sysdeps/unix/sysv/linux/epoll_pwait2.c
> +++ b/sysdeps/unix/sysv/linux/epoll_pwait2.c
> @@ -17,7 +17,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <sys/epoll.h>
> -#include <sysdep.h>
> +#include <sysdep-cancel.h>
>  
>  int
>  __epoll_pwait2_time64 (int fd, struct epoll_event *ev, int maxev,
> diff --git a/sysdeps/unix/sysv/linux/recvmmsg.c b/sysdeps/unix/sysv/linux/recvmmsg.c
> index 6fbe4b80aa5..6a89f914c27 100644
> --- a/sysdeps/unix/sysv/linux/recvmmsg.c
> +++ b/sysdeps/unix/sysv/linux/recvmmsg.c
> @@ -16,7 +16,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <sys/socket.h>
> -#include <sysdep.h>
> +#include <sysdep-cancel.h>
>  #include <socketcall.h>
>  
>  static int
> diff --git a/sysdeps/unix/sysv/linux/sigtimedwait.c b/sysdeps/unix/sysv/linux/sigtimedwait.c
> index a4fa8e9e8ee..c2c5e2c0f25 100644
> --- a/sysdeps/unix/sysv/linux/sigtimedwait.c
> +++ b/sysdeps/unix/sysv/linux/sigtimedwait.c
> @@ -16,7 +16,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <signal.h>
> -#include <sysdep.h>
> +#include <sysdep-cancel.h>
>  
>  int
>  __sigtimedwait64 (const sigset_t *set, siginfo_t *info,
> diff --git a/sysdeps/unix/sysv/linux/sysdep-cancel.h b/sysdeps/unix/sysv/linux/sysdep-cancel.h
> index 7fd8258ba59..4b7278915a7 100644
> --- a/sysdeps/unix/sysv/linux/sysdep-cancel.h
> +++ b/sysdeps/unix/sysv/linux/sysdep-cancel.h
> @@ -21,5 +21,66 @@
>  #define _SYSDEP_CANCEL_H
>  
>  #include <sysdep.h>
> +#include "pthreadP.h"
> +
> +/* Called by the INTERNAL_SYSCALL_CANCEL macro, check for cancellation and
> +   returns the syscall value or its negative error code.  */
> +static __always_inline long int
> +internal_syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2,
> +			 __syscall_arg_t a3, __syscall_arg_t a4,
> +			 __syscall_arg_t a5, __syscall_arg_t a6,
> +			 __SYSCALL_CANCEL7_ARG_DEF
> +			 __syscall_arg_t nr)
> +{
> +  long int result;
> +  struct pthread *pd = THREAD_SELF;
> +
> +  /* If cancellation is not enabled, call the syscall directly and also
> +     for thread terminatation to avoid call __syscall_do_cancel while
> +     executing cleanup handlers.  */
> +  int ch = atomic_load_relaxed (&pd->cancelhandling);
> +  if (SINGLE_THREAD_P || !cancel_enabled (ch) || cancel_exiting (ch))
> +    {
> +      result = INTERNAL_SYSCALL_NCS_CALL (nr, a1, a2, a3, a4, a5, a6
> +					  __SYSCALL_CANCEL7_ARCH_ARG7);
> +      if (INTERNAL_SYSCALL_ERROR_P (result))
> +	return -INTERNAL_SYSCALL_ERRNO (result);
> +      return result;
> +    }
> +
> +  /* Call the arch-specific entry points that contains the globals markers
> +     to be checked by SIGCANCEL handler.  */
> +  result = __syscall_cancel_arch (&pd->cancelhandling, nr, a1, a2, a3, a4, a5,
> +			          a6 __SYSCALL_CANCEL7_ARCH_ARG7);
> +
> +  /* If the cancellable syscall was interrupted by SIGCANCEL and it has no
> +     side-effect, cancel the thread if cancellation is enabled.  */
> +  ch = atomic_load_relaxed (&pd->cancelhandling);
> +  /* The behaviour here assumes that EINTR is returned only if there are no
> +     visible side effects.  POSIX Issue 7 has not yet provided any stronger
> +     language for close, and in theory the close syscall could return EINTR
> +     and leave the file descriptor open (conforming and leaks).  It expects
> +     that no such kernel is used with glibc.  */
> +  if (result == -EINTR && cancel_enabled_and_canceled (ch))
> +    __syscall_do_cancel ();
> +
> +  return result;
> +}
> +
> +/* Called by the SYSCALL_CANCEL macro, check for cancellation and return the
> +   syscall expected success value (usually 0) or, in case of failure, -1 and
> +   sets errno to syscall return value.  */
> +static __always_inline long int
> +syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2,
> +		__syscall_arg_t a3, __syscall_arg_t a4,
> +		__syscall_arg_t a5, __syscall_arg_t a6,
> +		__SYSCALL_CANCEL7_ARG_DEF __syscall_arg_t nr)
> +{
> +  long int r = internal_syscall_cancel (a1, a2, a3, a4, a5, a6,
> +					__SYSCALL_CANCEL7_ARG nr);
> +  return __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (r))
> +	 ? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (r))
> +	 : r;
> +}
>  
>  #endif