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 <dan@codesourcery.com>
Acked-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6096 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
aurel32 2008-12-18 22:44:13 +00:00
parent 2b1319c85c
commit ca587a8ebd
3 changed files with 290 additions and 31 deletions

254
gdbstub.c
View File

@ -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 <signal.h>
/* 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;
}
@ -1852,6 +2050,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)
{
@ -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)
{

View File

@ -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

View File

@ -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) {