mirror of
https://sourceware.org/git/glibc.git
synced 2025-12-08 02:02:23 +03:00
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
94 lines
2.7 KiB
C
94 lines
2.7 KiB
C
/* Special use of signals internally. Linux version.
|
|
Copyright (C) 2014-2019 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/>. */
|
|
|
|
#ifndef __INTERNAL_SIGNALS_H
|
|
# define __INTERNAL_SIGNALS_H
|
|
|
|
#include <signal.h>
|
|
#include <sigsetops.h>
|
|
#include <stdbool.h>
|
|
#include <limits.h>
|
|
#include <sysdep.h>
|
|
|
|
/* The signal used for asynchronous cancelation. */
|
|
#define SIGCANCEL __SIGRTMIN
|
|
|
|
|
|
/* Signal needed for the kernel-supported POSIX timer implementation.
|
|
We can reuse the cancellation signal since we can distinguish
|
|
cancellation from timer expirations. */
|
|
#define SIGTIMER SIGCANCEL
|
|
|
|
|
|
/* Signal used to implement the setuid et.al. functions. */
|
|
#define SIGSETXID (__SIGRTMIN + 1)
|
|
|
|
|
|
/* Return is sig is used internally. */
|
|
static inline bool
|
|
__is_internal_signal (int sig)
|
|
{
|
|
return (sig == SIGCANCEL) || (sig == SIGSETXID);
|
|
}
|
|
|
|
/* Remove internal glibc signal from the mask. */
|
|
static inline void
|
|
__clear_internal_signals (sigset_t *set)
|
|
{
|
|
__sigdelset (set, SIGCANCEL);
|
|
__sigdelset (set, SIGSETXID);
|
|
}
|
|
|
|
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,
|
|
set, _NSIG / 8);
|
|
}
|
|
|
|
/* Block all application signals (excluding internal glibc ones). */
|
|
static inline int
|
|
__libc_signal_block_app (sigset_t *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);
|
|
}
|
|
|
|
/* Restore current process signal mask. */
|
|
static inline int
|
|
__libc_signal_restore_set (const sigset_t *set)
|
|
{
|
|
INTERNAL_SYSCALL_DECL (err);
|
|
return INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, set, NULL,
|
|
_NSIG / 8);
|
|
}
|
|
|
|
/* Used to communicate with signal handler. */
|
|
extern struct xid_command *__xidcmd attribute_hidden;
|
|
|
|
#endif
|