diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2020-12-07 16:21:55 -0300 |
---|---|---|
committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2021-06-09 15:16:45 -0300 |
commit | f779b1efb35fe141e47952af3ac7f0540acca401 (patch) | |
tree | c85d1e5a2b7f93048159938802f187d98a1b8d09 | |
parent | 8c1c0aae2079039a629b15098d78f3d11aabefb4 (diff) | |
download | glibc-f779b1efb35fe141e47952af3ac7f0540acca401.tar.gz glibc-f779b1efb35fe141e47952af3ac7f0540acca401.tar.xz glibc-f779b1efb35fe141e47952af3ac7f0540acca401.zip |
nptl: Implement raise in terms of pthread_kill
Now that pthread_kill is provided by libc.so it is possible to implement the generic POSIX implementation as 'pthread_kill(pthread_self(), sig)'. For Linux implementation, pthread_kill read the targeting TID from the TCB. For raise, this it not possible because it would make raise fail when issue after vfork (where creates the resulting process has a different TID from the parent, but its TCB is not updated as for pthread_create). To make raise use pthread_kill, it is make usable from vfork by getting the target thread id through gettid syscall. Checked on x86_64-linux-gnu and aarch64-linux-gnu.
-rw-r--r-- | include/pthread.h | 5 | ||||
-rw-r--r-- | nptl/Makefile | 1 | ||||
-rw-r--r-- | nptl/pthreadP.h | 4 | ||||
-rw-r--r-- | nptl/pthread_kill.c | 42 | ||||
-rw-r--r-- | nptl/pthread_self.c | 4 | ||||
-rw-r--r-- | sysdeps/htl/pthreadP.h | 2 | ||||
-rw-r--r-- | sysdeps/posix/raise.c | 11 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/raise.c | 52 |
8 files changed, 50 insertions, 71 deletions
diff --git a/include/pthread.h b/include/pthread.h index a3e1cf51b0..1158919247 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -16,4 +16,9 @@ extern int __pthread_barrier_wait (pthread_barrier_t *__barrier) /* This function is called to initialize the pthread library. */ extern void __pthread_initialize (void) __attribute__ ((weak)); + +extern int __pthread_kill (pthread_t threadid, int signo); + +extern pthread_t __pthread_self (void); + #endif diff --git a/nptl/Makefile b/nptl/Makefile index f7d7a2c7e2..3e6cf0c21b 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -251,6 +251,7 @@ CFLAGS-pthread_clockjoin.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-pthread_once.c += $(uses-callbacks) -fexceptions \ -fasynchronous-unwind-tables CFLAGS-pthread_cond_wait.c += -fexceptions -fasynchronous-unwind-tables +CFLAGS-pthread_kill.c = -fexceptions -fasynchronous-unwind-tables CFLAGS-sem_wait.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-sem_timedwait.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-sem_clockwait.c = -fexceptions -fasynchronous-unwind-tables diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h index 3e7a4f52ab..618922f47a 100644 --- a/nptl/pthreadP.h +++ b/nptl/pthreadP.h @@ -508,11 +508,13 @@ extern int __pthread_once (pthread_once_t *once_control, libc_hidden_proto (__pthread_once) extern int __pthread_atfork (void (*prepare) (void), void (*parent) (void), void (*child) (void)); -extern pthread_t __pthread_self (void); +libc_hidden_proto (__pthread_self) extern int __pthread_equal (pthread_t thread1, pthread_t thread2); extern int __pthread_detach (pthread_t th); libc_hidden_proto (__pthread_detach) extern int __pthread_kill (pthread_t threadid, int signo); +libc_hidden_proto (__pthread_kill) +extern int __pthread_cancel (pthread_t th); extern void __pthread_exit (void *value) __attribute__ ((__noreturn__)); libc_hidden_proto (__pthread_exit) extern int __pthread_join (pthread_t threadid, void **thread_return); diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c index ad7e011779..8982011ba8 100644 --- a/nptl/pthread_kill.c +++ b/nptl/pthread_kill.c @@ -28,24 +28,40 @@ __pthread_kill (pthread_t threadid, int signo) if (__is_internal_signal (signo)) return EINVAL; - /* Force load of pd->tid into local variable or register. Otherwise - if a thread exits between ESRCH test and tgkill, we might return - EINVAL, because pd->tid would be cleared by the kernel. */ + pid_t tid; struct pthread *pd = (struct pthread *) threadid; - pid_t tid = atomic_forced_read (pd->tid); - if (__glibc_unlikely (tid <= 0)) - /* Not a valid thread handle. */ - return ESRCH; - /* We have a special syscall to do the work. */ - pid_t pid = __getpid (); + if (pd == THREAD_SELF) + /* It is a special case to handle raise() implementation after a vfork + call (which does not update the PD tid field). */ + tid = INLINE_SYSCALL_CALL (gettid); + else + /* Force load of pd->tid into local variable or register. Otherwise + if a thread exits between ESRCH test and tgkill, we might return + EINVAL, because pd->tid would be cleared by the kernel. */ + tid = atomic_forced_read (pd->tid); - int val = INTERNAL_SYSCALL_CALL (tgkill, pid, tid, signo); - return (INTERNAL_SYSCALL_ERROR_P (val) - ? INTERNAL_SYSCALL_ERRNO (val) : 0); + int val; + if (__glibc_likely (tid > 0)) + { + pid_t pid = __getpid (); + + val = INTERNAL_SYSCALL_CALL (tgkill, pid, tid, signo); + val = (INTERNAL_SYSCALL_ERROR_P (val) + ? INTERNAL_SYSCALL_ERRNO (val) : 0); + } + else + val = ESRCH; + + return val; } +/* Some architectures (for instance arm) might pull raise through libgcc, so + avoid the symbol version if it ends up being used on ld.so. */ +#if !IS_IN(rtld) +libc_hidden_def (__pthread_kill) versioned_symbol (libc, __pthread_kill, pthread_kill, GLIBC_2_34); -#if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34) +# if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34) compat_symbol (libc, __pthread_kill, pthread_kill, GLIBC_2_0); +# endif #endif diff --git a/nptl/pthread_self.c b/nptl/pthread_self.c index f877a2e6bd..196d93fb8e 100644 --- a/nptl/pthread_self.c +++ b/nptl/pthread_self.c @@ -20,7 +20,9 @@ #include <tls.h> pthread_t -pthread_self (void) +__pthread_self (void) { return (pthread_t) THREAD_SELF; } +libc_hidden_def (__pthread_self) +weak_alias (__pthread_self, pthread_self) diff --git a/sysdeps/htl/pthreadP.h b/sysdeps/htl/pthreadP.h index 8e2cf2ce65..3b357b7bdc 100644 --- a/sysdeps/htl/pthreadP.h +++ b/sysdeps/htl/pthreadP.h @@ -31,8 +31,6 @@ extern void __pthread_init_static_tls (struct link_map *) attribute_hidden; /* These represent the interface used by glibc itself. */ -extern pthread_t __pthread_self (void); -extern int __pthread_kill (pthread_t threadid, int signo); extern struct __pthread **__pthread_threads; extern int __pthread_mutex_init (pthread_mutex_t *__mutex, const pthread_mutexattr_t *__attr); diff --git a/sysdeps/posix/raise.c b/sysdeps/posix/raise.c index 9fdb4d538b..4806c0cc63 100644 --- a/sysdeps/posix/raise.c +++ b/sysdeps/posix/raise.c @@ -16,13 +16,20 @@ <https://www.gnu.org/licenses/>. */ #include <signal.h> -#include <unistd.h> +#include <errno.h> +#include <pthread.h> /* Raise the signal SIG. */ int raise (int sig) { - return __kill (__getpid (), sig); + int ret = __pthread_kill (__pthread_self (), sig); + if (ret != 0) + { + __set_errno (ret); + ret = -1; + } + return ret; } libc_hidden_def (raise) weak_alias (raise, gsignal) diff --git a/sysdeps/unix/sysv/linux/raise.c b/sysdeps/unix/sysv/linux/raise.c deleted file mode 100644 index 9be3b37f53..0000000000 --- a/sysdeps/unix/sysv/linux/raise.c +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (C) 2002-2021 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. - - 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 <signal.h> -#include <sysdep.h> -#include <errno.h> -#include <sys/types.h> -#include <unistd.h> -#include <internal-signals.h> - -int -raise (int sig) -{ - /* rt_sigprocmask may fail if: - - 1. sigsetsize != sizeof (sigset_t) (EINVAL) - 2. a failure in copy from/to user space (EFAULT) - 3. an invalid 'how' operation (EINVAL) - - The first case is already handle in glibc syscall call by using the arch - defined _NSIG. Second case is handled by using a stack allocated mask. - The last one should be handled by the block/unblock functions. */ - - sigset_t set; - __libc_signal_block_app (&set); - - pid_t pid = INTERNAL_SYSCALL_CALL (getpid); - pid_t tid = INTERNAL_SYSCALL_CALL (gettid); - - int ret = INLINE_SYSCALL_CALL (tgkill, pid, tid, sig); - - __libc_signal_restore_set (&set); - - return ret; -} -libc_hidden_def (raise) -weak_alias (raise, gsignal) |