* Copyright (c) 2014-2020 Google, Inc. All rights reserved.
* **********************************************************/
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
* signal_linux_arm.c - Linux and ARM specific signal code
*/
#include "signal_private.h"
#ifndef LINUX
# error Linux-only
#endif
#ifndef ARM
# error ARM-only
#endif
#include "arch.h"
#define VFP_QUERY_SIG SIGILL
void
save_fpstate(dcontext_t *dcontext, sigframe_rt_t *frame)
{
ASSERT_NOT_IMPLEMENTED(false);
}
#ifdef DEBUG
void
dump_sigcontext(dcontext_t *dcontext, sigcontext_t *sc)
{
LOG(THREAD, LOG_ASYNCH, 1, "\tr0 =" PFX "\n", sc->SC_R0);
LOG(THREAD, LOG_ASYNCH, 1, "\tr1 =" PFX "\n", sc->SC_R1);
LOG(THREAD, LOG_ASYNCH, 1, "\tr2 =" PFX "\n", sc->SC_R2);
LOG(THREAD, LOG_ASYNCH, 1, "\tr3 =" PFX "\n", sc->SC_R3);
LOG(THREAD, LOG_ASYNCH, 1, "\tr4 =" PFX "\n", sc->SC_R4);
LOG(THREAD, LOG_ASYNCH, 1, "\tr5 =" PFX "\n", sc->SC_R5);
LOG(THREAD, LOG_ASYNCH, 1, "\tr6 =" PFX "\n", sc->SC_R6);
LOG(THREAD, LOG_ASYNCH, 1, "\tr7 =" PFX "\n", sc->SC_R7);
LOG(THREAD, LOG_ASYNCH, 1, "\tr8 =" PFX "\n", sc->SC_R8);
LOG(THREAD, LOG_ASYNCH, 1, "\tr9 =" PFX "\n", sc->SC_R9);
LOG(THREAD, LOG_ASYNCH, 1, "\tr10 =" PFX "\n", sc->SC_R10);
LOG(THREAD, LOG_ASYNCH, 1, "\tr11 =" PFX "\n", sc->SC_R11);
LOG(THREAD, LOG_ASYNCH, 1, "\tr12 =" PFX "\n", sc->SC_R12);
LOG(THREAD, LOG_ASYNCH, 1, "\tsp =" PFX "\n", sc->SC_XSP);
LOG(THREAD, LOG_ASYNCH, 1, "\tr14 =" PFX "\n", sc->SC_LR);
LOG(THREAD, LOG_ASYNCH, 1, "\tpc =" PFX "\n", sc->SC_XIP);
LOG(THREAD, LOG_ASYNCH, 1, "\tcpsr=" PFX "\n", sc->SC_XFLAGS);
}
#endif
* enabled but the hardware is not present or not used: the VFP frame in the
* ucontext is offset as if there were a preceding IWMMXT frame, though this
* memory is in fact not written to by the kernel. In 4.13 the bug is fixed
* with a minimal change to the ABI by writing a dummy padding block before
* the VFP frame. We work around the bug and handle all cases by sending
* ourselves a signal at init time and looking for the VFP frame in both places.
* If it was found in the offset position we do not expect there to be a valid
* dummy padding block when reading the sigcontext but we create one when
* writing the sigcontext.
* XXX: Because of the IWMMXT space being unset by earlier kernels it is
* possible that we might find the VFP frame header in both places. We could
* guard against that by clearing some memory below the SP before sending the
* signal (assuming sigaltstack has not been used).
*/
static bool vfp_is_offset = false;
void
sigcontext_to_mcontext_simd(priv_mcontext_t *mc, sig_full_cxt_t *sc_full)
{
if (sc_full->fp_simd_state == NULL)
return;
char *frame = ((char *)sc_full->fp_simd_state +
(vfp_is_offset ? sizeof(kernel_iwmmxt_sigframe_t) : 0));
kernel_vfp_sigframe_t *vfp = (kernel_vfp_sigframe_t *)frame;
ASSERT(sizeof(mc->simd) == sizeof(vfp->ufp.fpregs));
ASSERT(vfp->magic == VFP_MAGIC);
ASSERT(vfp->size == sizeof(kernel_vfp_sigframe_t));
memcpy(&mc->simd[0], &vfp->ufp.fpregs[0], sizeof(mc->simd));
}
void
mcontext_to_sigcontext_simd(sig_full_cxt_t *sc_full, priv_mcontext_t *mc)
{
if (sc_full->fp_simd_state == NULL)
return;
char *frame = (char *)sc_full->fp_simd_state;
kernel_vfp_sigframe_t *vfp;
if (vfp_is_offset) {
kernel_iwmmxt_sigframe_t *dummy = (kernel_iwmmxt_sigframe_t *)frame;
dummy->magic = DUMMY_MAGIC;
dummy->size = sizeof(*dummy);
frame += sizeof(*dummy);
}
vfp = (kernel_vfp_sigframe_t *)frame;
ASSERT(sizeof(mc->simd) == sizeof(vfp->ufp.fpregs));
vfp->magic = VFP_MAGIC;
vfp->size = sizeof(kernel_vfp_sigframe_t);
memcpy(&vfp->ufp.fpregs[0], &mc->simd[0], sizeof(vfp->ufp.fpregs));
vfp[1].magic = 0;
}
size_t
signal_frame_extra_size(bool include_alignment)
{
return 0;
}
static void
vfp_query_signal_handler(int sig, kernel_siginfo_t *siginfo, kernel_ucontext_t *ucxt)
{
uint offset = sizeof(kernel_iwmmxt_sigframe_t);
char *coproc = (char *)&ucxt->coproc;
* exactly one of them. See longer comment above.
*/
kernel_vfp_sigframe_t *vfp0 = (kernel_vfp_sigframe_t *)coproc;
kernel_vfp_sigframe_t *vfp1 = (kernel_vfp_sigframe_t *)(coproc + offset);
bool vfp0_good =
(vfp0->magic == VFP_MAGIC && vfp0->size == sizeof(kernel_vfp_sigframe_t));
bool vfp1_good =
(vfp1->magic == VFP_MAGIC && vfp1->size == sizeof(kernel_vfp_sigframe_t));
ASSERT(offset == 160);
if (vfp0_good == vfp1_good) {
SYSLOG(SYSLOG_CRITICAL, CANNOT_FIND_VFP_FRAME, 2, get_application_name(),
get_application_pid());
os_terminate(NULL, TERMINATE_PROCESS);
}
vfp_is_offset = vfp1_good;
ASSERT(!(vfp0->magic == IWMMXT_MAGIC && vfp0->size == offset));
}
void
signal_arch_init(void)
{
kernel_sigaction_t act, oldact;
int rc;
memset(&act, 0, sizeof(act));
set_handler_sigact(&act, VFP_QUERY_SIG, (handler_t)vfp_query_signal_handler);
rc = sigaction_syscall(VFP_QUERY_SIG, &act, &oldact);
ASSERT(rc == 0);
thread_signal(get_process_id(), get_sys_thread_id(), VFP_QUERY_SIG);
rc = sigaction_syscall(VFP_QUERY_SIG, &oldact, NULL);
ASSERT(rc == 0);
}