signal.c revision 9acbbeaf2a1ffe5c14b244867d427714fab43c5c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/segments.h>
#include <sys/lx_types.h>
#include <sys/lx_brand.h>
#include <sys/lx_debug.h>
#include <sys/lx_signal.h>
#include <sys/lx_syscall.h>
#include <sys/lx_thread.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <thread.h>
#include <ucontext.h>
#include <unistd.h>
#include <stdio.h>
#include <libintl.h>
#include <ieeefp.h>
/*
* Delivering signals to a Linux process is complicated by differences in
* signal numbering, stack structure and contents, and the action taken when a
* signal handler exits. In addition, many signal-related structures, such as
* sigset_ts, vary between Solaris and Linux.
*
* To support user-level signal handlers, the brand uses a double layer of
* indirection to process and deliver signals to branded threads.
*
* When a Linux process sends a signal using the kill(2) system call, we must
* translate the signal into the Solaris equivalent before handing control off
* to the standard signalling mechanism. When a signal is delivered to a Linux
* process, we translate the signal number from Solaris to back to Linux.
* Translating signals both at generation and delivery time ensures both that
* Solaris signals are sent properly to Linux applications and that signals'
* default behavior works as expected.
*
* In a normal Solaris process, signal delivery is interposed on for any thread
* registering a signal handler by libc. Libc needs to do various bits of magic
* to provide thread-safe critical regions, so it registers its own handler,
* named sigacthandler(), using the sigaction(2) system call. When a signal is
* received, sigacthandler() is called, and after some processing, libc turns
* around and calls the user's signal handler via a routine named
* call_user_handler().
*
* Adding a Linux branded thread to the mix complicates things somewhat.
*
* First, when a thread receives a signal, it may be running with a Linux value
* in the x86 %gs segment register as opposed to the value Solaris threads
* expect; if control were passed directly to Solaris code, such as libc's
* sigacthandler(), that code would experience a segmentation fault the first
* time it tried to dereference a memory location using %gs.
*
* Second, the signal number translation referenced above must take place.
* Further, as was the case with Solaris libc, before the Linux signal handler
* is called, the value of the %gs segment register MUST be restored to the
* value Linux code expects.
*
* This need to translate signal numbers and manipulate the %gs register means
* that while with standard Solaris libc, following a signal from generation to
* delivery looks something like:
*
* kernel ->
* sigacthandler() ->
* call_user_handler() ->
* user signal handler
*
* while for the brand's Linux threads, this would look like:
*
* kernel ->
* lx_sigacthandler() ->
* sigacthandler() ->
* call_user_handler() ->
* lx_call_user_handler() ->
* Linux user signal handler
*
* The new addtions are:
*
* lx_sigacthandler
* ================
* This routine is responsible for setting the %gs segment register to the
* value Solaris code expects, and jumping to Solaris' libc signal
* interposition handler, sigacthandler().
*
* lx_call_user_handler
* ====================
* This routine is responsible for translating Solaris signal numbers to
* their Linux equivalents, building a Linux signal stack based on the
* information Solaris has provided, and passing the stack to the
* registered Linux signal handler. It is, in effect, the Linux thread
* equivalent to libc's call_user_handler().
*
* Installing lx_sigacthandler() is a bit tricky, as normally libc's
* sigacthandler() routine is hidden from user programs. To facilitate this, a
* new private function was added to libc, setsigaction():
*
* void setsigacthandler(void (*new_handler)(int, siginfo_t *, void *),
* void (**old_handler)(int, siginfo_t *, void *))
*
* The routine works by modifying the per-thread data structure libc already
* keeps that keeps track of the address of its own interposition handler with
* the address passed in; the old handler's address is set in the pointer
* pointed to by the second argument, if it is non-NULL, mimicking the behavior
* of sigaction() itself. Once setsigacthandler() has been executed, all
* future branded threads this thread may create will automatically have the
* proper interposition handler installed as the result of a normal
* sigaction() call.
*
* Note that none of this interposition is necessary unless a Linux thread
* registers a user signal handler, as the default action for all signals is the
* same between Solaris and Linux save for one signal, SIGPWR. For this reason,
* the brand ALWAYS installs its own internal signal handler for SIGPWR that
* translates the action to the Linux default, to terminate the process.
* (Solaris' default action is to ignore SIGPWR.)
*
* It is also important to note that when signals are not translated, the brand
* relies upon code interposing upon the wait(2) system call to translate
* signals to their proper values for any Linux threads retrieving the status
* of others. So while the Solaris signal number for a particular signal is set
* in a process' data structures (and would be returned as the result of say,
* WTERMSIG()), the brand's interposiiton upon wait(2) is responsible for
* translating the value WTERMSIG() would return from a Solaris signal number
* to the appropriate Linux value.
*
* The process of returning to an interrupted thread of execution from a user
* signal handler is entirely different between Solaris and Linux. While
* Solaris generally expects to set the context to the interrupted one on a
* normal return from a signal handler, in the normal case Linux instead calls
* code that calls a specific Linux system call, sigreturn(2). Thus when a
* Linux signal handler completes execution, instead of returning through what
* would in libc be a call to setcontext(2), the sigreturn(2) Linux system call
* is responsible for accomplishing much the same thing.
*
* This trampoline code looks something like this:
*
* pop %eax
* mov LX_SYS_rt_sigreturn, %eax
* int $0x80
*
* so when the Linux user signal handler is eventually called, the stack looks
* like this (in the case of an "lx_sigstack" stack:
*
* =========================================================
* | Pointer to actual trampoline code (in code segment) |
* =========================================================
* | Linux signal number |
* =========================================================
* | Pointer to Linux siginfo_t (or NULL) |
* =========================================================
* | Pointer to Linux ucontext_t (or NULL) |
* =========================================================
* | Linux siginfo_t |
* =========================================================
* | Linux ucontext_t |
* =========================================================
* | Linux struct _fpstate |
* =========================================================
* | Trampoline code (marker for gdb, not really executed) |
* =========================================================
*
* The brand takes the approach of intercepting the Linux sigreturn(2) system
* call in order to turn it into the return through the libc call stack that
* Solaris expects. This is done by the lx_sigreturn() and lx_rt_sigreturn()
* routines, which remove the Linux signal frame from the stack and pass the
* resulting stack pointer to another routine, lx_sigreturn_tolibc(), which
* makes libc believe the user signal handler it had called returned.
*
* (Note that the trampoline code actually lives in a proper executable segment
* and not on the stack, but gdb checks for the exact code sequence of the
* trampoline code on the stack to determine whether it is in a signal stack
* frame or not. Really.)
*
* When control then returns to libc's call_user_handler() routine, a
* setcontext(2) will be done that (in most cases) returns the thread executing
* the code back to the location originally interrupted by receipt of the
* signal.
*/
/*
* Two flavors of Linux signal stacks:
*
* lx_sigstack - used for "modern" signal handlers, in practice those
* that have the sigaction(2) flag SA_SIGINFO set
*
* lx_oldsigstack - used for legacy signal handlers, those that do not have
* the sigaction(2) flag SA_SIGINFO set or that were setup via
* the signal(2) call.
*
* NOTE: Since these structures will be placed on the stack and stack math will
* be done with their sizes, they must be word aligned in size (32 bits)
* so the stack remains word aligned per the i386 ABI.
*/
struct lx_sigstack {
void (*retaddr)(); /* address of real lx_rt_sigreturn code */
int sig; /* signal number */
};
struct lx_oldsigstack {
void (*retaddr)(); /* address of real lx_sigreturn code */
int sig; /* signal number */
int sig_extra; /* signal mask for signals [32 .. NSIG - 1] */
};
/*
* libc_sigacthandler is set to the address of the libc signal interposition
* routine, sigacthandler().
*/
void (*libc_sigacthandler)(int, siginfo_t *, void*);
/*
* The lx_sighandlers structure needs to be a global due to the semantics of
* clone().
*
* If CLONE_SIGHAND is set, the calling process and child share signal
* handlers, and if either calls sigaction(2) it should change the behavior
* in the other thread. Each thread does, however, have its own signal mask
* and set of pending signals.
*
* If CLONE_SIGHAND is not set, the child process should inherit a copy of
* the signal handlers at the time of the clone() but later calls to
* sigaction(2) should only affect the individual thread calling it.
*
* This maps perfectly to a thr_create(3C) thread semantic in the first
* case and a fork(2)-type semantic in the second case. By making
* lx_sighandlers global, we automatically get the correct behavior.
*/
static lx_sighandlers_t lx_sighandlers;
/*
* stol_stack() and ltos_stack() convert between Solaris and Linux stack_t
* structures.
*
* These routines are needed because although the two structures have the same
* contents, their contents are declared in a different order, so the content
* of the structures cannot be copied with a simple bcopy().
*/
static void
{
}
static void
{
}
static int
{
lx_sigset_t l;
return (-errno);
(void) sigemptyset(s_sigsetp);
if (lx_sigismember(&l, lx_sig) &&
}
return (0);
}
static int
{
lx_sigset_t l;
bzero(&l, sizeof (lx_sigset_t));
lx_sigaddset(&l, lx_sig);
}
? -errno : 0);
}
static int
{
return (-errno);
(void) sigemptyset(s_sigsetp);
return (0);
}
static int
{
lx_osigset_t lo = 0;
/*
* Note that an lx_osigset_t can only represent the signals from
* [1 .. OSIGSET_NBITS], so even though a signal may be present in the
* Solaris sigset_t, it may not be representable as a bit in the
* lx_osigset_t.
*/
(lx_sig <= OSIGSET_NBITS))
? -errno : 0);
}
static int
stol_sigcode(int si_code)
{
switch (si_code) {
case SI_USER:
return (LX_SI_USER);
case SI_LWP:
return (LX_SI_TKILL);
case SI_QUEUE:
return (LX_SI_QUEUE);
case SI_TIMER:
return (LX_SI_TIMER);
case SI_ASYNCIO:
return (LX_SI_ASYNCIO);
case SI_MESGQ:
return (LX_SI_MESGQ);
default:
return (si_code);
}
}
static int
{
return (-1);
}
switch (lx_siginfo.lsi_signo) {
/*
* Semantics ARE defined for SIGKILL, but since
* we can't catch it, we can't translate it. :-(
*/
case LX_SIGPOLL:
break;
case LX_SIGCHLD:
break;
case LX_SIGILL:
case LX_SIGBUS:
case LX_SIGFPE:
break;
default:
break;
}
? -errno : 0);
}
static void
{
/*
* The Solaris struct _fpstate and lx_fpstate_t are identical from the
* beginning of the structure to the lx_fpstate_t "magic" field, so
* just bcopy() those entries.
*/
/*
* These fields are all only significant for the first 16 bits.
*/
/*
* Linux uses the "magic" field to denote whether the XMM
* registers contain legal data or not. Since we can't get to
* %cr4 from userland to check the status of the OSFXSR bit,
* check the mxcsr field to see if it's 0, which it should
* never be on a system with the OXFXSR bit enabled.
*/
} else {
}
}
static void
{
/*
* The lx_fpstate_t and Solaris struct _fpstate are identical from the
* beginning of the structure to the struct _fpstate "mxcsr" field, so
* just bcopy() those entries.
*/
/*
* These fields are all only significant for the first 16 bits.
*/
}
/*
* The brand needs a lx version of this because the format of the lx stack_t
* differs from the Solaris stack_t not really in content but in ORDER,
* so we can't simply pass pointers and expect things to work (sigh...)
*/
int
{
if (nsp) {
return (-errno);
return (-ENOMEM);
}
return (-errno);
if (osp) {
return (-errno);
}
return (0);
}
/*
* The following routines are needed because sigset_ts and siginfo_ts are
* different in format between Linux and Solaris.
*
* Note that there are two different lx_sigset structures, lx_sigset_ts and
* lx_osigset_ts:
*
* + An lx_sigset_t is the equivalent of a Solaris sigset_t and supports
* more than 32 signals.
*
* + An lx_osigset_t is simply a uint32_t, so it by definition only supports
* 32 signals.
*
* When there are two versions of a routine, one prefixed with lx_rt_ and
* one prefixed with lx_ alone, in GENERAL the lx_rt_ routines deal with
* lx_sigset_ts while the lx_ routines deal with lx_osigset_ts. Unfortunately,
* this is not always the case (e.g. lx_sigreturn() vs. lx_rt_sigreturn())
*/
int
{
if (sigpending(&sigpendset) != 0)
return (-errno);
}
int
{
return (-EINVAL);
if (sigpending(&sigpendset) != 0)
return (-errno);
}
/*
* Create a common routine to encapsulate all of the sigprocmask code,
* as the only difference between lx_sigprocmask() and lx_rt_sigprocmask()
* is the usage of lx_osigset_ts vs. lx_sigset_ts, as toggled in the code by
* the setting of the "sigset_type" flag.
*/
static int
{
int err;
if (l_setp) {
switch (how) {
case LX_SIG_BLOCK:
break;
case LX_SIG_UNBLOCK:
how = SIG_UNBLOCK;
break;
case LX_SIG_SETMASK:
how = SIG_SETMASK;
break;
default:
return (-EINVAL);
}
if (sigset_type == USE_SIGSET)
else
if (err != 0)
return (err);
}
/*
* In a multithreaded environment, a call to sigprocmask(2) should
* only affect the current thread's signal mask so we don't need to
* explicitly call thr_sigsetmask(3C) here.
*/
return (-errno);
if (l_osetp) {
if (sigset_type == USE_SIGSET)
else
if (err != 0) {
/*
* Encountered a fault while writing to the old signal
* mask buffer, so unwind the signal mask change made
* above.
*/
return (err);
}
}
return (0);
}
int
{
}
int
lx_sgetmask(void)
{
}
int
{
}
int
{
return (-EINVAL);
}
int
{
return (-errno);
}
int
{
return (-EINVAL);
return (-errno);
}
int
{
int rc;
return (-errno);
return (-errno);
return (rc);
}
int
{
int rc;
return (-EINVAL);
return (-errno);
return (-errno);
return (rc);
}
int
{
int rc;
return (-errno);
return (-errno);
return (rc);
}
int
{
int rc;
return (-EINVAL);
return (-errno);
return (-errno);
return (rc);
}
/*
* Intercept the Linux sigreturn() syscall to turn it into the return through
* the libc call stack that Solaris expects.
*
* When control returns to libc's call_user_handler() routine, a setcontext(2)
* will be done that returns thread execution to the point originally
* interrupted by receipt of the signal.
*/
int
lx_sigreturn(void)
{
struct lx_oldsigstack *lx_ossp;
rp = lx_syscall_regs();
/*
* NOTE: The sp saved in the context is eight bytes off of where we
* need it to be.
*/
/*
* At this point, the stack pointer should point to the struct
* lx_oldsigstack that lx_build_old_signal_frame() constructed and
* placed on the stack. We need to reference it a bit later, so
* save a pointer to it before incrementing our copy of the sp.
*/
sp += sizeof (struct lx_oldsigstack);
/*
* lx_sigdeliver() pushes LX_SIGRT_MAGIC on the stack before it
* creates the struct lx_oldsigstack.
*
* If we don't find it here, the stack's been corrupted and we need to
* kill ourselves.
*/
"sp @ 0x%p, expected 0x%x, found 0x%x!"),
/*
* For signal mask handling to be done properly, this call needs to
* return to the libc routine that originally called the signal handler
* rather than directly set the context back to the place the signal
* interrupted execution as the original Linux code would do.
*
* Here *sp points to the Solaris ucontext_t, so we need to copy
* machine registers the Linux signal handler may have modified
* back to the Solaris version.
*/
/* general registers copy back as-is */
/* copy back FP regs if present */
/* convert Linux signal mask back to its Solaris equivalent */
/*
* At this point sp contains the value of the stack pointer when
* lx_call_user_handler() was called.
*
* Pop one more value off the stack and pass the new sp to
* lx_sigreturn_tolibc(), which will in turn manipulate the x86
* registers to make it appear to libc's call_user_handler() as if the
* handler it had called returned.
*/
/*NOTREACHED*/
return (0);
}
int
lx_rt_sigreturn(void)
{
struct lx_sigstack *lx_ssp;
rp = lx_syscall_regs();
/*
* NOTE: Because of some silly compatibility measures done in the
* signal trampoline code to make sure it uses the _exact same_
* instruction sequence Linux does, we have to manually "pop"
* one extra four byte instruction off the stack here before
* passing the stack address to the syscall because the
* trampoline code isn't allowed to do it.
*
* No, I'm not kidding.
*
* The sp saved in the context is eight bytes off of where we
* need it to be, so the need to pop the extra four byte
* instruction means we need to subtract a net four bytes from
* the sp before "popping" the struct lx_sigstack off the stack.
* This will yield the value the stack pointer had before
* lx_sigdeliver() created the stack frame for the Linux signal
* handler.
*/
/*
* At this point, the stack pointer should point to the struct
* lx_sigstack that lx_build_signal_frame() constructed and
* placed on the stack. We need to reference it a bit later, so
* save a pointer to it before incrementing our copy of the sp.
*/
sp += sizeof (struct lx_sigstack);
/*
* lx_sigdeliver() pushes LX_SIGRT_MAGIC on the stack before it
* creates the struct lx_sigstack (and possibly struct lx_fpstate_t).
*
* If we don't find it here, the stack's been corrupted and we need to
* kill ourselves.
*/
/*
* For signal mask handling to be done properly, this call needs to
* return to the libc routine that originally called the signal handler
* rather than directly set the context back to the place the signal
* interrupted execution as the original Linux code would do.
*
* Here *sp points to the Solaris ucontext_t, so we need to copy
* machine registers the Linux signal handler may have modified
* back to the Solaris version.
*/
/* general registers copy back as-is */
sizeof (gregset_t));
/*
* Convert the Linux signal mask and stack back to their
* Solaris equivalents.
*/
}
/*
* At this point sp contains the value of the stack pointer when
* lx_call_user_handler() was called.
*
* Pop one more value off the stack and pass the new sp to
* lx_sigreturn_tolibc(), which will in turn manipulate the x86
* registers to make it appear to libc's call_user_handler() as if the
* handler it had called returned.
*/
/*NOTREACHED*/
return (0);
}
/*
* Build signal frame for processing for "old" (legacy) Linux signals
*/
static void
{
extern void lx_sigreturn_tramp();
struct lx_sigaction *lxsap;
lxsap->lxsa_restorer) {
} else {
}
/* convert Solaris signal mask and stack to their Linux equivalents */
/* general registers copy across as-is */
/*
* cr2 contains the faulting address, and Linux only sets cr2 for a
* a segmentation fault.
*/
/* convert FP regs if present */
} else
/*
* Believe it or not, gdb wants to SEE the trampoline code on the
* bottom of the stack to determine whether the stack frame belongs to
* a signal handler, even though this code is no longer actually
* called.
*
* You can't make this stuff up.
*/
sizeof (lx_ossp->trampoline));
}
/*
* Build signal frame for processing for modern Linux signals
*/
static void
{
extern void lx_rt_sigreturn_tramp();
struct lx_sigaction *lxsap;
lxsap->lxsa_restorer) {
} else {
}
/* Linux has these fields but always clears them to 0 */
/* convert Solaris signal mask and stack to their Linux equivalents */
/* general registers copy across as-is */
/*
* cr2 contains the faulting address, which Linux only sets for a
* a segmentation fault.
*/
/*
* Point the lx_siginfo_t pointer to the signal stack's lx_siginfo_t
* if there was a Solaris siginfo_t to convert, otherwise set it to
* NULL.
*/
else
/* convert FP regs if present */
/*
* Copy FP regs to the appropriate place in the the lx_sigstack
* structure.
*/
} else
/*
* Believe it or not, gdb wants to SEE the trampoline code on the
* bottom of the stack to determine whether the stack frame belongs to
* a signal handler, even though this code is no longer actually
* called.
*
* You can't make this stuff up.
*/
sizeof (lx_ssp->trampoline));
}
/*
* This is the second level interposition handler for Linux signals.
*/
static void
{
void (*user_handler)();
void (*stk_builder)();
struct lx_sigaction *lxsap;
/*
* If Solaris signal has no Linux equivalent, effectively
* ignore it.
*/
return;
}
lx_debug("interpose caught solaris signal %d, translating to Linux "
/* Linux SIG_DFL for SIGPWR is to terminate */
}
"lxsa_handler",
"%s: unable to read thread-specific data: %s"),
/*
* Any zero %gs value should be caught when a save is attempted in
* lx_emulate(), but this extra check will catch any zero values due to
* bugs in the library.
*/
stksize = sizeof (struct lx_sigstack);
} else {
stksize = sizeof (struct lx_oldsigstack);
}
/*
* lx_sigdeliver() doesn't return, so it relies on the Linux
* signal handlers to clean up the stack, reset the current
* signal mask and return to the code interrupted by the signal.
*/
}
/*
* Common routine to modify sigaction characteristics of a thread.
*
* We shouldn't need any special locking code here as we actually use
* libc's sigaction() to do all the real work, so its thread locking should
* take care of any issues for us.
*/
static int
struct lx_sigaction *olxsp)
{
struct lx_sigaction *lxsap;
return (-EINVAL);
return (-errno);
struct lx_sigaction lxsa;
return (-errno);
/*
* Block this signal while messing with its dispostion
*/
(void) sigemptyset(&new_set);
return (-err);
}
/*
* We don't really need the old signal disposition at
* this point, but this weeds out signals that would
* cause sigaction() to return an error before we change
* anything other than the current signal mask.
*/
lx_debug("sigaction() to get old "
"disposition for signal %d failed: "
return (-err);
}
/*
* The interposition signal handler needs the
* information provided via the SA_SIGINFO flag.
*/
/*
* Can't use RESETHAND with SIGPWR due to
* different default actions between Linux
* and Solaris.
*/
NULL);
return (-err);
}
lx_debug("interposing handler @ 0x%p for "
"signal %d (lx %d), flags 0x%x",
lx_debug("sigaction() to set new "
"disposition for signal %d failed: "
NULL);
return (-err);
}
/*
* There's no need to interpose for SIG_DFL or
* SIG_IGN so just call libc's sigaction(), but
* don't allow SIG_DFL for SIGPWR due to
* differing default actions between Linux and
* Solaris.
*
* Get the previous disposition first so things
* like sa_mask and sa_flags are preserved over
* a transition to SIG_DFL or SIG_IGN, which is
* what Linux expects.
*/
lx_debug("sigaction(%d, %s) failed: %s",
"SIG_DFL" : "SIG_IGN"),
NULL);
return (-err);
}
}
} else {
lx_debug("Linux signal with no kill support "
"specified: %d", lx_sig);
}
/*
* Save the new disposition for the signal in the global
* lx_sighandlers structure.
*/
/*
* Reset the signal mask to what we came in with if
* we were modifying a kill-supported signal.
*/
if (sig != -1)
}
return (0);
}
int
{
int val;
struct lx_osigaction *osp;
/*
* If we have a source pointer, convert source lxsa_mask from
* lx_osigset_t to lx_sigset_t format.
*/
if (sap) {
}
return (val);
/*
* If we have a save pointer, convert the old lxsa_mask from
* lx_sigset_t to lx_osigset_t format.
*/
if (osap) {
}
return (0);
}
int
{
/*
* The "new" rt_sigaction call checks the setsize
* parameter.
*/
return (-EINVAL);
(struct lx_sigaction *)oactp));
}
/*
* Convert signal syscall to a call to the lx_sigaction() syscall
*/
int
{
int rc;
/*
* Use sigaction to mimic SYSV signal() behavior; glibc will
* actually call sigaction(2) itself, so we're really reaching
* back for signal(2) semantics here.
*/
}
int
{
return (-EINVAL);
"BrandZ tgkill(2) does not support gid != pid\n"));
return (-ENOTSUP);
}
/*
* Pad the lx_tkill() call with NULLs to match the IN_KERNEL_SYSCALL
* prototype generated for it by IN_KERNEL_SYSCALL in lx_brand.c.
*/
}
/*
* This C routine to save the passed %gs value into the thread-specific save
* area is called by the assembly routine lx_sigacthandler.
*/
void
{
int err;
/*
* While a %gs of 0 is technically legal (as long as the application
* never dereferences memory using %gs), Solaris has its own ideas as
* to how a zero %gs should be handled in _update_sregs(), such that
* any 32-bit user process with a %gs of zero running on a system with
* a 64-bit kernel will have its %gs hidden base register stomped on on
* return from a system call, leaving an incorrect base address in
* place until the next time %gs is actually reloaded (forcing a reload
* of the base address from the appropriate descriptor table.)
*
* Of course the kernel will once again stomp on THAT base address when
* returning from a system call, resulting in an application
* segmentation fault.
*
* To avoid this situation, disallow a save of a zero %gs here in order
* to try and capture any Linux process that takes a signal with a zero
* %gs installed.
*/
assert(signalled_gs != 0);
if (signalled_gs != LWPGS_SEL) {
(void **)&lx_tsd)) != 0)
"%s: unable to read thread-specific data: %s"),
lx_debug("lx_sigsavegs(): gsp 0x%p, saved gs: 0x%x\n",
}
}
int
lx_siginit(void)
{
extern void set_setcontext_enforcement(int);
extern void lx_sigacthandler(int, siginfo_t *, void *);
/*
* Block all signals possible while setting up the signal imposition
* mechanism.
*/
(void) sigfillset(&new_set);
/*
* Ignore any signals that have no Linux analog so that those
* signals cannot be sent to Linux processes from the global zone
*/
if (stol_signo[sig] < 0)
/*
* As mentioned previously, when a user signal handler is installed
* via sigaction(), libc interposes on the mechanism by actually
* installing an internal routine sigacthandler() as the signal
* handler. On receipt of the signal, libc does some thread-related
* processing via sigacthandler(), then calls the registered user
* signal handler on behalf of the user.
*
* We need to interpose on that mechanism to make sure the correct
* %gs segment register value is installed before the libc routine
* is called, otherwise the libc code will die with a segmentation
* fault.
*
* The private libc routine setsigacthandler() will set our
* interposition routine, lx_sigacthandler(), as the default
* "sigacthandler" routine for all new signal handlers for this
* thread.
*/
lx_debug("lx_sigacthandler installed, libc_sigacthandler = 0x%p",
/*
* Mark any signals that are ignored as ignored in our interposition
* handler array
*/
"disposition for signal %d: %s"),
lx_debug("marking signal %d (lx %d) as SIG_IGN",
}
}
/*
* Have our interposition handler handle SIGPWR to start with,
* as it has a default action of terminating the process in Linux
* but its default is to be ignored in Solaris.
*/
/*
* Solaris' libc forces certain register values in the ucontext_t
* used to restore a post-signal user context to be those Solaris
* expects; however that is not what we want to happen if the signal
* was taken while branded code was executing, so we must disable
* that behavior.
*/
/*
* Reset the signal mask to what we came in with
*/
lx_debug("interposition handler setup for SIGPWR");
return (0);
}