blob: 01c1c058c48b913313ef86ae02020fde899d0668 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Create/destroy signal delivery frames. ---*/
/*--- sigframe-arm-linux.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2000-2010 Nicholas Nethercote
njn@valgrind.org
Copyright (C) 2004-2010 Paul Mackerras
paulus@samba.org
Copyright (C) 2008-2010 Evan Geller
gaze@bea.ms
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_arm_linux)
#include "pub_core_basics.h"
#include "pub_core_vki.h"
#include "pub_core_vkiscnums.h"
#include "pub_core_threadstate.h"
#include "pub_core_aspacemgr.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
#include "pub_core_machine.h"
#include "pub_core_options.h"
#include "pub_core_sigframe.h"
#include "pub_core_signals.h"
#include "pub_core_tooliface.h"
#include "pub_core_trampoline.h"
#include "pub_core_transtab.h" // VG_(discard_translations)
struct vg_sig_private {
UInt magicPI;
UInt sigNo_private;
VexGuestARMState vex_shadow1;
VexGuestARMState vex_shadow2;
};
struct sigframe {
struct vki_ucontext uc;
unsigned long retcode[2];
struct vg_sig_private vp;
};
struct rt_sigframe {
vki_siginfo_t info;
struct sigframe sig;
};
static Bool extend ( ThreadState *tst, Addr addr, SizeT size )
{
ThreadId tid = tst->tid;
NSegment const* stackseg = NULL;
if (VG_(extend_stack)(addr, tst->client_stack_szB)) {
stackseg = VG_(am_find_nsegment)(addr);
if (0 && stackseg)
VG_(printf)("frame=%#lx seg=%#lx-%#lx\n",
addr, stackseg->start, stackseg->end);
}
if (stackseg == NULL || !stackseg->hasR || !stackseg->hasW) {
VG_(message)(
Vg_UserMsg,
"Can't extend stack to %#lx during signal delivery for thread %d:",
addr, tid);
if (stackseg == NULL)
VG_(message)(Vg_UserMsg, " no stack segment");
else
VG_(message)(Vg_UserMsg, " too small or bad protection modes");
/* set SIGSEGV to default handler */
VG_(set_default_handler)(VKI_SIGSEGV);
VG_(synth_fault_mapping)(tid, addr);
/* The whole process should be about to die, since the default
action of SIGSEGV to kill the whole process. */
return False;
}
/* For tracking memory events, indicate the entire frame has been
allocated. */
VG_TRACK( new_mem_stack_signal, addr - VG_STACK_REDZONE_SZB,
size + VG_STACK_REDZONE_SZB, tid );
return True;
}
static void synth_ucontext( ThreadId tid, const vki_siginfo_t *si,
UWord trapno, UWord err, const vki_sigset_t *set,
struct vki_ucontext *uc){
ThreadState *tst = VG_(get_ThreadState)(tid);
struct vki_sigcontext *sc = &uc->uc_mcontext;
VG_(memset)(uc, 0, sizeof(*uc));
uc->uc_flags = 0;
uc->uc_link = 0;
uc->uc_sigmask = *set;
uc->uc_stack = tst->altstack;
# define SC2(reg,REG) sc->arm_##reg = tst->arch.vex.guest_##REG
SC2(r0,R0);
SC2(r1,R1);
SC2(r2,R2);
SC2(r3,R3);
SC2(r4,R4);
SC2(r5,R5);
SC2(r6,R6);
SC2(r7,R7);
SC2(r8,R8);
SC2(r9,R9);
SC2(r10,R10);
SC2(fp,R11);
SC2(ip,R12);
SC2(sp,R13);
SC2(lr,R14);
SC2(pc,R15T);
# undef SC2
sc->trap_no = trapno;
sc->error_code = err;
sc->fault_address = (UInt)si->_sifields._sigfault._addr;
}
static void build_sigframe(ThreadState *tst,
struct sigframe *frame,
const vki_siginfo_t *siginfo,
const struct vki_ucontext *siguc,
void *handler, UInt flags,
const vki_sigset_t *mask,
void *restorer){
UWord trapno;
UWord err;
Int sigNo = siginfo->si_signo;
struct vg_sig_private *priv = &frame->vp;
VG_TRACK( pre_mem_write, Vg_CoreSignal, tst->tid, "signal handler frame",
(Addr)frame, offsetof(struct sigframe, vp));
if(siguc) {
trapno = siguc->uc_mcontext.trap_no;
err = siguc->uc_mcontext.error_code;
} else {
trapno = 0;
err = 0;
}
synth_ucontext(tst->tid, siginfo, trapno, err, mask, &frame->uc);
VG_TRACK( post_mem_write, Vg_CoreSignal, tst->tid,
(Addr)frame, offsetof(struct sigframe, vp));
priv->magicPI = 0x31415927;
priv->sigNo_private = sigNo;
priv->vex_shadow1 = tst->arch.vex_shadow1;
priv->vex_shadow2 = tst->arch.vex_shadow2;
}
/* EXPORTED */
void VG_(sigframe_create)( ThreadId tid,
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 )
{
// struct vg_sig_private *priv;
Addr sp = sp_top_of_frame;
ThreadState *tst;
Int sigNo = siginfo->si_signo;
// Addr faultaddr;
UInt size;
tst = VG_(get_ThreadState)(tid);
size = flags & VKI_SA_SIGINFO ? sizeof(struct rt_sigframe) :
sizeof(struct sigframe);
sp -= size;
sp = VG_ROUNDDN(sp, 16);
if(!extend(tst, sp, size))
I_die_here; // XXX Incorrect behavior
if (flags & VKI_SA_SIGINFO){
struct rt_sigframe *rsf = (struct rt_sigframe *)sp;
/* Track our writes to siginfo */
VG_TRACK( pre_mem_write, Vg_CoreSignal, tst->tid, /* VVVVV */
"signal handler siginfo", (Addr)rsf,
offsetof(struct rt_sigframe, sig));
VG_(memcpy)(&rsf->info, siginfo, sizeof(vki_siginfo_t));
if(sigNo == VKI_SIGILL && siginfo->si_code > 0) {
rsf->info._sifields._sigfault._addr = (Addr *) (tst)->arch.vex.guest_R12; /* IP */
}
VG_TRACK( post_mem_write, Vg_CoreSignal, tst->tid, /* ^^^^^ */
(Addr)rsf, offsetof(struct rt_sigframe, sig));
build_sigframe(tst, &rsf->sig, siginfo, siguc,
handler, flags, mask, restorer);
tst->arch.vex.guest_R1 = (Addr)&rsf->info;
tst->arch.vex.guest_R2 = (Addr)&rsf->sig.uc;
}
else {
build_sigframe(tst, (struct sigframe *)sp, siginfo, siguc,
handler, flags, mask, restorer);
}
VG_(set_SP)(tid, sp);
VG_TRACK( post_reg_write, Vg_CoreSignal, tid, VG_O_STACK_PTR,
sizeof(Addr));
tst->arch.vex.guest_R0 = sigNo;
if (flags & VKI_SA_RESTORER)
tst->arch.vex.guest_R14 = (Addr) restorer;
tst->arch.vex.guest_R15T = (Addr) handler; /* R15 == PC */
}
/*------------------------------------------------------------*/
/*--- Destroying signal frames ---*/
/*------------------------------------------------------------*/
/* EXPORTED */
void VG_(sigframe_destroy)( ThreadId tid, Bool isRT )
{
ThreadState *tst;
struct vg_sig_private *priv;
Addr sp;
UInt frame_size;
struct vki_sigcontext *mc;
Int sigNo;
Bool has_siginfo = isRT;
vg_assert(VG_(is_valid_tid)(tid));
tst = VG_(get_ThreadState)(tid);
sp = tst->arch.vex.guest_R13;
if (has_siginfo) {
struct rt_sigframe *frame = (struct rt_sigframe *)sp;
frame_size = sizeof(*frame);
mc = &frame->sig.uc.uc_mcontext;
priv = &frame->sig.vp;
vg_assert(priv->magicPI == 0x31415927);
tst->sig_mask = frame->sig.uc.uc_sigmask;
} else {
struct sigframe *frame = (struct sigframe *)sp;
frame_size = sizeof(*frame);
mc = &frame->uc.uc_mcontext;
priv = &frame->vp;
vg_assert(priv->magicPI == 0x31415927);
tst->sig_mask = frame->uc.uc_sigmask;
/*tst->sig_mask.sig[0] = frame->uc.uc_mcontext.oldmask;
tst->sig_mask.sig[1] = frame->uc.uc_mcontext._unused[3];
VG_(printf)("Setting signmask to %08x%08x\n",tst->sig_mask[0],tst->sig_mask[1]);
*/
}
tst->tmp_sig_mask = tst->sig_mask;
sigNo = priv->sigNo_private;
//XXX: restore regs
# define REST(reg,REG) tst->arch.vex.guest_##REG = mc->arm_##reg;
REST(r0,R0);
REST(r1,R1);
REST(r2,R2);
REST(r3,R3);
REST(r4,R4);
REST(r5,R5);
REST(r6,R6);
REST(r7,R7);
REST(r8,R8);
REST(r9,R9);
REST(r10,R10);
REST(fp,R11);
REST(ip,R12);
REST(sp,R13);
REST(lr,R14);
REST(pc,R15T);
# undef REST
tst->arch.vex_shadow1 = priv->vex_shadow1;
tst->arch.vex_shadow2 = priv->vex_shadow2;
VG_TRACK( die_mem_stack_signal, sp - VG_STACK_REDZONE_SZB,
frame_size + VG_STACK_REDZONE_SZB );
if (VG_(clo_trace_signals))
VG_(message)(Vg_DebugMsg,
"vg_pop_signal_frame (thread %d): "
"isRT=%d valid magic; PC=%#x",
tid, has_siginfo, tst->arch.vex.guest_R15T);
/* tell the tools */
VG_TRACK( post_deliver_signal, tid, sigNo );
}
#endif // defined(VGP_arm_linux)
/*--------------------------------------------------------------------*/
/*--- end sigframe-arm-linux.c ---*/
/*--------------------------------------------------------------------*/