diff options
author | philippe <philippe@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2015-05-17 13:38:25 +0000 |
---|---|---|
committer | philippe <philippe@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2015-05-17 13:38:25 +0000 |
commit | b301469137f05afa7288790a67ed0628df1efb7e (patch) | |
tree | 506a3e1551b9346c9d5e269379809098443dc2ac | |
parent | 2d616330ecf6a1771461bacb134f5a59ac233525 (diff) | |
download | valgrind-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-- | NEWS | 7 | ||||
-rw-r--r-- | coregrind/m_gdbserver/m_gdbserver.c | 40 | ||||
-rw-r--r-- | coregrind/m_gdbserver/server.c | 54 | ||||
-rw-r--r-- | coregrind/m_gdbserver/server.h | 12 | ||||
-rw-r--r-- | coregrind/m_gdbserver/target.c | 36 | ||||
-rw-r--r-- | coregrind/m_signals.c | 27 | ||||
-rw-r--r-- | coregrind/pub_core_gdbserver.h | 11 |
7 files changed, 133 insertions, 54 deletions
@@ -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. |