Allow the VCPU state to be examined and manipulated from within a signal handler. Integer state is pretty easy, but FP state requires a lot of fiddley conversion between the CPU's state and the structure passed to the signal handler. core.h | 0 coregrind/vg_scheduler.c | 18 -- coregrind/vg_signals.c | 211 ++++++++++++++------------ coregrind/x86/core_arch.h | 36 ++++ coregrind/x86/signal.c | 305 ++++++++++++++++++++++++++++++--------- coregrind/x86/state.c | 4 none/tests/Makefile.am | 7 none/tests/sigcontext.c | 92 +++++++++++ none/tests/sigcontext.stderr.exp | 2 none/tests/sigcontext.stdout.exp | 4 none/tests/sigcontext.vgtest | 1 11 files changed, 499 insertions(+), 181 deletions(-) diff -puN coregrind/core.h~signal-state coregrind/core.h diff -puN coregrind/vg_signals.c~signal-state coregrind/vg_signals.c --- valgrind/coregrind/vg_signals.c~signal-state 2005-01-10 16:40:48.000000000 -0800 +++ valgrind-jeremy/coregrind/vg_signals.c 2005-01-11 10:13:13.000000000 -0800 @@ -727,17 +727,14 @@ void vg_push_signal_frame ( ThreadId tid } /* Clear the signal frame created by vg_push_signal_frame, restore the - simulated machine state, and return the signal number that the - frame was for. */ + simulated machine state. */ static -Int vg_pop_signal_frame ( ThreadId tid ) +void vg_pop_signal_frame ( ThreadId tid ) { Int sigNo = VGA_(pop_signal_frame)(tid); /* Notify tools */ VG_TRACK( post_deliver_signal, tid, sigNo ); - - return sigNo; } @@ -748,13 +745,9 @@ Int vg_pop_signal_frame ( ThreadId tid ) void VG_(signal_returns) ( ThreadId tid ) { - Int sigNo; - /* Pop the signal frame and restore tid's status to what it was before the signal was delivered. */ - sigNo = vg_pop_signal_frame(tid); - - vg_assert(sigNo >= 1 && sigNo <= _VKI_NSIG); + vg_pop_signal_frame(tid); } static const Char *signame(Int sigNo) @@ -1150,9 +1143,14 @@ static void make_coredump(ThreadId tid, if (!should_dump(seg)) continue; - vg_assert(VG_(lseek)(core_fd, 0, VKI_SEEK_CUR) == phdrs[i].p_offset); - if (phdrs[i].p_filesz > 0) - VG_(write)(core_fd, (void *)seg->addr, seg->len); + if (phdrs[i].p_filesz > 0) { + Int ret; + + vg_assert(VG_(lseek)(core_fd, phdrs[i].p_offset, VKI_SEEK_SET) == phdrs[i].p_offset); + + vg_assert(seg->len >= phdrs[i].p_filesz); + ret = VG_(write)(core_fd, (void *)seg->addr, phdrs[i].p_filesz); + } } VG_(close)(core_fd); @@ -1169,8 +1167,10 @@ static void make_coredump(ThreadId tid, static void vg_default_action(const vki_siginfo_t *info, ThreadId tid) { Int sigNo = info->si_signo; - Bool terminate = False; - Bool core = False; + Bool terminate = False; /* kills process */ + Bool core = False; /* kills process w/ core */ + struct vki_rlimit corelim; + Bool could_core; vg_assert(VG_(is_running_thread)(tid)); @@ -1209,108 +1209,125 @@ static void vg_default_action(const vki_ vg_assert(!core || (core && terminate)); if (VG_(clo_trace_signals)) - VG_(message)(Vg_DebugMsg, "delivering %d to default handler %s%s", - sigNo, terminate ? "terminate" : "", core ? "+core" : ""); + VG_(message)(Vg_DebugMsg, "delivering %d (code %d) to default handler %s%s", + sigNo, info->si_code, terminate ? "terminate" : "", core ? "+core" : ""); - if (terminate) { - struct vki_rlimit corelim; - Bool could_core = core; - - if (core) { - /* If they set the core-size limit to zero, don't generate a - core file */ + if (!terminate) + return; /* nothing to do */ + + could_core = core; + + if (core) { + /* If they set the core-size limit to zero, don't generate a + core file */ - VG_(getrlimit)(VKI_RLIMIT_CORE, &corelim); + VG_(getrlimit)(VKI_RLIMIT_CORE, &corelim); - if (corelim.rlim_cur == 0) - core = False; - } + if (corelim.rlim_cur == 0) + core = False; + } - if (VG_(clo_verbosity) != 0 && (could_core || VG_(clo_verbosity) > 1)) { - VG_(message)(Vg_UserMsg, ""); - VG_(message)(Vg_UserMsg, "Process terminating with default action of signal %d (%s)%s", - sigNo, signame(sigNo), core ? ": dumping core" : ""); - - /* Be helpful - decode some more details about this fault */ - if (info->si_code > VKI_SI_USER) { - const Char *event = NULL; - - switch(sigNo) { - case VKI_SIGSEGV: - switch(info->si_code) { - case 1: event = "Access not within mapped region"; break; - case 2: event = "Bad permissions for mapped region"; break; - } + if (VG_(clo_verbosity) != 0 && (could_core || VG_(clo_verbosity) > 1)) { + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, "Process terminating with default action of signal %d (%s)%s", + sigNo, signame(sigNo), core ? ": dumping core" : ""); + + /* Be helpful - decode some more details about this fault */ + if (info->si_code > VKI_SI_USER) { + const Char *event = NULL; + Bool haveaddr = True; + + switch(sigNo) { + case VKI_SIGSEGV: + switch(info->si_code) { + case 1: event = "Access not within mapped region"; break; + case 2: event = "Bad permissions for mapped region"; break; + case 128: + /* General Protection Fault: The CPU/kernel + isn't telling us anything useful, but this + is commonly the result of exceeding a + segment limit, such as the one imposed by + --pointercheck=yes. */ + if (VG_(clo_pointercheck)) + event = "GPF (Pointer out of bounds?)"; + else + event = "General Protection Fault"; + haveaddr = False; break; + } + break; - case VKI_SIGILL: - switch(info->si_code) { - case 1: event = "Illegal opcode"; break; - case 2: event = "Illegal operand"; break; - case 3: event = "Illegal addressing mode"; break; - case 4: event = "Illegal trap"; break; - case 5: event = "Privileged opcode"; break; - case 6: event = "Privileged register"; break; - case 7: event = "Coprocessor error"; break; - case 8: event = "Internal stack error"; break; - } - break; + case VKI_SIGILL: + switch(info->si_code) { + case 1: event = "Illegal opcode"; break; + case 2: event = "Illegal operand"; break; + case 3: event = "Illegal addressing mode"; break; + case 4: event = "Illegal trap"; break; + case 5: event = "Privileged opcode"; break; + case 6: event = "Privileged register"; break; + case 7: event = "Coprocessor error"; break; + case 8: event = "Internal stack error"; break; + } + break; - case VKI_SIGFPE: - switch (info->si_code) { - case 1: event = "Integer divide by zero"; break; - case 2: event = "Integer overflow"; break; - case 3: event = "FP divide by zero"; break; - case 4: event = "FP overflow"; break; - case 5: event = "FP underflow"; break; - case 6: event = "FP inexact"; break; - case 7: event = "FP invalid operation"; break; - case 8: event = "FP subscript out of range"; break; - } - break; + case VKI_SIGFPE: + switch (info->si_code) { + case 1: event = "Integer divide by zero"; break; + case 2: event = "Integer overflow"; break; + case 3: event = "FP divide by zero"; break; + case 4: event = "FP overflow"; break; + case 5: event = "FP underflow"; break; + case 6: event = "FP inexact"; break; + case 7: event = "FP invalid operation"; break; + case 8: event = "FP subscript out of range"; break; + } + break; - case VKI_SIGBUS: - switch (info->si_code) { - case 1: event = "Invalid address alignment"; break; - case 2: event = "Non-existent physical address"; break; - case 3: event = "Hardware error"; break; - } - break; + case VKI_SIGBUS: + switch (info->si_code) { + case 1: event = "Invalid address alignment"; break; + case 2: event = "Non-existent physical address"; break; + case 3: event = "Hardware error"; break; } + break; + } - if (event != NULL) + if (event != NULL) { + if (haveaddr) VG_(message)(Vg_UserMsg, " %s at address %p", event, info->_sifields._sigfault._addr); - } - - if (tid != VG_INVALID_THREADID) { - ExeContext *ec = VG_(get_ExeContext)(tid); - VG_(pp_ExeContext)(ec); + else + VG_(message)(Vg_UserMsg, " %s", event); } } - if (VG_(is_action_requested)( "Attach to debugger", & VG_(clo_db_attach) )) { - VG_(start_debugger)( tid ); + if (tid != VG_INVALID_THREADID) { + ExeContext *ec = VG_(get_ExeContext)(tid); + VG_(pp_ExeContext)(ec); } + } - if (core) { - const static struct vki_rlimit zero = { 0, 0 }; - - make_coredump(tid, info, corelim.rlim_cur); + if (VG_(is_action_requested)( "Attach to debugger", & VG_(clo_db_attach) )) { + VG_(start_debugger)( tid ); + } - /* Make sure we don't get a confusing kernel-generated - coredump when we finally exit */ - VG_(setrlimit)(VKI_RLIMIT_CORE, &zero); - } + if (core) { + const static struct vki_rlimit zero = { 0, 0 }; - /* stash fatal signal in main thread */ - VG_(threads)[VG_(master_tid)].os_state.fatalsig = sigNo; + make_coredump(tid, info, corelim.rlim_cur); - /* everyone dies */ - VG_(nuke_all_threads_except)(tid, VgSrc_FatalSig); - VG_(threads)[tid].exitreason = VgSrc_FatalSig; - VG_(threads)[tid].os_state.fatalsig = sigNo; + /* Make sure we don't get a confusing kernel-generated + coredump when we finally exit */ + VG_(setrlimit)(VKI_RLIMIT_CORE, &zero); } + + /* stash fatal signal in main thread */ + VG_(threads)[VG_(master_tid)].os_state.fatalsig = sigNo; + + /* everyone dies */ + VG_(nuke_all_threads_except)(tid, VgSrc_FatalSig); + VG_(threads)[tid].exitreason = VgSrc_FatalSig; + VG_(threads)[tid].os_state.fatalsig = sigNo; } static void synth_fault_common(ThreadId tid, Addr addr, Int si_code) @@ -1533,7 +1550,7 @@ void vg_sync_signalhandler ( Int sigNo, -1, 0)) { if (VG_(clo_trace_signals)) VG_(message)(Vg_DebugMsg, - " -> extended stack base to %p\n", base); + " -> extended stack base to %p", base); return; // extension succeeded, restart instruction } else VG_(message)(Vg_UserMsg, "Stack overflow in thread %d", tid); diff -puN coregrind/x86/core_arch.h~signal-state coregrind/x86/core_arch.h --- valgrind/coregrind/x86/core_arch.h~signal-state 2005-01-10 16:40:48.000000000 -0800 +++ valgrind-jeremy/coregrind/x86/core_arch.h 2005-01-10 16:40:48.000000000 -0800 @@ -241,6 +241,36 @@ typedef struct _LDT_ENTRY { /* ... and in words ... */ #define VG_SIZE_OF_SSESTATE_W ((VG_SIZE_OF_SSESTATE+3)/4) +#define X86_FXSR_MAGIC 0x0000 + +struct i387_fsave_struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ + long status; /* software status information */ +}; + +struct i387_fxsave_struct { + unsigned short cwd; + unsigned short swd; + unsigned short twd; + unsigned short fop; + long fip; + long fcs; + long foo; + long fos; + long mxcsr; + long mxcsr_mask; + long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ + long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ + long padding[56]; +} __attribute__ ((aligned (16))); + // Architecture-specific part of a ThreadState // XXX: eventually this should be made abstract, ie. the fields not visible @@ -291,7 +321,11 @@ typedef struct { required 16-byte alignment required to get stuff in/out by fxsave/fxrestore. So we have to do it "by hand". */ - UInt m_sse[VG_SIZE_OF_SSESTATE_W]; + union { + UInt state[VG_SIZE_OF_SSESTATE_W]; + struct i387_fsave_struct fsave; + struct i387_fxsave_struct fxsave; + } m_sse; UInt sh_eax; UInt sh_ebx; diff -puN coregrind/x86/signal.c~signal-state coregrind/x86/signal.c --- valgrind/coregrind/x86/signal.c~signal-state 2005-01-10 16:40:48.000000000 -0800 +++ valgrind-jeremy/coregrind/x86/signal.c 2005-01-11 10:13:09.000000000 -0800 @@ -48,7 +48,7 @@ typedef The first two words are common for both frame formats - they're the return address and the signal number. */ - /* Sig handler's (bogus) return address */ + /* Sig handler's return address */ Addr retaddr; /* The arg to the sig handler. We need to inspect this after the handler returns, but it's unreasonable to assume that the @@ -72,27 +72,20 @@ typedef /* Sanity check word. */ UInt magicPI; + + UInt handlerflags; /* flags for signal handler */ + /* pointed to by psigInfo */ vki_siginfo_t sigInfo; /* pointed to by puContext */ struct vki_ucontext uContext; + struct _vki_fpstate fpstate; /* Safely-saved version of sigNo, as described above. */ Int sigNo_private; - /* Saved processor state. */ - UInt m_sse[VG_SIZE_OF_SSESTATE_W]; - - UInt m_eax; - UInt m_ecx; - UInt m_edx; - UInt m_ebx; - UInt m_ebp; - UInt m_esp; - UInt m_esi; - UInt m_edi; - UInt m_eflags; - Addr m_eip; + /* XXX This is wrong. Surely we should store the shadow values + into the shadow memory behind the actual values? */ UInt sh_eax; UInt sh_ebx; UInt sh_ecx; @@ -106,10 +99,6 @@ typedef /* saved signal mask to be restored when handler returns */ vki_sigset_t mask; - /* Scheduler-private stuff: what was the thread's status prior to - delivering this signal? */ - ThreadStatus status; - /* Sanity check word. Is the highest-addressed word; do not move!*/ UInt magicE; @@ -120,9 +109,194 @@ typedef /*--- Signal operations ---*/ /*------------------------------------------------------------*/ +/* + Great gobs of FP state conversion taken wholesale from + linux/arch/i386/kernel/i387.c + */ + +/* + * FXSR floating point environment conversions. + */ +#define X86_FXSR_MAGIC 0x0000 + +/* + * FPU tag word conversions. + */ + +static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) +{ + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + return tmp; +} + +static unsigned long twd_fxsr_to_i387( const struct i387_fxsave_struct *fxsave ) +{ + struct _vki_fpxreg *st = NULL; + unsigned long twd = (unsigned long) fxsave->twd; + unsigned long tag; + unsigned long ret = 0xffff0000u; + int i; + +#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16); + + for ( i = 0 ; i < 8 ; i++ ) { + if ( twd & 0x1 ) { + st = (struct _vki_fpxreg *) FPREG_ADDR( fxsave, i ); + + switch ( st->exponent & 0x7fff ) { + case 0x7fff: + tag = 2; /* Special */ + break; + case 0x0000: + if ( !st->significand[0] && + !st->significand[1] && + !st->significand[2] && + !st->significand[3] ) { + tag = 1; /* Zero */ + } else { + tag = 2; /* Special */ + } + break; + default: + if ( st->significand[3] & 0x8000 ) { + tag = 0; /* Valid */ + } else { + tag = 2; /* Special */ + } + break; + } + } else { + tag = 3; /* Empty */ + } + ret |= (tag << (2 * i)); + twd = twd >> 1; + } + return ret; +} + +static void convert_fxsr_to_user( struct _vki_fpstate *buf, + const struct i387_fxsave_struct *fxsave ) +{ + unsigned long env[7]; + struct _vki_fpreg *to; + struct _vki_fpxreg *from; + int i; + + env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul; + env[1] = (unsigned long)fxsave->swd | 0xffff0000ul; + env[2] = twd_fxsr_to_i387(fxsave); + env[3] = fxsave->fip; + env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16); + env[5] = fxsave->foo; + env[6] = fxsave->fos; + + VG_(memcpy)(buf, env, 7 * sizeof(unsigned long)); + + to = &buf->_st[0]; + from = (struct _vki_fpxreg *) &fxsave->st_space[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + unsigned long __user *t = (unsigned long __user *)to; + unsigned long *f = (unsigned long *)from; + + t[0] = f[0]; + t[1] = f[1]; + to->exponent = from->exponent; + } +} + +static void convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, + const struct _vki_fpstate *buf ) +{ + unsigned long env[7]; + struct _vki_fpxreg *to; + const struct _vki_fpreg *from; + int i; + + VG_(memcpy)(env, buf, 7 * sizeof(long)); + + fxsave->cwd = (unsigned short)(env[0] & 0xffff); + fxsave->swd = (unsigned short)(env[1] & 0xffff); + fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff)); + fxsave->fip = env[3]; + fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16); + fxsave->fcs = (env[4] & 0xffff); + fxsave->foo = env[5]; + fxsave->fos = env[6]; + + to = (struct _vki_fpxreg *) &fxsave->st_space[0]; + from = &buf->_st[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + unsigned long *t = (unsigned long *)to; + unsigned long __user *f = (unsigned long __user *)from; + + t[0] = f[0]; + t[1] = f[1]; + to->exponent = from->exponent; + } +} + +static inline void save_i387_fsave( arch_thread_t *regs, struct _vki_fpstate *buf ) +{ + struct i387_fsave_struct *fs = ®s->m_sse.fsave; + + fs->status = fs->swd; + VG_(memcpy)(buf, fs, sizeof(*fs)); +} + +static void save_i387_fxsave( arch_thread_t *regs, struct _vki_fpstate *buf ) +{ + const struct i387_fxsave_struct *fx = ®s->m_sse.fxsave; + convert_fxsr_to_user( buf, fx ); + + buf->status = fx->swd; + buf->magic = X86_FXSR_MAGIC; + VG_(memcpy)(buf->_fxsr_env, fx, sizeof(struct i387_fxsave_struct)); +} + +static void save_i387( arch_thread_t *regs, struct _vki_fpstate *buf ) +{ + if ( VG_(have_ssestate) ) + save_i387_fxsave( regs, buf ); + else + save_i387_fsave( regs, buf ); +} + +static inline void restore_i387_fsave( arch_thread_t *regs, const struct _vki_fpstate __user *buf ) +{ + VG_(memcpy)( ®s->m_sse.fsave, buf, sizeof(struct i387_fsave_struct) ); +} + +static void restore_i387_fxsave( arch_thread_t *regs, const struct _vki_fpstate __user *buf ) +{ + VG_(memcpy)(®s->m_sse.fxsave, &buf->_fxsr_env[0], + sizeof(struct i387_fxsave_struct) ); + /* mxcsr reserved bits must be masked to zero for security reasons */ + regs->m_sse.fxsave.mxcsr &= 0xffbf; + convert_fxsr_from_user( ®s->m_sse.fxsave, buf ); +} + +static void restore_i387( arch_thread_t *regs, const struct _vki_fpstate __user *buf ) +{ + if ( VG_(have_ssestate) ) { + restore_i387_fxsave( regs, buf ); + } else { + restore_i387_fsave( regs, buf ); + } +} + + /* Make up a plausible-looking thread state from the thread's current state */ static void synth_ucontext(ThreadId tid, const vki_siginfo_t *si, - const vki_sigset_t *set, struct vki_ucontext *uc) + const vki_sigset_t *set, + struct vki_ucontext *uc, struct _vki_fpstate *fpstate) { ThreadState *tst = VG_(get_ThreadState)(tid); struct vki_sigcontext *sc = &uc->uc_mcontext; @@ -133,6 +307,9 @@ static void synth_ucontext(ThreadId tid, uc->uc_link = 0; uc->uc_sigmask = *set; uc->uc_stack = tst->altstack; + sc->fpstate = fpstate; + + save_i387(&tst->arch, fpstate); #define SC(reg) sc->reg = tst->arch.m_##reg SC(gs); @@ -172,7 +349,6 @@ void VGA_(push_signal_frame)(ThreadId ti { Addr esp; ThreadState* tst; - Int i; VgSigFrame* frame; Int sigNo = siginfo->si_signo; @@ -197,31 +373,36 @@ void VGA_(push_signal_frame)(ThreadId ti frame->retaddr = (UInt)VG_(client_trampoline_code)+VG_(tramp_sigreturn_offset); frame->sigNo = sigNo; frame->sigNo_private = sigNo; + VG_TRACK( post_mem_write, (Addr)frame, offsetof(VgSigFrame, handlerArgs) ); if (flags & VKI_SA_SIGINFO) { /* if the client asked for a siginfo delivery, then build the stack that way */ + + /* pointers to siginfo and ucontext */ VG_TRACK( pre_mem_write, Vg_CoreSignal, tid, "signal handler frame (siginfo)", (Addr)&frame->handlerArgs, sizeof(frame->handlerArgs.sigInfo) ); frame->handlerArgs.sigInfo.psigInfo = (Addr)&frame->sigInfo; frame->handlerArgs.sigInfo.puContext = (Addr)&frame->uContext; VG_TRACK( post_mem_write, (Addr)&frame->handlerArgs, sizeof(frame->handlerArgs.sigInfo) ); + /* siginfo */ VG_TRACK( pre_mem_write, Vg_CoreSignal, tid, "signal handler frame (siginfo)", (Addr)&frame->sigInfo, sizeof(frame->sigInfo) ); VG_(memcpy)(&frame->sigInfo, siginfo, sizeof(vki_siginfo_t)); VG_TRACK( post_mem_write, (Addr)&frame->sigInfo, sizeof(frame->sigInfo) ); - VG_TRACK( pre_mem_write, Vg_CoreSignal, tid, "signal handler frame (siginfo)", + /* ucontext */ + VG_TRACK( pre_mem_write, Vg_CoreSignal, tid, "signal handler frame (ucontext)", (Addr)&frame->uContext, sizeof(frame->uContext) ); - synth_ucontext(tid, siginfo, mask, &frame->uContext); + synth_ucontext(tid, siginfo, mask, &frame->uContext, &frame->fpstate); VG_TRACK( post_mem_write, (Addr)&frame->uContext, sizeof(frame->uContext) ); } else { struct vki_ucontext uc; - /* otherwise just put the sigcontext there */ + /* non-siginfo: just put the sigcontext there */ - synth_ucontext(tid, siginfo, mask, &uc); + synth_ucontext(tid, siginfo, mask, &uc, &frame->fpstate); VG_TRACK( pre_mem_write, Vg_CoreSignal, tid, "signal handler frame (sigcontext)", (Addr)&frame->handlerArgs, sizeof(frame->handlerArgs.sigContext) ); @@ -235,20 +416,6 @@ void VGA_(push_signal_frame)(ThreadId ti frame->magicPI = 0x31415927; - for (i = 0; i < VG_SIZE_OF_SSESTATE_W; i++) - frame->m_sse[i] = tst->arch.m_sse[i]; - - frame->m_eax = tst->arch.m_eax; - frame->m_ecx = tst->arch.m_ecx; - frame->m_edx = tst->arch.m_edx; - frame->m_ebx = tst->arch.m_ebx; - frame->m_ebp = tst->arch.m_ebp; - frame->m_esp = tst->arch.m_esp; - frame->m_esi = tst->arch.m_esi; - frame->m_edi = tst->arch.m_edi; - frame->m_eflags = tst->arch.m_eflags; - frame->m_eip = tst->arch.m_eip; - if (VG_(needs).shadow_regs) { frame->sh_eax = tst->arch.sh_eax; frame->sh_ecx = tst->arch.sh_ecx; @@ -263,13 +430,8 @@ void VGA_(push_signal_frame)(ThreadId ti frame->mask = tst->sig_mask; - /* If the thread is currently blocked in a syscall, we want it to - resume as runnable. */ - if (tst->status == VgTs_WaitSys) - frame->status = VgTs_Runnable; - else - frame->status = tst->status; - + frame->handlerflags = flags; + frame->magicE = 0x27182818; /* Ensure 'tid' and 'tst' correspond */ @@ -284,16 +446,16 @@ void VGA_(push_signal_frame)(ThreadId ti caller to do. */ if (0) - VG_(printf)("pushed signal frame; %%ESP now = %p, next %%EBP = %p, status=%d\n", + VG_(printf)("pushed signal frame; %%ESP now = %p, next %%EIP = %p, status=%d\n", esp, tst->arch.m_eip, tst->status); } Int VGA_(pop_signal_frame)(ThreadId tid) { Addr esp; - Int i; VgSigFrame* frame; ThreadState* tst; + struct vki_sigcontext *sc; vg_assert(VG_(is_valid_tid)(tid)); tst = & VG_(threads)[tid]; @@ -306,27 +468,35 @@ Int VGA_(pop_signal_frame)(ThreadId tid) vg_assert(frame->magicPI == 0x31415927); vg_assert(frame->magicE == 0x27182818); - if (VG_(clo_trace_signals)) - VG_(message)(Vg_DebugMsg, - "vg_pop_signal_frame (thread %d): valid magic; EIP=%p", tid, frame->m_eip); /* Mark the frame structure as nonaccessible. */ VG_TRACK( die_mem_stack_signal, (Addr)frame, sizeof(VgSigFrame) ); /* restore machine state */ - for (i = 0; i < VG_SIZE_OF_SSESTATE_W; i++) - tst->arch.m_sse[i] = frame->m_sse[i]; + if (frame->handlerflags & VKI_SA_SIGINFO) + sc = &frame->uContext.uc_mcontext; + else + sc = &frame->handlerArgs.sigContext; - tst->arch.m_eax = frame->m_eax; - tst->arch.m_ecx = frame->m_ecx; - tst->arch.m_edx = frame->m_edx; - tst->arch.m_ebx = frame->m_ebx; - tst->arch.m_ebp = frame->m_ebp; - tst->arch.m_esp = frame->m_esp; - tst->arch.m_esi = frame->m_esi; - tst->arch.m_edi = frame->m_edi; - tst->arch.m_eflags = frame->m_eflags; - tst->arch.m_eip = frame->m_eip; + tst->arch.m_eax = sc->eax; + tst->arch.m_ecx = sc->ecx; + tst->arch.m_edx = sc->edx; + tst->arch.m_ebx = sc->ebx; + tst->arch.m_ebp = sc->ebp; + tst->arch.m_esp = sc->esp; + tst->arch.m_esi = sc->esi; + tst->arch.m_edi = sc->edi; + tst->arch.m_eflags = sc->eflags; + tst->arch.m_eip = sc->eip; + + tst->arch.m_cs = sc->cs; + tst->arch.m_ss = sc->ss; + tst->arch.m_ds = sc->ds; + tst->arch.m_es = sc->es; + tst->arch.m_fs = sc->fs; + tst->arch.m_gs = sc->gs; + + restore_i387(&tst->arch, &frame->fpstate); if (VG_(needs).shadow_regs) { tst->arch.sh_eax = frame->sh_eax; @@ -340,15 +510,16 @@ Int VGA_(pop_signal_frame)(ThreadId tid) tst->arch.sh_eflags = frame->sh_eflags; } - /* And restore the thread's status to what it was before the signal - was delivered. */ - tst->status = frame->status; + if (VG_(clo_trace_signals)) + VG_(message)(Vg_DebugMsg, + "vg_pop_signal_frame (thread %d): valid magic; EIP=%p", + tid, sc->eip); tst->sig_mask = frame->mask; /* don't use the copy exposed to the handler; it might have changed it. */ - return frame->sigNo_private; + return frame->sigNo_private; } /*------------------------------------------------------------*/ @@ -438,7 +609,7 @@ void VGA_(fill_elffpxregs_from_BB) ( vki void VGA_(fill_elffpxregs_from_tst) ( vki_elf_fpxregset_t* xfpu, const arch_thread_t* arch ) { - VG_(memcpy)(xfpu, arch->m_sse, sizeof(*xfpu)); + VG_(memcpy)(xfpu, arch->m_sse.state, sizeof(*xfpu)); } /*--------------------------------------------------------------------*/ diff -puN coregrind/x86/state.c~signal-state coregrind/x86/state.c --- valgrind/coregrind/x86/state.c~signal-state 2005-01-10 16:40:48.000000000 -0800 +++ valgrind-jeremy/coregrind/x86/state.c 2005-01-10 16:40:48.000000000 -0800 @@ -316,7 +316,7 @@ void VGA_(load_state) ( arch_thread_t* a VG_(baseBlock)[VGOFF_(m_eip)] = arch->m_eip; for (i = 0; i < VG_SIZE_OF_SSESTATE_W; i++) - VG_(baseBlock)[VGOFF_(m_ssestate) + i] = arch->m_sse[i]; + VG_(baseBlock)[VGOFF_(m_ssestate) + i] = arch->m_sse.state[i]; if (VG_(needs).shadow_regs) { VG_(baseBlock)[VGOFF_(sh_eax)] = arch->sh_eax; @@ -395,7 +395,7 @@ n", arch->m_eip = VG_(baseBlock)[VGOFF_(m_eip)]; for (i = 0; i < VG_SIZE_OF_SSESTATE_W; i++) - arch->m_sse[i] + arch->m_sse.state[i] = VG_(baseBlock)[VGOFF_(m_ssestate) + i]; if (VG_(needs).shadow_regs) { diff -puN none/tests/Makefile.am~signal-state none/tests/Makefile.am --- valgrind/none/tests/Makefile.am~signal-state 2005-01-10 16:40:48.000000000 -0800 +++ valgrind-jeremy/none/tests/Makefile.am 2005-01-11 10:13:09.000000000 -0800 @@ -48,11 +48,12 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ sha1_test.stderr.exp sha1_test.vgtest \ shortpush.stderr.exp shortpush.vgtest \ shorts.stderr.exp shorts.vgtest \ - tls.stderr.exp tls.stdout.exp \ + sigcontext.stdout.exp sigcontext.stderr.exp sigcontext.vgtest \ smc1.stderr.exp smc1.stdout.exp smc1.vgtest \ syscall-restart1.vgtest syscall-restart1.stdout.exp syscall-restart1.stderr.exp \ syscall-restart2.vgtest syscall-restart2.stdout.exp syscall-restart2.stderr.exp \ system.stderr.exp system.vgtest \ + tls.stderr.exp tls.stdout.exp \ yield.stderr.exp yield.stdout.exp yield.vgtest check_PROGRAMS = \ @@ -61,7 +62,8 @@ check_PROGRAMS = \ fucomip getseg \ munmap_exe map_unaligned map_unmap mq mremap rcrl readline1 \ resolv rlimit_nofile sem semlimit sha1_test \ - shortpush shorts smc1 susphello pending pth_blockedsig pth_stackalign \ + shortpush shorts sigcontext \ + smc1 susphello pending pth_blockedsig pth_stackalign \ syscall-restart1 syscall-restart2 system \ coolo_sigaction gxx304 yield @@ -101,6 +103,7 @@ smc1_SOURCES = smc1.c sha1_test_SOURCES = sha1_test.c shortpush_SOURCES = shortpush.c shorts_SOURCES = shorts.c +sigcontext_SOURCES = sigcontext.c susphello_SOURCES = susphello.c susphello_LDADD = -lpthread syscall_restart1_SOURCES = syscall-restart1.c diff -puN /dev/null none/tests/sigcontext.c --- /dev/null 2004-02-23 13:02:56.000000000 -0800 +++ valgrind-jeremy/none/tests/sigcontext.c 2005-01-10 16:40:48.000000000 -0800 @@ -0,0 +1,92 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#define VAL1 0x11223344 +#define VAL2 0x44332211 + +static void handler1(int sig, siginfo_t *si, ucontext_t *uc) +{ + /* Since the handler will be called as kill leaves the kernel, + this is replacing the kill syscall's return value. */ + if (uc->uc_mcontext.gregs[REG_EAX] != 0) + printf("FAILED: handler2 expected eax == 0, not %d\n", uc->uc_mcontext.gregs[REG_EAX]); + uc->uc_mcontext.gregs[REG_EAX] = VAL1; + + asm volatile ( + "movl $0, %%edx\n" + "movl $0, %%esi\n" + "movl $0, %%edi\n" + : : : "edx", "esi", "edi"); +} + +static void handler2(int sig, struct sigcontext sc) +{ + /* Since the handler will be called as kill leaves the kernel, + this is replacing the kill syscall's return value. */ + if (sc.eax != 0) + printf("FAILED: handler2 expected eax == 0, not %d\n", sc.eax); + + sc.eax = VAL2; + + asm volatile ( + "movl $0, %%edx\n" + "movl $0, %%esi\n" + "movl $0, %%edi\n" + : : : "edx", "esi", "edi"); +} + +int main() +{ + struct sigaction sa; + int ret; + int v1, v2, v3, v4; + + sa.sa_handler = handler1; + sa.sa_flags = SA_SIGINFO; + sigfillset(&sa.sa_mask); + + sigaction(SIGUSR1, &sa, NULL); + + sa.sa_handler = handler2; + sa.sa_flags = 0; + sigfillset(&sa.sa_mask); + + sigaction(SIGUSR2, &sa, NULL); + + asm volatile ( + //"movl $0x11111111, %%ebp\n" + "movl $0x22222222, %%edx\n" + "movl $0x33333333, %%esi\n" + "movl $0x44444444, %%edi\n" + "int $0x80" + : "=a" (ret), "=d" (v2), "=S" (v3), "=D" (v4) + : "0" (__NR_kill), "b" (getpid()), "c" (SIGUSR1)); + printf("v2=%x v3=%x v4=%x\n", v2, v3, v4); + + if (ret == VAL1) + printf("PASS %x\n", ret); + else + printf("FAIL ret=%x not %x\n", ret, VAL1); + + asm volatile ( + //"movl $0x11111111, %%ebp\n" + "movl $0x22222222, %%edx\n" + "movl $0x33333333, %%esi\n" + "movl $0x44444444, %%edi\n" + "int $0x80" + : "=a" (ret), "=d" (v2), "=S" (v3), "=D" (v4) + : "0" (__NR_kill), "b" (getpid()), "c" (SIGUSR2)); + printf("v2=%x v3=%x v4=%x\n", v2, v3, v4); + + if (ret == VAL2) + printf("PASS %x\n", ret); + else + printf("FAIL ret=%x not %x\n", ret, VAL2); + + return 0; +} diff -puN /dev/null none/tests/sigcontext.stdout.exp --- /dev/null 2004-02-23 13:02:56.000000000 -0800 +++ valgrind-jeremy/none/tests/sigcontext.stdout.exp 2005-01-10 16:40:48.000000000 -0800 @@ -0,0 +1,4 @@ +v2=22222222 v3=33333333 v4=44444444 +PASS 11223344 +v2=22222222 v3=33333333 v4=44444444 +PASS 44332211 diff -puN /dev/null none/tests/sigcontext.vgtest --- /dev/null 2004-02-23 13:02:56.000000000 -0800 +++ valgrind-jeremy/none/tests/sigcontext.vgtest 2005-01-10 16:40:48.000000000 -0800 @@ -0,0 +1 @@ +prog: sigcontext diff -puN /dev/null none/tests/sigcontext.stderr.exp --- /dev/null 2004-02-23 13:02:56.000000000 -0800 +++ valgrind-jeremy/none/tests/sigcontext.stderr.exp 2005-01-10 16:40:48.000000000 -0800 @@ -0,0 +1,2 @@ + + diff -puN coregrind/vg_scheduler.c~signal-state coregrind/vg_scheduler.c --- valgrind/coregrind/vg_scheduler.c~signal-state 2005-01-10 16:40:48.000000000 -0800 +++ valgrind-jeremy/coregrind/vg_scheduler.c 2005-01-11 10:13:13.000000000 -0800 @@ -657,31 +657,26 @@ static void handle_syscall(ThreadId tid) runnable again. We could take a signal while the syscall runs. */ SCHEDSETJMP(tid, sigcode, VG_(client_syscall)(tid)); - + + vg_assert(VG_(is_running_thread)(tid)); + switch(sigcode) { case VgSig_None: break; /* nothing */ case VgSig_Exiting: vg_assert(VG_(is_exiting)(tid)); - if (tst->status != VgTs_Runnable) { - vg_assert(tst->status == VgTs_WaitSys); - VG_(set_running)(tid); - } break; case VgSig_AsyncSig: case VgSig_FatalSig: - if (tst->status != VgTs_Runnable) { - vg_assert(tst->status == VgTs_WaitSys); - VG_(set_running)(tid); - } - if (sigcode == VgSig_FatalSig) { /* was fatal, we're exiting */ - tst->exitreason = VgSrc_FatalSig; + vg_assert(VG_(is_exiting)(tid)); } + VG_(sigprocmask)(VKI_SIG_SETMASK, &VG_(blocked_mask), NULL); + tst->siginfo.si_signo = 0; /* don't care about signal state */ break; @@ -692,7 +687,6 @@ static void handle_syscall(ThreadId tid) } vg_assert(tst->siginfo.si_signo == 0); - vg_assert(VG_(is_running_thread)(tid)); } /* _