summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2005-12-03 10:34:16 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2005-12-03 10:34:16 +0000
commit292b534a1e9e7cc7ebfe334d4507f0b138fc8b32 (patch)
tree4a489b8cf57e2cfcdca9fa4333cb60b3837dbe66
parent780a9f84cec32e3b12399f8223432644862a0ace (diff)
Threads support.
-rw-r--r--gnu/usr.bin/binutils/gdb/Makefile.in5
-rw-r--r--gnu/usr.bin/binutils/gdb/bsd-uthread.c516
-rw-r--r--gnu/usr.bin/binutils/gdb/bsd-uthread.h40
-rw-r--r--gnu/usr.bin/binutils/gdb/defs.h4
-rw-r--r--gnu/usr.bin/binutils/gdb/doc/observer.texi50
-rw-r--r--gnu/usr.bin/binutils/gdb/solib.c54
-rw-r--r--gnu/usr.bin/binutils/gdb/solib.h2
-rw-r--r--gnu/usr.bin/binutils/gdb/utils.c14
8 files changed, 655 insertions, 30 deletions
diff --git a/gnu/usr.bin/binutils/gdb/Makefile.in b/gnu/usr.bin/binutils/gdb/Makefile.in
index f4b987185b5..040db280e42 100644
--- a/gnu/usr.bin/binutils/gdb/Makefile.in
+++ b/gnu/usr.bin/binutils/gdb/Makefile.in
@@ -643,6 +643,7 @@ bfd_target_h = bfd-target.h
block_h = block.h
breakpoint_h = breakpoint.h $(frame_h) $(value_h) $(gdb_events_h)
bsd_kvm_h = bsd-kvm.h
+bsd_uthread_h = bsd-uthread.h
buildsym_h = buildsym.h
call_cmds_h = call-cmds.h
charset_h = charset.h
@@ -1743,6 +1744,10 @@ breakpoint.o: breakpoint.c $(defs_h) $(symtab_h) $(frame_h) $(breakpoint_h) \
bsd-kvm.o: bsd-kvm.c $(defs_h) $(cli_cmds_h) $(command_h) $(frame_h) \
$(regcache_h) $(target_h) $(value_h) $(gdbcore_h) $(gdb_assert_h) \
$(readline_h) $(bsd_kvm_h)
+bsd-uthread.o: bsd-uthread.c $(defs_h) $(gdbcore_h) $(gdbthread_h) \
+ $(inferior_h) $(objfiles_h) $(observer_h) $(regcache_h) $(solib_h) \
+ $(solist_h) $(symfile_h) $(target_h) $(gdb_assert_h) \
+ $(gdb_obstack_h) $(bsd_uthread_h)
buildsym.o: buildsym.c $(defs_h) $(bfd_h) $(gdb_obstack_h) $(symtab_h) \
$(symfile_h) $(objfiles_h) $(gdbtypes_h) $(gdb_assert_h) \
$(complaints_h) $(gdb_string_h) $(expression_h) $(bcache_h) \
diff --git a/gnu/usr.bin/binutils/gdb/bsd-uthread.c b/gnu/usr.bin/binutils/gdb/bsd-uthread.c
new file mode 100644
index 00000000000..e626c49169b
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/bsd-uthread.c
@@ -0,0 +1,516 @@
+/* BSD user-level threads support.
+
+ Copyright 2005 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "regcache.h"
+#include "solib.h"
+#include "solist.h"
+#include "symfile.h"
+#include "target.h"
+
+#include "gdb_assert.h"
+#include "gdb_obstack.h"
+
+#include "bsd-uthread.h"
+
+/* HACK: Save the bsd_uthreads ops returned by bsd_uthread_target. */
+static struct target_ops *bsd_uthread_ops_hack;
+
+
+/* Architecture-specific operations. */
+
+/* Per-architecture data key. */
+static struct gdbarch_data *bsd_uthread_data;
+
+struct bsd_uthread_ops
+{
+ /* Supply registers for an inactive thread to a register cache. */
+ void (*supply_uthread)(struct regcache *, int, CORE_ADDR);
+
+ /* Collect registers for an inactive thread from a register cache. */
+ void (*collect_uthread)(const struct regcache *, int, CORE_ADDR);
+};
+
+static void *
+bsd_uthread_init (struct obstack *obstack)
+{
+ struct bsd_uthread_ops *ops;
+
+ ops = OBSTACK_ZALLOC (obstack, struct bsd_uthread_ops);
+ return ops;
+}
+
+/* Set the function that supplies registers from an inactive thread
+ for architecture GDBARCH to SUPPLY_UTHREAD. */
+
+void
+bsd_uthread_set_supply_uthread (struct gdbarch *gdbarch,
+ void (*supply_uthread) (struct regcache *,
+ int, CORE_ADDR))
+{
+ struct bsd_uthread_ops *ops = gdbarch_data (gdbarch, bsd_uthread_data);
+ ops->supply_uthread = supply_uthread;
+}
+
+/* Set the function that collects registers for an inactive thread for
+ architecture GDBARCH to SUPPLY_UTHREAD. */
+
+void
+bsd_uthread_set_collect_uthread (struct gdbarch *gdbarch,
+ void (*collect_uthread) (const struct regcache *,
+ int, CORE_ADDR))
+{
+ struct bsd_uthread_ops *ops = gdbarch_data (gdbarch, bsd_uthread_data);
+ ops->collect_uthread = collect_uthread;
+}
+
+/* Magic number to help recognize a valid thread structure. */
+#define BSD_UTHREAD_PTHREAD_MAGIC 0xd09ba115
+
+/* Check whether the thread structure at ADDR is valid. */
+
+static void
+bsd_uthread_check_magic (CORE_ADDR addr)
+{
+ ULONGEST magic = read_memory_unsigned_integer (addr, 4);
+
+ if (magic != BSD_UTHREAD_PTHREAD_MAGIC)
+ error (_("Bad magic"));
+}
+
+/* Thread states. */
+#define BSD_UTHREAD_PS_RUNNING 0
+#define BSD_UTHREAD_PS_DEAD 18
+
+/* Address of the pointer to the the thread structure for the running
+ thread. */
+static CORE_ADDR bsd_uthread_thread_run_addr;
+
+/* Address of the list of all threads. */
+static CORE_ADDR bsd_uthread_thread_list_addr;
+
+/* Offsets of various "interesting" bits in the thread structure. */
+static int bsd_uthread_thread_state_offset = -1;
+static int bsd_uthread_thread_next_offset = -1;
+static int bsd_uthread_thread_ctx_offset;
+
+/* Name of shared threads library. */
+static const char *bsd_uthread_solib_name;
+
+/* Non-zero if the thread startum implemented by this module is active. */
+static int bsd_uthread_active;
+
+static CORE_ADDR
+bsd_uthread_lookup_address (const char *name, struct objfile *objfile)
+{
+ struct minimal_symbol *sym;
+
+ sym = lookup_minimal_symbol (name, NULL, objfile);
+ if (sym)
+ return SYMBOL_VALUE_ADDRESS (sym);
+
+ return 0;
+}
+
+static int
+bsd_uthread_lookup_offset (const char *name, struct objfile *objfile)
+{
+ CORE_ADDR addr;
+
+ addr = bsd_uthread_lookup_address (name, objfile);
+ if (addr == 0)
+ return 0;
+
+ return read_memory_unsigned_integer (addr, 4);
+}
+
+/* If OBJFILE contains the symbols corresponding to one of the
+ supported user-level threads libraries, activate the thread stratum
+ implemented by this module. */
+
+static int
+bsd_uthread_activate (struct objfile *objfile)
+{
+ struct gdbarch *gdbarch = current_gdbarch;
+ struct bsd_uthread_ops *ops = gdbarch_data (gdbarch, bsd_uthread_data);
+
+ /* Skip if the thread stratum has already been activated. */
+ if (bsd_uthread_active)
+ return 0;
+
+ /* There's no point in enabling this module if no
+ architecture-specific operations are provided. */
+ if (!ops->supply_uthread)
+ return 0;
+
+ bsd_uthread_thread_run_addr =
+ bsd_uthread_lookup_address ("_thread_run", objfile);
+ if (bsd_uthread_thread_run_addr == 0)
+ return 0;
+
+ bsd_uthread_thread_list_addr =
+ bsd_uthread_lookup_address ("_thread_list", objfile);
+ if (bsd_uthread_thread_list_addr == 0)
+ return 0;
+
+ bsd_uthread_thread_state_offset =
+ bsd_uthread_lookup_offset ("_thread_state_offset", objfile);
+ if (bsd_uthread_thread_state_offset == 0)
+ return 0;
+
+ bsd_uthread_thread_next_offset =
+ bsd_uthread_lookup_offset ("_thread_next_offset", objfile);
+ if (bsd_uthread_thread_next_offset == 0)
+ return 0;
+
+ bsd_uthread_thread_ctx_offset =
+ bsd_uthread_lookup_offset ("_thread_ctx_offset", objfile);
+
+ push_target (bsd_uthread_ops_hack);
+ bsd_uthread_active = 1;
+ return 1;
+}
+
+/* Deactivate the thread stratum implemented by this module. */
+
+static void
+bsd_uthread_deactivate (void)
+{
+ /* Skip if the thread stratum has already been deactivated. */
+ if (!bsd_uthread_active)
+ return;
+
+ bsd_uthread_active = 0;
+ unpush_target (bsd_uthread_ops_hack);
+
+ bsd_uthread_thread_run_addr = 0;
+ bsd_uthread_thread_list_addr = 0;
+ bsd_uthread_thread_state_offset = 0;
+ bsd_uthread_thread_next_offset = 0;
+ bsd_uthread_thread_ctx_offset = 0;
+ bsd_uthread_solib_name = NULL;
+}
+
+void
+bsd_uthread_inferior_created (struct target_ops *ops, int from_tty)
+{
+ bsd_uthread_activate (NULL);
+}
+
+/* Likely candidates for the threads library. */
+static const char *bsd_uthread_solib_names[] =
+{
+ "/usr/lib/libc_r.so", /* FreeBSD */
+ "/usr/lib/libpthread.so", /* OpenBSD */
+ NULL
+};
+
+void
+bsd_uthread_solib_loaded (struct so_list *so)
+{
+ const char **names = bsd_uthread_solib_names;
+
+ for (names = bsd_uthread_solib_names; *names; names++)
+ {
+ if (strncmp (so->so_original_name, *names, strlen (*names)) == 0)
+ {
+ solib_read_symbols (so, so->from_tty);
+
+ if (bsd_uthread_activate (so->objfile))
+ {
+ bsd_uthread_solib_name == so->so_original_name;
+ return;
+ }
+ }
+ }
+}
+
+void
+bsd_uthread_solib_unloaded (struct so_list *so)
+{
+ if (!bsd_uthread_solib_name)
+ return;
+
+ if (strcmp (so->so_original_name, bsd_uthread_solib_name) == 0)
+ bsd_uthread_deactivate ();
+}
+
+static void
+bsd_uthread_mourn_inferior (void)
+{
+ find_target_beneath (bsd_uthread_ops_hack)->to_mourn_inferior ();
+ bsd_uthread_deactivate ();
+}
+
+static void
+bsd_uthread_fetch_registers (int regnum)
+{
+ struct gdbarch *gdbarch = current_gdbarch;
+ struct bsd_uthread_ops *ops = gdbarch_data (gdbarch, bsd_uthread_data);
+ CORE_ADDR addr = ptid_get_tid (inferior_ptid);
+ CORE_ADDR active_addr;
+
+ /* Always fetch the appropriate registers from the layer beneath. */
+ find_target_beneath (bsd_uthread_ops_hack)->to_fetch_registers (regnum);
+
+ /* FIXME: That might have gotten us more than we asked for. Make
+ sure we overwrite all relevant registers with values from the
+ thread structure. This can go once we fix the underlying target. */
+ regnum = -1;
+
+ active_addr = read_memory_typed_address (bsd_uthread_thread_run_addr,
+ builtin_type_void_data_ptr);
+ if (addr != 0 && addr != active_addr)
+ {
+ bsd_uthread_check_magic (addr);
+ ops->supply_uthread (current_regcache, regnum,
+ addr + bsd_uthread_thread_ctx_offset);
+ }
+}
+
+static void
+bsd_uthread_store_registers (int regnum)
+{
+ struct gdbarch *gdbarch = current_gdbarch;
+ struct bsd_uthread_ops *ops = gdbarch_data (gdbarch, bsd_uthread_data);
+ CORE_ADDR addr = ptid_get_tid (inferior_ptid);
+ CORE_ADDR active_addr;
+
+ active_addr = read_memory_typed_address (bsd_uthread_thread_run_addr,
+ builtin_type_void_data_ptr);
+ if (addr != 0 && addr != active_addr)
+ {
+ bsd_uthread_check_magic (addr);
+ ops->collect_uthread (current_regcache, regnum,
+ addr + bsd_uthread_thread_ctx_offset);
+ }
+ else
+ {
+ /* Updating the thread that is currently running; pass the
+ request to the layer beneath. */
+ find_target_beneath (bsd_uthread_ops_hack)->to_store_registers (regnum);
+ }
+}
+
+/* FIXME: This function is only there because otherwise GDB tries to
+ invoke deprecate_xfer_memory. */
+
+static LONGEST
+bsd_uthread_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, void *readbuf,
+ const void *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ gdb_assert (ops->beneath->to_xfer_partial);
+ return ops->beneath->to_xfer_partial (ops->beneath, object, annex, readbuf,
+ writebuf, offset, len);
+}
+
+static ptid_t
+bsd_uthread_wait (ptid_t ptid, struct target_waitstatus *status)
+{
+ CORE_ADDR addr;
+
+ /* Pass the request to the layer beneath. */
+ ptid = find_target_beneath (bsd_uthread_ops_hack)->to_wait (ptid, status);
+
+ /* Fetch the corresponding thread ID, and augment the returned
+ process ID with it. */
+ addr = read_memory_typed_address (bsd_uthread_thread_run_addr,
+ builtin_type_void_data_ptr);
+ if (addr != 0)
+ {
+ char buf[4];
+
+ /* FIXME: For executables linked statically with the threads
+ library, we end up here before the program has actually been
+ executed. In that case ADDR will be garbage since it has
+ been read from the wrong virtual memory image. */
+ if (target_read_memory (addr, buf, 4) == 0)
+ {
+ ULONGEST magic = extract_unsigned_integer (buf, 4);
+ if (magic == BSD_UTHREAD_PTHREAD_MAGIC)
+ ptid = ptid_build (ptid_get_pid (ptid), 0, addr);
+ }
+ }
+
+ /* HACK: Twiddle INFERIOR_PTID such that the initial thread of a
+ process isn't recognized as a new thread. */
+ if (ptid_get_tid (ptid) != 0 && !in_thread_list (ptid)
+ && ptid_get_tid (inferior_ptid) == 0)
+ {
+ add_thread (ptid);
+ inferior_ptid = ptid;
+ }
+
+ return ptid;
+}
+
+static void
+bsd_uthread_resume (ptid_t ptid, int step, enum target_signal sig)
+{
+ /* Pass the request to the layer beneath. */
+ find_target_beneath (bsd_uthread_ops_hack)->to_resume (ptid, step, sig);
+}
+
+static int
+bsd_uthread_thread_alive (ptid_t ptid)
+{
+ CORE_ADDR addr = ptid_get_tid (inferior_ptid);
+
+ if (addr != 0)
+ {
+ int offset = bsd_uthread_thread_state_offset;
+ ULONGEST state;
+
+ bsd_uthread_check_magic (addr);
+
+ state = read_memory_unsigned_integer (addr + offset, 4);
+ if (state == BSD_UTHREAD_PS_DEAD)
+ return 0;
+ }
+
+ return find_target_beneath (bsd_uthread_ops_hack)->to_thread_alive (ptid);
+}
+
+static void
+bsd_uthread_find_new_threads (void)
+{
+ pid_t pid = ptid_get_pid (inferior_ptid);
+ int offset = bsd_uthread_thread_next_offset;
+ CORE_ADDR addr;
+
+ addr = read_memory_typed_address (bsd_uthread_thread_list_addr,
+ builtin_type_void_data_ptr);
+ while (addr != 0)
+ {
+ ptid_t ptid = ptid_build (pid, 0, addr);
+
+ if (!in_thread_list (ptid))
+ add_thread (ptid);
+
+ addr = read_memory_typed_address (addr + offset,
+ builtin_type_void_data_ptr);
+ }
+}
+
+/* Possible states a thread can be in. */
+static char *bsd_uthread_state[] =
+{
+ "RUNNING",
+ "SIGTHREAD",
+ "MUTEX_WAIT",
+ "COND_WAIT",
+ "FDLR_WAIT",
+ "FDLW_WAIT",
+ "FDR_WAIT",
+ "FDW_WAIT",
+ "FILE_WAIT",
+ "POLL_WAIT",
+ "SELECT_WAIT",
+ "SLEEP_WAIT",
+ "WAIT_WAIT",
+ "SIGSUSPEND",
+ "SIGWAIT",
+ "SPINBLOCK",
+ "JOIN",
+ "SUSPENDED",
+ "DEAD",
+ "DEADLOCK"
+};
+
+/* Return a string describing th state of the thread specified by
+ INFO. */
+
+static char *
+bsd_uthread_extra_thread_info (struct thread_info *info)
+{
+ CORE_ADDR addr = ptid_get_tid (info->ptid);
+
+ if (addr != 0)
+ {
+ int offset = bsd_uthread_thread_state_offset;
+ ULONGEST state;
+
+ state = read_memory_unsigned_integer (addr + offset, 4);
+ if (state < ARRAY_SIZE (bsd_uthread_state))
+ return bsd_uthread_state[state];
+ }
+
+ return NULL;
+}
+
+static char *
+bsd_uthread_pid_to_str (ptid_t ptid)
+{
+ if (ptid_get_tid (ptid) != 0)
+ {
+ static char buf[64];
+
+ xsnprintf (buf, sizeof buf, "process %d, thread 0x%lx",
+ ptid_get_pid (ptid), ptid_get_tid (ptid));
+ return buf;
+ }
+
+ return normal_pid_to_str (ptid);
+}
+
+struct target_ops *
+bsd_uthread_target (void)
+{
+ struct target_ops *t = XZALLOC (struct target_ops);
+
+ t->to_shortname = "bsd-uthreads";
+ t->to_longname = "BSD user-level threads";
+ t->to_doc = "BSD user-level threads";
+ t->to_mourn_inferior = bsd_uthread_mourn_inferior;
+ t->to_fetch_registers = bsd_uthread_fetch_registers;
+ t->to_store_registers = bsd_uthread_store_registers;
+ t->to_xfer_partial = bsd_uthread_xfer_partial;
+ t->to_wait = bsd_uthread_wait;
+ t->to_resume = bsd_uthread_resume;
+ t->to_thread_alive = bsd_uthread_thread_alive;
+ t->to_find_new_threads = bsd_uthread_find_new_threads;
+ t->to_extra_thread_info = bsd_uthread_extra_thread_info;
+ t->to_pid_to_str = bsd_uthread_pid_to_str;
+ t->to_stratum = thread_stratum;
+ t->to_magic = OPS_MAGIC;
+ bsd_uthread_ops_hack = t;
+
+ return t;
+}
+
+void
+_initialize_bsd_uthread (void)
+{
+ add_target (bsd_uthread_target ());
+
+ bsd_uthread_data = gdbarch_data_register_pre_init (bsd_uthread_init);
+
+ observer_attach_inferior_created (bsd_uthread_inferior_created);
+ observer_attach_solib_loaded (bsd_uthread_solib_loaded);
+ observer_attach_solib_unloaded (bsd_uthread_solib_unloaded);
+}
diff --git a/gnu/usr.bin/binutils/gdb/bsd-uthread.h b/gnu/usr.bin/binutils/gdb/bsd-uthread.h
new file mode 100644
index 00000000000..b4ac0feafaf
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/bsd-uthread.h
@@ -0,0 +1,40 @@
+/* BSD user-level threads support.
+
+ Copyright 2005 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef BSD_UTHREAD_H
+#define BSD_UTHREAD_H 1
+
+/* Set the function that supplies registers for an inactive thread for
+ architecture GDBARCH to SUPPLY_UTHREAD. */
+
+extern void bsd_uthread_set_supply_uthread (struct gdbarch *gdbarch,
+ void (*supply_uthread) (struct regcache *,
+ int, CORE_ADDR));
+
+
+/* Set the function that collects registers for an inactive thread for
+ architecture GDBARCH to SUPPLY_UTHREAD. */
+
+extern void bsd_uthread_set_collect_uthread (struct gdbarch *gdbarch,
+ void (*collect_uthread) (const struct regcache *,
+ int, CORE_ADDR));
+
+#endif /* bsd-uthread.h */
diff --git a/gnu/usr.bin/binutils/gdb/defs.h b/gnu/usr.bin/binutils/gdb/defs.h
index fd96665d44b..4642c782c98 100644
--- a/gnu/usr.bin/binutils/gdb/defs.h
+++ b/gnu/usr.bin/binutils/gdb/defs.h
@@ -879,6 +879,10 @@ extern void xvasprintf (char **ret, const char *format, va_list ap);
extern char *xstrprintf (const char *format, ...) ATTR_FORMAT (printf, 1, 2);
extern char *xstrvprintf (const char *format, va_list ap);
+/* Like snprintf, but throw an error if the output buffer is too small. */
+extern int xsnprintf (char *str, size_t size, const char *format, ...)
+ ATTR_FORMAT (printf, 3, 4);
+
extern int parse_escape (char **);
/* Message to be printed before the error message, when an error occurs. */
diff --git a/gnu/usr.bin/binutils/gdb/doc/observer.texi b/gnu/usr.bin/binutils/gdb/doc/observer.texi
index de48a192a36..5f7171cc6c0 100644
--- a/gnu/usr.bin/binutils/gdb/doc/observer.texi
+++ b/gnu/usr.bin/binutils/gdb/doc/observer.texi
@@ -35,6 +35,11 @@ The observer implementation is also currently not reentrant.
In particular, it is therefore not possible to call the attach
or detach routines during a notification.
+@section Debugging
+Observer notifications can be traced using the command @samp{set debug
+observer 1} (@pxref{Debugging Output, , Optional messages about
+internal happenings, gdb, Debugging with @var{GDBN}}).
+
@section @code{normal_stop} Notifications
@cindex @code{normal_stop} observer
@cindex notification about inferior execution stop
@@ -50,21 +55,48 @@ a condition that is not met. If the breakpoint has any associated
commands list, the commands are executed after the notification
is emitted.
-The following interface is available to manage @code{normal_stop}
-observers:
+The following interfaces are available to manage observers:
-@deftypefun extern struct observer *observer_attach_normal_stop (observer_normal_stop_ftype *@var{f})
-Attach the given @code{normal_stop} callback function @var{f} and
-return the associated observer.
+@deftypefun extern struct observer *observer_attach_@var{event} (observer_@var{event}_ftype *@var{f})
+Using the function @var{f}, create an observer that is notified when
+ever @var{event} occures, return the observer.
@end deftypefun
-@deftypefun extern void observer_detach_normal_stop (struct observer *@var{observer});
+@deftypefun extern void observer_detach_@var{event} (struct observer *@var{observer});
Remove @var{observer} from the list of observers to be notified when
-a @code{normal_stop} event occurs.
+@var{event} occurs.
+@end deftypefun
+
+@deftypefun extern void observer_notify_@var{event} (void);
+Send a notification to all @var{event} observers.
@end deftypefun
-@deftypefun extern void observer_notify_normal_stop (void);
-Send a notification to all @code{normal_stop} observers.
+The following observable events are defined:
+
+@c note: all events must take at least one parameter.
+
+@deftypefun void normal_stop (struct bpstats *@var{bs})
+The inferior has stopped for real.
@end deftypefun
+@deftypefun void target_changed (struct target_ops *@var{target})
+The target's register contents have changed.
+@end deftypefun
+@deftypefun void inferior_created (struct target_ops *@var{objfile}, int @var{from_tty})
+@value{GDBN} has just connected to an inferior. For @samp{run},
+@value{GDBN} calls this observer while the inferior is still stopped
+at the entry-point instruction. For @samp{attach} and @samp{core},
+@value{GDBN} calls this observer immediately after connecting to the
+inferior, and before any information on the inferior has been printed.
+@end deftypefun
+
+@deftypefun void solib_loaded (struct so_list *@var{solib})
+The shared library specified by @var{solib} has been loaded. Note that
+when @value{GDBN} calls this observer, the library's symbols probably
+haven't been loaded yet.
+@end deftypefun
+
+@deftypefun void solib_unloaded (struct so_list *@var{solib})
+The specified shared library has been discovered to be unloaded.
+@end deftypefun
diff --git a/gnu/usr.bin/binutils/gdb/solib.c b/gnu/usr.bin/binutils/gdb/solib.c
index 35480bfbbb9..b0885fb9cae 100644
--- a/gnu/usr.bin/binutils/gdb/solib.c
+++ b/gnu/usr.bin/binutils/gdb/solib.c
@@ -369,6 +369,33 @@ symbol_add_stub (void *arg)
return (1);
}
+/* Read in symbols for shared object SO. If FROM_TTY is non-zero, be
+ chatty about it. Return non-zero if any symbols were actually
+ loaded. */
+
+int
+solib_read_symbols (struct so_list *so, int from_tty)
+{
+ if (so->symbols_loaded)
+ {
+ if (from_tty)
+ printf_unfiltered (_("Symbols already loaded for %s\n"), so->so_name);
+ }
+ else
+ {
+ if (catch_errors (symbol_add_stub, so,
+ "Error while reading shared library symbols:\n",
+ RETURN_MASK_ALL))
+ {
+ if (from_tty)
+ printf_unfiltered (_("Loaded symbols for %s\n"), so->so_name);
+ so->symbols_loaded = 1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
/* LOCAL FUNCTION
@@ -533,6 +560,10 @@ update_solib_list (int from_tty, struct target_ops *target)
count * sizeof (i->sections[0]));
}
}
+
+ /* Notify any observer that the shared object has been
+ loaded now that we've added it to GDB's tables. */
+ observer_notify_solib_loaded (i);
}
}
}
@@ -584,27 +615,8 @@ solib_add (char *pattern, int from_tty, struct target_ops *target, int readsyms)
if (! pattern || re_exec (gdb->so_name))
{
any_matches = 1;
-
- if (gdb->symbols_loaded)
- {
- if (from_tty)
- printf_unfiltered ("Symbols already loaded for %s\n",
- gdb->so_name);
- }
- else if (readsyms)
- {
- if (catch_errors
- (symbol_add_stub, gdb,
- "Error while reading shared library symbols:\n",
- RETURN_MASK_ALL))
- {
- if (from_tty)
- printf_unfiltered ("Loaded symbols for %s\n",
- gdb->so_name);
- gdb->symbols_loaded = 1;
- loaded_any_symbols = 1;
- }
- }
+ if (readsyms && solib_read_symbols (gdb, from_tty))
+ loaded_any_symbols = 1;
}
if (from_tty && pattern && ! any_matches)
diff --git a/gnu/usr.bin/binutils/gdb/solib.h b/gnu/usr.bin/binutils/gdb/solib.h
index cb4ba2d382a..1318c3085f0 100644
--- a/gnu/usr.bin/binutils/gdb/solib.h
+++ b/gnu/usr.bin/binutils/gdb/solib.h
@@ -23,6 +23,7 @@
#define SOLIB_H
/* Forward decl's for prototypes */
+struct so_list;
struct target_ops;
/* Called when we free all symtabs, to free the shared library information
@@ -38,6 +39,7 @@ extern void clear_solib (void);
solib_add (filename, from_tty, targ, readsyms)
extern void solib_add (char *, int, struct target_ops *, int);
+extern int solib_read_symbols (struct so_list *, int);
/* Function to be called when the inferior starts up, to discover the names
of shared libraries that are dynamically linked, the base addresses to
diff --git a/gnu/usr.bin/binutils/gdb/utils.c b/gnu/usr.bin/binutils/gdb/utils.c
index e30808c38b4..78530a34d1e 100644
--- a/gnu/usr.bin/binutils/gdb/utils.c
+++ b/gnu/usr.bin/binutils/gdb/utils.c
@@ -1133,6 +1133,20 @@ xstrvprintf (const char *format, va_list ap)
return ret;
}
+int
+xsnprintf (char *str, size_t size, const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start (args, format);
+ ret = vsnprintf (str, size, format, args);
+ gdb_assert (ret < size);
+ va_end (args);
+
+ return ret;
+}
+
/* My replacement for the read system call.
Used like `read' but keeps going if `read' returns too soon. */