[PATCH v16 5/8] nptl: Introduce <rseq-access.h> for RSEQ_* accessors

Frank Scheiner frank.scheiner@web.de
Mon Jan 13 10:49:39 GMT 2025


Dear Michael, Florian,

On 09.01.25 17:32, Michael Jeanson wrote:
> In preparation to move the rseq area to the 'extra TLS' block, we need
> accessors based on the thread pointer and the rseq offset. The ONCE
> variant of the accessors ensures single-copy atomicity for loads and
> stores which is required for all fields once the registration is active.
>
> A separate header is required to allow including <atomic.h> which
> results in an include loop when added to <tcb-access.h>.

The changes in 494d651 ([1]) break our ia64 toolchain builds as it
looks like ia64 is one of (or) the (only) arch(es) w/o
__builtin_thread_pointer() in the GCC:

```
In function '__thread_pointer',
    inlined from 'RSEQ_SELF' at ../sysdeps/unix/sysv/linux/rseq-internal.h:93:41,
    inlined from '__pthread_create_2_1' at pthread_create.c:699:13:
../sysdeps/generic/thread_pointer.h:25:10: error: '__builtin_thread_pointer' is not supported on this target
   25 |   return __builtin_thread_pointer ();
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~
```

[1]: https://sourceware.org/git/?p=glibc.git;a=commit;h=494d65129ed5ae1154b75cc189bbdde5e9ecf1df

Looking into Florian's recent changes regarding <thread_pointer.h> ([2])
and spotting the similarities between:

* sysdeps/loongarch/thread_pointer.h
* sysdeps/powerpc/thread_pointer.h

...and sysdeps/ia64/nptl/tls.h ([3]), specifically:

```
register struct pthread *__thread_self __asm__("r13");
```

...plus getting confirmation from the "IA-64 Software Conventions and
Runtime Architecture Guide" (245358-002) and taking [4] into account,
I came up with the attached patch (based on the powerpc version) to
introduce __thread_pointer() also for ia64.

[2]: https://sourceware.org/git/?p=glibc.git;a=commit;h=7a3e2e877a70153a6d1b786925b34f3b396e20f1

[3]: https://github.com/linux-ia64/glibc-ia64/blob/4b5c3b3de53b138cc509d58d47148917013fead4/sysdeps/ia64/nptl/tls.h

[4]: https://sourceware.org/git/?p=glibc.git;a=commit;h=cb976fba4c51ede7bf8cee5035888527c308dfbc

This seems to indeed "fix" the ia64 build for me, I just don't really know if
it is fully correct to use __thread_self like that:

```
register struct pthread *__thread_self __asm__("r13");

static inline void *
__thread_pointer (void)
{
  return __thread_self;
}
```

But I guess a pointer is a pointer and the compiler seems to be happy,
too.

Cheers,
Frank


> Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
> Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> ---
> Changes since v15:
> - Rename VOLATILE macros to ONCE
> Changes since v14:
> - Update copyright year to 2025
> Changes since v13:
> - Ensure that the VOLATILE variant static assert on 64bit types
>   on 32bit architectures
> - Split to separate header to allow including 'atomic.h' without
>   an include loop
> - Move rtld_hidden_proto rseq symbols to a separate patch
> Changes since v12:
> - Split RSEQ_SET/GETMEM from THREAD_SET/GETMEM
> - Rename rseq_get_area() to RSEQ_SELF()
> - Add rtld_hidden_proto to __rseq_size and __rseq_offset
> ---
>  sysdeps/i386/nptl/rseq-access.h         | 98 +++++++++++++++++++++++++
>  sysdeps/nptl/rseq-access.h              | 56 ++++++++++++++
>  sysdeps/unix/sysv/linux/rseq-internal.h |  8 ++
>  sysdeps/x86_64/nptl/rseq-access.h       | 77 +++++++++++++++++++
>  4 files changed, 239 insertions(+)
>  create mode 100644 sysdeps/i386/nptl/rseq-access.h
>  create mode 100644 sysdeps/nptl/rseq-access.h
>  create mode 100644 sysdeps/x86_64/nptl/rseq-access.h
>
> diff --git a/sysdeps/i386/nptl/rseq-access.h b/sysdeps/i386/nptl/rseq-access.h
> new file mode 100644
> index 0000000000..5e7e09d494
> --- /dev/null
> +++ b/sysdeps/i386/nptl/rseq-access.h
> @@ -0,0 +1,98 @@
> +/* RSEQ_* accessors.  i386 version.
> +   Copyright (C) 2002-2025 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#define __RSEQ_GETMEM(member) \
> +  ({ __typeof (RSEQ_SELF()->member) __value;				      \
> +     if (sizeof (__value) == 1)						      \
> +       asm volatile ("movb %%gs:%P2(%3),%b0"				      \
> +		     : "=q" (__value)					      \
> +		     : "0" (0), "i" (offsetof (struct rseq_area, member)),   \
> +		     "r" (__rseq_offset));				      \
> +     else if (sizeof (__value) == 4)					      \
> +       asm volatile ("movl %%gs:%P1(%2),%0"				      \
> +		     : "=r" (__value)					      \
> +		     : "i" (offsetof (struct rseq_area, member)),	      \
> +		       "r" (__rseq_offset));				      \
> +     else /* 8 */							      \
> +       {								      \
> +	 asm volatile  ("movl %%gs:%P1(%2),%%eax\n\t"			      \
> +			"movl %%gs:4+%P1(%2),%%edx"			      \
> +			: "=&A" (__value)				      \
> +			: "i" (offsetof (struct rseq_area, member)),	      \
> +			  "r" (__rseq_offset));				      \
> +       }								      \
> +     __value; })
> +
> +/* Read member of the RSEQ area directly.  */
> +#define RSEQ_GETMEM(member) \
> +  ({									      \
> +     _Static_assert (sizeof (RSEQ_SELF()->member) == 1			      \
> +		     || sizeof (RSEQ_SELF()->member) == 4		      \
> +		     || sizeof (RSEQ_SELF()->member) == 8,		      \
> +		     "size of rseq data");				      \
> +     __RSEQ_GETMEM(member); })
> +
> +/* Read member of the RSEQ area directly, with single-copy atomicity semantics.
> +   Static assert for types >= 64 bits since they can't be loaded atomically on
> +   x86-32.  */
> +#define RSEQ_GETMEM_ONCE(member) \
> +  ({									      \
> +     _Static_assert (sizeof (RSEQ_SELF()->member) == 1			      \
> +		     || sizeof (RSEQ_SELF()->member) == 4,		      \
> +		     "size of rseq data");				      \
> +     __RSEQ_GETMEM(member); })
> +
> +#define __RSEQ_SETMEM(member, value) \
> +  ({									      \
> +     if (sizeof (RSEQ_SELF()->member) == 1)				      \
> +       asm volatile ("movb %b0,%%gs:%P1(%2)" :				      \
> +		     : "iq" (value),					      \
> +		       "i" (offsetof (struct rseq_area, member)),	      \
> +		       "r" (__rseq_offset));				      \
> +     else if (sizeof (RSEQ_SELF()->member) == 4)			      \
> +       asm volatile ("movl %0,%%gs:%P1(%2)" :				      \
> +		     : "ir" (value),					      \
> +		       "i" (offsetof (struct rseq_area, member)),	      \
> +		       "r" (__rseq_offset));				      \
> +     else /* 8 */							      \
> +       {								      \
> +	 asm volatile ("movl %%eax,%%gs:%P1(%2)\n\t"			      \
> +		       "movl %%edx,%%gs:4+%P1(%2)" :			      \
> +		       : "A" ((uint64_t) cast_to_integer (value)),	      \
> +			 "i" (offsetof (struct rseq_area, member)),	      \
> +			 "r" (__rseq_offset));				      \
> +       }})
> +
> +/* Set member of the RSEQ area directly.  */
> +#define RSEQ_SETMEM(member, value) \
> +  ({									      \
> +     _Static_assert (sizeof (RSEQ_SELF()->member) == 1			      \
> +		     || sizeof (RSEQ_SELF()->member) == 4		      \
> +		     || sizeof (RSEQ_SELF()->member) == 8,		      \
> +		     "size of rseq data");				      \
> +     __RSEQ_SETMEM(member, value); })
> +
> +/* Set member of the RSEQ area directly, with single-copy atomicity semantics.
> +   Static assert for types >= 64 bits since they can't be stored atomically on
> +   x86-32.  */
> +#define RSEQ_SETMEM_ONCE(member, value) \
> +  ({									      \
> +     _Static_assert (sizeof (RSEQ_SELF()->member) == 1			      \
> +		     || sizeof (RSEQ_SELF()->member) == 4,		      \
> +		     "size of rseq data");				      \
> +     __RSEQ_SETMEM(member, value); })
> diff --git a/sysdeps/nptl/rseq-access.h b/sysdeps/nptl/rseq-access.h
> new file mode 100644
> index 0000000000..450f2dcca3
> --- /dev/null
> +++ b/sysdeps/nptl/rseq-access.h
> @@ -0,0 +1,56 @@
> +/* RSEQ_* accessors.  Generic version.
> +   Copyright (C) 2002-2025 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <atomic.h>
> +
> +/* Read member of the RSEQ area directly.  */
> +#define RSEQ_GETMEM(member) \
> +  RSEQ_SELF()->member
> +
> +/* Set member of the RSEQ area directly.  */
> +#define RSEQ_SETMEM(member, value) \
> +  RSEQ_SELF()->member = (value)
> +
> +/* Static assert for types that can't be loaded/stored atomically on the
> +   current architecture.  */
> +#if __HAVE_64B_ATOMICS
> +#define __RSEQ_ASSERT_ATOMIC(member) \
> +   _Static_assert (sizeof (RSEQ_SELF()->member) == 1			      \
> +		   || sizeof (RSEQ_SELF()->member) == 4			      \
> +		   || sizeof (RSEQ_SELF()->member) == 8,		      \
> +		   "size of rseq data")
> +#else
> +#define __RSEQ_ASSERT_ATOMIC(member) \
> +   _Static_assert (sizeof (RSEQ_SELF()->member) == 1			      \
> +		   || sizeof (RSEQ_SELF()->member) == 4,		      \
> +		   "size of rseq data")
> +#endif
> +
> +/* Read member of the RSEQ area directly, with single-copy atomicity semantics.  */
> +#define RSEQ_GETMEM_ONCE(member) \
> +  ({									      \
> +     __RSEQ_ASSERT_ATOMIC(member);					      \
> +     (*(volatile __typeof (RSEQ_SELF()->member) *)&RSEQ_SELF()->member);      \
> +  })
> +
> +/* Set member of the RSEQ area directly, with single-copy atomicity semantics.  */
> +#define RSEQ_SETMEM_ONCE(member, value) \
> +  ({									      \
> +     __RSEQ_ASSERT_ATOMIC(member);					      \
> +     (*(volatile __typeof (RSEQ_SELF()->member) *)&RSEQ_SELF()->member = (value)); \
> +  })
> diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
> index 3993431707..00be15cfc8 100644
> --- a/sysdeps/unix/sysv/linux/rseq-internal.h
> +++ b/sysdeps/unix/sysv/linux/rseq-internal.h
> @@ -25,6 +25,7 @@
>  #include <stdio.h>
>  #include <sys/rseq.h>
>  #include <ldsodefs.h>
> +#include <thread_pointer.h>
>
>  /* Minimum size of the rseq area allocation required by the syscall.  The
>     actually used rseq feature size may be less (20 bytes initially).  */
> @@ -59,6 +60,13 @@ extern ptrdiff_t _rseq_offset attribute_hidden;
>  rtld_hidden_proto (__rseq_size)
>  rtld_hidden_proto (__rseq_offset)
>
> +/* Returns a pointer to the current thread rseq area.  */
> +static inline struct rseq_area *
> +RSEQ_SELF (void)
> +{
> +  return (struct rseq_area *) ((char *) __thread_pointer () + __rseq_offset);
> +}
> +
>  #ifdef RSEQ_SIG
>  static inline bool
>  rseq_register_current_thread (struct pthread *self, bool do_rseq)
> diff --git a/sysdeps/x86_64/nptl/rseq-access.h b/sysdeps/x86_64/nptl/rseq-access.h
> new file mode 100644
> index 0000000000..535e36281f
> --- /dev/null
> +++ b/sysdeps/x86_64/nptl/rseq-access.h
> @@ -0,0 +1,77 @@
> +/* RSEQ_* accessors.  x86_64 version.
> +   Copyright (C) 2002-2025 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* Read member of the RSEQ area directly, with single-copy atomicity semantics.  */
> +#define RSEQ_GETMEM_ONCE(member) \
> +  ({ __typeof (RSEQ_SELF()->member) __value;				      \
> +     _Static_assert (sizeof (__value) == 1				      \
> +		     || sizeof (__value) == 4				      \
> +		     || sizeof (__value) == 8,				      \
> +		     "size of rseq data");			      \
> +     if (sizeof (__value) == 1)						      \
> +       asm volatile ("movb %%fs:%P2(%q3),%b0"				      \
> +		     : "=q" (__value)					      \
> +		     : "0" (0), "i" (offsetof (struct rseq_area, member)),    \
> +		       "r" (__rseq_offset));				      \
> +     else if (sizeof (__value) == 4)					      \
> +       asm volatile ("movl %%fs:%P1(%q2),%0"				      \
> +		     : "=r" (__value)					      \
> +		     : "i" (offsetof (struct rseq_area, member)),	      \
> +		       "r" (__rseq_offset));				      \
> +     else /* 8 */							      \
> +       {								      \
> +	 asm volatile ("movq %%fs:%P1(%q2),%q0"				      \
> +		       : "=r" (__value)					      \
> +		       : "i" (offsetof (struct rseq_area, member)),	      \
> +			 "r" (__rseq_offset));				      \
> +       }								      \
> +     __value; })
> +
> +/* Read member of the RSEQ area directly.  */
> +#define RSEQ_GETMEM(member) RSEQ_GETMEM_ONCE(member)
> +
> +/* Set member of the RSEQ area directly, with single-copy atomicity semantics.  */
> +#define RSEQ_SETMEM_ONCE(member, value) \
> +  ({									      \
> +     _Static_assert (sizeof (RSEQ_SELF()->member) == 1			      \
> +		     || sizeof (RSEQ_SELF()->member) == 4		      \
> +		     || sizeof (RSEQ_SELF()->member) == 8,		      \
> +		     "size of rseq data");			      \
> +     if (sizeof (RSEQ_SELF()->member) == 1)				      \
> +       asm volatile ("movb %b0,%%fs:%P1(%q2)" :				      \
> +		     : "iq" (value),					      \
> +		       "i" (offsetof (struct rseq_area, member)),	      \
> +		       "r" (__rseq_offset));				      \
> +     else if (sizeof (RSEQ_SELF()->member) == 4)			      \
> +       asm volatile ("movl %0,%%fs:%P1(%q2)" :				      \
> +		     : IMM_MODE (value),				      \
> +		       "i" (offsetof (struct rseq_area, member)),	      \
> +		       "r" (__rseq_offset));				      \
> +     else /* 8 */							      \
> +       {								      \
> +	 /* Since movq takes a signed 32-bit immediate or a register source   \
> +	    operand, use "er" constraint for 32-bit signed integer constant   \
> +	    or register.  */						      \
> +	 asm volatile ("movq %q0,%%fs:%P1(%q2)" :			      \
> +		       : "er" ((uint64_t) cast_to_integer (value)),	      \
> +			 "i" (offsetof (struct rseq_area, member)),	      \
> +			 "r" (__rseq_offset));				      \
> +       }})
> +
> +/* Set member of the RSEQ area directly.  */
> +#define RSEQ_SETMEM(member, value) RSEQ_SETMEM_ONCE(member, value)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Introduce-__thread_pointer-also-for-ia64.patch
Type: text/x-patch
Size: 2723 bytes
Desc: not available
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20250113/d9f61a2d/attachment-0001.bin>


More information about the Libc-alpha mailing list