aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorphilippe <philippe@a5019735-40e9-0310-863c-91ae7b9d1cf9>2015-05-17 13:38:25 +0000
committerphilippe <philippe@a5019735-40e9-0310-863c-91ae7b9d1cf9>2015-05-17 13:38:25 +0000
commitb301469137f05afa7288790a67ed0628df1efb7e (patch)
tree506a3e1551b9346c9d5e269379809098443dc2ac
parent2d616330ecf6a1771461bacb134f5a59ac233525 (diff)
downloadvalgrind-b301469137f05afa7288790a67ed0628df1efb7e.tar.gz
* Let GDB user modify the signal to send to the guest process
* implement qXfer:siginfo:read: packet to allow GDB to show $_siginfo git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15248 a5019735-40e9-0310-863c-91ae7b9d1cf9
-rw-r--r--NEWS7
-rw-r--r--coregrind/m_gdbserver/m_gdbserver.c40
-rw-r--r--coregrind/m_gdbserver/server.c54
-rw-r--r--coregrind/m_gdbserver/server.h12
-rw-r--r--coregrind/m_gdbserver/target.c36
-rw-r--r--coregrind/m_signals.c27
-rw-r--r--coregrind/pub_core_gdbserver.h11
7 files changed, 133 insertions, 54 deletions
diff --git a/NEWS b/NEWS
index 653162309..3c3fb2c21 100644
--- a/NEWS
+++ b/NEWS
@@ -49,6 +49,13 @@ Release 3.11.0 is under development, not yet released.
compiler version is 14.0 or later.
* New and modified GDB server monitor features:
+ - When a signal is reported in GDB, you can now use the GDB convenience
+ variable $_siginfo to examine detailed signal information.
+
+ - Valgrind gdbserver now allows the user to change the signal
+ to deliver to the process. So, use 'signal SIGNAL' to continue execution
+ with SIGNAL instead of the signal reported to GDB. Use 'signal 0' to
+ continue without passing the signal to the process.
- With recent GDB (>= 7.9.50.20150514-cvs), the command 'target remote'
will automatically load the executable file of the process running
diff --git a/coregrind/m_gdbserver/m_gdbserver.c b/coregrind/m_gdbserver/m_gdbserver.c
index 2238ca5ed..888340ae0 100644
--- a/coregrind/m_gdbserver/m_gdbserver.c
+++ b/coregrind/m_gdbserver/m_gdbserver.c
@@ -934,16 +934,24 @@ Bool VG_(gdbserver_activity) (ThreadId tid)
return ret;
}
-
-void VG_(gdbserver_report_fatal_signal) (Int vki_sigNo, ThreadId tid)
+static void dlog_signal (const HChar *who, const vki_siginfo_t *info,
+ ThreadId tid)
{
- dlog(1, "VG core calling VG_(gdbserver_report_fatal_signal) "
+ dlog(1, "VG core calling %s "
"vki_nr %d %s gdb_nr %d %s tid %d\n",
- vki_sigNo, VG_(signame)(vki_sigNo),
- target_signal_from_host (vki_sigNo),
- target_signal_to_name(target_signal_from_host (vki_sigNo)),
+ who,
+ info->si_signo, VG_(signame)(info->si_signo),
+ target_signal_from_host (info->si_signo),
+ target_signal_to_name(target_signal_from_host (info->si_signo)),
tid);
+}
+
+void VG_(gdbserver_report_fatal_signal) (const vki_siginfo_t *info,
+ ThreadId tid)
+{
+ dlog_signal("VG_(gdbserver_report_fatal_signal)", info, tid);
+
if (remote_connected()) {
dlog(1, "already connected, assuming already reported\n");
return;
@@ -952,21 +960,16 @@ void VG_(gdbserver_report_fatal_signal) (Int vki_sigNo, ThreadId tid)
VG_(umsg)("(action on fatal signal) vgdb me ... \n");
/* indicate to gdbserver that there is a signal */
- gdbserver_signal_encountered (vki_sigNo);
+ gdbserver_signal_encountered (info);
/* let gdbserver do some work, e.g. show the signal to the user */
call_gdbserver (tid, signal_reason);
}
-Bool VG_(gdbserver_report_signal) (Int vki_sigNo, ThreadId tid)
+Bool VG_(gdbserver_report_signal) (vki_siginfo_t *info, ThreadId tid)
{
- dlog(1, "VG core calling VG_(gdbserver_report_signal) "
- "vki_nr %d %s gdb_nr %d %s tid %d\n",
- vki_sigNo, VG_(signame)(vki_sigNo),
- target_signal_from_host (vki_sigNo),
- target_signal_to_name(target_signal_from_host (vki_sigNo)),
- tid);
+ dlog_signal("VG_(gdbserver_report_signal)", info, tid);
/* if gdbserver is currently not connected, then signal
is to be given to the process */
@@ -977,19 +980,20 @@ Bool VG_(gdbserver_report_signal) (Int vki_sigNo, ThreadId tid)
/* if gdb has informed gdbserver that this signal can be
passed directly without informing gdb, then signal is
to be given to the process. */
- if (pass_signals[target_signal_from_host(vki_sigNo)]) {
+ if (pass_signals[target_signal_from_host(info->si_signo)]) {
dlog(1, "pass_signals => pass\n");
return True;
}
/* indicate to gdbserver that there is a signal */
- gdbserver_signal_encountered (vki_sigNo);
+ gdbserver_signal_encountered (info);
- /* let gdbserver do some work, e.g. show the signal to the user */
+ /* let gdbserver do some work, e.g. show the signal to the user.
+ User can also decide to ignore the signal or change the signal. */
call_gdbserver (tid, signal_reason);
/* ask gdbserver what is the final decision */
- if (gdbserver_deliver_signal (vki_sigNo)) {
+ if (gdbserver_deliver_signal (info)) {
dlog(1, "gdbserver deliver signal\n");
return True;
} else {
diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c
index 6fb507ddc..6361c5aca 100644
--- a/coregrind/m_gdbserver/server.c
+++ b/coregrind/m_gdbserver/server.c
@@ -857,8 +857,10 @@ void handle_query (char *arg_own_buf, int *new_packet_len_p)
}
VG_(lseek) (fd, ofs, VKI_SEEK_SET);
len_read = VG_(read) (fd, toread, len);
- *new_packet_len_p = write_qxfer_response (arg_own_buf, (unsigned char *)toread,
- len_read, ofs + len_read < doc_len);
+ *new_packet_len_p = write_qxfer_response (arg_own_buf,
+ (unsigned char *)toread,
+ len_read,
+ ofs + len_read < doc_len);
VG_(close) (fd);
return;
}
@@ -878,8 +880,8 @@ void handle_query (char *arg_own_buf, int *new_packet_len_p)
return;
}
- if (len > PBUFSIZ - 2)
- len = PBUFSIZ - 2;
+ if (len > PBUFSIZ - POVERHSIZ)
+ len = PBUFSIZ - POVERHSIZ;
data = malloc (len);
{
@@ -925,12 +927,13 @@ void handle_query (char *arg_own_buf, int *new_packet_len_p)
unsigned long pid;
const HChar *name;
- /* Reject any annex; grab the offset and length. */
+ /* grab the annex, offset and length. */
if (decode_xfer_read (arg_own_buf + 21, &annex, &ofs, &len) < 0) {
strcpy (arg_own_buf, "E00");
return;
}
+ /* Reject any annex with invalid/unexpected pid */
if (strlen(annex) > 0)
pid = strtoul (annex, NULL, 16);
else
@@ -974,6 +977,44 @@ void handle_query (char *arg_own_buf, int *new_packet_len_p)
return;
}
+ if (strncmp ("qXfer:siginfo:read:", arg_own_buf, 19) == 0) {
+ vki_siginfo_t info;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ const char *annex;
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (arg_own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0') {
+ strcpy (arg_own_buf, "E00");
+ return;
+ }
+
+ if (len > PBUFSIZ - POVERHSIZ)
+ len = PBUFSIZ - POVERHSIZ;
+
+ gdbserver_pending_signal_to_report(&info);
+
+ if (ofs >= sizeof(info))
+ n = -1;
+ else
+ n = sizeof(info) - ofs;
+
+ if (n < 0)
+ write_enn (arg_own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (arg_own_buf,
+ (unsigned char *)&info,
+ len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (arg_own_buf,
+ (unsigned char *)&info,
+ n, 0);
+
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", arg_own_buf, 10) == 0
&& (arg_own_buf[10] == ':' || arg_own_buf[10] == '\0')) {
@@ -1000,6 +1041,7 @@ void handle_query (char *arg_own_buf, int *new_packet_len_p)
initialize_shadow_low(False);
}
strcat (arg_own_buf, ";qXfer:exec-file:read+");
+ strcat (arg_own_buf, ";qXfer:siginfo:read+");
return;
}
@@ -1097,7 +1139,7 @@ void server_main (void)
putpkt (own_buf);
}
- /* If we our status is terminal (exit or fatal signal) get out
+ /* If our status is terminal (exit or fatal signal) get out
as quickly as we can. We won't be able to handle any request
anymore. */
if (status == 'W' || status == 'X') {
diff --git a/coregrind/m_gdbserver/server.h b/coregrind/m_gdbserver/server.h
index 1dd0d1a06..d2bbfd7c4 100644
--- a/coregrind/m_gdbserver/server.h
+++ b/coregrind/m_gdbserver/server.h
@@ -206,11 +206,17 @@ struct thread_info;
A call to call_gdbserver is needed to send the resume reply to GDB.
After this call, gdbserver_deliver_signal indicates if the signal
is effectively to be delivered to the guest process. */
-extern void gdbserver_signal_encountered (Int vki_sigNo);
-/* between these two calls, call call_gdbserver */
+extern void gdbserver_signal_encountered (const vki_siginfo_t *info);
+/* between these two calls, call call_gdbserver.
+ Between these 2 calls the signal to report to GDB can be retrieved using
+ gdbserver_pending_signal_to_report. */
/* If gdbserver_deliver_signal True, then gdb did not ask
to ignore the signal, so signal can be delivered to the guest. */
-extern Bool gdbserver_deliver_signal (Int vki_sigNo);
+extern Bool gdbserver_deliver_signal (vki_siginfo_t *info);
+
+/* Signal info last provided with gdbserver_signal_encountered.
+ It is what is/will be reported to GDB. */
+extern void gdbserver_pending_signal_to_report (vki_siginfo_t /* OUT */ *info);
/* Called when a process is about to go with reason ('W' or 'X') and code.
This sets global variables that will be used to return the process
diff --git a/coregrind/m_gdbserver/target.c b/coregrind/m_gdbserver/target.c
index 1dc6e41a5..64ce5a31e 100644
--- a/coregrind/m_gdbserver/target.c
+++ b/coregrind/m_gdbserver/target.c
@@ -153,17 +153,28 @@ static CORE_ADDR stop_pc;
*/
static CORE_ADDR resume_pc;
-static int vki_signal_to_report;
+static vki_siginfo_t vki_signal_to_report;
+static vki_siginfo_t vki_signal_to_deliver;
-void gdbserver_signal_encountered (Int vki_sigNo)
+void gdbserver_signal_encountered (const vki_siginfo_t *info)
{
- vki_signal_to_report = vki_sigNo;
+ vki_signal_to_report = *info;
+ vki_signal_to_deliver = *info;
}
-static int vki_signal_to_deliver;
-Bool gdbserver_deliver_signal (Int vki_sigNo)
+void gdbserver_pending_signal_to_report (vki_siginfo_t *info)
{
- return vki_sigNo == vki_signal_to_deliver;
+ *info = vki_signal_to_report;
+}
+
+Bool gdbserver_deliver_signal (vki_siginfo_t *info)
+{
+ if (info->si_signo != vki_signal_to_deliver.si_signo)
+ dlog(1, "GDB changed signal info %d to_report %d to_deliver %d\n",
+ info->si_signo, vki_signal_to_report.si_signo,
+ vki_signal_to_deliver.si_signo);
+ *info = vki_signal_to_deliver;
+ return vki_signal_to_deliver.si_signo != 0;
}
static unsigned char exit_status_to_report;
@@ -238,7 +249,10 @@ void valgrind_resume (struct thread_resume *resume_info)
C2v(stopped_data_address));
VG_(set_watchpoint_stop_address) ((Addr) 0);
}
- vki_signal_to_deliver = resume_info->sig;
+ vki_signal_to_deliver.si_signo = resume_info->sig;
+ /* signal was reported to GDB, GDB told us to resume execution.
+ So, reset the signal to report to 0. */
+ VG_(memset) (&vki_signal_to_report, 0, sizeof(vki_signal_to_report));
stepping = resume_info->step;
resume_pc = (*the_low_target.get_pc) ();
@@ -288,12 +302,10 @@ unsigned char valgrind_wait (char *ourstatus)
and with a signal TRAP (i.e. a breakpoint), unless there is
a signal to report. */
*ourstatus = 'T';
- if (vki_signal_to_report == 0)
+ if (vki_signal_to_report.si_signo == 0)
sig = TARGET_SIGNAL_TRAP;
- else {
- sig = target_signal_from_host(vki_signal_to_report);
- vki_signal_to_report = 0;
- }
+ else
+ sig = target_signal_from_host(vki_signal_to_report.si_signo);
if (vgdb_interrupted_tid != 0)
tst = VG_(get_ThreadState) (vgdb_interrupted_tid);
diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c
index abfa0d72c..16d220c8f 100644
--- a/coregrind/m_signals.c
+++ b/coregrind/m_signals.c
@@ -694,12 +694,15 @@ static SKSS skss;
/* returns True if signal is to be ignored.
To check this, possibly call gdbserver with tid. */
-static Bool is_sig_ign(Int sigNo, ThreadId tid)
+static Bool is_sig_ign(vki_siginfo_t *info, ThreadId tid)
{
- vg_assert(sigNo >= 1 && sigNo <= _VKI_NSIG);
+ vg_assert(info->si_signo >= 1 && info->si_signo <= _VKI_NSIG);
- return scss.scss_per_sig[sigNo].scss_handler == VKI_SIG_IGN
- || !VG_(gdbserver_report_signal) (sigNo, tid);
+ /* If VG_(gdbserver_report_signal) tells to report the signal,
+ then verify if this signal is not to be ignored. GDB might have
+ modified si_signo, so we check after the call to gdbserver. */
+ return !VG_(gdbserver_report_signal) (info, tid)
+ || scss.scss_per_sig[info->si_signo].scss_handler == VKI_SIG_IGN;
}
/* ---------------------------------------------------------------------
@@ -1786,7 +1789,7 @@ static void default_action(const vki_siginfo_t *info, ThreadId tid)
&& VG_(dyn_vgdb_error) <= VG_(get_n_errs_shown)() + 1) {
/* Note: we add + 1 to n_errs_shown as the fatal signal was not
reported through error msg, and so was not counted. */
- VG_(gdbserver_report_fatal_signal) (sigNo, tid);
+ VG_(gdbserver_report_fatal_signal) (info, tid);
}
if (VG_(is_action_requested)( "Attach to debugger", & VG_(clo_db_attach) )) {
@@ -1922,7 +1925,7 @@ static void synth_fault_common(ThreadId tid, Addr addr, Int si_code)
/* Even if gdbserver indicates to ignore the signal, we must deliver it.
So ignore the return value of VG_(gdbserver_report_signal). */
- (void) VG_(gdbserver_report_signal) (VKI_SIGSEGV, tid);
+ (void) VG_(gdbserver_report_signal) (&info, tid);
/* If they're trying to block the signal, force it to be delivered */
if (VG_(sigismember)(&VG_(threads)[tid].sig_mask, VKI_SIGSEGV))
@@ -1962,7 +1965,7 @@ void VG_(synth_sigill)(ThreadId tid, Addr addr)
info.si_code = VKI_ILL_ILLOPC; /* jrs: no idea what this should be */
info.VKI_SIGINFO_si_addr = (void*)addr;
- if (VG_(gdbserver_report_signal) (VKI_SIGILL, tid)) {
+ if (VG_(gdbserver_report_signal) (&info, tid)) {
resume_scheduler(tid);
deliver_signal(tid, &info, NULL);
}
@@ -1987,7 +1990,7 @@ void VG_(synth_sigbus)(ThreadId tid)
in .si_addr. Oh well. */
/* info.VKI_SIGINFO_si_addr = (void*)addr; */
- if (VG_(gdbserver_report_signal) (VKI_SIGBUS, tid)) {
+ if (VG_(gdbserver_report_signal) (&info, tid)) {
resume_scheduler(tid);
deliver_signal(tid, &info, NULL);
}
@@ -2027,7 +2030,7 @@ void VG_(synth_sigtrap)(ThreadId tid)
# endif
/* fixs390: do we need to do anything here for s390 ? */
- if (VG_(gdbserver_report_signal) (VKI_SIGTRAP, tid)) {
+ if (VG_(gdbserver_report_signal) (&info, tid)) {
resume_scheduler(tid);
deliver_signal(tid, &info, &uc);
}
@@ -2236,7 +2239,7 @@ void async_signalhandler ( Int sigNo,
/* (2) */
/* Set up the thread's state to deliver a signal */
- if (!is_sig_ign(info->si_signo, tid))
+ if (!is_sig_ign(info, tid))
deliver_signal(tid, info, uc);
/* It's crucial that (1) and (2) happen in the order (1) then (2)
@@ -2508,7 +2511,7 @@ void sync_signalhandler_from_kernel ( ThreadId tid,
}
if (VG_(in_generated_code)) {
- if (VG_(gdbserver_report_signal) (sigNo, tid)
+ if (VG_(gdbserver_report_signal) (info, tid)
|| VG_(sigismember)(&tst->sig_mask, sigNo)) {
/* Can't continue; must longjmp back to the scheduler and thus
enter the sighandler immediately. */
@@ -2714,7 +2717,7 @@ void VG_(poll_signals)(ThreadId tid)
/* OK, something to do; deliver it */
if (VG_(clo_trace_signals))
VG_(dmsg)("Polling found signal %d for tid %d\n", sip->si_signo, tid);
- if (!is_sig_ign(sip->si_signo, tid))
+ if (!is_sig_ign(sip, tid))
deliver_signal(tid, sip, NULL);
else if (VG_(clo_trace_signals))
VG_(dmsg)(" signal %d ignored\n", sip->si_signo);
diff --git a/coregrind/pub_core_gdbserver.h b/coregrind/pub_core_gdbserver.h
index c7bcd973f..6ec9dafc6 100644
--- a/coregrind/pub_core_gdbserver.h
+++ b/coregrind/pub_core_gdbserver.h
@@ -105,14 +105,19 @@ extern void VG_(invoke_gdbserver) ( int check );
// no gdb is connected, or gdb instructs to pass the signal.
// Note that if the below returns True, the signal might
// still be ignored if this is the action desired by the
-// guest program.
-extern Bool VG_(gdbserver_report_signal) (Int vki_signo, ThreadId tid);
+// guest program. Using GDB, the user can also modify the signal to be
+// reported (e.g. changing the signo to pass to the guest).
+// If this function returns True, m_signals.c should deliver the signal
+// info as modified by VG_(gdbserver_report_signal).
+// If this function returns False, no signal should be reported.
+extern Bool VG_(gdbserver_report_signal) (vki_siginfo_t *info, ThreadId tid);
// If no gdb is connected yet, wait for a gdb to connect and report
// this (supposedly) fatal signal.
// If a gdb is already connected, this does nothing (as normally
// the signal was already reported to the already connected gdb).
-extern void VG_(gdbserver_report_fatal_signal) (Int vki_signo, ThreadId tid);
+extern void VG_(gdbserver_report_fatal_signal) (const vki_siginfo_t *info,
+ ThreadId tid);
/* Entry point invoked by scheduler.c to execute the request
VALGRIND_CLIENT_MONITOR_COMMAND.