[PATCH 12/23] nptl: Introduce __nptl_deferred_init

Florian Weimer fweimer@redhat.com
Wed May 12 16:58:24 GMT 2021


This function is called once by pthread_create, before spawning
the first thread, and also within pthread_cancel.  Since it sets up
cancellation handlers, place it along with the pthread_cancel
implementation within libc.

It is necessary to drop the weak attribute for the unwinding function
because otherwise, the implementation is no longer being pulled in.
This increases ldconfig size by about 1200 bytes, despite the updated
static stubs.
---
 elf/static-stubs.c                          | 14 ++++
 nptl/Versions                               |  5 +-
 nptl/nptl-init.c                            | 75 ------------------
 nptl/pthreadP.h                             | 17 ++---
 nptl/pthread_cancel.c                       | 84 +++++++++++++++++++++
 nptl/pthread_create.c                       |  8 +-
 sysdeps/unix/sysv/linux/ia64/static-stubs.c | 26 +++++++
 7 files changed, 140 insertions(+), 89 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/ia64/static-stubs.c

diff --git a/elf/static-stubs.c b/elf/static-stubs.c
index a8f24b9028..1752bc7372 100644
--- a/elf/static-stubs.c
+++ b/elf/static-stubs.c
@@ -33,6 +33,20 @@ _Unwind_Resume (struct _Unwind_Exception *exc __attribute__ ((unused)))
   abort ();
 }
 
+_Unwind_Word
+_Unwind_GetCFA (struct _Unwind_Context *ctx __attribute__ ((unused)))
+{
+  abort ();
+}
+
+_Unwind_Reason_Code
+_Unwind_ForcedUnwind (struct _Unwind_Exception *exc __attribute__ ((unused)),
+		      _Unwind_Stop_Fn fn __attribute__ ((unused)),
+		      void *ptr __attribute__ ((unused)))
+{
+  abort ();
+}
+
 _Unwind_Reason_Code
 __gcc_personality_v0 (int version __attribute__ ((unused)),
 		      _Unwind_Action actions __attribute__ ((unused)),
diff --git a/nptl/Versions b/nptl/Versions
index 6e2def1b4c..200055cffa 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -351,8 +351,6 @@ libc {
     tss_set;
   }
   GLIBC_PRIVATE {
-     __nptl_create_event;
-     __nptl_death_event;
     __default_pthread_attr;
     __default_pthread_attr_lock;
     __futex_abstimed_wait64;
@@ -370,8 +368,11 @@ libc {
     __lll_trylock_elision;
     __lll_unlock_elision;
     __mutex_aconf;
+    __nptl_create_event;
     __nptl_deallocate_stack;
     __nptl_deallocate_tsd;
+    __nptl_death_event;
+    __nptl_deferred_init;
     __nptl_free_tcb;
     __nptl_nthreads;
     __nptl_setxid_sighandler;
diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
index f4b86fbfaf..bc4831ac89 100644
--- a/nptl/nptl-init.c
+++ b/nptl/nptl-init.c
@@ -44,84 +44,9 @@ size_t __static_tls_align_m1;
 /* Version of the library, used in libthread_db to detect mismatches.  */
 static const char nptl_version[] __attribute_used__ = VERSION;
 
-/* For asynchronous cancellation we use a signal.  This is the handler.  */
-static void
-sigcancel_handler (int sig, siginfo_t *si, void *ctx)
-{
-  /* Safety check.  It would be possible to call this function for
-     other signals and send a signal from another process.  This is not
-     correct and might even be a security problem.  Try to catch as
-     many incorrect invocations as possible.  */
-  if (sig != SIGCANCEL
-      || si->si_pid != __getpid()
-      || si->si_code != SI_TKILL)
-    return;
-
-  struct pthread *self = THREAD_SELF;
-
-  int oldval = THREAD_GETMEM (self, cancelhandling);
-  while (1)
-    {
-      /* We are canceled now.  When canceled by another thread this flag
-	 is already set but if the signal is directly send (internally or
-	 from another process) is has to be done here.  */
-      int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
-
-      if (oldval == newval || (oldval & EXITING_BITMASK) != 0)
-	/* Already canceled or exiting.  */
-	break;
-
-      int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval,
-					      oldval);
-      if (curval == oldval)
-	{
-	  /* Set the return value.  */
-	  THREAD_SETMEM (self, result, PTHREAD_CANCELED);
-
-	  /* Make sure asynchronous cancellation is still enabled.  */
-	  if ((newval & CANCELTYPE_BITMASK) != 0)
-	    /* Run the registered destructors and terminate the thread.  */
-	    __do_cancel ();
-
-	  break;
-	}
-
-      oldval = curval;
-    }
-}
-
-
-/* When using __thread for this, we do it in libc so as not
-   to give libpthread its own TLS segment just for this.  */
-extern void **__libc_dl_error_tsd (void) __attribute__ ((const));
-
-
 void
 __pthread_initialize_minimal_internal (void)
 {
-  struct sigaction sa;
-  __sigemptyset (&sa.sa_mask);
-
-  /* Install the cancellation signal handler.  If for some reason we
-     cannot install the handler we do not abort.  Maybe we should, but
-     it is only asynchronous cancellation which is affected.  */
-  sa.sa_sigaction = sigcancel_handler;
-  sa.sa_flags = SA_SIGINFO;
-  (void) __libc_sigaction (SIGCANCEL, &sa, NULL);
-
-  /* Install the handle to change the threads' uid/gid.  */
-  sa.sa_sigaction = __nptl_setxid_sighandler;
-  sa.sa_flags = SA_SIGINFO | SA_RESTART;
-  (void) __libc_sigaction (SIGSETXID, &sa, NULL);
-
-  /* The parent process might have left the signals blocked.  Just in
-     case, unblock it.  We reuse the signal mask in the sigaction
-     structure.  It is already cleared.  */
-  __sigaddset (&sa.sa_mask, SIGCANCEL);
-  __sigaddset (&sa.sa_mask, SIGSETXID);
-  INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sa.sa_mask,
-			 NULL, __NSIG_BYTES);
-
   /* Get the size of the static and alignment requirements for the TLS
      block.  */
   size_t static_tls_align;
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index a4a87cb549..e33b071a4d 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -262,18 +262,10 @@ libc_hidden_proto (__pthread_current_priority)
 
 
 extern void __pthread_unwind (__pthread_unwind_buf_t *__buf)
-     __cleanup_fct_attribute __attribute ((__noreturn__))
-#if !defined SHARED && !IS_IN (libpthread)
-     weak_function
-#endif
-     ;
+     __cleanup_fct_attribute __attribute ((__noreturn__));
 libc_hidden_proto (__pthread_unwind)
 extern void __pthread_unwind_next (__pthread_unwind_buf_t *__buf)
-     __cleanup_fct_attribute __attribute ((__noreturn__))
-#ifndef SHARED
-     weak_function
-#endif
-     ;
+     __cleanup_fct_attribute __attribute ((__noreturn__));
 /* NB: No hidden proto for __pthread_unwind_next: inside glibc, the
    legacy unwinding mechanism is used.  */
 
@@ -301,6 +293,11 @@ __do_cancel (void)
 
 /* Internal prototypes.  */
 
+/* One-time initialization activities before pthread_create spawns a
+   new thread.  */
+void __nptl_deferred_init (void);
+libc_hidden_proto (__nptl_deferred_init)
+
 /* Deallocate a thread's stack after optionally making sure the thread
    descriptor is still valid.  */
 extern void __nptl_free_tcb (struct pthread *pd);
diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c
index e4ad602900..76daa791ea 100644
--- a/nptl/pthread_cancel.c
+++ b/nptl/pthread_cancel.c
@@ -26,6 +26,81 @@
 #include <unwind-link.h>
 #include <stdio.h>
 #include <gnu/lib-names.h>
+#include <sys/single_threaded.h>
+
+/* For asynchronous cancellation we use a signal.  This is the handler.  */
+static void
+sigcancel_handler (int sig, siginfo_t *si, void *ctx)
+{
+  /* Safety check.  It would be possible to call this function for
+     other signals and send a signal from another process.  This is not
+     correct and might even be a security problem.  Try to catch as
+     many incorrect invocations as possible.  */
+  if (sig != SIGCANCEL
+      || si->si_pid != __getpid()
+      || si->si_code != SI_TKILL)
+    return;
+
+  struct pthread *self = THREAD_SELF;
+
+  int oldval = THREAD_GETMEM (self, cancelhandling);
+  while (1)
+    {
+      /* We are canceled now.  When canceled by another thread this flag
+	 is already set but if the signal is directly send (internally or
+	 from another process) is has to be done here.  */
+      int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
+
+      if (oldval == newval || (oldval & EXITING_BITMASK) != 0)
+	/* Already canceled or exiting.  */
+	break;
+
+      int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval,
+					      oldval);
+      if (curval == oldval)
+	{
+	  /* Set the return value.  */
+	  THREAD_SETMEM (self, result, PTHREAD_CANCELED);
+
+	  /* Make sure asynchronous cancellation is still enabled.  */
+	  if ((newval & CANCELTYPE_BITMASK) != 0)
+	    /* Run the registered destructors and terminate the thread.  */
+	    __do_cancel ();
+
+	  break;
+	}
+
+      oldval = curval;
+    }
+}
+
+void
+__nptl_deferred_init (void)
+{
+  struct sigaction sa;
+  __sigemptyset (&sa.sa_mask);
+
+  /* Install the cancellation signal handler.  If for some reason we
+     cannot install the handler we do not abort.  Maybe we should, but
+     it is only asynchronous cancellation which is affected.  */
+  sa.sa_sigaction = sigcancel_handler;
+  sa.sa_flags = SA_SIGINFO;
+  (void) __libc_sigaction (SIGCANCEL, &sa, NULL);
+
+  /* Install the handle to change the threads' uid/gid.  */
+  sa.sa_sigaction = __nptl_setxid_sighandler;
+  sa.sa_flags = SA_SIGINFO | SA_RESTART;
+  (void) __libc_sigaction (SIGSETXID, &sa, NULL);
+
+  /* The parent process might have left the signals blocked.  Just in
+     case, unblock it.  We reuse the signal mask in the sigaction
+     structure.  It is already cleared.  */
+  __sigaddset (&sa.sa_mask, SIGCANCEL);
+  __sigaddset (&sa.sa_mask, SIGSETXID);
+  INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sa.sa_mask,
+			 NULL, __NSIG_BYTES);
+}
+libc_hidden_def (__nptl_deferred_init)
 
 int
 __pthread_cancel (pthread_t th)
@@ -46,6 +121,15 @@ __pthread_cancel (pthread_t th)
 		    " must be installed for pthread_cancel to work\n");
   }
 #endif
+
+  /* Perform the deferred initialization if necessary, to install the
+     signal handler for a potential self-cancellation below.  */
+  if (__libc_single_threaded)
+    {
+      __nptl_deferred_init ();
+      __libc_single_threaded = 0;
+    }
+
   int result = 0;
   int oldval;
   int newval;
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 770656453d..43f3722f57 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -459,9 +459,13 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
 {
   STACK_VARIABLES;
 
-  /* Avoid a data race in the multi-threaded case.  */
+  /* Avoid a data race in the multi-threaded case, and call the
+     deferred initialization only once.  */
   if (__libc_single_threaded)
-    __libc_single_threaded = 0;
+    {
+      __nptl_deferred_init ();
+      __libc_single_threaded = 0;
+    }
 
   const struct pthread_attr *iattr = (struct pthread_attr *) attr;
   union pthread_attr_transparent default_attr;
diff --git a/sysdeps/unix/sysv/linux/ia64/static-stubs.c b/sysdeps/unix/sysv/linux/ia64/static-stubs.c
new file mode 100644
index 0000000000..8bd594c2f1
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/ia64/static-stubs.c
@@ -0,0 +1,26 @@
+/* Stub implementations of functions to link into statically linked
+   programs without needing libgcc_eh.  ia64 version.
+   Copyright (C) 2021 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 <elf/static-stubs.c>
+
+_Unwind_Word
+_Unwind_GetBSP (struct _Unwind_Context *ctx)
+{
+  abort ();
+}
-- 
2.31.1




More information about the Libc-alpha mailing list