From 4aa995e123f47a4aca553ae4ed4fdebe2d00e316 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 6 Feb 2009 22:59:01 +0000 Subject: [PATCH] 2009-02-06 Pedro Alves gdb/ * target.h (enum target_object): Add new TARGET_OBJECT_SIGNAL_INFO. * infrun.c (siginfo_value_read, siginfo_value_write): New. (siginfo_value_funcs): New. (siginfo_make_value): New. (_initialize_infrun): Create the $_siginfo convenience variable. * gdbtypes.h (append_composite_type_field_aligned): Declare. * gdbtypes.c (append_composite_type_field): Rename to... (append_composite_type_field_aligned): ... this. Add ALIGNMENT argument. Handle it. (append_composite_type_field): Rewrite on top of append_composite_type_field_aligned. * value.h (internalvar_make_value): New typedef. (struct internalvar) : New field. (create_internalvar_type_lazy): Declare. * value.c (create_internalvar): Clear make_value. (create_internalvar_type_lazy): New. (value_of_internalvar): If make_value is set use it. (preserve_values): Skip internal variables that don't have a value. * gdbarch.sh (get_siginfo_type): New. * gdbarch.h, gdbarch.c: Regenerate. * linux-tdep.h, linux-tdep.c: New. * amd64-linux-tdep.c: Include "linux-tdep.h". (amd64_linux_init_abi): Register linux_get_siginfo_type and linux_get_siginfo_mapper. * i386-linux-tdep.c: Include "linux-tdep.h". (i386_linux_init_abi): Register linux_get_siginfo_type and linux_get_siginfo_mapper. * arm-linux-tdep.c: Include "linux-tdep.h". (i386_linux_init_abi): Register linux_get_siginfo_type and linux_get_siginfo_mapper. * linux-nat.c (linux_xfer_siginfo): New. (linux_nat_xfer_partial): Handle TARGET_OBJECT_SIGNAL_INFO. * remote.c (PACKET_qXfer_siginfo_read) (PACKET_qXfer_siginfo_write): New. (feature remote_protocol_features): Add "qXfer:siginfo:read" and "qXfer:siginfo:write" features. (remote_xfer_partial): Handle TARGET_OBJECT_SIGNAL_INFO. (_initialize_remote): Add "set/show remote read-siginfo-object" and "set/show remote write-siginfo-object" commands. * Makefile.in (ALL_TARGET_OBS): Add linux-tdep.o. (HFILES_NO_SRCDIR): Add linux-tdep.h. (ALLDEPFILES): Add linux-tdep.c. * configure.tgt (arm*-*-linux* | arm*-*-uclinux*) (i[34567]86-*-linux*, x86_64-*-linux*): Add linux-tdep.o to gdb_target_obs. 2009-02-06 Pedro Alves gdb/gdbserver/ * server.c (handle_query): Report qXfer:siginfo:read and qXfer:siginfo:write as supported and handle them. * target.h (struct target_ops) : New field. * linux-low.c (linux_xfer_siginfo): New. (linux_target_ops): Set it. 2009-02-06 Pedro Alves gdb/doc/ * gdb.texinfo (Signals): Document $_siginfo. (Convenience Variables): Mention $_siginfo. (Remote Configuration): Document qXfer:siginfo:read, qXfer:siginfo:write packets, and the read-siginfo-object, write-siginfo-object commands. 2009-02-06 Pedro Alves gdb/testsuite/ * gdb.base/siginfo-obj.c, gdb.base/siginfo-obj.exp: New. --- gdb/ChangeLog | 53 ++++++++++ gdb/Makefile.in | 4 +- gdb/amd64-linux-tdep.c | 3 + gdb/arm-linux-tdep.c | 3 + gdb/configure.tgt | 6 +- gdb/doc/ChangeLog | 8 ++ gdb/doc/gdb.texinfo | 101 ++++++++++++++++++ gdb/gdbarch.c | 33 ++++++ gdb/gdbarch.h | 10 ++ gdb/gdbarch.sh | 5 + gdb/gdbserver/ChangeLog | 8 ++ gdb/gdbserver/linux-low.c | 39 +++++++ gdb/gdbserver/server.c | 74 +++++++++++++ gdb/gdbserver/target.h | 5 + gdb/gdbtypes.c | 29 +++++- gdb/gdbtypes.h | 4 + gdb/i386-linux-tdep.c | 3 + gdb/infrun.c | 87 ++++++++++++++++ gdb/linux-nat.c | 50 ++++++++- gdb/linux-tdep.c | 138 +++++++++++++++++++++++++ gdb/linux-tdep.h | 25 +++++ gdb/remote.c | 25 +++++ gdb/target.h | 5 +- gdb/testsuite/ChangeLog | 4 + gdb/testsuite/gdb.base/siginfo-obj.c | 70 +++++++++++++ gdb/testsuite/gdb.base/siginfo-obj.exp | 131 +++++++++++++++++++++++ gdb/value.c | 62 +++++++---- gdb/value.h | 6 ++ 28 files changed, 962 insertions(+), 29 deletions(-) create mode 100644 gdb/linux-tdep.c create mode 100644 gdb/linux-tdep.h create mode 100644 gdb/testsuite/gdb.base/siginfo-obj.c create mode 100644 gdb/testsuite/gdb.base/siginfo-obj.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 01e4156ecd3..56496fd49d6 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,56 @@ +2009-02-06 Pedro Alves + + * target.h (enum target_object): Add new TARGET_OBJECT_SIGNAL_INFO. + * infrun.c (siginfo_value_read, siginfo_value_write): New. + (siginfo_value_funcs): New. + (siginfo_make_value): New. + (_initialize_infrun): Create the $_siginfo convenience variable. + * gdbtypes.h (append_composite_type_field_aligned): Declare. + * gdbtypes.c (append_composite_type_field): Rename to... + (append_composite_type_field_aligned): ... this. Add ALIGNMENT + argument. Handle it. + (append_composite_type_field): Rewrite on top of + append_composite_type_field_aligned. + * value.h (internalvar_make_value): New typedef. + (struct internalvar) : New field. + (create_internalvar_type_lazy): Declare. + * value.c (create_internalvar): Clear make_value. + (create_internalvar_type_lazy): New. + (value_of_internalvar): If make_value is set use it. + (preserve_values): Skip internal variables that don't have a + value. + * gdbarch.sh (get_siginfo_type): New. + * gdbarch.h, gdbarch.c: Regenerate. + + * linux-tdep.h, linux-tdep.c: New. + * amd64-linux-tdep.c: Include "linux-tdep.h". + (amd64_linux_init_abi): Register linux_get_siginfo_type and + linux_get_siginfo_mapper. + * i386-linux-tdep.c: Include "linux-tdep.h". + (i386_linux_init_abi): Register linux_get_siginfo_type and + linux_get_siginfo_mapper. + * arm-linux-tdep.c: Include "linux-tdep.h". + (i386_linux_init_abi): Register linux_get_siginfo_type and + linux_get_siginfo_mapper. + + * linux-nat.c (linux_xfer_siginfo): New. + (linux_nat_xfer_partial): Handle TARGET_OBJECT_SIGNAL_INFO. + * remote.c (PACKET_qXfer_siginfo_read) + (PACKET_qXfer_siginfo_write): New. + (feature remote_protocol_features): Add "qXfer:siginfo:read" and + "qXfer:siginfo:write" features. + (remote_xfer_partial): Handle TARGET_OBJECT_SIGNAL_INFO. + (_initialize_remote): Add "set/show remote read-siginfo-object" + and "set/show remote write-siginfo-object" commands. + + * Makefile.in (ALL_TARGET_OBS): Add linux-tdep.o. + (HFILES_NO_SRCDIR): Add linux-tdep.h. + (ALLDEPFILES): Add linux-tdep.c. + + * configure.tgt (arm*-*-linux* | arm*-*-uclinux*) + (i[34567]86-*-linux*, x86_64-*-linux*): Add linux-tdep.o to + gdb_target_obs. + 2009-02-06 Jim Blandy Daniel Jacobowitz Vladimir Prus diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 7400702a68f..06bdd98302c 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -483,6 +483,7 @@ ALL_TARGET_OBS = \ i386-sol2-tdep.o i386-tdep.o i387-tdep.o \ i386-dicos-tdep.o \ iq2000-tdep.o \ + linux-tdep.o \ m32c-tdep.o \ m32r-linux-tdep.o m32r-tdep.o \ m68hc11-tdep.o \ @@ -730,7 +731,7 @@ config/sparc/nm-sol2.h config/nm-linux.h config/mips/nm-irix5.h \ config/rs6000/nm-rs6000.h top.h bsd-kvm.h gdb-stabs.h reggroups.h \ annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \ remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \ -sentinel-frame.h bcache.h symfile.h windows-tdep.h +sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h # Header files that already have srcdir in them, or which are in objdir. @@ -1301,6 +1302,7 @@ ALLDEPFILES = \ irix5-nat.c \ libunwind-frame.c \ linux-fork.c \ + linux-tdep.c \ m68hc11-tdep.c \ m32r-tdep.c \ m32r-linux-nat.c m32r-linux-tdep.c \ diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index b8e779e5388..badc0b43e77 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -29,6 +29,7 @@ #include "gdbtypes.h" #include "reggroups.h" #include "amd64-linux-tdep.h" +#include "linux-tdep.h" #include "gdb_string.h" @@ -296,6 +297,8 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) simple_displaced_step_free_closure); set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); + + set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); } diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index c66e814ff0b..fbe80eef257 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -36,6 +36,7 @@ #include "arm-tdep.h" #include "arm-linux-tdep.h" +#include "linux-tdep.h" #include "glibc-tdep.h" #include "gdb_string.h" @@ -647,6 +648,8 @@ arm_linux_init_abi (struct gdbarch_info info, /* Core file support. */ set_gdbarch_regset_from_core_section (gdbarch, arm_linux_regset_from_core_section); + + set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); } void diff --git a/gdb/configure.tgt b/gdb/configure.tgt index c778cbff6ca..8053ba4ad11 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -75,7 +75,7 @@ arm*-wince-pe | arm*-*-mingw32ce*) arm*-*-linux*) # Target: ARM based machine running GNU/Linux gdb_target_obs="arm-tdep.o arm-linux-tdep.o glibc-tdep.o \ - solib.o solib-svr4.o symfile-mem.o corelow.o" + solib.o solib-svr4.o symfile-mem.o corelow.o linux-tdep.o" build_gdbserver=yes ;; arm*-*-netbsd* | arm*-*-knetbsd*-gnu) @@ -190,7 +190,7 @@ i[34567]86-*-solaris*) i[34567]86-*-linux*) # Target: Intel 386 running GNU/Linux gdb_target_obs="i386-tdep.o i386-linux-tdep.o glibc-tdep.o i387-tdep.o \ - solib.o solib-svr4.o symfile-mem.o corelow.o" + solib.o solib-svr4.o symfile-mem.o corelow.o linux-tdep.o" build_gdbserver=yes ;; i[34567]86-*-gnu*) @@ -513,7 +513,7 @@ x86_64-*-linux*) # Target: GNU/Linux x86-64 gdb_target_obs="amd64-tdep.o amd64-linux-tdep.o i386-tdep.o \ i387-tdep.o i386-linux-tdep.o glibc-tdep.o \ - solib.o solib-svr4.o corelow.o symfile-mem.o" + solib.o solib-svr4.o corelow.o symfile-mem.o linux-tdep.o" build_gdbserver=yes ;; x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu) diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 473c6e35890..0874a56a833 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,11 @@ +2009-02-06 Pedro Alves + + * gdb.texinfo (Signals): Document $_siginfo. + (Convenience Variables): Mention $_siginfo. + (Remote Configuration): Document qXfer:siginfo:read, + qXfer:siginfo:write packets, and the read-siginfo-object, + write-siginfo-object commands. + 2009-02-06 Pedro Alves * gdbint.texinfo (Values): New chapter. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 354b88804a1..9ba56a8e60c 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -4472,6 +4472,55 @@ a result of the fatal signal once it saw the signal. To prevent this, you can continue with @samp{signal 0}. @xref{Signaling, ,Giving your Program a Signal}. +@cindex extra signal information +@anchor{extra signal information} + +On some targets, @value{GDBN} can inspect extra signal information +associated with the intercepted signal, before it is actually +delivered to the program being debugged. This information is exported +by the convenience variable @code{$_siginfo}, and consists of data +that is passed by the kernel to the signal handler at the time of the +receipt of a signal. The data type of the information itself is +target dependent. You can see the data type using the @code{ptype +$_siginfo} command. On Unix systems, it typically corresponds to the +standard @code{siginfo_t} type, as defined in the @file{signal.h} +system header. + +Here's an example, on a @sc{gnu}/Linux system, printing the stray +referenced address that raised a segmentation fault. + +@smallexample +@group +(@value{GDBP}) continue +Program received signal SIGSEGV, Segmentation fault. +0x0000000000400766 in main () +69 *(int *)p = 0; +(@value{GDBP}) ptype $_siginfo +type = struct @{ + int si_signo; + int si_errno; + int si_code; + union @{ + int _pad[28]; + struct @{...@} _kill; + struct @{...@} _timer; + struct @{...@} _rt; + struct @{...@} _sigchld; + struct @{...@} _sigfault; + struct @{...@} _sigpoll; + @} _sifields; +@} +(@value{GDBP}) ptype $_siginfo._sifields._sigfault +type = struct @{ + void *si_addr; +@} +(@value{GDBP}) p $_siginfo._sifields._sigfault.si_addr +$1 = (void *) 0x7ffff7ff7000 +@end group +@end smallexample + +Depending on target support, @code{$_siginfo} may also be writable. + @node Thread Stops @section Stopping and Starting Multi-thread Programs @@ -7348,6 +7397,11 @@ to match the format in which the data was printed. @vindex $_exitcode@r{, convenience variable} The variable @code{$_exitcode} is automatically set to the exit code when the program being debugged terminates. + +@item $_siginfo +@vindex $_siginfo@r{, convenience variable} +The variable @code{$_siginfo} is bound to extra signal information +inspection (@pxref{extra signal information}). @end table On HP-UX systems, if you refer to a function or variable name that @@ -14307,6 +14361,14 @@ are: @tab @code{qXfer:spu:write} @tab @code{info spu} +@item @code{read-siginfo-object} +@tab @code{qXfer:siginfo:read} +@tab @code{print $_siginfo} + +@item @code{write-siginfo-object} +@tab @code{qXfer:siginfo:write} +@tab @code{set $_siginfo} + @item @code{get-thread-local-@*storage-address} @tab @code{qGetTLSAddr} @tab Displaying @code{__thread} variables @@ -26879,6 +26941,16 @@ These are the currently defined stub features and their properties: @tab @samp{-} @tab Yes +@item @samp{qXfer:siginfo:read} +@tab No +@tab @samp{-} +@tab Yes + +@item @samp{qXfer:siginfo:write} +@tab No +@tab @samp{-} +@tab Yes + @item @samp{QNonStop} @tab No @tab @samp{-} @@ -26939,6 +27011,14 @@ The remote stub understands the @samp{qXfer:spu:read} packet The remote stub understands the @samp{qXfer:spu:write} packet (@pxref{qXfer spu write}). +@item qXfer:siginfo:read +The remote stub understands the @samp{qXfer:siginfo:read} packet +(@pxref{qXfer siginfo read}). + +@item qXfer:siginfo:write +The remote stub understands the @samp{qXfer:siginfo:write} packet +(@pxref{qXfer siginfo write}). + @item QNonStop The remote stub understands the @samp{QNonStop} packet (@pxref{QNonStop}). @@ -27097,6 +27177,16 @@ annex part of the generic @samp{qXfer} packet must be empty This packet is not probed by default; the remote stub must request it, by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}). +@item qXfer:siginfo:read::@var{offset},@var{length} +@anchor{qXfer siginfo read} +Read contents of the extra signal information on the target +system. The annex part of the generic @samp{qXfer} packet must be +empty (@pxref{qXfer read}). + +This packet is not probed by default; the remote stub must request it, +by supplying an appropriate @samp{qSupported} response +(@pxref{qSupported}). + @item qXfer:spu:read:@var{annex}:@var{offset},@var{length} @anchor{qXfer spu read} Read contents of an @code{spufs} file on the target system. The @@ -27149,6 +27239,7 @@ the stub, or that the object does not support reading. @item qXfer:@var{object}:write:@var{annex}:@var{offset}:@var{data}@dots{} @cindex write data into object, remote request +@anchor{qXfer write} Write uninterpreted bytes into the target's special data area identified by the keyword @var{object}, starting at @var{offset} bytes into the data. @var{data}@dots{} is the binary-encoded data @@ -27161,6 +27252,16 @@ Here are the specific requests of this form defined so far. All formats, listed below. @table @samp +@item qXfer:siginfo:write::@var{offset}:@var{data}@dots{} +@anchor{qXfer siginfo write} +Write @var{data} to the extra signal information on the target system. +The annex part of the generic @samp{qXfer} packet must be +empty (@pxref{qXfer write}). + +This packet is not probed by default; the remote stub must request it, +by supplying an appropriate @samp{qSupported} response +(@pxref{qSupported}). + @item qXfer:spu:write:@var{annex}:@var{offset}:@var{data}@dots{} @anchor{qXfer spu write} Write @var{data} to an @code{spufs} file on the target system. The diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index c328a2b19c9..220771bd4a6 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -239,6 +239,7 @@ struct gdbarch int sofun_address_maybe_missing; gdbarch_target_signal_from_host_ftype *target_signal_from_host; gdbarch_target_signal_to_host_ftype *target_signal_to_host; + gdbarch_get_siginfo_type_ftype *get_siginfo_type; gdbarch_record_special_symbol_ftype *record_special_symbol; int has_global_solist; }; @@ -371,6 +372,7 @@ struct gdbarch startup_gdbarch = 0, /* sofun_address_maybe_missing */ default_target_signal_from_host, /* target_signal_from_host */ default_target_signal_to_host, /* target_signal_to_host */ + 0, /* get_siginfo_type */ 0, /* record_special_symbol */ 0, /* has_global_solist */ /* startup_gdbarch() */ @@ -624,6 +626,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of sofun_address_maybe_missing, invalid_p == 0 */ /* Skip verify of target_signal_from_host, invalid_p == 0 */ /* Skip verify of target_signal_to_host, invalid_p == 0 */ + /* Skip verify of get_siginfo_type, has predicate */ /* Skip verify of record_special_symbol, has predicate */ /* Skip verify of has_global_solist, invalid_p == 0 */ buf = ui_file_xstrdup (log, &dummy); @@ -834,6 +837,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: get_longjmp_target = <%s>\n", host_address_to_string (gdbarch->get_longjmp_target)); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_get_siginfo_type_p() = %d\n", + gdbarch_get_siginfo_type_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: get_siginfo_type = <0x%lx>\n", + (long) gdbarch->get_siginfo_type); fprintf_unfiltered (file, "gdbarch_dump: has_global_solist = %s\n", plongest (gdbarch->has_global_solist)); @@ -3219,6 +3228,30 @@ set_gdbarch_target_signal_to_host (struct gdbarch *gdbarch, gdbarch->target_signal_to_host = target_signal_to_host; } +int +gdbarch_get_siginfo_type_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->get_siginfo_type != NULL; +} + +struct type * +gdbarch_get_siginfo_type (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->get_siginfo_type != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_get_siginfo_type called\n"); + return gdbarch->get_siginfo_type (gdbarch); +} + +void +set_gdbarch_get_siginfo_type (struct gdbarch *gdbarch, + gdbarch_get_siginfo_type_ftype get_siginfo_type) +{ + gdbarch->get_siginfo_type = get_siginfo_type; +} + int gdbarch_record_special_symbol_p (struct gdbarch *gdbarch) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 35f8a36f4cd..4f6db30bc32 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -803,6 +803,16 @@ typedef int (gdbarch_target_signal_to_host_ftype) (struct gdbarch *gdbarch, enum extern int gdbarch_target_signal_to_host (struct gdbarch *gdbarch, enum target_signal ts); extern void set_gdbarch_target_signal_to_host (struct gdbarch *gdbarch, gdbarch_target_signal_to_host_ftype *target_signal_to_host); +/* Extra signal info inspection. + + Return a type suitable to inspect extra signal information. */ + +extern int gdbarch_get_siginfo_type_p (struct gdbarch *gdbarch); + +typedef struct type * (gdbarch_get_siginfo_type_ftype) (struct gdbarch *gdbarch); +extern struct type * gdbarch_get_siginfo_type (struct gdbarch *gdbarch); +extern void set_gdbarch_get_siginfo_type (struct gdbarch *gdbarch, gdbarch_get_siginfo_type_ftype *get_siginfo_type); + /* Record architecture-specific information from the symbol table. */ extern int gdbarch_record_special_symbol_p (struct gdbarch *gdbarch); diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index dc8fb088dd7..bcca6c937d9 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -705,6 +705,11 @@ m:enum target_signal:target_signal_from_host:int signo:signo::default_target_sig # signal number. m:int:target_signal_to_host:enum target_signal ts:ts::default_target_signal_to_host::0 +# Extra signal info inspection. +# +# Return a type suitable to inspect extra signal information. +M:struct type *:get_siginfo_type:void: + # Record architecture-specific information from the symbol table. M:void:record_special_symbol:struct objfile *objfile, asymbol *sym:objfile, sym diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 3fda37e45da..30d7e0f0420 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,11 @@ +2009-02-06 Pedro Alves + + * server.c (handle_query): Report qXfer:siginfo:read and + qXfer:siginfo:write as supported and handle them. + * target.h (struct target_ops) : New field. + * linux-low.c (linux_xfer_siginfo): New. + (linux_target_ops): Set it. + 2009-01-26 Pedro Alves * server.c (gdbserver_usage): Mention --remote-debug. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 3fa06ec9255..38dd0007d73 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -2180,6 +2180,44 @@ linux_qxfer_osdata (const char *annex, return len; } +static int +linux_xfer_siginfo (const char *annex, unsigned char *readbuf, + unsigned const char *writebuf, CORE_ADDR offset, int len) +{ + struct siginfo siginfo; + long pid = -1; + + if (current_inferior == NULL) + return -1; + + pid = pid_of (get_thread_process (current_inferior)); + + if (debug_threads) + fprintf (stderr, "%s siginfo for lwp %ld.\n", + readbuf != NULL ? "Reading" : "Writing", + pid); + + if (offset > sizeof (siginfo)) + return -1; + + if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo) != 0) + return -1; + + if (offset + len > sizeof (siginfo)) + len = sizeof (siginfo) - offset; + + if (readbuf != NULL) + memcpy (readbuf, (char *) &siginfo + offset, len); + else + { + memcpy ((char *) &siginfo + offset, writebuf, len); + if (ptrace (PTRACE_SETSIGINFO, pid, 0, &siginfo) != 0) + return -1; + } + + return len; +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -2213,6 +2251,7 @@ static struct target_ops linux_target_ops = { NULL, hostio_last_error_from_errno, linux_qxfer_osdata, + linux_xfer_siginfo, }; static void diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index c4e167210bd..416ceb3e33c 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -810,6 +810,77 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) return; } + if (the_target->qxfer_siginfo != NULL + && strncmp ("qXfer:siginfo:read:", own_buf, 19) == 0) + { + unsigned char *data; + int n; + CORE_ADDR ofs; + unsigned int len; + char *annex; + + require_running (own_buf); + + /* Reject any annex; grab the offset and length. */ + if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0 + || annex[0] != '\0') + { + strcpy (own_buf, "E00"); + return; + } + + /* Read one extra byte, as an indicator of whether there is + more. */ + if (len > PBUFSIZ - 2) + len = PBUFSIZ - 2; + data = malloc (len + 1); + if (!data) + return; + n = (*the_target->qxfer_siginfo) (annex, data, NULL, ofs, len + 1); + if (n < 0) + write_enn (own_buf); + else if (n > len) + *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1); + else + *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0); + + free (data); + return; + } + + if (the_target->qxfer_siginfo != NULL + && strncmp ("qXfer:siginfo:write:", own_buf, 20) == 0) + { + char *annex; + int n; + unsigned int len; + CORE_ADDR ofs; + unsigned char *data; + + require_running (own_buf); + + strcpy (own_buf, "E00"); + data = malloc (packet_len - 19); + if (!data) + return; + if (decode_xfer_write (own_buf + 20, packet_len - 20, &annex, + &ofs, &len, data) < 0) + { + free (data); + return; + } + + n = (*the_target->qxfer_siginfo) + (annex, NULL, (unsigned const char *)data, ofs, len); + if (n < 0) + write_enn (own_buf); + else + sprintf (own_buf, "%x", n); + + free (data); + return; + } + /* Protocol features query. */ if (strncmp ("qSupported", own_buf, 10) == 0 && (own_buf[10] == ':' || own_buf[10] == '\0')) @@ -826,6 +897,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (the_target->qxfer_spu != NULL) strcat (own_buf, ";qXfer:spu:read+;qXfer:spu:write+"); + if (the_target->qxfer_siginfo != NULL) + strcat (own_buf, ";qXfer:siginfo:read+;qXfer:siginfo:write+"); + /* We always report qXfer:features:read, as targets may install XML files on a subsequent call to arch_setup. If we reported to GDB on startup that we don't support diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index a1dfad644bf..7851a4c0d9e 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -193,6 +193,11 @@ struct target_ops int (*qxfer_osdata) (const char *annex, unsigned char *readbuf, unsigned const char *writebuf, CORE_ADDR offset, int len); + + /* Read/Write extra signal info. */ + int (*qxfer_siginfo) (const char *annex, unsigned char *readbuf, + unsigned const char *writebuf, + CORE_ADDR offset, int len); }; extern struct target_ops *the_target; diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index 9f7317318c8..81023211530 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -1822,8 +1822,8 @@ init_composite_type (char *name, enum type_code code) /* Helper function. Append a field to a composite type. */ void -append_composite_type_field (struct type *t, char *name, - struct type *field) +append_composite_type_field_aligned (struct type *t, char *name, + struct type *field, int alignment) { struct field *f; TYPE_NFIELDS (t) = TYPE_NFIELDS (t) + 1; @@ -1842,12 +1842,31 @@ append_composite_type_field (struct type *t, char *name, { TYPE_LENGTH (t) = TYPE_LENGTH (t) + TYPE_LENGTH (field); if (TYPE_NFIELDS (t) > 1) - FIELD_BITPOS (f[0]) = (FIELD_BITPOS (f[-1]) - + (TYPE_LENGTH (FIELD_TYPE (f[-1])) - * TARGET_CHAR_BIT)); + { + FIELD_BITPOS (f[0]) = (FIELD_BITPOS (f[-1]) + + (TYPE_LENGTH (FIELD_TYPE (f[-1])) + * TARGET_CHAR_BIT)); + + if (alignment) + { + int left = FIELD_BITPOS (f[0]) % (alignment * TARGET_CHAR_BIT); + if (left) + { + FIELD_BITPOS (f[0]) += left; + TYPE_LENGTH (t) += left / TARGET_CHAR_BIT; + } + } + } } } +void +append_composite_type_field (struct type *t, char *name, + struct type *field) +{ + append_composite_type_field_aligned (t, name, field, 0); +} + int can_dereference (struct type *t) { diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index e78d3cf5d3b..c90b6d7bdb4 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -1112,6 +1112,10 @@ extern struct type *init_type (enum type_code, int, int, char *, extern struct type *init_composite_type (char *name, enum type_code code); extern void append_composite_type_field (struct type *t, char *name, struct type *field); +extern void append_composite_type_field_aligned (struct type *t, + char *name, + struct type *field, + int alignment); /* Helper functions to construct a bit flags type. An initially empty type is created using init_flag_type(). Flags are then added using diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index 81c8d31c9b4..1a2e4f0a012 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -31,6 +31,7 @@ #include "i386-tdep.h" #include "i386-linux-tdep.h" +#include "linux-tdep.h" #include "glibc-tdep.h" #include "solib-svr4.h" #include "symtab.h" @@ -469,6 +470,8 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) simple_displaced_step_free_closure); set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); + + set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); } /* Provide a prototype to silence -Wmissing-prototypes. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index 3a67ac5400a..bed628d5abb 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -4804,6 +4804,87 @@ signals_info (char *signum_exp, int from_tty) printf_filtered (_("\nUse the \"handle\" command to change these tables.\n")); } + +/* The $_siginfo convenience variable is a bit special. We don't know + for sure the type of the value until we actually have a chance to + fetch the data. The type can change depending on gdbarch, so it it + also dependent on which thread you have selected. + + 1. making $_siginfo be an internalvar that creates a new value on + access. + + 2. making the value of $_siginfo be an lval_computed value. */ + +/* This function implements the lval_computed support for reading a + $_siginfo value. */ + +static void +siginfo_value_read (struct value *v) +{ + LONGEST transferred; + + transferred = + target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, + NULL, + value_contents_all_raw (v), + value_offset (v), + TYPE_LENGTH (value_type (v))); + + if (transferred != TYPE_LENGTH (value_type (v))) + error (_("Unable to read siginfo")); +} + +/* This function implements the lval_computed support for writing a + $_siginfo value. */ + +static void +siginfo_value_write (struct value *v, struct value *fromval) +{ + LONGEST transferred; + + transferred = target_write (¤t_target, + TARGET_OBJECT_SIGNAL_INFO, + NULL, + value_contents_all_raw (fromval), + value_offset (v), + TYPE_LENGTH (value_type (fromval))); + + if (transferred != TYPE_LENGTH (value_type (fromval))) + error (_("Unable to write siginfo")); +} + +static struct lval_funcs siginfo_value_funcs = + { + siginfo_value_read, + siginfo_value_write + }; + +/* Return a new value with the correct type for the siginfo object of + the current thread. Return a void value if there's no object + available. */ + +struct value * +siginfo_make_value (struct internalvar *var) +{ + struct type *type; + struct gdbarch *gdbarch; + + if (target_has_stack + && !ptid_equal (inferior_ptid, null_ptid)) + { + gdbarch = get_frame_arch (get_current_frame ()); + + if (gdbarch_get_siginfo_type_p (gdbarch)) + { + type = gdbarch_get_siginfo_type (gdbarch); + + return allocate_computed_value (type, &siginfo_value_funcs, NULL); + } + } + + return allocate_value (builtin_type_void); +} + /* Inferior thread state. These are details related to the inferior itself, and don't include @@ -5467,4 +5548,10 @@ Options are 'forward' or 'reverse'."), observer_attach_thread_ptid_changed (infrun_thread_ptid_changed); observer_attach_thread_stop_requested (infrun_thread_stop_requested); + + /* Explicitly create without lookup, since that tries to create a + value with a void typed value, and when we get here, gdbarch + isn't initialized yet. At this point, we're quite sure there + isn't another convenience variable of the same name. */ + create_internalvar_type_lazy ("_siginfo", siginfo_make_value); } diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 33b05fa455d..0b098a07194 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -3223,15 +3223,63 @@ linux_nat_mourn_inferior (struct target_ops *ops) linux_fork_mourn_inferior (); } +static LONGEST +linux_xfer_siginfo (struct target_ops *ops, enum target_object object, + const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, ULONGEST offset, LONGEST len) +{ + struct lwp_info *lp; + LONGEST n; + int pid; + struct siginfo siginfo; + + gdb_assert (object == TARGET_OBJECT_SIGNAL_INFO); + gdb_assert (readbuf || writebuf); + + pid = GET_LWP (inferior_ptid); + if (pid == 0) + pid = GET_PID (inferior_ptid); + + if (offset > sizeof (siginfo)) + return -1; + + errno = 0; + ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo); + if (errno != 0) + return -1; + + if (offset + len > sizeof (siginfo)) + len = sizeof (siginfo) - offset; + + if (readbuf != NULL) + memcpy (readbuf, (char *)&siginfo + offset, len); + else + { + memcpy ((char *)&siginfo + offset, writebuf, len); + errno = 0; + ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo); + if (errno != 0) + return -1; + } + + return len; +} + static LONGEST linux_nat_xfer_partial (struct target_ops *ops, enum target_object object, const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { - struct cleanup *old_chain = save_inferior_ptid (); + struct cleanup *old_chain; LONGEST xfer; + if (object == TARGET_OBJECT_SIGNAL_INFO) + return linux_xfer_siginfo (ops, object, annex, readbuf, writebuf, + offset, len); + + old_chain = save_inferior_ptid (); + if (is_lwp (inferior_ptid)) inferior_ptid = pid_to_ptid (GET_LWP (inferior_ptid)); diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c new file mode 100644 index 00000000000..f1f73735e35 --- /dev/null +++ b/gdb/linux-tdep.c @@ -0,0 +1,138 @@ +/* Target-dependent code for GNU/Linux, architecture independent. + + Copyright (C) 2009 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 3 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, see . */ + +#include "defs.h" +#include "gdbtypes.h" + +/* This function is suitable for architectures that don't + extend/override the standard siginfo structure. */ + +struct type * +linux_get_siginfo_type (struct gdbarch *gdbarch) +{ + struct type *int_type, *uint_type, *long_type, *void_ptr_type; + struct type *uid_type, *pid_type; + struct type *sigval_type, *clock_type; + struct type *siginfo_type, *sifields_type; + struct type *type; + + int_type = init_type (TYPE_CODE_INT, + gdbarch_int_bit (gdbarch) / HOST_CHAR_BIT, + 0, "int", NULL); + uint_type = init_type (TYPE_CODE_INT, + gdbarch_int_bit (gdbarch) / HOST_CHAR_BIT, + 0, "unsigned int", NULL); + long_type = init_type (TYPE_CODE_INT, + gdbarch_long_bit (gdbarch) / HOST_CHAR_BIT, + 0, "long", NULL); + void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void); + + /* sival_t */ + sigval_type = init_composite_type (NULL, TYPE_CODE_UNION); + TYPE_NAME (sigval_type) = xstrdup ("sigval_t"); + append_composite_type_field (sigval_type, "sival_int", int_type); + append_composite_type_field (sigval_type, "sival_ptr", void_ptr_type); + + /* __pid_t */ + pid_type = init_type (TYPE_CODE_TYPEDEF, TYPE_LENGTH (int_type), + TYPE_FLAG_TARGET_STUB, NULL, NULL); + TYPE_NAME (pid_type) = xstrdup ("__pid_t"); + TYPE_TARGET_TYPE (pid_type) = int_type; + + /* __uid_t */ + uid_type = init_type (TYPE_CODE_TYPEDEF, TYPE_LENGTH (uint_type), + TYPE_FLAG_TARGET_STUB, NULL, NULL); + TYPE_NAME (uid_type) = xstrdup ("__uid_t"); + TYPE_TARGET_TYPE (uid_type) = uint_type; + + /* __clock_t */ + clock_type = init_type (TYPE_CODE_TYPEDEF, TYPE_LENGTH (long_type), + TYPE_FLAG_TARGET_STUB, NULL, NULL); + TYPE_NAME (clock_type) = xstrdup ("__clock_t"); + TYPE_TARGET_TYPE (clock_type) = long_type; + + /* _sifields */ + sifields_type = init_composite_type (NULL, TYPE_CODE_UNION); + + { + const int si_max_size = 128; + int si_pad_size; + int size_of_int = gdbarch_int_bit (gdbarch) / HOST_CHAR_BIT; + + /* _pad */ + if (gdbarch_ptr_bit (gdbarch) == 64) + si_pad_size = (si_max_size / size_of_int) - 4; + else + si_pad_size = (si_max_size / size_of_int) - 3; + append_composite_type_field (sifields_type, "_pad", + init_vector_type (int_type, si_pad_size)); + } + + /* _kill */ + type = init_composite_type (NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "si_pid", pid_type); + append_composite_type_field (type, "si_uid", uid_type); + append_composite_type_field (sifields_type, "_kill", type); + + /* _timer */ + type = init_composite_type (NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "si_tid", int_type); + append_composite_type_field (type, "si_overrun", int_type); + append_composite_type_field (type, "si_sigval", sigval_type); + append_composite_type_field (sifields_type, "_timer", type); + + /* _rt */ + type = init_composite_type (NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "si_pid", pid_type); + append_composite_type_field (type, "si_uid", uid_type); + append_composite_type_field (type, "si_sigval", sigval_type); + append_composite_type_field (sifields_type, "_rt", type); + + /* _sigchld */ + type = init_composite_type (NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "si_pid", pid_type); + append_composite_type_field (type, "si_uid", uid_type); + append_composite_type_field (type, "si_status", int_type); + append_composite_type_field (type, "si_utime", clock_type); + append_composite_type_field (type, "si_stime", clock_type); + append_composite_type_field (sifields_type, "_sigchld", type); + + /* _sigfault */ + type = init_composite_type (NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "si_addr", void_ptr_type); + append_composite_type_field (sifields_type, "_sigfault", type); + + /* _sigpoll */ + type = init_composite_type (NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "si_band", long_type); + append_composite_type_field (type, "si_fd", int_type); + append_composite_type_field (sifields_type, "_sigpoll", type); + + /* struct siginfo */ + siginfo_type = init_composite_type (NULL, TYPE_CODE_STRUCT); + TYPE_NAME (siginfo_type) = xstrdup ("siginfo"); + append_composite_type_field (siginfo_type, "si_signo", int_type); + append_composite_type_field (siginfo_type, "si_errno", int_type); + append_composite_type_field (siginfo_type, "si_code", int_type); + append_composite_type_field_aligned (siginfo_type, + "_sifields", sifields_type, + TYPE_LENGTH (long_type)); + + return siginfo_type; +} diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h new file mode 100644 index 00000000000..50af511562f --- /dev/null +++ b/gdb/linux-tdep.h @@ -0,0 +1,25 @@ +/* Target-dependent code for GNU/Linux, architecture independent. + + Copyright (C) 2009 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 3 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, see . */ + +#ifndef LINUX_TDEP_H +#define LINUX_TDEP_H + +struct type *linux_get_siginfo_type (struct gdbarch *); + +#endif /* linux-tdep.h */ diff --git a/gdb/remote.c b/gdb/remote.c index aeeb8b8a873..af2c10b7677 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -998,6 +998,8 @@ enum { PACKET_vRun, PACKET_QStartNoAckMode, PACKET_vKill, + PACKET_qXfer_siginfo_read, + PACKET_qXfer_siginfo_write, PACKET_MAX }; @@ -2962,6 +2964,10 @@ static struct protocol_feature remote_protocol_features[] = { PACKET_QStartNoAckMode }, { "multiprocess", PACKET_DISABLE, remote_multi_process_feature, -1 }, { "QNonStop", PACKET_DISABLE, remote_non_stop_feature, -1 }, + { "qXfer:siginfo:read", PACKET_DISABLE, remote_supported_packet, + PACKET_qXfer_siginfo_read }, + { "qXfer:siginfo:write", PACKET_DISABLE, remote_supported_packet, + PACKET_qXfer_siginfo_write }, }; static void @@ -7323,6 +7329,19 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object, [PACKET_qXfer_spu_write]); } + /* Handle extra signal info using qxfer packets. */ + if (object == TARGET_OBJECT_SIGNAL_INFO) + { + if (readbuf) + return remote_read_qxfer (ops, "siginfo", annex, readbuf, offset, len, + &remote_protocol_packets + [PACKET_qXfer_siginfo_read]); + else + return remote_write_qxfer (ops, "siginfo", annex, writebuf, offset, len, + &remote_protocol_packets + [PACKET_qXfer_siginfo_write]); + } + /* Only handle flash writes. */ if (writebuf != NULL) { @@ -9070,6 +9089,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata], "qXfer:osdata:read", "osdata", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read], + "qXfer:siginfo:read", "read-siginfo-object", 0); + + add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_write], + "qXfer:siginfo:write", "write-siginfo-object", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qGetTLSAddr], "qGetTLSAddr", "get-thread-local-storage-address", 0); diff --git a/gdb/target.h b/gdb/target.h index cb7064678be..916d8e949df 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -221,7 +221,10 @@ enum target_object TARGET_OBJECT_LIBRARIES, /* Get OS specific data. The ANNEX specifies the type (running processes, etc.). */ - TARGET_OBJECT_OSDATA + TARGET_OBJECT_OSDATA, + /* Extra signal info. Usually the contents of `siginfo_t' on unix + platforms. */ + TARGET_OBJECT_SIGNAL_INFO, /* Possible future objects: TARGET_OBJECT_FILE, ... */ }; diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 0b72438cb43..bf6de5101a5 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2009-02-06 Pedro Alves + + * gdb.base/siginfo-obj.c, gdb.base/siginfo-obj.exp: New. + 2009-02-06 Thiago Jung Bauermann * gdb.python/python-cmd.exp: New file. diff --git a/gdb/testsuite/gdb.base/siginfo-obj.c b/gdb/testsuite/gdb.base/siginfo-obj.c new file mode 100644 index 00000000000..b88976ea0ac --- /dev/null +++ b/gdb/testsuite/gdb.base/siginfo-obj.c @@ -0,0 +1,70 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2004, 2007, 2008 Free Software Foundation, Inc. + + 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 3 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, see . + +*/ + +#include +#include +#include +#include +#include + +static void *p; + +static void +handler (int sig, siginfo_t *info, void *context) +{ + /* Copy to local vars, as the test wants to read them, and si_addr, + etc. may be preprocessor defines. */ + int ssi_errno = info->si_errno; + int ssi_signo = info->si_signo; + int ssi_code = info->si_code; + void *ssi_addr = info->si_addr; + + _exit (0); /* set breakpoint here */ +} + +int +main (void) +{ + /* Set up unwritable memory. */ + { + size_t len; + len = sysconf(_SC_PAGESIZE); + p = mmap (0, len, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + { + perror ("mmap"); + return 1; + } + } + /* Set up the signal handler. */ + { + struct sigaction action; + memset (&action, 0, sizeof (action)); + action.sa_sigaction = handler; + action.sa_flags |= SA_SIGINFO; + if (sigaction (SIGSEGV, &action, NULL)) + { + perror ("sigaction"); + return 1; + } + } + /* Trigger SIGSEGV. */ + *(int *)p = 0; + return 0; +} diff --git a/gdb/testsuite/gdb.base/siginfo-obj.exp b/gdb/testsuite/gdb.base/siginfo-obj.exp new file mode 100644 index 00000000000..0dea59ab8df --- /dev/null +++ b/gdb/testsuite/gdb.base/siginfo-obj.exp @@ -0,0 +1,131 @@ +# Copyright 2004, 2007, 2008 Free Software Foundation, Inc. + +# 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 3 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, see . + + +# The program siginfo-obj.c arranges for a signal handler registered +# using sigaction's sa_sigaction / SA_SIGINFO to be called with +# si_addr filled in. + +# This test confirms that we can inspect signal info using the +# $_siginfo convenience variable. + +if [target_info exists gdb,nosignals] { + verbose "Skipping siginfo-obj.exp because of nosignals." + continue +} + +if { ! [istarget "i?86-*-linux*"] + && ! [istarget "x86_64-*-linux*"] + && ! [istarget "arm*-*-linux*"] } { + verbose "Skipping siginfo-obj.exp because of lack of support." + return +} + +if $tracelevel then { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +set testfile siginfo-obj +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "Couldn't compile ${srcfile}.c" + return -1 +} + +# get things started +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +# Advance to main +if { ![runto_main] } then { + gdb_suppress_tests; +} + +# Run to the signal. +gdb_test "continue" ".*Program received signal SIGSEGV.*" "continue to signal" + +set ssi_addr "" +set test "Extract si_addr" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_addr = ($hex).*$gdb_prompt $" { + set ssi_addr $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_errno" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_errno = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_errno $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_code" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_code = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_code $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_signo" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_signo = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_signo $expect_out(1,string) + pass "$test" + } +} + +set bp_location [gdb_get_line_number "set breakpoint here"] + +gdb_test "break $bp_location" +gdb_test "continue" ".* handler .*" "continue to handler" + +gdb_test "p ssi_addr" " = \\(void \\*\\) $ssi_addr" +gdb_test "p ssi_errno" " = $ssi_errno" +gdb_test "p ssi_code" " = $ssi_code" +gdb_test "p ssi_signo" " = $ssi_signo" + +# Again, but this time, patch si_addr and check that the inferior sees +# the changed value. + +# Advance to main +if { ![runto_main] } then { + gdb_suppress_tests; +} + +# Run to the signal. +gdb_test "continue" ".*Program received signal SIGSEGV.*" "continue to signal" + +set test "Set si_addr" +gdb_test "p \$_siginfo._sifields._sigfault.si_addr = 0x666" " = \\(void \\*\\) 0x666" +gdb_test "p \$_siginfo.si_errno = 666" " = 666" +gdb_test "p \$_siginfo.si_code = 999" " = 999" +gdb_test "p \$_siginfo.si_signo = 11" " = 11" + +gdb_test "break $bp_location" +gdb_test "continue" ".* handler .*" "continue to handler" + +gdb_test "p ssi_addr" " = \\(void \\*\\) 0x666" +gdb_test "p ssi_errno" " = 666" +gdb_test "p ssi_code" " = 999" +gdb_test "p ssi_signo" " = 11" diff --git a/gdb/value.c b/gdb/value.c index 6a9ac5ff544..4d4329e5b17 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -901,12 +901,31 @@ create_internalvar (char *name) var->name = concat (name, (char *)NULL); var->value = allocate_value (builtin_type_void); var->endian = gdbarch_byte_order (current_gdbarch); + var->make_value = NULL; release_value (var->value); var->next = internalvars; internalvars = var; return var; } +/* Create an internal variable with name NAME and register FUN as the + function that value_of_internalvar uses to create a value whenever + this variable is referenced. NAME should not normally include a + dollar sign. */ + +struct internalvar * +create_internalvar_type_lazy (char *name, internalvar_make_value fun) +{ + struct internalvar *var; + var = (struct internalvar *) xmalloc (sizeof (struct internalvar)); + var->name = concat (name, (char *)NULL); + var->value = NULL; + var->make_value = fun; + var->endian = gdbarch_byte_order (current_gdbarch); + var->next = internalvars; + internalvars = var; + return var; +} /* Look up an internal variable with name NAME. NAME should not normally include a dollar sign. @@ -933,25 +952,31 @@ value_of_internalvar (struct internalvar *var) int i, j; gdb_byte temp; - val = value_copy (var->value); - if (value_lazy (val)) - value_fetch_lazy (val); - - /* If the variable's value is a computed lvalue, we want references - to it to produce another computed lvalue, where referencces and - assignments actually operate through the computed value's - functions. - - This means that internal variables with computed values behave a - little differently from other internal variables: assignments to - them don't just replace the previous value altogether. At the - moment, this seems like the behavior we want. */ - if (var->value->lval == lval_computed) - VALUE_LVAL (val) = lval_computed; + if (var->make_value != NULL) + val = (*var->make_value) (var); else { - VALUE_LVAL (val) = lval_internalvar; - VALUE_INTERNALVAR (val) = var; + val = value_copy (var->value); + if (value_lazy (val)) + value_fetch_lazy (val); + + /* If the variable's value is a computed lvalue, we want + references to it to produce another computed lvalue, where + referencces and assignments actually operate through the + computed value's functions. + + This means that internal variables with computed values + behave a little differently from other internal variables: + assignments to them don't just replace the previous value + altogether. At the moment, this seems like the behavior we + want. */ + if (var->value->lval == lval_computed) + VALUE_LVAL (val) = lval_computed; + else + { + VALUE_LVAL (val) = lval_internalvar; + VALUE_INTERNALVAR (val) = var; + } } /* Values are always stored in the target's byte order. When connected to a @@ -1075,7 +1100,8 @@ preserve_values (struct objfile *objfile) preserve_one_value (cur->values[i], objfile, copied_types); for (var = internalvars; var; var = var->next) - preserve_one_value (var->value, objfile, copied_types); + if (var->value) + preserve_one_value (var->value, objfile, copied_types); for (val = values_in_python; val; val = val->next) preserve_one_value (val, objfile, copied_types); diff --git a/gdb/value.h b/gdb/value.h index 0c8522382d9..aa43365efe5 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -305,11 +305,14 @@ extern struct value *coerce_array (struct value *value); /* Internal variables (variables for convenience of use of debugger) are recorded as a chain of these structures. */ +typedef struct value * (*internalvar_make_value) (struct internalvar *); + struct internalvar { struct internalvar *next; char *name; struct value *value; + internalvar_make_value make_value; int endian; }; @@ -534,6 +537,9 @@ extern struct internalvar *lookup_only_internalvar (char *name); extern struct internalvar *create_internalvar (char *name); +extern struct internalvar * + create_internalvar_type_lazy (char *name, internalvar_make_value fun); + extern struct internalvar *lookup_internalvar (char *name); extern int value_equal (struct value *arg1, struct value *arg2); -- 2.30.2