mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-13 22:46:59 +00:00
262 lines
7.8 KiB
C
262 lines
7.8 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- OS-specific stuff. linux/core_os.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2000-2005 Nicholas Nethercote
|
|
njn@valgrind.org
|
|
|
|
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.
|
|
*/
|
|
|
|
#include "core.h"
|
|
#include "pub_core_options.h"
|
|
#include "pub_core_tooliface.h"
|
|
|
|
void VGA_(os_state_clear)(ThreadState *tst)
|
|
{
|
|
tst->os_state.lwpid = 0;
|
|
tst->os_state.threadgroup = 0;
|
|
}
|
|
|
|
void VGA_(os_state_init)(ThreadState *tst)
|
|
{
|
|
tst->os_state.valgrind_stack_base = 0;
|
|
tst->os_state.valgrind_stack_szB = 0;
|
|
|
|
VGA_(os_state_clear)(tst);
|
|
}
|
|
|
|
static void terminate(ThreadId tid, VgSchedReturnCode src)
|
|
{
|
|
vg_assert(tid == VG_(master_tid));
|
|
vg_assert(VG_(count_living_threads)() == 0);
|
|
|
|
//--------------------------------------------------------------
|
|
// Exit, according to the scheduler's return code
|
|
//--------------------------------------------------------------
|
|
switch (src) {
|
|
case VgSrc_ExitSyscall: /* the normal way out */
|
|
VG_(exit)( VG_(threads)[VG_(master_tid)].os_state.exitcode );
|
|
/* NOT ALIVE HERE! */
|
|
VG_(core_panic)("entered the afterlife in main() -- ExitSyscall");
|
|
break; /* what the hell :) */
|
|
|
|
case VgSrc_FatalSig:
|
|
/* We were killed by a fatal signal, so replicate the effect */
|
|
vg_assert(VG_(threads)[VG_(master_tid)].os_state.fatalsig != 0);
|
|
VG_(kill_self)(VG_(threads)[VG_(master_tid)].os_state.fatalsig);
|
|
VG_(core_panic)("main(): signal was supposed to be fatal");
|
|
break;
|
|
|
|
default:
|
|
VG_(core_panic)("main(): unexpected scheduler return code");
|
|
}
|
|
}
|
|
|
|
/* Run a thread from beginning to end. Does not return. */
|
|
void VGA_(thread_wrapper)(Word /*ThreadId*/ tidW)
|
|
{
|
|
VgSchedReturnCode ret;
|
|
ThreadId tid = (ThreadId)tidW;
|
|
ThreadState* tst = VG_(get_ThreadState)(tid);
|
|
|
|
vg_assert(tst->status == VgTs_Init);
|
|
|
|
/* make sure we get the CPU lock before doing anything significant */
|
|
VG_(set_running)(tid);
|
|
|
|
if (0)
|
|
VG_(printf)("thread tid %d started: stack = %p\n",
|
|
tid, &tid);
|
|
|
|
VG_TRACK ( post_thread_create, tst->os_state.parent, tid );
|
|
|
|
tst->os_state.lwpid = VG_(gettid)();
|
|
tst->os_state.threadgroup = VG_(getpid)();
|
|
|
|
/* Thread created with all signals blocked; scheduler will set the
|
|
appropriate mask */
|
|
|
|
ret = VG_(scheduler)(tid);
|
|
|
|
vg_assert(VG_(is_exiting)(tid));
|
|
|
|
vg_assert(tst->status == VgTs_Runnable);
|
|
vg_assert(VG_(is_running_thread)(tid));
|
|
|
|
if (tid == VG_(master_tid)) {
|
|
VG_(shutdown_actions)(tid);
|
|
terminate(tid, ret);
|
|
}
|
|
}
|
|
|
|
/* wait until all other threads are dead */
|
|
static Bool alldead(void *v)
|
|
{
|
|
/* master_tid must be alive... */
|
|
Int c = VG_(count_living_threads)();
|
|
//VG_(printf)("alldead: count=%d\n", c);
|
|
return c <= 1;
|
|
}
|
|
|
|
static void sigvgchld_handler(Int sig)
|
|
{
|
|
VG_(printf)("got a sigvgchld?\n");
|
|
}
|
|
|
|
/*
|
|
Wait until some predicate about threadstates is satisfied.
|
|
|
|
This uses SIGVGCHLD as a notification that it is now worth
|
|
re-evaluating the predicate.
|
|
*/
|
|
static void wait_for_threadstate(Bool (*pred)(void *), void *arg)
|
|
{
|
|
vki_sigset_t set, saved;
|
|
struct vki_sigaction sa, old_sa;
|
|
|
|
/*
|
|
SIGVGCHLD is set to be ignored, and is unblocked by default.
|
|
This means all such signals are simply discarded.
|
|
|
|
In this loop, we actually block it, and then poll for it with
|
|
sigtimedwait.
|
|
*/
|
|
VG_(sigemptyset)(&set);
|
|
VG_(sigaddset)(&set, VKI_SIGVGCHLD);
|
|
|
|
VG_(set_sleeping)(VG_(master_tid), VgTs_Yielding);
|
|
VG_(sigprocmask)(VKI_SIG_BLOCK, &set, &saved);
|
|
|
|
/* It shouldn't be necessary to set a handler, since the signal is
|
|
always blocked, but it seems to be necessary to convice the
|
|
kernel not to just toss the signal... */
|
|
sa.ksa_handler = sigvgchld_handler;
|
|
sa.sa_flags = 0;
|
|
VG_(sigfillset)(&sa.sa_mask);
|
|
VG_(sigaction)(VKI_SIGVGCHLD, &sa, &old_sa);
|
|
|
|
vg_assert(old_sa.ksa_handler == VKI_SIG_IGN);
|
|
|
|
while(!(*pred)(arg)) {
|
|
struct vki_siginfo si;
|
|
Int ret = VG_(sigtimedwait)(&set, &si, NULL);
|
|
|
|
if (ret > 0 && VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg, "Got %d (code=%d) from tid lwp %d",
|
|
ret, si.si_code, si._sifields._kill._pid);
|
|
}
|
|
|
|
VG_(sigaction)(VKI_SIGVGCHLD, &old_sa, NULL);
|
|
VG_(sigprocmask)(VKI_SIG_SETMASK, &saved, NULL);
|
|
VG_(set_running)(VG_(master_tid));
|
|
}
|
|
|
|
void VGA_(reap_threads)(ThreadId self)
|
|
{
|
|
vg_assert(self == VG_(master_tid));
|
|
wait_for_threadstate(alldead, NULL);
|
|
}
|
|
|
|
/* The we need to know the address of it so it can be
|
|
called at program exit. */
|
|
static Addr __libc_freeres_wrapper;
|
|
|
|
void VGA_(intercept_libc_freeres_wrapper)(Addr addr)
|
|
{
|
|
__libc_freeres_wrapper = addr;
|
|
}
|
|
|
|
/* Final clean-up before terminating the process.
|
|
Clean up the client by calling __libc_freeres() (if requested) */
|
|
void VGA_(final_tidyup)(ThreadId tid)
|
|
{
|
|
vg_assert(VG_(is_running_thread)(tid));
|
|
|
|
if (!VG_(needs).libc_freeres ||
|
|
!VG_(clo_run_libc_freeres) ||
|
|
__libc_freeres_wrapper == 0)
|
|
return; /* can't/won't do it */
|
|
|
|
if (VG_(clo_verbosity) > 2 ||
|
|
VG_(clo_trace_syscalls) ||
|
|
VG_(clo_trace_sched))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"Caught __NR_exit; running __libc_freeres()");
|
|
|
|
/* point thread context to point to libc_freeres_wrapper */
|
|
INSTR_PTR(VG_(threads)[tid].arch) = __libc_freeres_wrapper;
|
|
// XXX should we use a special stack?
|
|
|
|
/* Block all blockable signals by copying the real block state into
|
|
the thread's block state*/
|
|
VG_(sigprocmask)(VKI_SIG_BLOCK, NULL, &VG_(threads)[tid].sig_mask);
|
|
VG_(threads)[tid].tmp_sig_mask = VG_(threads)[tid].sig_mask;
|
|
|
|
/* and restore handlers to default */
|
|
VG_(set_default_handler)(VKI_SIGSEGV);
|
|
VG_(set_default_handler)(VKI_SIGBUS);
|
|
VG_(set_default_handler)(VKI_SIGILL);
|
|
VG_(set_default_handler)(VKI_SIGFPE);
|
|
|
|
// We were exiting, so assert that...
|
|
vg_assert(VG_(is_exiting)(tid));
|
|
// ...but now we're not again
|
|
VG_(threads)[tid].exitreason = VgSrc_None;
|
|
|
|
// run until client thread exits - ideally with LIBC_FREERES_DONE,
|
|
// but exit/exitgroup/signal will do
|
|
VG_(scheduler)(tid);
|
|
|
|
vg_assert(VG_(is_exiting)(tid));
|
|
}
|
|
|
|
// Arch-specific client requests
|
|
Bool VGA_(client_request)(ThreadId tid, UWord *args)
|
|
{
|
|
Bool handled = True;
|
|
|
|
vg_assert(VG_(is_running_thread)(tid));
|
|
|
|
switch(args[0]) {
|
|
case VG_USERREQ__LIBC_FREERES_DONE:
|
|
/* This is equivalent to an exit() syscall, but we don't set the
|
|
exitcode (since it might already be set) */
|
|
if (0 || VG_(clo_trace_syscalls) || VG_(clo_trace_sched))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"__libc_freeres() done; really quitting!");
|
|
VG_(threads)[tid].exitreason = VgSrc_ExitSyscall;
|
|
break;
|
|
|
|
default:
|
|
handled = False;
|
|
break;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end ---*/
|
|
/*--------------------------------------------------------------------*/
|