From 5a1e0fb4b3ab6f535817df825f6124dbb0ebf582 Mon Sep 17 00:00:00 2001 From: aurel32 Date: Thu, 18 Dec 2008 22:44:13 +0000 Subject: [PATCH] User-mode GDB stub improvements - handle signals Handle signals in the user-mode GDB stub. Report them to GDB, and allow it to change or cancel them. Also correct the protocol numbering; it happens to match Linux numbering for SIGINT and SIGTRAP, but that's just good fortune. Signed-off-by: Daniel Jacobowitz Acked-by: Edgar E. Iglesias Signed-off-by: Aurelien Jarno git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6096 c046a42c-6fe2-441c-8c8c-71466251a162 --- gdbstub.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++---- gdbstub.h | 2 + linux-user/signal.c | 65 ++++++++++---- 3 files changed, 290 insertions(+), 31 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 2e112b2..f7c065c 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -38,18 +38,213 @@ #define MAX_PACKET_LENGTH 4096 #include "qemu_socket.h" -#ifdef _WIN32 -/* XXX: these constants may be independent of the host ones even for Unix */ -#ifndef SIGTRAP -#define SIGTRAP 5 -#endif -#ifndef SIGINT -#define SIGINT 2 -#endif + + +enum { + GDB_SIGNAL_0 = 0, + GDB_SIGNAL_INT = 2, + GDB_SIGNAL_TRAP = 5, + GDB_SIGNAL_UNKNOWN = 143 +}; + +#ifdef CONFIG_USER_ONLY + +/* Map target signal numbers to GDB protocol signal numbers and vice + * versa. For user emulation's currently supported systems, we can + * assume most signals are defined. + */ + +static int gdb_signal_table[] = { + 0, + TARGET_SIGHUP, + TARGET_SIGINT, + TARGET_SIGQUIT, + TARGET_SIGILL, + TARGET_SIGTRAP, + TARGET_SIGABRT, + -1, /* SIGEMT */ + TARGET_SIGFPE, + TARGET_SIGKILL, + TARGET_SIGBUS, + TARGET_SIGSEGV, + TARGET_SIGSYS, + TARGET_SIGPIPE, + TARGET_SIGALRM, + TARGET_SIGTERM, + TARGET_SIGURG, + TARGET_SIGSTOP, + TARGET_SIGTSTP, + TARGET_SIGCONT, + TARGET_SIGCHLD, + TARGET_SIGTTIN, + TARGET_SIGTTOU, + TARGET_SIGIO, + TARGET_SIGXCPU, + TARGET_SIGXFSZ, + TARGET_SIGVTALRM, + TARGET_SIGPROF, + TARGET_SIGWINCH, + -1, /* SIGLOST */ + TARGET_SIGUSR1, + TARGET_SIGUSR2, + TARGET_SIGPWR, + -1, /* SIGPOLL */ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + __SIGRTMIN + 1, + __SIGRTMIN + 2, + __SIGRTMIN + 3, + __SIGRTMIN + 4, + __SIGRTMIN + 5, + __SIGRTMIN + 6, + __SIGRTMIN + 7, + __SIGRTMIN + 8, + __SIGRTMIN + 9, + __SIGRTMIN + 10, + __SIGRTMIN + 11, + __SIGRTMIN + 12, + __SIGRTMIN + 13, + __SIGRTMIN + 14, + __SIGRTMIN + 15, + __SIGRTMIN + 16, + __SIGRTMIN + 17, + __SIGRTMIN + 18, + __SIGRTMIN + 19, + __SIGRTMIN + 20, + __SIGRTMIN + 21, + __SIGRTMIN + 22, + __SIGRTMIN + 23, + __SIGRTMIN + 24, + __SIGRTMIN + 25, + __SIGRTMIN + 26, + __SIGRTMIN + 27, + __SIGRTMIN + 28, + __SIGRTMIN + 29, + __SIGRTMIN + 30, + __SIGRTMIN + 31, + -1, /* SIGCANCEL */ + __SIGRTMIN, + __SIGRTMIN + 32, + __SIGRTMIN + 33, + __SIGRTMIN + 34, + __SIGRTMIN + 35, + __SIGRTMIN + 36, + __SIGRTMIN + 37, + __SIGRTMIN + 38, + __SIGRTMIN + 39, + __SIGRTMIN + 40, + __SIGRTMIN + 41, + __SIGRTMIN + 42, + __SIGRTMIN + 43, + __SIGRTMIN + 44, + __SIGRTMIN + 45, + __SIGRTMIN + 46, + __SIGRTMIN + 47, + __SIGRTMIN + 48, + __SIGRTMIN + 49, + __SIGRTMIN + 50, + __SIGRTMIN + 51, + __SIGRTMIN + 52, + __SIGRTMIN + 53, + __SIGRTMIN + 54, + __SIGRTMIN + 55, + __SIGRTMIN + 56, + __SIGRTMIN + 57, + __SIGRTMIN + 58, + __SIGRTMIN + 59, + __SIGRTMIN + 60, + __SIGRTMIN + 61, + __SIGRTMIN + 62, + __SIGRTMIN + 63, + __SIGRTMIN + 64, + __SIGRTMIN + 65, + __SIGRTMIN + 66, + __SIGRTMIN + 67, + __SIGRTMIN + 68, + __SIGRTMIN + 69, + __SIGRTMIN + 70, + __SIGRTMIN + 71, + __SIGRTMIN + 72, + __SIGRTMIN + 73, + __SIGRTMIN + 74, + __SIGRTMIN + 75, + __SIGRTMIN + 76, + __SIGRTMIN + 77, + __SIGRTMIN + 78, + __SIGRTMIN + 79, + __SIGRTMIN + 80, + __SIGRTMIN + 81, + __SIGRTMIN + 82, + __SIGRTMIN + 83, + __SIGRTMIN + 84, + __SIGRTMIN + 85, + __SIGRTMIN + 86, + __SIGRTMIN + 87, + __SIGRTMIN + 88, + __SIGRTMIN + 89, + __SIGRTMIN + 90, + __SIGRTMIN + 91, + __SIGRTMIN + 92, + __SIGRTMIN + 93, + __SIGRTMIN + 94, + __SIGRTMIN + 95, + -1, /* SIGINFO */ + -1, /* UNKNOWN */ + -1, /* DEFAULT */ + -1, + -1, + -1, + -1, + -1, + -1 +}; #else -#include +/* In system mode we only need SIGINT and SIGTRAP; other signals + are not yet supported. */ + +enum { + TARGET_SIGINT = 2, + TARGET_SIGTRAP = 5 +}; + +static int gdb_signal_table[] = { + -1, + -1, + TARGET_SIGINT, + -1, + -1, + TARGET_SIGTRAP +}; +#endif + +#ifdef CONFIG_USER_ONLY +static int target_signal_to_gdb (int sig) +{ + int i; + for (i = 0; i < ARRAY_SIZE (gdb_signal_table); i++) + if (gdb_signal_table[i] == sig) + return i; + return GDB_SIGNAL_UNKNOWN; +} #endif +static int gdb_signal_to_target (int sig) +{ + if (sig < ARRAY_SIZE (gdb_signal_table)) + return gdb_signal_table[sig]; + else + return -1; +} + //#define DEBUG_GDB typedef struct GDBRegisterState { @@ -1300,7 +1495,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) switch(ch) { case '?': /* TODO: Make this return the correct value for user-mode. */ - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", SIGTRAP, + snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, s->c_cpu->cpu_index+1); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, @@ -1331,10 +1526,13 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) s->c_cpu->pc = addr; #endif } + s->signal = 0; gdb_continue(s); return RS_IDLE; case 'C': - s->signal = strtoul(p, (char **)&p, 16); + s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16)); + if (s->signal == -1) + s->signal = 0; gdb_continue(s); return RS_IDLE; case 'k': @@ -1692,16 +1890,16 @@ static void gdb_vm_stopped(void *opaque, int reason) } snprintf(buf, sizeof(buf), "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - SIGTRAP, env->cpu_index+1, type, + GDB_SIGNAL_TRAP, env->cpu_index+1, type, env->watchpoint_hit->vaddr); put_packet(s, buf); env->watchpoint_hit = NULL; return; } tb_flush(env); - ret = SIGTRAP; + ret = GDB_SIGNAL_TRAP; } else if (reason == EXCP_INTERRUPT) { - ret = SIGINT; + ret = GDB_SIGNAL_INT; } else { ret = 0; } @@ -1853,6 +2051,19 @@ static void gdb_read_byte(GDBState *s, int ch) #ifdef CONFIG_USER_ONLY int +gdb_queuesig (void) +{ + GDBState *s; + + s = gdbserver_state; + + if (gdbserver_fd < 0 || s->fd < 0) + return 0; + else + return 1; +} + +int gdb_handlesig (CPUState *env, int sig) { GDBState *s; @@ -1869,7 +2080,7 @@ gdb_handlesig (CPUState *env, int sig) if (sig != 0) { - snprintf(buf, sizeof(buf), "S%02x", sig); + snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb (sig)); put_packet(s, buf); } /* put_packet() might have detected that the peer terminated the @@ -1915,6 +2126,19 @@ void gdb_exit(CPUState *env, int code) put_packet(s, buf); } +/* Tell the remote gdb that the process has exited due to SIG. */ +void gdb_signalled(CPUState *env, int sig) +{ + GDBState *s; + char buf[4]; + + s = gdbserver_state; + if (gdbserver_fd < 0 || s->fd < 0) + return; + + snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb (sig)); + put_packet(s, buf); +} static void gdb_accept(void) { diff --git a/gdbstub.h b/gdbstub.h index 4bfd872..c7d1c4b 100644 --- a/gdbstub.h +++ b/gdbstub.h @@ -10,8 +10,10 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...); int use_gdb_syscalls(void); void gdb_set_stop_cpu(CPUState *env); #ifdef CONFIG_USER_ONLY +int gdb_queuesig (void); int gdb_handlesig (CPUState *, int); void gdb_exit(CPUState *, int); +void gdb_signalled(CPUState *, int); int gdbserver_start(int); void gdbserver_fork(CPUState *); #else diff --git a/linux-user/signal.c b/linux-user/signal.c index e0f6aaf..5e30522 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -264,6 +264,26 @@ void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo) (void *)(long)tswapl(tinfo->_sifields._rt._sigval.sival_ptr); } +static int fatal_signal (int sig) +{ + switch (sig) { + case TARGET_SIGCHLD: + case TARGET_SIGURG: + case TARGET_SIGWINCH: + /* Ignored by default. */ + return 0; + case TARGET_SIGCONT: + case TARGET_SIGSTOP: + case TARGET_SIGTSTP: + case TARGET_SIGTTIN: + case TARGET_SIGTTOU: + /* Job control signals. */ + return 0; + default: + return 1; + } +} + void signal_init(void) { struct sigaction act; @@ -298,10 +318,12 @@ void signal_init(void) } /* If there's already a handler installed then something has gone horribly wrong, so don't even try to handle that case. */ - /* Install some handlers for our own use. */ - if (host_sig == SIGSEGV || host_sig == SIGBUS) { + /* Install some handlers for our own use. We need at least + SIGSEGV and SIGBUS, to detect exceptions. We can not just + trap all signals because it affects syscall interrupt + behavior. But do trap all default-fatal signals. */ + if (fatal_signal (i)) sigaction(host_sig, &act, NULL); - } } } @@ -332,6 +354,7 @@ static void __attribute((noreturn)) force_sig(int sig) fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n", sig, strsignal(host_sig)); #if 1 + gdb_signalled(thread_env, sig); _exit(-host_sig); #else { @@ -353,14 +376,16 @@ int queue_signal(CPUState *env, int sig, target_siginfo_t *info) struct emulated_sigtable *k; struct sigqueue *q, **pq; abi_ulong handler; + int queue; #if defined(DEBUG_SIGNAL) fprintf(stderr, "queue_signal: sig=%d\n", sig); #endif k = &ts->sigtab[sig - 1]; + queue = gdb_queuesig (); handler = sigact_table[sig - 1]._sa_handler; - if (handler == TARGET_SIG_DFL) { + if (!queue && handler == TARGET_SIG_DFL) { if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) { kill(getpid(),SIGSTOP); return 0; @@ -374,10 +399,10 @@ int queue_signal(CPUState *env, int sig, target_siginfo_t *info) } else { return 0; /* indicate ignored */ } - } else if (handler == TARGET_SIG_IGN) { + } else if (!queue && handler == TARGET_SIG_IGN) { /* ignore signal */ return 0; - } else if (handler == TARGET_SIG_ERR) { + } else if (!queue && handler == TARGET_SIG_ERR) { force_sig(sig); } else { pq = &k->first; @@ -417,7 +442,8 @@ static void host_signal_handler(int host_signum, siginfo_t *info, /* the CPU emulator uses some host signals to detect exceptions, we we forward to it some signals */ - if (host_signum == SIGSEGV || host_signum == SIGBUS) { + if ((host_signum == SIGSEGV || host_signum == SIGBUS) + && info->si_code == SI_KERNEL) { if (cpu_signal_handler(host_signum, info, puc)) return; } @@ -544,7 +570,10 @@ int do_sigaction(int sig, const struct target_sigaction *act, if (k->_sa_handler == TARGET_SIG_IGN) { act1.sa_sigaction = (void *)SIG_IGN; } else if (k->_sa_handler == TARGET_SIG_DFL) { - act1.sa_sigaction = (void *)SIG_DFL; + if (fatal_signal (sig)) + act1.sa_sigaction = host_signal_handler; + else + act1.sa_sigaction = (void *)SIG_DFL; } else { act1.sa_sigaction = host_signal_handler; } @@ -3107,17 +3136,21 @@ void process_pending_signals(CPUState *cpu_env) sig = gdb_handlesig (cpu_env, sig); if (!sig) { - fprintf (stderr, "Lost signal\n"); - abort(); + sa = NULL; + handler = TARGET_SIG_IGN; + } else { + sa = &sigact_table[sig - 1]; + handler = sa->_sa_handler; } - sa = &sigact_table[sig - 1]; - handler = sa->_sa_handler; if (handler == TARGET_SIG_DFL) { - /* default handler : ignore some signal. The other are fatal */ - if (sig != TARGET_SIGCHLD && - sig != TARGET_SIGURG && - sig != TARGET_SIGWINCH) { + /* default handler : ignore some signal. The other are job control or fatal */ + if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) { + kill(getpid(),SIGSTOP); + } else if (sig != TARGET_SIGCHLD && + sig != TARGET_SIGURG && + sig != TARGET_SIGWINCH && + sig != TARGET_SIGCONT) { force_sig(sig); } } else if (handler == TARGET_SIG_IGN) { -- 2.7.4