| |
| /*--------------------------------------------------------------------*/ |
| /*--- Create/destroy signal delivery frames. ---*/ |
| /*--- sigframe-solaris.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2011-2015 Petr Pavlu |
| setup@dagobah.cz |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program 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 |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| #if defined(VGP_x86_solaris) || defined(VGP_amd64_solaris) |
| |
| #include "pub_core_basics.h" |
| #include "pub_core_vki.h" |
| #include "pub_core_threadstate.h" |
| #include "pub_core_aspacemgr.h" |
| #include "pub_core_libcassert.h" |
| #include "pub_core_libcbase.h" |
| #include "pub_core_libcprint.h" |
| #include "pub_core_machine.h" |
| #include "pub_core_options.h" |
| #include "pub_core_signals.h" |
| #include "pub_core_tooliface.h" |
| #include "pub_core_sigframe.h" /* Self */ |
| #include "pub_core_syswrap.h" |
| #include "priv_sigframe.h" |
| |
| /* This module creates and removes signal frames for signal deliveries |
| on x86/amd64-solaris. */ |
| |
| /* Create a signal frame for thread 'tid'. Make a 3-arg frame regardless of |
| whether the client originally requested a 1-arg version (no SA_SIGINFO) or |
| a 3-arg one (SA_SIGINFO) since in the former case, the x86/amd64 calling |
| conventions will simply cause the extra 2 args to be ignored (inside the |
| handler). */ |
| void VG_(sigframe_create)(ThreadId tid, Bool on_altstack, |
| Addr sp_top_of_frame, const vki_siginfo_t *siginfo, |
| const struct vki_ucontext *siguc, |
| void *handler, UInt flags, const vki_sigset_t *mask, |
| void *restorer) |
| { |
| ThreadState *tst = VG_(get_ThreadState)(tid); |
| Addr esp; |
| vki_sigframe_t *frame; |
| Int signo = siginfo->si_signo; |
| |
| /* Calculate new stack pointer. */ |
| esp = sp_top_of_frame - sizeof(vki_sigframe_t); |
| esp = VG_ROUNDDN(esp, 16) - sizeof(UWord); |
| |
| if (!ML_(sf_maybe_extend_stack)(tst, esp, sp_top_of_frame - esp, flags)) |
| return; |
| |
| /* Fill in the frame. */ |
| frame = (vki_sigframe_t*)esp; |
| |
| /* Set a bogus return address. This return address should be never used |
| because to return from a signal handler a program has to call |
| setcontext() explicitly. */ |
| frame->return_addr = (void*)~0UL; |
| |
| /* Save current context. (This has to be done before the thread state is |
| modified in any way.) */ |
| VG_(save_context)(tid, &frame->ucontext, Vg_CoreSignal); |
| |
| /* Fill in the siginfo. */ |
| frame->siginfo = *siginfo; |
| /* Set expected si_addr value. |
| |
| Manual page siginfo.h(3HEAD) describes that some signals define si_addr |
| to be an address of the faulting instruction (SIGILL). Then it is needed |
| to change the real CPU address to the VCPU address. Some signals define |
| si_addr to be an address of the faulting memory reference (SIGSEGV, |
| SIGBUS). Then the address should be passed unmodified. |
| |
| However documentation contained in the manpage does not reflect the |
| reality found in the Solaris kernel - uts/<arch>/os/trap.c. Here one can |
| observe that in some cases si_addr is set to address provided by the |
| underlying subsystem. In some cases si_addr is set to the current |
| program counter. Other signals are missing documentation altogether. |
| It is almost impossible to determine what value is stored in si_addr |
| based on the information provided by kernel to the signal handler. |
| |
| POSIX.1-2008 says about si_addr: |
| SIGILL, SIGFPE ... Address of faulting instruction. |
| SIGSEGV, SIGBUS ... Address of faulting memory reference. |
| For some implementations, the value of si_addr may be inaccurate. |
| |
| See tests none/tests/faultstatus and none/tests/x86/badseg for examples. |
| The code below simply follows the POSIX standard, but propagates any |
| possibly incorrect values from the kernel to the user. |
| */ |
| switch (signo) { |
| case VKI_SIGSEGV: |
| switch (siginfo->si_code) { |
| case VKI_SEGV_ACCERR: |
| case VKI_SEGV_MAPERR: |
| default: |
| break; |
| case VKI_SEGV_MADE_UP_GPF: |
| /* Translate si_code synthesized by Valgrind to SEGV_MAPPER. */ |
| frame->siginfo.si_code = VKI_SEGV_MAPERR; |
| break; |
| } |
| break; |
| case VKI_SIGBUS: |
| break; |
| case VKI_SIGFPE: |
| case VKI_SIGILL: |
| case VKI_SIGTRAP: |
| frame->siginfo.si_addr = (void*)VG_(get_IP)(tid); |
| break; |
| case VKI_SIGPROF: |
| frame->siginfo.si_faddr = (void*)VG_(get_IP)(tid); |
| break; |
| default: |
| break; |
| } |
| VG_TRACK(post_mem_write, Vg_CoreSignal, tid, (Addr)&frame->siginfo, |
| sizeof(frame->siginfo)); |
| |
| /* Save the signal number in an unused slot. Later, when a return from the |
| signal is made, this value is used to inform the tool that the |
| processing for the given signal has ended. */ |
| VKI_UC_SIGNO(&frame->ucontext) = signo | ((~(UWord)signo & 0xFFFF) << 16); |
| /* Old context has to point to the saved ucontext. */ |
| tst->os_state.oldcontext = &frame->ucontext; |
| /* Save ERR and TRAPNO if siguc is present. */ |
| if (siguc) { |
| frame->ucontext.uc_mcontext.gregs[VKI_REG_ERR] |
| = siguc->uc_mcontext.gregs[VKI_REG_ERR]; |
| VG_TRACK(post_mem_write, Vg_CoreSignal, tid, |
| (Addr)&frame->ucontext.uc_mcontext.gregs[VKI_REG_ERR], |
| sizeof(UWord)); |
| frame->ucontext.uc_mcontext.gregs[VKI_REG_TRAPNO] |
| = siguc->uc_mcontext.gregs[VKI_REG_TRAPNO]; |
| VG_TRACK(post_mem_write, Vg_CoreSignal, tid, |
| (Addr)&frame->ucontext.uc_mcontext.gregs[VKI_REG_TRAPNO], |
| sizeof(UWord)); |
| } |
| |
| /* Prepare parameters for a signal handler. */ |
| frame->a1_signo = signo; |
| /* The first parameter has to be 16-byte aligned, resembling function |
| calls. */ |
| { |
| /* Using |
| vg_assert(VG_IS_16_ALIGNED(&frame->a1_signo)); |
| seems to get miscompiled on amd64 with GCC 4.7.2. */ |
| Addr signo_addr = (Addr)&frame->a1_signo; |
| vg_assert(VG_IS_16_ALIGNED(signo_addr)); |
| } |
| frame->a2_siginfo = &frame->siginfo; |
| VG_TRACK(post_mem_write, Vg_CoreSignal, tid, (Addr)&frame->a1_signo, |
| sizeof(frame->a1_signo) + sizeof(frame->a2_siginfo)); |
| #if defined(VGP_x86_solaris) |
| frame->a3_ucontext = &frame->ucontext; |
| VG_TRACK(post_mem_write, Vg_CoreSignal, tid, (Addr)&frame->a3_ucontext, |
| sizeof(frame->a3_ucontext)); |
| #elif defined(VGP_amd64_solaris) |
| tst->arch.vex.guest_RDI = signo; |
| VG_TRACK(post_reg_write, Vg_CoreSignal, tid, offsetof(VexGuestAMD64State, |
| guest_RDI), sizeof(ULong)); |
| tst->arch.vex.guest_RSI = (Addr)&frame->siginfo; |
| VG_TRACK(post_reg_write, Vg_CoreSignal, tid, offsetof(VexGuestAMD64State, |
| guest_RSI), sizeof(ULong)); |
| tst->arch.vex.guest_RDX = (Addr)&frame->ucontext; |
| VG_TRACK(post_reg_write, Vg_CoreSignal, tid, offsetof(VexGuestAMD64State, |
| guest_RDX), sizeof(ULong)); |
| #endif |
| |
| /* Set up the stack pointer. */ |
| vg_assert(esp == (Addr)&frame->return_addr); |
| VG_(set_SP)(tid, esp); |
| VG_TRACK(post_reg_write, Vg_CoreSignal, tid, VG_O_STACK_PTR, sizeof(Addr)); |
| |
| /* Set up the program counter. Note that we don't inform a tool about IP |
| write because IP is always defined. */ |
| VG_(set_IP)(tid, (Addr)handler); |
| |
| /* If the signal is delivered on the alternate stack, copy it out to |
| ustack. This has to be done after setting a new IP so the SS_ONSTACK |
| flag is set by VG_(do_sys_sigaltstack)(). */ |
| if (on_altstack && tst->os_state.ustack |
| && VG_(am_is_valid_for_client)((Addr)tst->os_state.ustack, |
| sizeof(*tst->os_state.ustack), |
| VKI_PROT_WRITE)) { |
| SysRes res; |
| vki_stack_t altstack; |
| |
| /* Get information about alternate stack. */ |
| res = VG_(do_sys_sigaltstack)(tid, NULL, &altstack); |
| vg_assert(!sr_isError(res)); |
| |
| /* Copy it to ustack. */ |
| *tst->os_state.ustack = altstack; |
| VG_TRACK(post_mem_write, Vg_CoreSignal, tid, (Addr)tst->os_state.ustack, |
| sizeof(*tst->os_state.ustack)); |
| } |
| |
| if (VG_(clo_trace_signals)) |
| VG_(message)(Vg_DebugMsg, |
| "sigframe_create (thread %u): next IP=%#lx, " |
| "next SP=%#lx\n", |
| tid, (Addr)handler, (Addr)frame); |
| } |
| |
| void VG_(sigframe_destroy)(ThreadId tid, Bool isRT) |
| { |
| /* Not used on Solaris. */ |
| vg_assert(0); |
| } |
| |
| void VG_(sigframe_return)(ThreadId tid, const vki_ucontext_t *uc) |
| { |
| Int signo; |
| |
| /* Check if a signal number was saved in the restored context. */ |
| signo = VKI_UC_SIGNO_CONST(uc) & 0xFFFF; |
| if (!signo || signo != ((~VKI_UC_SIGNO_CONST(uc) >> 16) & 0xFFFF)) |
| return; |
| |
| /* Note: The active tool should be informed here about the dead stack area. |
| However, this was already done when the original context was restored (in |
| VG_(restore_context)()) so it is not necessary to do it here again. |
| |
| There is a small nuance though, VG_(restore_context)() triggers the |
| die_mem_stack event while in this case, it should really trigger the |
| die_mem_stack_signal event. This is not currently a problem because all |
| official tools handle these two events in the same way. |
| |
| If a return from an alternate stack is made then no die_mem_stack event |
| is currently triggered. */ |
| |
| /* Returning from a signal handler. */ |
| if (VG_(clo_trace_signals)) |
| VG_(message)(Vg_DebugMsg, |
| "sigframe_return (thread %u): IP=%#lx\n", |
| tid, VG_(get_IP)(tid)); |
| |
| /* Tell the tool. */ |
| VG_TRACK(post_deliver_signal, tid, signo); |
| } |
| |
| #endif // defined(VGP_x86_solaris) || defined(VGP_amd64_solaris) |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end ---*/ |
| /*--------------------------------------------------------------------*/ |