diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2019-12-09 15:50:29 -0300 |
---|---|---|
committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2019-12-19 10:30:49 -0300 |
commit | 1b132d55e2d3a4eb421c0f77f63b67b5022c22e3 (patch) | |
tree | 1e08bb5248f6418de138de92c283be6f5f937205 /sysdeps | |
parent | 0331bffe1be698d32c3b4a93b6e7f02d1f1b47fb (diff) | |
download | glibc-1b132d55e2d3a4eb421c0f77f63b67b5022c22e3.tar.gz glibc-1b132d55e2d3a4eb421c0f77f63b67b5022c22e3.tar.xz glibc-1b132d55e2d3a4eb421c0f77f63b67b5022c22e3.zip |
Fix __libc_signal_block_all on sparc64
The posix_spawn on sparc issues invalid sigprocmask calls: rt_sigprocmask(0xffe5e15c /* SIG_??? */, ~[], 0xffe5e1dc, 8) = -1 EINVAL (Invalid argument) Which make support/tst-support_capture_subprocess fails with random output (due the child signal being wrongly captured by the parent). Tracking the culprit it seems to be a wrong code generation in the INTERNAL_SYSCALL due the automatic sigset_t used on __libc_signal_block_all: return INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_BLOCK, &SIGALL_SET, set, _NSIG / 8); Where SIGALL_SET is defined as: ((__sigset_t) { .__val = {[0 ... _SIGSET_NWORDS-1 ] = -1 } }) Building the expanded __libc_signal_block_all on sparc64 with recent compiler (gcc 8.3.1 and 9.1.1): #include <signal> int _libc_signal_block_all (sigset_t *set) { INTERNAL_SYSCALL_DECL (err); return INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_BLOCK, &SIGALL_SET, set, _NSIG / 8); } The first argument (SIG_BLOCK) is not correctly set on 'o0' register: __libc_signal_block_all: save %sp, -304, %sp add %fp, 1919, %o0 mov 128, %o2 sethi %hi(.LC0), %o1 call memcpy, 0 or %o1, %lo(.LC0), %o1 add %fp, 1919, %o1 mov %i0, %o2 mov 8, %o3 mov 103, %g1 ta 0x6d; bcc,pt %xcc, 1f mov 0, %g1 sub %g0, %o0, %o0 mov 1, %g1 1: sra %o0, 0, %i0 return %i7+8 nop Where if SIGALL_SET is defined a const object, gcc correctly sets the expected kernel argument in correct register: sethi %hi(.LC0), %o1 call memcpy, 0 or %o1, %lo(.LC0), %o1 -> mov 1, %o0 add %fp, 1919, %o1 Another possible fix is use a static const object. Although there should not be a difference between a const compound literal and a static const object, the gcc C99 status page [1] has a note stating that this optimization is not implemented: "const-qualified compound literals could share storage with each other and with string literals, but currently don't.". This patch fixes it by moving both sigset_t that represent the signal sets to static const data object. It generates slight better code where the object reference is used directly instead of a stack allocation plus the content materialization. Checked on x86_64-linux-gnu, i686-linux-gnu, and sparc64-linux-gnu. [1] https://gcc.gnu.org/c99status.html
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/unix/sysv/linux/internal-signals.h | 14 |
1 files changed, 8 insertions, 6 deletions
diff --git a/sysdeps/unix/sysv/linux/internal-signals.h b/sysdeps/unix/sysv/linux/internal-signals.h index 01d8bf0a4c..a496c7174c 100644 --- a/sysdeps/unix/sysv/linux/internal-signals.h +++ b/sysdeps/unix/sysv/linux/internal-signals.h @@ -22,6 +22,7 @@ #include <signal.h> #include <sigsetops.h> #include <stdbool.h> +#include <limits.h> #include <sysdep.h> /* The signal used for asynchronous cancelation. */ @@ -53,15 +54,16 @@ __clear_internal_signals (sigset_t *set) __sigdelset (set, SIGSETXID); } -#define SIGALL_SET \ - ((__sigset_t) { .__val = {[0 ... _SIGSET_NWORDS-1 ] = -1 } }) +static const sigset_t sigall_set = { + .__val = {[0 ... _SIGSET_NWORDS-1 ] = -1 } +}; /* Block all signals, including internal glibc ones. */ static inline int __libc_signal_block_all (sigset_t *set) { INTERNAL_SYSCALL_DECL (err); - return INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_BLOCK, &SIGALL_SET, + return INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_BLOCK, &sigall_set, set, _NSIG / 8); } @@ -69,11 +71,11 @@ __libc_signal_block_all (sigset_t *set) static inline int __libc_signal_block_app (sigset_t *set) { - sigset_t allset = SIGALL_SET; + sigset_t allset = sigall_set; __clear_internal_signals (&allset); INTERNAL_SYSCALL_DECL (err); - return INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_BLOCK, &allset, set, - _NSIG / 8); + return INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_BLOCK, &allset, + set, _NSIG / 8); } /* Restore current process signal mask. */ |