linux: Inline syscall cancellation to keep wrapper frames observable (BZ 34338)

Message ID 20260630190945.100044-1-adhemerval.zanella@linaro.org (mailing list archive)
State Under Review
Delegated to: Wilco Dijkstra
Headers
Series linux: Inline syscall cancellation to keep wrapper frames observable (BZ 34338) |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Test failed

Commit Message

Adhemerval Zanella Netto June 30, 2026, 7:09 p.m. UTC
  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
---
 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(-)
  

Comments

Andreas K. Huettel July 19, 2026, 11:29 a.m. UTC | #1
Am Mittwoch, 1. Juli 2026, 04:09:25 Japanische Normalzeit schrieb Adhemerval Zanella:
> 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.

Let's postpone this now please. It's not critical and can be backported.

> 
> 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
> ---
>  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
>
  
Adhemerval Zanella Netto July 19, 2026, 1:36 p.m. UTC | #2
On 19/07/26 08:29, Andreas K. Huettel wrote:
> Am Mittwoch, 1. Juli 2026, 04:09:25 Japanische Normalzeit schrieb Adhemerval Zanella:
>> 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.
> 
> Let's postpone this now please. It's not critical and can be backported.

Sure, I recall that we decided to postpone it to next relase on last weekly call.
  

Patch

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