diff options
Diffstat (limited to 'gdb/gdb_bfd.c')
-rw-r--r-- | gdb/gdb_bfd.c | 298 |
1 files changed, 174 insertions, 124 deletions
diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c index 7e272c7..2e477eb 100644 --- a/gdb/gdb_bfd.c +++ b/gdb/gdb_bfd.c @@ -1,6 +1,6 @@ /* Definitions for BFD wrappers used by GDB. - Copyright (C) 2011-2024 Free Software Foundation, Inc. + Copyright (C) 2011-2025 Free Software Foundation, Inc. This file is part of GDB. @@ -33,7 +33,8 @@ #include "gdbsupport/fileio.h" #include "inferior.h" #include "cli/cli-style.h" -#include <unordered_map> +#include "gdbsupport/unordered_map.h" +#include "gdbsupport/unordered_set.h" #if CXX_STD_THREAD @@ -80,12 +81,12 @@ struct gdb_bfd_section_data void *map_addr; }; -/* A hash table holding every BFD that gdb knows about. This is not +/* A hash set holding every BFD that gdb knows about. This is not to be confused with 'gdb_bfd_cache', which is used for sharing BFDs; in contrast, this hash is used just to implement "maint info bfd". */ -static htab_t all_bfds; +static gdb::unordered_set<bfd *> all_bfds; /* An object of this type is stored in each BFD's user data. */ @@ -142,8 +143,28 @@ struct gdb_bfd_data /* Table of all the bfds this bfd has included. */ std::vector<gdb_bfd_ref_ptr> included_bfds; + /* This is used by gdb_bfd_canonicalize_symtab to hold the symbols + returned by canonicalization. */ + std::optional<gdb::def_vector<asymbol *>> symbol_table; + /* If an error occurred while canonicalizing the symtab, this holds + the error message. */ + std::string symbol_error; + /* The registry. */ registry<bfd> registry_fields; + +#if CXX_STD_THREAD + /* Most of the locking needed for multi-threaded operation is + handled by BFD itself. However, the current BFD model is that + locking is only needed for global operations -- but it turned out + that the background DWARF reader could race with the auto-load + code reading the .debug_gdb_scripts section from the same BFD. + + This lock is the fix: wrappers for important BFD functions will + acquire this lock before performing operations that might modify + the state of this BFD. */ + std::mutex per_bfd_mutex; +#endif }; registry<bfd> * @@ -153,10 +174,6 @@ registry_accessor<bfd>::get (bfd *abfd) return &gdata->registry_fields; } -/* A hash table storing all the BFDs maintained in the cache. */ - -static htab_t gdb_bfd_cache; - /* When true gdb will reuse an existing bfd object if the filename, modification time, and file size all match. */ @@ -202,34 +219,42 @@ struct gdb_bfd_cache_search dev_t device_id; }; -/* A hash function for BFDs. */ - -static hashval_t -hash_bfd (const void *b) +struct bfd_cache_hash { - const bfd *abfd = (const struct bfd *) b; + using is_transparent = void; - /* It is simplest to just hash the filename. */ - return htab_hash_string (bfd_get_filename (abfd)); -} + std::size_t operator() (bfd *abfd) const noexcept + { + /* It is simplest to just hash the filename. */ + return htab_hash_string (bfd_get_filename (abfd)); + } -/* An equality function for BFDs. Note that this expects the caller - to search using struct gdb_bfd_cache_search only, not BFDs. */ + std::size_t operator() (const gdb_bfd_cache_search &search) const noexcept + { return htab_hash_string (search.filename); } +}; -static int -eq_bfd (const void *a, const void *b) +struct bfd_cache_eq { - const bfd *abfd = (const struct bfd *) a; - const struct gdb_bfd_cache_search *s - = (const struct gdb_bfd_cache_search *) b; - struct gdb_bfd_data *gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd); + using is_transparent = void; - return (gdata->mtime == s->mtime - && gdata->size == s->size - && gdata->inode == s->inode - && gdata->device_id == s->device_id - && strcmp (bfd_get_filename (abfd), s->filename) == 0); -} + bool operator() (bfd *lhs, bfd *rhs) const noexcept + { return lhs == rhs; } + + bool operator() (const gdb_bfd_cache_search &s, bfd *abfd) const noexcept + { + auto gdata = static_cast<gdb_bfd_data *> (bfd_usrdata (abfd)); + + return (gdata->mtime == s.mtime + && gdata->size == s.size + && gdata->inode == s.inode + && gdata->device_id == s.device_id + && strcmp (bfd_get_filename (abfd), s.filename) == 0); + } +}; + +/* A hash set storing all the BFDs maintained in the cache. */ + +static gdb::unordered_set<bfd *, bfd_cache_hash, bfd_cache_eq> gdb_bfd_cache; /* See gdb_bfd.h. */ @@ -482,7 +507,6 @@ static void gdb_bfd_init_data (struct bfd *abfd, struct stat *st) { struct gdb_bfd_data *gdata; - void **slot; gdb_assert (bfd_usrdata (abfd) == nullptr); @@ -493,9 +517,8 @@ gdb_bfd_init_data (struct bfd *abfd, struct stat *st) bfd_set_usrdata (abfd, gdata); /* This is the first we've seen it, so add it to the hash table. */ - slot = htab_find_slot (all_bfds, abfd, INSERT); - gdb_assert (slot && !*slot); - *slot = abfd; + bool inserted = all_bfds.emplace (abfd).second; + gdb_assert (inserted); } /* See gdb_bfd.h. */ @@ -504,10 +527,7 @@ gdb_bfd_ref_ptr gdb_bfd_open (const char *name, const char *target, int fd, bool warn_if_slow) { - hashval_t hash; - void **slot; bfd *abfd; - struct gdb_bfd_cache_search search; struct stat st; if (is_target_filename (name)) @@ -532,10 +552,6 @@ gdb_bfd_open (const char *name, const char *target, int fd, std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex); #endif - if (gdb_bfd_cache == NULL) - gdb_bfd_cache = htab_create_alloc (1, hash_bfd, eq_bfd, NULL, - xcalloc, xfree); - if (fd == -1) { fd = gdb_open_cloexec (name, O_RDONLY | O_BINARY, 0).release (); @@ -556,25 +572,26 @@ gdb_bfd_open (const char *name, const char *target, int fd, return gdb_bfd_ref_ptr::new_reference (abfd); } + gdb_bfd_cache_search search; + search.filename = name; search.mtime = st.st_mtime; search.size = st.st_size; search.inode = st.st_ino; search.device_id = st.st_dev; - /* Note that this must compute the same result as hash_bfd. */ - hash = htab_hash_string (name); - /* Note that we cannot use htab_find_slot_with_hash here, because - opening the BFD may fail; and this would violate hashtab - invariants. */ - abfd = (struct bfd *) htab_find_with_hash (gdb_bfd_cache, &search, hash); - if (bfd_sharing && abfd != NULL) + if (bfd_sharing) { - bfd_cache_debug_printf ("Reusing cached bfd %s for %s", - host_address_to_string (abfd), - bfd_get_filename (abfd)); - close (fd); - return gdb_bfd_ref_ptr::new_reference (abfd); + if (auto iter = gdb_bfd_cache.find (search); + iter != gdb_bfd_cache.end ()) + { + abfd = *iter; + bfd_cache_debug_printf ("Reusing cached bfd %s for %s", + host_address_to_string (abfd), + bfd_get_filename (abfd)); + close (fd); + return gdb_bfd_ref_ptr::new_reference (abfd); + } } abfd = bfd_fopen (name, target, FOPEN_RB, fd); @@ -587,20 +604,20 @@ gdb_bfd_open (const char *name, const char *target, int fd, host_address_to_string (abfd), bfd_get_filename (abfd)); - if (bfd_sharing) - { - slot = htab_find_slot_with_hash (gdb_bfd_cache, &search, hash, INSERT); - gdb_assert (!*slot); - *slot = abfd; - } - /* It's important to pass the already-computed stat info here, rather than, say, calling gdb_bfd_ref_ptr::new_reference. BFD by default will "stat" the file each time bfd_get_mtime is called -- - and since we already entered it into the hash table using this + and since we will enter it into the hash table using this mtime, if the file changed at the wrong moment, the race would lead to a hash table corruption. */ gdb_bfd_init_data (abfd, &st); + + if (bfd_sharing) + { + bool inserted = gdb_bfd_cache.emplace (abfd).second; + gdb_assert (inserted); + } + return gdb_bfd_ref_ptr (abfd); } @@ -635,7 +652,8 @@ static int gdb_bfd_close_or_warn (struct bfd *abfd) { int ret; - const char *name = bfd_get_filename (abfd); + gdb::unique_xmalloc_ptr<char> name + = make_unique_xstrdup (bfd_get_filename (abfd)); for (asection *sect : gdb_bfd_sections (abfd)) free_one_bfd_section (sect); @@ -643,7 +661,7 @@ gdb_bfd_close_or_warn (struct bfd *abfd) ret = bfd_close (abfd); if (!ret) - gdb_bfd_close_warning (name, + gdb_bfd_close_warning (name.get (), bfd_errmsg (bfd_get_error ())); return ret; @@ -686,7 +704,6 @@ void gdb_bfd_unref (struct bfd *abfd) { struct gdb_bfd_data *gdata; - struct gdb_bfd_cache_search search; bfd *archive_bfd; if (abfd == NULL) @@ -713,28 +730,14 @@ gdb_bfd_unref (struct bfd *abfd) bfd_get_filename (abfd)); archive_bfd = gdata->archive_bfd; - search.filename = bfd_get_filename (abfd); - if (gdb_bfd_cache && search.filename) - { - hashval_t hash = htab_hash_string (search.filename); - void **slot; - - search.mtime = gdata->mtime; - search.size = gdata->size; - search.inode = gdata->inode; - search.device_id = gdata->device_id; - slot = htab_find_slot_with_hash (gdb_bfd_cache, &search, hash, - NO_INSERT); - - if (slot && *slot) - htab_clear_slot (gdb_bfd_cache, slot); - } + if (bfd_get_filename (abfd) != nullptr) + gdb_bfd_cache.erase (abfd); delete gdata; bfd_set_usrdata (abfd, NULL); /* Paranoia. */ - htab_remove_elt (all_bfds, abfd); + all_bfds.erase (abfd); gdb_bfd_close_or_warn (abfd); @@ -776,6 +779,11 @@ gdb_bfd_map_section (asection *sectp, bfd_size_type *size) abfd = sectp->owner; +#if CXX_STD_THREAD + gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); + std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); +#endif + descriptor = get_section_descriptor (sectp); /* If the data was already read for this BFD, just reuse it. */ @@ -930,29 +938,6 @@ gdb_bfd_openw (const char *filename, const char *target) return gdb_bfd_ref_ptr::new_reference (result); } -/* Wrap f (args) and handle exceptions by: - - returning val, and - - calling set_quit_flag or set_force_quit_flag, if needed. */ - -template <typename R, R val, typename F, typename... Args> -static R -catch_exceptions (F &&f, Args&&... args) -{ - try - { - return f (std::forward<Args> (args)...); - } - catch (const gdb_exception &ex) - { - if (ex.reason == RETURN_QUIT) - set_quit_flag (); - else if (ex.reason == RETURN_FORCED_QUIT) - set_force_quit_flag (); - } - - return val; -} - /* See gdb_bfd.h. */ gdb_bfd_ref_ptr @@ -1130,6 +1115,11 @@ bool gdb_bfd_get_full_section_contents (bfd *abfd, asection *section, gdb::byte_vector *contents) { +#if CXX_STD_THREAD + gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); + std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); +#endif + bfd_size_type section_size = bfd_section_size (section); contents->resize (section_size); @@ -1138,6 +1128,32 @@ gdb_bfd_get_full_section_contents (bfd *abfd, asection *section, section_size); } +/* See gdb_bfd.h. */ + +int +gdb_bfd_stat (bfd *abfd, struct stat *sbuf) +{ +#if CXX_STD_THREAD + gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); + std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); +#endif + + return bfd_stat (abfd, sbuf); +} + +/* See gdb_bfd.h. */ + +long +gdb_bfd_get_mtime (bfd *abfd) +{ +#if CXX_STD_THREAD + gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); + std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); +#endif + + return bfd_get_mtime (abfd); +} + #define AMBIGUOUS_MESS1 ".\nMatching formats:" #define AMBIGUOUS_MESS2 \ ".\nUse \"set gnutarget format-name\" to specify the format." @@ -1168,23 +1184,52 @@ gdb_bfd_errmsg (bfd_error_type error_tag, char **matching) return ret; } -/* A callback for htab_traverse that prints a single BFD. */ +/* See gdb_bfd.h. */ -static int -print_one_bfd (void **slot, void *data) +gdb::array_view<asymbol *> +gdb_bfd_canonicalize_symtab (bfd *abfd, bool should_throw) { - bfd *abfd = (struct bfd *) *slot; struct gdb_bfd_data *gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd); - struct ui_out *uiout = (struct ui_out *) data; - ui_out_emit_tuple tuple_emitter (uiout, NULL); - uiout->field_signed ("refcount", gdata->refc); - uiout->field_string ("addr", host_address_to_string (abfd)); - uiout->field_string ("filename", bfd_get_filename (abfd), - file_name_style.style ()); - uiout->text ("\n"); + if (!gdata->symbol_table.has_value ()) + { + /* Ensure it exists. */ + gdb::def_vector<asymbol *> &symbol_table + = gdata->symbol_table.emplace (); + + long storage_needed = bfd_get_symtab_upper_bound (abfd); + if (storage_needed < 0) + gdata->symbol_error = bfd_errmsg (bfd_get_error ()); + else if (storage_needed > 0) + { + symbol_table.resize (storage_needed / sizeof (asymbol *)); + long number_of_symbols + = bfd_canonicalize_symtab (abfd, symbol_table.data ()); + if (number_of_symbols < 0) + { + symbol_table.clear (); + gdata->symbol_error = bfd_errmsg (bfd_get_error ()); + } + } + } - return 1; + if (!gdata->symbol_error.empty ()) + { + if (should_throw) + error (_("Cannot parse symbols of \"%s\": %s"), + bfd_get_filename (abfd), gdata->symbol_error.c_str ()); + return {}; + } + + gdb::def_vector<asymbol *> &symbol_table = *gdata->symbol_table; + if (symbol_table.empty ()) + return {}; + + /* bfd_canonicalize_symtab adds a trailing NULL, but don't include + this in the array view. */ + gdb_assert (symbol_table.back () == nullptr); + return gdb::make_array_view (symbol_table.data (), + symbol_table.size () - 1); } /* Implement the 'maint info bfd' command. */ @@ -1200,14 +1245,24 @@ maintenance_info_bfds (const char *arg, int from_tty) uiout->table_header (40, ui_left, "filename", "Filename"); uiout->table_body (); - htab_traverse_noresize (all_bfds, print_one_bfd, uiout); + + for (auto abfd : all_bfds) + { + auto gdata = static_cast<gdb_bfd_data *> (bfd_usrdata (abfd)); + ui_out_emit_tuple tuple_emitter (uiout, nullptr); + uiout->field_signed ("refcount", gdata->refc); + uiout->field_string ("addr", host_address_to_string (abfd)); + uiout->field_string ("filename", bfd_get_filename (abfd), + file_name_style.style ()); + uiout->text ("\n"); + } } /* BFD related per-inferior data. */ struct bfd_inferior_data { - std::unordered_map<std::string, unsigned long> bfd_error_string_counts; + gdb::unordered_map<std::string, unsigned long> bfd_error_string_counts; }; /* Per-inferior data key. */ @@ -1291,13 +1346,8 @@ gdb_bfd_init () error (_("fatal error: libbfd ABI mismatch")); } -void _initialize_gdb_bfd (); -void -_initialize_gdb_bfd () +INIT_GDB_FILE (gdb_bfd) { - all_bfds = htab_create_alloc (10, htab_hash_pointer, htab_eq_pointer, - NULL, xcalloc, xfree); - add_cmd ("bfds", class_maintenance, maintenance_info_bfds, _("\ List the BFDs that are currently open."), &maintenanceinfolist); |