gdb/ 2012-07-12 Gary Benson * breakpoint.h (handle_solib_event): Moved function definition to solib.h, and added a new parameter. * breakpoint.c (handle_solib_event): Moved function to solib.c and added a new parameter. (bpstat_stop_status): Pass new argument to handle_solib_event. * solib.h (breakpoint.h): New include. (handle_solib_event): Moved function definition from breakpoint.h and added a new parameter. * solib.c (handle_solib_event): Moved function from breakpoint.c and added a new parameter. * infrun.c (handle_inferior_event): Pass new argument to handle_solib_event. * solist.h (breakpoint.h): New include. (target_so_ops): New field "handle_solib_event". * solib-svr4.c (svr4_free_library_list): New forward declaration. (probe_action): New enum. (probe_info): New field "action". (svr4_info): New field "solib_cache". (free_solib_cache): New function. (svr4_pspace_data_cleanup): Call the above. (svr4_copy_library_list): New function. (svr4_read_so_list): New parameter "prev_lm". Changed return type from void to int. Return nonzero on success, zero on error. (svr4_current_sos): Return cached list if available. Add new argument to calls to svr4_read_so_list. (solib_event_probe_action): New function. (solib_cache_update_full): Likewise. (solib_cache_update_incremental): Likewise. (svr4_handle_solib_event): Likewise. (svr4_solib_create_inferior_hook): Free any cached solibs. (_initialize_svr4_solib): Initialise svr4_so_ops.handle_solib_event. gdb/testsuite 2012-07-12 Gary Benson * gdb.base/info-shared.exp: New file. * gdb.base/info-shared.c: Likewise. * gdb.base/info-shared-solib1.c: Likewise. * gdb.base/info-shared-solib2.c: Likewise. diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 4e4f875..f678702 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -1515,6 +1515,4 @@ extern int user_breakpoint_p (struct breakpoint *); /* Attempt to determine architecture of location identified by SAL. */ extern struct gdbarch *get_sal_arch (struct symtab_and_line sal); -extern void handle_solib_event (void); - #endif /* !defined (BREAKPOINT_H) */ diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 6b9faf3..51c98a7 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -5205,7 +5205,7 @@ bpstat_stop_status (struct address_space *aspace, { if (bs->breakpoint_at && bs->breakpoint_at->type == bp_shlib_event) { - handle_solib_event (); + handle_solib_event (bs); break; } } @@ -5301,25 +5301,6 @@ handle_jit_event (void) target_terminal_inferior (); } -/* Handle an solib event by calling solib_add. */ - -void -handle_solib_event (void) -{ - clear_program_space_solib_cache (current_inferior ()->pspace); - - /* Check for any newly added shared libraries if we're supposed to - be adding them automatically. Switch terminal for any messages - produced by breakpoint_re_set. */ - target_terminal_ours_for_output (); -#ifdef SOLIB_ADD - SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add); -#else - solib_add (NULL, 0, ¤t_target, auto_solib_add); -#endif - target_terminal_inferior (); -} - /* Prepare WHAT final decision for infrun. */ /* Decide what infrun needs to do with this bpstat. */ diff --git a/gdb/solib.h b/gdb/solib.h index 65e3857..6e92ddc 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -21,6 +21,9 @@ #ifndef SOLIB_H #define SOLIB_H +/* For bpstat. */ +#include "breakpoint.h" + /* Forward decl's for prototypes */ struct so_list; struct target_ops; @@ -91,6 +94,13 @@ extern CORE_ADDR gdb_bfd_lookup_symbol_from_symtab (bfd *abfd, void *), void *data); +/* Handle an solib event by calling solib_add. Targets which handle + solib events using breakpoints must pass a valid bpstat. Targets + which handle solib events using some other mechanism should pass + NULL. */ + +extern void handle_solib_event (bpstat bs); + /* Enable or disable optional solib event breakpoints as appropriate. */ extern void update_solib_breakpoints (void); diff --git a/gdb/solib.c b/gdb/solib.c index dda0130..072fe4d 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -1212,6 +1212,30 @@ no_shared_libraries (char *ignored, int from_tty) /* See solib.h. */ void +handle_solib_event (bpstat bs) +{ + struct target_so_ops *ops = solib_ops (target_gdbarch); + + if (ops->handle_solib_event != NULL) + ops->handle_solib_event (bs); + + clear_program_space_solib_cache (current_inferior ()->pspace); + + /* Check for any newly added shared libraries if we're supposed to + be adding them automatically. Switch terminal for any messages + produced by breakpoint_re_set. */ + target_terminal_ours_for_output (); +#ifdef SOLIB_ADD + SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add); +#else + solib_add (NULL, 0, ¤t_target, auto_solib_add); +#endif + target_terminal_inferior (); +} + +/* See solib.h. */ + +void update_solib_breakpoints (void) { struct target_so_ops *ops = solib_ops (target_gdbarch); diff --git a/gdb/infrun.c b/gdb/infrun.c index 89fbcfc..7c621ca 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3318,7 +3318,7 @@ handle_inferior_event (struct execution_control_state *ecs) context_switch (ecs->ptid); regcache = get_thread_regcache (ecs->ptid); - handle_solib_event (); + handle_solib_event (NULL); ecs->event_thread->control.stop_bpstat = bpstat_stop_status (get_regcache_aspace (regcache), diff --git a/gdb/solist.h b/gdb/solist.h index 0d9046d..ea580a9 100644 --- a/gdb/solist.h +++ b/gdb/solist.h @@ -23,6 +23,8 @@ #define SO_NAME_MAX_PATH_SIZE 512 /* FIXME: Should be dynamic */ /* For domain_enum domain. */ #include "symtab.h" +/* For bpstat. */ +#include "breakpoint.h" /* Forward declaration for target specific link map information. This struct is opaque to all but the target specific file. */ @@ -150,6 +152,13 @@ struct target_so_ops int (*keep_data_in_core) (CORE_ADDR vaddr, unsigned long size); + /* Target-specific handling of solib events. For targets which + handle solib events using breakpoints a valid bpstat must be + passed. Targets which handle solib events using some other + mechanism should pass NULL. This pointer can be NULL, in which + case no specific handling is necessary for this target. */ + void (*handle_solib_event) (bpstat bs); + /* Enable or disable optional solib event breakpoints as appropriate. This should be called whenever stop_on_solib_events is changed. This pointer can be diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index c111f04..ebb8c3c 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -52,6 +52,7 @@ static struct link_map_offsets *svr4_fetch_link_map_offsets (void); static int svr4_have_link_map_offsets (void); static void svr4_relocate_main_executable (void); +static void svr4_free_library_list (void *p_list); /* Link map info to include in an allocated so_list entry. */ @@ -94,6 +95,25 @@ static const char * const solib_break_names[] = NULL }; +/* What to do with the link_map cache. */ + +enum probe_action + { + /* No action is required. The cache is still valid. */ + LM_CACHE_NO_ACTION, + + /* Something went wrong. The cache may be invalid and must be + cleared. Do not attempt further caching at this stop. */ + LM_CACHE_INVALIDATE, + + /* The cache should be reloaded now. */ + LM_CACHE_RELOAD, + + /* Attempt to incrementally update the cache. If the update + fails or is not possible, fall back to LM_CACHE_RELOAD. */ + LM_CACHE_UPDATE_OR_RELOAD + }; + /* A list of named probes which, if present in the dynamic linker, allow more fine-grained breakpoints to be placed on shared library events. */ @@ -106,16 +126,20 @@ struct probe_info /* Nonzero if this probe must be stopped at even when stop-on-solib-events is off. */ int mandatory; + + /* What to do with the link_map cache when a breakpoint at this + probe is hit. */ + enum probe_action action; }; static const struct probe_info probe_info[] = { - { "init_start", 0 }, - { "init_complete", 1 }, - { "map_start", 0 }, - { "reloc_complete", 1 }, - { "unmap_start", 0 }, - { "unmap_complete", 1 }, + { "init_start", 0, LM_CACHE_NO_ACTION }, + { "init_complete", 1, LM_CACHE_RELOAD }, + { "map_start", 0, LM_CACHE_NO_ACTION }, + { "reloc_complete", 1, LM_CACHE_UPDATE_OR_RELOAD }, + { "unmap_start", 0, LM_CACHE_NO_ACTION }, + { "unmap_complete", 1, LM_CACHE_RELOAD }, }; #define NUM_PROBES ARRAY_SIZE (probe_info) @@ -347,6 +371,10 @@ struct svr4_info /* Named probes in the dynamic linker. */ VEC (probe_p) *probes[NUM_PROBES]; + + /* List of objects loaded from the inferior, used by the + probes-based interface to support incremental updates. */ + struct so_list *solib_cache; }; /* Per-program-space data key. */ @@ -365,6 +393,18 @@ free_probes (struct svr4_info *info) memset (info->probes, 0, sizeof (info->probes)); } +/* Free any cached solibs. */ + +static void +free_solib_cache (struct svr4_info *info) +{ + if (info->solib_cache == NULL) + return; + + svr4_free_library_list (&info->solib_cache); + info->solib_cache = NULL; +} + static void svr4_pspace_data_cleanup (struct program_space *pspace, void *arg) { @@ -375,6 +415,7 @@ svr4_pspace_data_cleanup (struct program_space *pspace, void *arg) return; free_probes (info); + free_solib_cache (info); xfree (info); } @@ -1036,6 +1077,36 @@ svr4_free_library_list (void *p_list) } } +/* Copy library list. */ + +static struct so_list * +svr4_copy_library_list (struct so_list *src) +{ + struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + struct so_list *dst = NULL; + struct so_list **link = &dst; + + while (src != NULL) + { + struct so_list *new; + + new = XZALLOC (struct so_list); + + memcpy (new, src, sizeof (struct so_list)); + + new->lm_info = xmalloc (lmo->link_map_size); + memcpy (new->lm_info, src->lm_info, lmo->link_map_size); + + new->next = NULL; + *link = new; + link = &new->next; + + src = src->next; + } + + return dst; +} + #ifdef HAVE_LIBEXPAT #include "xml-support.h" @@ -1215,15 +1286,17 @@ svr4_default_sos (void) return new; } -/* Read the whole inferior libraries chain starting at address LM. Add the - entries to the tail referenced by LINK_PTR_PTR. Ignore the first entry if - IGNORE_FIRST and set global MAIN_LM_ADDR according to it. */ +/* Read the whole inferior libraries chain starting at address LM. + Expect the first entry in the chain's previous entry to be PREV_LM. + Add the entries to the tail referenced by LINK_PTR_PTR. Ignore the + first entry if IGNORE_FIRST and set global MAIN_LM_ADDR according + to it. Returns nonzero upon success. */ -static void -svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr, - int ignore_first) +static int +svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm, + struct so_list ***link_ptr_ptr, int ignore_first) { - CORE_ADDR prev_lm = 0, next_lm; + CORE_ADDR next_lm; for (; lm != 0; prev_lm = lm, lm = next_lm) { @@ -1240,7 +1313,7 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr, if (new->lm_info == NULL) { do_cleanups (old_chain); - break; + return 0; } next_lm = new->lm_info->l_next; @@ -1251,7 +1324,7 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr, paddress (target_gdbarch, prev_lm), paddress (target_gdbarch, new->lm_info->l_prev)); do_cleanups (old_chain); - break; + return 0; } /* For SVR4 versions, the first entry in the link map is for the @@ -1297,6 +1370,8 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr, **link_ptr_ptr = new; *link_ptr_ptr = &new->next; } + + return 1; } /* Implement the "current_sos" target_so_ops method. */ @@ -1333,6 +1408,10 @@ svr4_current_sos (void) info = get_svr4_info (); + /* If we have a cached result then return a copy. */ + if (info->solib_cache != NULL) + return svr4_copy_library_list (info->solib_cache); + /* Always locate the debug struct, in case it has moved. */ info->debug_base = 0; locate_base (info); @@ -1355,7 +1434,7 @@ svr4_current_sos (void) `struct so_list' nodes. */ lm = solib_svr4_r_map (info); if (lm) - svr4_read_so_list (lm, &link_ptr, ignore_first); + svr4_read_so_list (lm, 0, &link_ptr, ignore_first); /* On Solaris, the dynamic linker is not in the normal list of shared objects, so make sure we pick it up too. Having @@ -1363,7 +1442,7 @@ svr4_current_sos (void) for skipping dynamic linker resolver code. */ lm = solib_svr4_r_ldsomap (info); if (lm) - svr4_read_so_list (lm, &link_ptr, 0); + svr4_read_so_list (lm, 0, &link_ptr, 0); discard_cleanups (back_to); @@ -1494,6 +1573,146 @@ solib_event_probe_at (struct bp_location *loc, struct probe_and_info *result) return NULL; } +/* Decide what action to take when the specified solib event probe is + hit. */ + +static enum probe_action +solib_event_probe_action (struct probe_and_info *pi) +{ + enum probe_action action; + int update; + struct obj_section *os; + unsigned probe_argc; + struct svr4_info *info; + CORE_ADDR debug_base; + + action = pi->info->action; + if (action == LM_CACHE_NO_ACTION || action == LM_CACHE_INVALIDATE) + return action; + + gdb_assert (action == LM_CACHE_RELOAD + || action == LM_CACHE_UPDATE_OR_RELOAD); + + os = find_pc_section (pi->probe->address); + if (os == NULL) + return LM_CACHE_INVALIDATE; + + /* Check that an appropriate number of arguments has been supplied. + We expect: + arg1: Lmid_t lmid (mandatory) + arg2: struct r_debug *r_debug (mandatory) + arg3: struct link_map *new (optional, for incremental updates) */ + probe_argc = get_probe_argument_count (os->objfile, pi->probe); + if (probe_argc == 2) + action = LM_CACHE_RELOAD; + else if (probe_argc < 2) + return LM_CACHE_INVALIDATE; + + /* We only currently support the global namespace (PR gdb/11839). + If the probe's r_debug doesn't match the global r_debug then + this event refers to some other namespace and must be ignored. */ + info = get_svr4_info (); + + /* Always locate the debug struct, in case it has moved. */ + info->debug_base = 0; + locate_base (info); + + debug_base = value_as_address (evaluate_probe_argument (os->objfile, + pi->probe, 1)); + + if (debug_base != info->debug_base) + return LM_CACHE_NO_ACTION; + + return action; +} + +/* Populate the solib cache with by reading the entire list of shared + objects from the inferior. */ + +static void +solib_cache_update_full (void) +{ + struct svr4_info *info = get_svr4_info (); + + gdb_assert (info->solib_cache == NULL); + info->solib_cache = svr4_current_sos (); +} + +/* Update the solib cache starting from the link-map supplied by the + linker in the probe's third argument. Returns nonzero if the list + was successfully updated, or zero to indicate failure. */ + +static int +solib_cache_update_incremental (struct probe_and_info *pi) +{ + struct svr4_info *info = get_svr4_info (); + struct so_list *tail, **link; + struct obj_section *os; + CORE_ADDR lm; + + if (info->solib_cache == NULL) + return 0; + + tail = info->solib_cache; + while (tail->next) + tail = tail->next; + link = &tail->next; + + os = find_pc_section (pi->probe->address); + if (os == NULL) + return 0; + + lm = value_as_address (evaluate_probe_argument (os->objfile, + pi->probe, 2)); + + if (lm == 0) + return 0; + + return svr4_read_so_list (lm, tail->lm_info->lm_addr, &link, 0); +} + +/* Update the solib cache as appropriate when using the probes-based + linker interface. Do nothing if using the standard interface. */ + +static void +svr4_handle_solib_event (bpstat bs) +{ + struct svr4_info *info = get_svr4_info (); + struct probe_and_info buf, *pi; + enum probe_action action; + + /* It is possible that this function will be called incorrectly + by the handle_solib_event in handle_inferior_event if GDB goes + fully multi-target. */ + gdb_assert (bs != NULL); + + if (!info->using_probes) + return; + + pi = solib_event_probe_at (bs->bp_location_at, &buf); + if (pi == NULL) + action = LM_CACHE_INVALIDATE; /* Should never happen. */ + else + action = solib_event_probe_action (pi); + + if (action == LM_CACHE_NO_ACTION) + return; + + if (action == LM_CACHE_UPDATE_OR_RELOAD) + { + if (solib_cache_update_incremental (pi)) + return; + + action = LM_CACHE_RELOAD; + } + + free_solib_cache (info); + if (action == LM_CACHE_INVALIDATE) + return; + + solib_cache_update_full (); +} + /* Helper function for svr4_update_solib_event_breakpoints. */ static int @@ -2446,6 +2665,9 @@ svr4_solib_create_inferior_hook (int from_tty) info = get_svr4_info (); + /* Free any solibs cached by the probes-based linker interface. */ + free_solib_cache (info); + /* Relocate the main executable if necessary. */ svr4_relocate_main_executable (); @@ -2712,5 +2934,6 @@ _initialize_svr4_solib (void) svr4_so_ops.lookup_lib_global_symbol = elf_lookup_lib_symbol; svr4_so_ops.same = svr4_same; svr4_so_ops.keep_data_in_core = svr4_keep_data_in_core; + svr4_so_ops.handle_solib_event = svr4_handle_solib_event; svr4_so_ops.update_breakpoints = svr4_update_solib_event_breakpoints; } diff --git a/gdb/testsuite/gdb.base/info-shared-solib1.c b/gdb/testsuite/gdb.base/info-shared-solib1.c new file mode 100644 index 0000000..9979ee7 --- /dev/null +++ b/gdb/testsuite/gdb.base/info-shared-solib1.c @@ -0,0 +1,24 @@ +/* Copyright 2012 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 + +int +foo (int n) +{ + printf ("foo %d\n", n); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/info-shared-solib2.c b/gdb/testsuite/gdb.base/info-shared-solib2.c new file mode 100644 index 0000000..d4ed1e6 --- /dev/null +++ b/gdb/testsuite/gdb.base/info-shared-solib2.c @@ -0,0 +1,24 @@ +/* Copyright 2012 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 + +int +bar (int n) +{ + printf ("bar %d\n", n); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/info-shared.c b/gdb/testsuite/gdb.base/info-shared.c new file mode 100644 index 0000000..d699a11 --- /dev/null +++ b/gdb/testsuite/gdb.base/info-shared.c @@ -0,0 +1,48 @@ +/* Copyright 2012 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 + +void +stop () +{ +} + +int +main () +{ + void *handle1, *handle2; + void (*func)(int); + + handle1 = dlopen (SHLIB1_NAME, RTLD_LAZY); + stop (); + + handle2 = dlopen (SHLIB2_NAME, RTLD_LAZY); + stop (); + + func = (void (*)(int)) dlsym (handle1, "foo"); + func (1); + + func = (void (*)(int)) dlsym (handle2, "bar"); + func (2); + + dlclose (handle1); + stop (); + + dlclose (handle2); + stop (); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/info-shared.exp b/gdb/testsuite/gdb.base/info-shared.exp new file mode 100644 index 0000000..f3a74c6 --- /dev/null +++ b/gdb/testsuite/gdb.base/info-shared.exp @@ -0,0 +1,139 @@ +# Copyright 2012 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 . + +if { [skip_shlib_tests] || [is_remote target] } { + return 0 +} + +standard_testfile + +set lib1name $testfile-solib1 +set srcfile_lib1 $srcdir/$subdir/$lib1name.c +set binfile_lib1 $objdir/$subdir/$lib1name.so +set define1 -DSHLIB1_NAME\=\"$binfile_lib1\" + +set lib2name $testfile-solib2 +set srcfile_lib2 $srcdir/$subdir/$lib2name.c +set binfile_lib2 $objdir/$subdir/$lib2name.so +set define2 -DSHLIB2_NAME\=\"$binfile_lib2\" + +if { [gdb_compile_shlib $srcfile_lib1 $binfile_lib1 \ + [list additional_flags=-fPIC]] != "" } { + untested "Could not compile $binfile_lib1." + return -1 +} + +if { [gdb_compile_shlib $srcfile_lib2 $binfile_lib2 \ + [list additional_flags=-fPIC]] != "" } { + untested "Could not compile $binfile_lib2." + return -1 +} + +set cflags "$define1 $define2" +if { [prepare_for_testing $testfile.exp $testfile $srcfile \ + [list additional_flags=$cflags libs=-ldl]] } { + return -1 +} + +# Run "info sharedlibrary" and check for the presence or absence of +# our libraries. +proc check_info_shared { test expect1 expect2 } { + global lib1name + global lib2name + global gdb_prompt + + set actual1 0 + set actual2 0 + + gdb_test_multiple "info sharedlibrary" $test { + -re $lib1name { + set actual1 1 + exp_continue + } + -re $lib2name { + set actual2 1 + exp_continue + } + -re "\r\n$gdb_prompt $" { + if { $actual1 == $expect1 && $actual2 == $expect2 } { + pass $test + } else { + fail $test + } + } + } +} + +# Set up breakpoints. +gdb_test "break stop" {Breakpoint [0-9]+ at .*} +gdb_test_no_output "set breakpoint pending on" +gdb_test "break foo" {Breakpoint [0-9]+ \(foo\) pending\.} +gdb_test "break bar" {Breakpoint [0-9]+ \(bar\) pending\.} + +# Check neither of the libraries are loaded at the start. +gdb_test "start" {Temporary breakpoint [0-9]+, .* in main \(\)} +check_info_shared "info sharedlibrary #1" 0 0 + +# Run to the first stop and check that only the first library is loaded. +gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)} +check_info_shared "info sharedlibrary #2" 1 0 + +# Run to the second stop and check that both libraries are loaded. +gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)} +check_info_shared "info sharedlibrary #3" 1 1 + +# Check that the next stop is in foo. +gdb_test "c" {Breakpoint [0-9]+, .* in foo \(\) from .*} + +# Check that the next stop is in bar. +gdb_test "c" {Breakpoint [0-9]+, .* in bar \(\) from .*} + +# Restart the inferior and make sure there are no breakpoint reset +# errors. These can happen with the probes-based runtime linker +# interface if the cache is not cleared correctly. +set test "restart" +gdb_test_multiple "run" $test { + -re {Start it from the beginning\? \(y or n\) } { + send_gdb "y\n" + exp_continue + } + -re {Error in re-setting breakpoint} { + fail $test + } + -re "\r\n$gdb_prompt $" { + pass $test + } +} + +# We're at the first stop. Check that only the first library is loaded. +check_info_shared "info sharedlibrary #4" 1 0 + +# Run to the second stop and check that both libraries are loaded. +gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)} +check_info_shared "info sharedlibrary #5" 1 1 + +# Check that the next stop is in foo. +gdb_test "c" {Breakpoint [0-9]+, .* in foo \(\) from .*} + +# Check that the next stop is in bar. +gdb_test "c" {Breakpoint [0-9]+, .* in bar \(\) from .*} + +# Run to the next stop and check that the first library has been unloaded. +gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)} +check_info_shared "info sharedlibrary #6" 0 1 + +# Run to the last stop and check that both libraries are gone. +gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)} +check_info_shared "info sharedlibrary #7" 0 0