typedef UInt ThreadKey;
-UInt VG_(written_shadow_reg);
+UInt VG_(syscall_altered_shadow_reg);
+UInt VG_(signal_delivery_altered_shadow_reg);
+UInt VG_(pthread_op_altered_shadow_reg);
+UInt VG_(client_request_altered_shadow_reg);
/* Forwards */
static void do_client_request ( ThreadId tid );
VG_(baseBlock)[VGOFF_(sh_eflags)] = VG_(threads)[tid].sh_eflags;
} else {
/* Fields shouldn't be used -- check their values haven't changed. */
- /* Nb: they are written to by some macros like SET_EDX, but they
- * should just write VG_UNUSED_SHADOW_REG_VALUE. */
vg_assert(
VG_UNUSED_SHADOW_REG_VALUE == VG_(threads)[tid].sh_eax &&
VG_UNUSED_SHADOW_REG_VALUE == VG_(threads)[tid].sh_ebx &&
vg_assert(VG_(is_valid_tid)(tid));
- /* Increment signal-returned counter. Used only to implement
- pause(). */
+ /* Increment signal-returned counter. Used only to implement pause(). */
VG_(threads)[tid].n_signals_returned++;
restart_blocked_syscalls = VG_(signal_returns)(tid);
|| VG_(threads)[tid].m_eax == __NR_write)) {
/* read() or write() interrupted. Force a return with EINTR. */
cleanup_waiting_fd_table(tid);
- VG_(threads)[tid].m_eax = -VKI_EINTR;
+ SET_SYSCALL_RETVAL(tid, -VKI_EINTR);
VG_(threads)[tid].status = VgTs_Runnable;
-
if (VG_(clo_trace_sched)) {
VG_(sprintf)(msg_buf,
"read() / write() interrupted by signal; return EINTR" );
rem->tv_sec = 0;
rem->tv_nsec = 1;
}
- SET_EAX(tid, -VKI_EINTR);
+ SET_SYSCALL_RETVAL(tid, -VKI_EINTR);
VG_(threads)[tid].status = VgTs_Runnable;
return;
}
static
void sched_do_syscall ( ThreadId tid )
{
- UInt saved_eax;
+ UInt saved_meax, saved_sheax;
Int res, syscall_no;
UInt fd;
void* pre_res;
pre_res = VG_(pre_known_blocking_syscall)(tid, syscall_no);
/* This trashes the thread's %eax; we have to preserve it. */
- saved_eax = VG_(threads)[tid].m_eax;
+ saved_meax = VG_(threads)[tid].m_eax;
+ saved_sheax = VG_(threads)[tid].sh_eax;
KERNEL_DO_SYSCALL(tid,res);
/* Restore original blockfulness of the fd. */
/* It would have blocked. First, restore %EAX to what it was
before our speculative call. */
- VG_(threads)[tid].m_eax = saved_eax;
+ saved_meax = VG_(threads)[tid].m_eax = saved_meax;
+ saved_sheax = VG_(threads)[tid].sh_eax = saved_sheax;
+
/* Put this fd in a table of fds on which we are waiting for
completion. The arguments for select() later are constructed
from this table. */
- add_waiting_fd(tid, fd, saved_eax /* which holds the syscall # */,
+ add_waiting_fd(tid, fd, saved_meax /* which holds the syscall # */,
pre_res);
/* Deschedule thread until an I/O completion happens. */
VG_(threads)[tid].status = VgTs_WaitFD;
VG_(sprintf)(msg_buf,"block until I/O ready on fd %d", fd);
print_sched_event(tid, msg_buf);
}
-
}
}
rem->tv_nsec = 0;
}
/* Make the syscall return 0 (success). */
- VG_(threads)[tid].m_eax = 0;
+ SET_SYSCALL_RETVAL(tid, 0);
+
/* Reschedule this thread. */
VG_(threads)[tid].status = VgTs_Runnable;
if (VG_(clo_trace_sched)) {
thread yield instead. Not essential, just an
optimisation. */
if (VG_(threads)[tid].m_eax == __NR_sched_yield) {
- SET_EAX(tid, 0); /* syscall returns with success */
+ SET_SYSCALL_RETVAL(tid, 0); /* syscall returns with success */
goto stage1; /* find a new thread to run */
}
vg_assert(VG_(threads)[tid].cancel_pend != NULL);
/* Push a suitable arg, and mark it as readable. */
- VG_(threads)[tid].m_esp -= 4;
+ SET_PTHREQ_ESP(tid, VG_(threads)[tid].m_esp - 4);
* (UInt*)(VG_(threads)[tid].m_esp) = (UInt)PTHREAD_CANCELED;
VG_TRACK( post_mem_write, VG_(threads)[tid].m_esp, sizeof(void*) );
need to have it so that the arg is at the correct stack offset.
Don't mark as readable; any attempt to read this is and internal
valgrind bug since thread_exit_wrapper should not return. */
- VG_(threads)[tid].m_esp -= 4;
+ SET_PTHREQ_ESP(tid, VG_(threads)[tid].m_esp - 4);
* (UInt*)(VG_(threads)[tid].m_esp) = 0xBEADDEEF;
/* .cancel_pend will hold &thread_exit_wrapper */
/* joiner returns with success */
VG_(threads)[jnr].status = VgTs_Runnable;
- SET_EDX(jnr, 0);
+ SET_PTHREQ_RETVAL(jnr, 0);
}
}
VG_(threads)[tid].custack[sp] = *cu;
sp++;
VG_(threads)[tid].custack_used = sp;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
}
vg_assert(sp >= 0 && sp <= VG_N_CLEANUPSTACK);
if (sp == 0) {
- SET_EDX(tid, -1);
+ SET_PTHREQ_RETVAL(tid, -1);
return;
}
sp--;
*cu = VG_(threads)[tid].custack[sp];
VG_TRACK( post_mem_write, (Addr)cu, sizeof(CleanupEntry) );
VG_(threads)[tid].custack_used = sp;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
VG_(sprintf)(msg_buf, "yield");
print_sched_event(tid, msg_buf);
}
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
make_thread_jump_to_cancelhdlr ( tid );
} else {
/* No, we keep going. */
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
}
} else {
VG_(core_panic)("do__set_cancelstate");
}
- SET_EDX(tid, old_st ? PTHREAD_CANCEL_ENABLE
- : PTHREAD_CANCEL_DISABLE);
+ SET_PTHREQ_RETVAL(tid, old_st ? PTHREAD_CANCEL_ENABLE
+ : PTHREAD_CANCEL_DISABLE);
}
} else {
VG_(core_panic)("do__set_canceltype");
}
- SET_EDX(tid, old_ty ? PTHREAD_CANCEL_DEFERRED
+ SET_PTHREQ_RETVAL(tid, old_ty ? PTHREAD_CANCEL_DEFERRED
: PTHREAD_CANCEL_ASYNCHRONOUS);
}
}
if (!VG_(is_valid_tid)(det)) {
- SET_EDX(tid, -1);
+ SET_PTHREQ_RETVAL(tid, -1);
return;
}
switch (what) {
case 2: /* get */
- SET_EDX(tid, VG_(threads)[det].detached ? 1 : 0);
+ SET_PTHREQ_RETVAL(tid, VG_(threads)[det].detached ? 1 : 0);
return;
case 1: /* set detached. If someone is in a join-wait for det,
do not detach. */
for (i = 1; i < VG_N_THREADS; i++) {
if (VG_(threads)[i].status == VgTs_WaitJoinee
&& VG_(threads)[i].joiner_jee_tid == det) {
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
if (VG_(clo_trace_sched)) {
VG_(sprintf)(msg_buf,
"tid %d not detached because %d in join-wait for it",
}
}
VG_(threads)[det].detached = True;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
return;
case 0: /* set not detached */
VG_(threads)[det].detached = False;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
return;
default:
VG_(core_panic)("do__set_or_get_detach");
}
VG_(record_pthread_error)( tid,
"pthread_cancel: target thread does not exist, or invalid");
- SET_EDX(tid, -VKI_ESRCH);
+ SET_PTHREQ_RETVAL(tid, -VKI_ESRCH);
return;
}
}
/* Thread doing the cancelling returns with success. */
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
/* Perhaps we can nuke the cancellee right now? */
do__testcancel(cee);
if (jee == tid) {
VG_(record_pthread_error)( tid,
"pthread_join: attempt to join to self");
- SET_EDX(tid, EDEADLK); /* libc constant, not a kernel one */
+ SET_PTHREQ_RETVAL(tid, EDEADLK); /* libc constant, not a kernel one */
VG_(threads)[tid].status = VgTs_Runnable;
return;
}
/* Invalid thread to join to. */
VG_(record_pthread_error)( tid,
"pthread_join: target thread does not exist, or invalid");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
VG_(threads)[tid].status = VgTs_Runnable;
return;
}
VG_(record_pthread_error)( tid,
"pthread_join: another thread already "
"in join-wait for target thread");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
VG_(threads)[tid].status = VgTs_Runnable;
return;
}
- mark everything below %esp inaccessible
- mark redzone at stack end inaccessible
*/
- VG_(threads)[tid].m_esp = VG_(threads)[tid].stack_base
- + VG_(threads)[tid].stack_size
- - VG_AR_CLIENT_STACKBASE_REDZONE_SZB;
+ SET_PTHREQ_ESP(tid, VG_(threads)[tid].stack_base
+ + VG_(threads)[tid].stack_size
+ - VG_AR_CLIENT_STACKBASE_REDZONE_SZB);
VG_TRACK ( die_mem_stack, VG_(threads)[tid].stack_base,
+ new_stk_szb - VG_AR_CLIENT_STACKBASE_REDZONE_SZB);
VG_AR_CLIENT_STACKBASE_REDZONE_SZB );
/* push two args */
- VG_(threads)[tid].m_esp -= 8;
+ SET_PTHREQ_ESP(tid, VG_(threads)[tid].m_esp - 8);
+
VG_TRACK ( new_mem_stack, (Addr)VG_(threads)[tid].m_esp, 2 * 4 );
VG_TRACK ( pre_mem_write, Vg_CorePThread, & VG_(threads)[tid],
"new thread: stack",
VG_(ksigemptyset)(&VG_(threads)[tid].sigs_waited_for);
/* return child's tid to parent */
- SET_EDX(parent_tid, tid); /* success */
+ SET_PTHREQ_RETVAL(parent_tid, tid); /* success */
}
if (mutex == NULL) {
VG_(record_pthread_error)( tid,
"pthread_mutex_lock/trylock: mutex is NULL");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
default:
VG_(record_pthread_error)( tid,
"pthread_mutex_lock/trylock: mutex is invalid");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
if (mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP) {
/* return 0 (success). */
mutex->__m_count++;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
if (0)
VG_(printf)("!!!!!! tid %d, mx %p -> locked %d\n",
tid, mutex, mutex->__m_count);
return;
} else {
if (is_trylock)
- SET_EDX(tid, EBUSY);
+ SET_PTHREQ_RETVAL(tid, EBUSY);
else
- SET_EDX(tid, EDEADLK);
+ SET_PTHREQ_RETVAL(tid, EDEADLK);
return;
}
} else {
/* GUARD: __m_count > 0 && __m_owner is valid */
if (is_trylock) {
/* caller is polling; so return immediately. */
- SET_EDX(tid, EBUSY);
+ SET_PTHREQ_RETVAL(tid, EBUSY);
} else {
VG_TRACK ( pre_mutex_lock, tid, mutex );
VG_(threads)[tid].status = VgTs_WaitMX;
VG_(threads)[tid].associated_mx = mutex;
- SET_EDX(tid, 0); /* pth_mx_lock success value */
+ SET_PTHREQ_RETVAL(tid, 0); /* pth_mx_lock success value */
if (VG_(clo_trace_pthread_level) >= 1) {
VG_(sprintf)(msg_buf, "%s mx %p: BLOCK",
caller, mutex );
mutex->__m_count = 1;
mutex->__m_owner = (_pthread_descr)tid;
- VG_TRACK( post_mutex_lock, tid, mutex);
-
/* return 0 (success). */
- SET_EDX(tid, 0);
- }
+ SET_PTHREQ_RETVAL(tid, 0);
+ VG_TRACK( post_mutex_lock, tid, mutex);
+ }
}
if (mutex == NULL) {
VG_(record_pthread_error)( tid,
"pthread_mutex_unlock: mutex is NULL");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
default:
VG_(record_pthread_error)( tid,
"pthread_mutex_unlock: mutex is invalid");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
/* nobody holds it */
VG_(record_pthread_error)( tid,
"pthread_mutex_unlock: mutex is not locked");
- SET_EDX(tid, EPERM);
+ SET_PTHREQ_RETVAL(tid, EPERM);
return;
}
/* we don't hold it */
VG_(record_pthread_error)( tid,
"pthread_mutex_unlock: mutex is locked by a different thread");
- SET_EDX(tid, EPERM);
+ SET_PTHREQ_RETVAL(tid, EPERM);
return;
}
if (mutex->__m_count > 1) {
vg_assert(mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP);
mutex->__m_count --;
- SET_EDX(tid, 0); /* success */
+ SET_PTHREQ_RETVAL(tid, 0); /* success */
return;
}
release_one_thread_waiting_on_mutex ( mutex, "pthread_mutex_lock" );
/* Our (tid's) pth_unlock() returns with 0 (success). */
- SET_EDX(tid, 0); /* Success. */
+ SET_PTHREQ_RETVAL(tid, 0); /* Success. */
}
/* Currently unheld; hand it out to thread tid. */
vg_assert(mx->__m_count == 0);
VG_(threads)[tid].status = VgTs_Runnable;
- SET_EDX(tid, ETIMEDOUT); /* pthread_cond_wait return value */
+ SET_PTHREQ_RETVAL(tid, ETIMEDOUT); /* pthread_cond_wait return value */
VG_(threads)[tid].associated_cv = NULL;
VG_(threads)[tid].associated_mx = NULL;
mx->__m_owner = (_pthread_descr)tid;
VG_TRACK( pre_mutex_lock, tid, mx );
VG_(threads)[tid].status = VgTs_WaitMX;
- SET_EDX(tid, ETIMEDOUT); /* pthread_cond_wait return value */
+ SET_PTHREQ_RETVAL(tid, ETIMEDOUT); /* pthread_cond_wait return value */
VG_(threads)[tid].associated_cv = NULL;
VG_(threads)[tid].associated_mx = mx;
if (VG_(clo_trace_pthread_level) >= 1) {
VG_(threads)[i].status = VgTs_WaitMX;
VG_(threads)[i].associated_cv = NULL;
VG_(threads)[i].associated_mx = mx;
- SET_EDX(i, 0); /* pth_cond_wait success value */
+ SET_PTHREQ_RETVAL(i, 0); /* pth_cond_wait success value */
if (VG_(clo_trace_pthread_level) >= 1) {
VG_(sprintf)(msg_buf, "%s cv %p: BLOCK for mx %p",
if (mutex == NULL || cond == NULL) {
VG_(record_pthread_error)( tid,
"pthread_cond_wait/timedwait: cond or mutex is NULL");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
default:
VG_(record_pthread_error)( tid,
"pthread_cond_wait/timedwait: mutex is invalid");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
VG_(record_pthread_error)( tid,
"pthread_cond_wait/timedwait: mutex is unlocked "
"or is locked but not owned by thread");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
if (cond == NULL) {
VG_(record_pthread_error)( tid,
"pthread_cond_signal/broadcast: cond is NULL");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
caller
);
- SET_EDX(tid, 0); /* success */
+ SET_PTHREQ_RETVAL(tid, 0); /* success */
}
&& VG_(threads)[tid].status == VgTs_Runnable);
if (is_valid_key((ThreadKey)key)) {
- SET_EDX(tid, 1);
+ SET_PTHREQ_RETVAL(tid, 1);
} else {
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
}
break;
if (i == VG_N_THREAD_KEYS) {
- /* SET_EDX(tid, EAGAIN);
+ /* SET_PTHREQ_RETVAL(tid, EAGAIN);
return;
*/
VG_(core_panic)("pthread_key_create: VG_N_THREAD_KEYS is too low;"
*key = i;
VG_TRACK( post_mem_write, (Addr)key, sizeof(pthread_key_t) );
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
if (!is_valid_key(key)) {
VG_(record_pthread_error)( tid,
"pthread_key_delete: key is invalid");
- SET_EDX(tid, EINVAL);
+ SET_PTHREQ_RETVAL(tid, EINVAL);
return;
}
vg_thread_keys[key].inuse = False;
vg_thread_keys[key].destructor = NULL;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
vg_assert(VG_(is_valid_or_empty_tid)(tid));
if (VG_(threads)[tid].status == VgTs_Empty) {
- SET_EDX(tid, 1);
+ SET_PTHREQ_RETVAL(tid, 1);
return;
}
vg_assert(specifics_ptr == NULL
|| IS_ALIGNED4_ADDR(specifics_ptr));
- SET_EDX(tid, (UInt)specifics_ptr);
+ SET_PTHREQ_RETVAL(tid, (UInt)specifics_ptr);
}
&& VG_(threads)[tid].status == VgTs_Runnable);
VG_(threads)[tid].specifics_ptr = ptr;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
vg_assert(key >= 0 && key < VG_N_THREAD_KEYS);
if (!vg_thread_keys[key].inuse) {
- SET_EDX(tid, -1);
+ SET_PTHREQ_RETVAL(tid, -1);
return;
}
VG_TRACK( pre_mem_write, Vg_CorePThread, & VG_(threads)[tid],
}
VG_TRACK( post_mem_write, (Addr)cu, sizeof(CleanupEntry) );
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
VG_TRACK( post_mem_write, (Addr)oldmask, sizeof(vki_ksigset_t) );
/* Success. */
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
if (!VG_(is_valid_tid)(thread)) {
VG_(record_pthread_error)( tid,
"pthread_kill: invalid target thread");
- SET_EDX(tid, -VKI_ESRCH);
+ SET_PTHREQ_RETVAL(tid, -VKI_ESRCH);
return;
}
if (sig < 1 || sig > VKI_KNSIG) {
- SET_EDX(tid, -VKI_EINVAL);
+ SET_PTHREQ_RETVAL(tid, -VKI_EINVAL);
return;
}
VG_(send_signal_to_thread)( thread, sig );
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
if (n >= 0 && n < VG_N_FORKHANDLERSTACK) {
vg_fhstack_used = n;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
} else {
- SET_EDX(tid, -1);
+ SET_PTHREQ_RETVAL(tid, -1);
}
}
n = vg_fhstack_used;
vg_assert(n >= 0 && n < VG_N_FORKHANDLERSTACK);
- SET_EDX(tid, n);
+ SET_PTHREQ_RETVAL(tid, n);
}
static
(Addr)fh, sizeof(ForkHandlerEntry));
if (n < 0 || n >= VG_N_FORKHANDLERSTACK) {
- SET_EDX(tid, -1);
+ SET_PTHREQ_RETVAL(tid, -1);
return;
}
vg_fhstack[n] = *fh;
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
}
(Addr)fh, sizeof(ForkHandlerEntry));
if (n < 0 || n >= VG_N_FORKHANDLERSTACK) {
- SET_EDX(tid, -1);
+ SET_PTHREQ_RETVAL(tid, -1);
return;
}
*fh = vg_fhstack[n];
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
VG_TRACK( post_mem_write, (Addr)fh, sizeof(ForkHandlerEntry) );
}
+/* ---------------------------------------------------------------------
+ Specifying shadow register values
+ ------------------------------------------------------------------ */
+
+void VG_(set_return_from_syscall_shadow) ( ThreadId tid, UInt ret_shadow )
+{
+ VG_(set_thread_shadow_archreg)(tid, R_EAX, ret_shadow);
+}
+
+UInt VG_(get_exit_status_shadow) ( void )
+{
+ return VG_(get_shadow_archreg)(R_EBX);
+}
+
/* ---------------------------------------------------------------------
Handle client requests.
static
void do_client_request ( ThreadId tid )
{
-# define RETURN_WITH(vvv) \
- { tst->m_edx = (vvv); \
- tst->sh_edx = VG_(written_shadow_reg); \
- }
-
ThreadState* tst = &VG_(threads)[tid];
UInt* arg = (UInt*)(VG_(threads)[tid].m_eax);
UInt req_no = arg[0];
/* VG_(printf)("req no = 0x%x\n", req_no); */
switch (req_no) {
- /* For the CLIENT_{,tst}CALL[0123] ones, have to do some nasty casting
- to make gcc believe it's a function. */
case VG_USERREQ__CLIENT_CALL0: {
UInt (*f)(void) = (void*)arg[1];
- RETURN_WITH(
- f ( )
- );
+ SET_CLCALL_RETVAL(tid, f ( ), (Addr)f);
break;
}
case VG_USERREQ__CLIENT_CALL1: {
UInt (*f)(UInt) = (void*)arg[1];
- RETURN_WITH(
- f ( arg[2] )
- );
+ SET_CLCALL_RETVAL(tid, f ( arg[2] ), (Addr)f );
break;
}
case VG_USERREQ__CLIENT_CALL2: {
UInt (*f)(UInt, UInt) = (void*)arg[1];
- RETURN_WITH(
- f ( arg[2], arg[3] )
- );
+ SET_CLCALL_RETVAL(tid, f ( arg[2], arg[3] ), (Addr)f );
break;
}
case VG_USERREQ__CLIENT_CALL3: {
UInt (*f)(UInt, UInt, UInt) = (void*)arg[1];
- RETURN_WITH(
- f ( arg[2], arg[3], arg[4] )
- );
+ SET_CLCALL_RETVAL(tid, f ( arg[2], arg[3], arg[4] ), (Addr)f );
break;
}
case VG_USERREQ__CLIENT_tstCALL0: {
UInt (*f)(ThreadState*) = (void*)arg[1];
- RETURN_WITH(
- f ( tst )
- );
+ SET_CLCALL_RETVAL(tid, f ( tst ), (Addr)f );
break;
}
case VG_USERREQ__CLIENT_tstCALL1: {
UInt (*f)(ThreadState*, UInt) = (void*)arg[1];
- RETURN_WITH(
- f ( tst, arg[2] )
- );
+ SET_CLCALL_RETVAL(tid, f ( tst, arg[2] ), (Addr)f );
break;
}
case VG_USERREQ__CLIENT_tstCALL2: {
UInt (*f)(ThreadState*, UInt, UInt) = (void*)arg[1];
- RETURN_WITH(
- f ( tst, arg[2], arg[3] )
- );
+ SET_CLCALL_RETVAL(tid, f ( tst, arg[2], arg[3] ), (Addr)f );
break;
}
case VG_USERREQ__CLIENT_tstCALL3: {
UInt (*f)(ThreadState*, UInt, UInt, UInt) = (void*)arg[1];
- RETURN_WITH(
- f ( tst, arg[2], arg[3], arg[4] )
- );
+ SET_CLCALL_RETVAL(tid, f ( tst, arg[2], arg[3], arg[4] ), (Addr)f );
break;
}
the comment in vg_defaults.c/SK_(malloc)() for why. */
case VG_USERREQ__MALLOC:
VG_(sk_malloc_called_by_scheduler) = True;
- RETURN_WITH(
- (UInt)SK_(malloc) ( tst, arg[1] )
+ SET_PTHREQ_RETVAL(
+ tid, (UInt)SK_(malloc) ( tst, arg[1] )
);
VG_(sk_malloc_called_by_scheduler) = False;
break;
VG_(sk_malloc_called_by_scheduler) = True;
SK_(free) ( tst, (void*)arg[1] );
VG_(sk_malloc_called_by_scheduler) = False;
- RETURN_WITH(0); /* irrelevant */
+ SET_PTHREQ_RETVAL(tid, 0); /* irrelevant */
break;
case VG_USERREQ__PTHREAD_GET_THREADID:
- RETURN_WITH(tid);
+ SET_PTHREQ_RETVAL(tid, tid);
break;
case VG_USERREQ__RUNNING_ON_VALGRIND:
- RETURN_WITH(1);
+ SET_CLREQ_RETVAL(tid, 1);
break;
case VG_USERREQ__GET_PTHREAD_TRACE_LEVEL:
- RETURN_WITH(VG_(clo_trace_pthread_level));
+ SET_PTHREQ_RETVAL(tid, VG_(clo_trace_pthread_level));
break;
case VG_USERREQ__READ_MILLISECOND_TIMER:
- RETURN_WITH(VG_(read_millisecond_timer)());
+ SET_PTHREQ_RETVAL(tid, VG_(read_millisecond_timer)());
break;
/* Some of these may make thread tid non-runnable, but the
break;
case VG_USERREQ__GET_N_SIGS_RETURNED:
- RETURN_WITH(VG_(threads)[tid].n_signals_returned);
+ SET_PTHREQ_RETVAL(tid, VG_(threads)[tid].n_signals_returned);
break;
case VG_USERREQ__PTHREAD_JOIN:
case VG_USERREQ__NUKE_OTHER_THREADS:
VG_(nuke_all_threads_except) ( tid );
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
break;
case VG_USERREQ__PTHREAD_ERROR:
VG_(record_pthread_error)( tid, (Char*)(arg[1]) );
- SET_EDX(tid, 0);
+ SET_PTHREQ_RETVAL(tid, 0);
break;
case VG_USERREQ__SET_FHSTACK_USED:
VG_(invalidate_translations)( arg[1], arg[2], True );
- SET_EDX( tid, 0 ); /* return value is meaningless */
+ SET_CLREQ_RETVAL( tid, 0 ); /* return value is meaningless */
break;
case VG_USERREQ__COUNT_ERRORS:
- SET_EDX( tid, VG_(n_errs_found) );
+ SET_CLREQ_RETVAL( tid, VG_(n_errs_found) );
break;
default:
arg[0], (void*)arg[1], arg[2] );
if (SK_(handle_client_request) ( &VG_(threads)[tid], arg, &ret ))
- SET_EDX(tid, ret);
+ SET_CLREQ_RETVAL(tid, ret);
} else {
static Bool whined = False;
}
break;
}
-
-# undef RETURN_WITH
}
if (ss != NULL) {
if (on_sig_stack(VG_(threads)[tid].m_esp)) {
- SET_EAX(tid, -VKI_EPERM);
+ SET_SYSCALL_RETVAL(tid, -VKI_EPERM);
return;
}
if (ss->ss_flags != VKI_SS_DISABLE
&& ss->ss_flags != VKI_SS_ONSTACK
&& ss->ss_flags != 0) {
- SET_EAX(tid, -VKI_EINVAL);
+ SET_SYSCALL_RETVAL(tid, -VKI_EINVAL);
return;
}
if (ss->ss_flags == VKI_SS_DISABLE) {
vg_scss.altstack.ss_sp = NULL;
} else {
if (ss->ss_size < VKI_MINSIGSTKSZ) {
- SET_EAX(tid, -VKI_ENOMEM);
+ SET_SYSCALL_RETVAL(tid, -VKI_ENOMEM);
return;
}
}
vg_scss.altstack.ss_sp = ss->ss_sp;
vg_scss.altstack.ss_size = ss->ss_size;
}
- SET_EAX(tid, 0);
+ SET_SYSCALL_RETVAL(tid, 0);
}
VG_(block_all_host_signals)( &irrelevant_sigmask );
VG_(handle_SCSS_change)( False /* lazy update */ );
}
- SET_EAX(tid, 0);
+ SET_SYSCALL_RETVAL(tid, 0);
return;
bad_signo:
VG_(message)(Vg_UserMsg,
"Warning: bad signal number %d in __NR_sigaction.",
signo);
- SET_EAX(tid, -VKI_EINVAL);
+ SET_SYSCALL_RETVAL(tid, -VKI_EINVAL);
return;
bad_sigkill_or_sigstop:
"Warning: attempt to set %s handler in __NR_sigaction.",
signo == VKI_SIGKILL ? "SIGKILL" : "SIGSTOP" );
- SET_EAX(tid, -VKI_EINVAL);
+ SET_SYSCALL_RETVAL(tid, -VKI_EINVAL);
return;
}
vg_assert(VG_(is_valid_tid)(tid));
do_setmask ( VG_INVALID_THREADID, how, set, oldset );
/* Syscall returns 0 (success) to its thread. */
- SET_EAX(tid, 0);
+ SET_SYSCALL_RETVAL(tid, 0);
} else {
VG_(message)(Vg_DebugMsg,
"sigprocmask: unknown `how' field %d", how);
- SET_EAX(tid, -VKI_EINVAL);
+ SET_SYSCALL_RETVAL(tid, -VKI_EINVAL);
}
}
/* Safely-saved version of sigNo, as described above. */
Int sigNo_private;
/* Saved processor state. */
- UInt ssestate[VG_SIZE_OF_SSESTATE_W];
- UInt eax;
- UInt ecx;
- UInt edx;
- UInt ebx;
- UInt ebp;
- UInt esp;
- UInt esi;
- UInt edi;
- Addr eip;
- UInt eflags;
+ 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;
+
+ UInt sh_eax;
+ UInt sh_ebx;
+ UInt sh_ecx;
+ UInt sh_edx;
+ UInt sh_esi;
+ UInt sh_edi;
+ UInt sh_ebp;
+ UInt sh_esp;
+ UInt sh_eflags;
+
/* Scheduler-private stuff: what was the thread's status prior to
delivering this signal? */
ThreadStatus status;
frame->magicPI = 0x31415927;
for (i = 0; i < VG_SIZE_OF_SSESTATE_W; i++)
- frame->ssestate[i] = tst->m_sse[i];
-
- frame->eax = tst->m_eax;
- frame->ecx = tst->m_ecx;
- frame->edx = tst->m_edx;
- frame->ebx = tst->m_ebx;
- frame->ebp = tst->m_ebp;
- frame->esp = tst->m_esp;
- frame->esi = tst->m_esi;
- frame->edi = tst->m_edi;
- frame->eip = tst->m_eip;
- frame->eflags = tst->m_eflags;
+ frame->m_sse[i] = tst->m_sse[i];
+
+ frame->m_eax = tst->m_eax;
+ frame->m_ecx = tst->m_ecx;
+ frame->m_edx = tst->m_edx;
+ frame->m_ebx = tst->m_ebx;
+ frame->m_ebp = tst->m_ebp;
+ frame->m_esp = tst->m_esp;
+ frame->m_esi = tst->m_esi;
+ frame->m_edi = tst->m_edi;
+ frame->m_eflags = tst->m_eflags;
+ frame->m_eip = tst->m_eip;
+
+ if (VG_(needs).shadow_regs) {
+ frame->sh_eax = tst->sh_eax;
+ frame->sh_ecx = tst->sh_ecx;
+ frame->sh_edx = tst->sh_edx;
+ frame->sh_ebx = tst->sh_ebx;
+ frame->sh_ebp = tst->sh_ebp;
+ frame->sh_esp = tst->sh_esp;
+ frame->sh_esi = tst->sh_esi;
+ frame->sh_edi = tst->sh_edi;
+ frame->sh_eflags = tst->sh_eflags;
+ }
frame->status = tst->status;
frame->magicE = 0x27182818;
+ /* Ensure 'tid' and 'tst' correspond */
+ vg_assert(& VG_(threads)[tid] == tst);
/* Set the thread so it will next run the handler. */
- tst->m_esp = esp;
+ /* tst->m_esp = esp; */
+ SET_SIGNAL_ESP(tid, esp);
+
tst->m_eip = (Addr)vg_scss.scss_per_sig[sigNo].scss_handler;
/* This thread needs to be marked runnable, but we leave that the
caller to do. */
*/
}
-
/* 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. */
VG_(message)(Vg_DebugMsg,
"vg_pop_signal_frame (thread %d): valid magic", tid);
- /* restore machine state */
- for (i = 0; i < VG_SIZE_OF_SSESTATE_W; i++)
- tst->m_sse[i] = frame->ssestate[i];
-
/* Mark the frame structure as nonaccessible. */
VG_TRACK( die_mem_stack_signal, (Addr)frame, sizeof(VgSigFrame) );
- /* Restore machine state from the saved context. */
- tst->m_eax = frame->eax;
- tst->m_ecx = frame->ecx;
- tst->m_edx = frame->edx;
- tst->m_ebx = frame->ebx;
- tst->m_ebp = frame->ebp;
- tst->m_esp = frame->esp;
- tst->m_esi = frame->esi;
- tst->m_edi = frame->edi;
- tst->m_eflags = frame->eflags;
- tst->m_eip = frame->eip;
-
+ /* restore machine state */
+ for (i = 0; i < VG_SIZE_OF_SSESTATE_W; i++)
+ tst->m_sse[i] = frame->m_sse[i];
+
+ tst->m_eax = frame->m_eax;
+ tst->m_ecx = frame->m_ecx;
+ tst->m_edx = frame->m_edx;
+ tst->m_ebx = frame->m_ebx;
+ tst->m_ebp = frame->m_ebp;
+ tst->m_esp = frame->m_esp;
+ tst->m_esi = frame->m_esi;
+ tst->m_edi = frame->m_edi;
+ tst->m_eflags = frame->m_eflags;
+ tst->m_eip = frame->m_eip;
+
+ if (VG_(needs).shadow_regs) {
+ tst->sh_eax = frame->sh_eax;
+ tst->sh_ecx = frame->sh_ecx;
+ tst->sh_edx = frame->sh_edx;
+ tst->sh_ebx = frame->sh_ebx;
+ tst->sh_ebp = frame->sh_ebp;
+ tst->sh_esp = frame->sh_esp;
+ tst->sh_esi = frame->sh_esi;
+ tst->sh_edi = frame->sh_edi;
+ tst->sh_eflags = frame->sh_eflags;
+ }
+
/* don't use the copy exposed to the handler; it might have changed
it. */
sigNo = frame->sigNo_private;
*(Int*)(sigwait_args[2]) = sigNo;
VG_TRACK( post_mem_write, (Addr)sigwait_args[2], sizeof(UInt));
}
- SET_EDX(tid, 0);
+ SET_SIGNAL_EDX(tid, 0);
tst->status = VgTs_Runnable;
VG_(ksigemptyset)(&tst->sigs_waited_for);
scss_changed = True;
extern UInt VG_(get_shadow_archreg) ( UInt archreg );
extern void VG_(set_shadow_archreg) ( UInt archreg, UInt val );
+extern void VG_(set_shadow_eflags) ( UInt val );
extern Addr VG_(shadow_archreg_address) ( UInt archreg );
extern UInt VG_(get_thread_shadow_archreg) ( ThreadId tid, UInt archreg );
extern ExeContext* VG_(get_ExeContext) ( ThreadState *tst );
/* Just grab the client's EIP, as a much smaller and cheaper
- indication of where they are. */
+ indication of where they are. ThreadState should be NULL if it's called
+ from within generated code. */
extern Addr VG_(get_EIP)( ThreadState *tst );
extern void VG_(HT_destruct) ( VgHashTable t );
+/*====================================================================*/
+/*=== Functions for shadow registers ===*/
+/*====================================================================*/
+
+/* Nb: make sure the shadow_regs 'need' is set before using these! */
+
+/* This one lets you override the shadow of the return value register for a
+ syscall. Call it from SK_(post_syscall)() (not SK_(pre_syscall)()!) to
+ override the default shadow register value. */
+extern void VG_(set_return_from_syscall_shadow) ( ThreadId tid,
+ UInt ret_shadow );
+
+/* This can be called from SK_(fini)() to find the shadow of the argument
+ to exit(), ie. the shadow of the program's return value. */
+extern UInt VG_(get_exit_status_shadow) ( void );
+
+
/*====================================================================*/
/*=== General stuff for replacing functions ===*/
/*====================================================================*/
/*=== Specific stuff for replacing malloc() and friends ===*/
/*====================================================================*/
-/* ------------------------------------------------------------------ */
-/* Replacing malloc() and friends */
-
/* If a skin replaces malloc() et al, the easiest way to do so is to link
with coregrind/vg_replace_malloc.c, and follow the following instructions.
You can do it from scratch, though, if you enjoy that sort of thing. */
/* ------------------------------------------------------------------ */
/* Some options that can be used by a skin if malloc() et al are replaced.
- The skin should use the VG_(process...)() and VG_(print...)() functions
- to give control over these aspects of Valgrind's version of malloc(). */
+ The skin should call the functions in the appropriate places to give
+ control over these aspects of Valgrind's version of malloc(). */
/* Round malloc sizes upwards to integral number of words? default: NO */
extern Bool VG_(clo_sloppy_malloc);
EV VG_(track_post_mem_write) ( void (*f)(Addr a, UInt size) );
+/* Register events -- if `shadow_regs' need is set, all should probably be
+ used. Use VG_(set_thread_shadow_archreg)() to set the shadow of the
+ changed register. */
+
+/* Use VG_(set_shadow_archreg)() to set the eight general purpose regs,
+ and use VG_(set_shadow_eflags)() to set eflags. */
+EV VG_(track_post_regs_write_init) ( void (*f)() );
+
+/* Use VG_(set_thread_shadow_archreg)() to set the shadow regs for these
+ events. */
+EV VG_(track_post_reg_write_syscall_return)
+ ( void (*f)(ThreadId tid, UInt reg) );
+EV VG_(track_post_reg_write_deliver_signal)
+ ( void (*f)(ThreadId tid, UInt reg) );
+EV VG_(track_post_reg_write_pthread_return)
+ ( void (*f)(ThreadId tid, UInt reg) );
+EV VG_(track_post_reg_write_clientreq_return)
+ ( void (*f)(ThreadId tid, UInt reg) );
+ /* This one is called for malloc() et al if they are replaced by a skin. */
+EV VG_(track_post_reg_write_clientcall_return)
+ ( void (*f)(ThreadId tid, UInt reg,
+ Addr called_function) );
+
+
/* Scheduler events (not exhaustive) */
EV VG_(track_thread_run) ( void (*f)(ThreadId tid) );
/* ------------------------------------------------------------------ */
/* VG_(needs).shadow_regs */
-/* Valid values for general registers and EFLAGS register, for initialising
- and updating registers when written in certain places in core. */
-extern void SK_(written_shadow_regs_values) ( UInt* gen_reg, UInt* eflags );
-
+/* No functions must be defined, but the post_reg[s]_write_* events should
+ be tracked. */
/* ------------------------------------------------------------------ */
/* VG_(needs).command_line_options */
/* ------------------------------------------------------------------ */
/* VG_(needs).client_requests */
-extern Bool SK_(handle_client_request) ( ThreadState* tst, UInt* arg_block, UInt *ret );
+/* If using client requests, the number of the first request should be equal
+ to VG_USERREQ_SKIN_BASE('X','Y'), where 'X' and 'Y' form a suitable two
+ character identification for the string. The second and subsequent
+ requests should follow. */
+
+/* This function should use the VG_IS_SKIN_USERREQ macro (in
+ include/valgrind.h) to first check if it's a request for this skin. Then
+ should handle it if it's recognised (and return True), or return False if
+ not recognised. arg_block[0] holds the request number, any further args
+ from the request are in arg_block[1..]. 'ret' is for the return value...
+ it should probably be filled, if only with 0. */
+extern Bool SK_(handle_client_request) ( ThreadState* tst, UInt* arg_block,
+ UInt *ret );
/* ------------------------------------------------------------------ */