diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2005-12-03 10:34:16 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2005-12-03 10:34:16 +0000 |
commit | 292b534a1e9e7cc7ebfe334d4507f0b138fc8b32 (patch) | |
tree | 4a489b8cf57e2cfcdca9fa4333cb60b3837dbe66 /gnu/usr.bin | |
parent | 780a9f84cec32e3b12399f8223432644862a0ace (diff) |
Threads support.
Diffstat (limited to 'gnu/usr.bin')
-rw-r--r-- | gnu/usr.bin/binutils/gdb/Makefile.in | 5 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/bsd-uthread.c | 516 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/bsd-uthread.h | 40 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/defs.h | 4 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/doc/observer.texi | 50 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/solib.c | 54 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/solib.h | 2 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/utils.c | 14 |
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. */ |