|  | /* Handle Darwin shared libraries for GDB, the GNU Debugger. | 
|  |  | 
|  | Copyright (C) 2009-2025 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 <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  |  | 
|  | #include "bfd.h" | 
|  | #include "extract-store-integer.h" | 
|  | #include "objfiles.h" | 
|  | #include "gdbcore.h" | 
|  | #include "target.h" | 
|  | #include "inferior.h" | 
|  | #include "regcache.h" | 
|  | #include "gdb_bfd.h" | 
|  |  | 
|  | #include "solib.h" | 
|  | #include "solib-darwin.h" | 
|  |  | 
|  | #include "mach-o.h" | 
|  | #include "mach-o/external.h" | 
|  |  | 
|  | /* solib_ops for Darwin systems.  */ | 
|  |  | 
|  | struct darwin_solib_ops : public solib_ops | 
|  | { | 
|  | using solib_ops::solib_ops; | 
|  |  | 
|  | void relocate_section_addresses (solib &so, target_section *) const override; | 
|  | void clear_solib (program_space *pspace) const override; | 
|  | void create_inferior_hook (int from_tty) const override; | 
|  | owning_intrusive_list<solib> current_sos () const override; | 
|  | gdb_bfd_ref_ptr bfd_open (const char *pathname) const override; | 
|  | }; | 
|  |  | 
|  | /* See solib-darwin.h.  */ | 
|  |  | 
|  | solib_ops_up | 
|  | make_darwin_solib_ops (program_space *pspace) | 
|  | { | 
|  | return std::make_unique<darwin_solib_ops> (pspace); | 
|  | } | 
|  |  | 
|  | struct gdb_dyld_image_info | 
|  | { | 
|  | /* Base address (which corresponds to the Mach-O header).  */ | 
|  | CORE_ADDR mach_header; | 
|  | /* Image file path.  */ | 
|  | CORE_ADDR file_path; | 
|  | /* st.m_time of image file.  */ | 
|  | unsigned long mtime; | 
|  | }; | 
|  |  | 
|  | /* Content of inferior dyld_all_image_infos structure. | 
|  | See /usr/include/mach-o/dyld_images.h for the documentation.  */ | 
|  | struct gdb_dyld_all_image_infos | 
|  | { | 
|  | /* Version (1).  */ | 
|  | unsigned int version; | 
|  | /* Number of images.  */ | 
|  | unsigned int count; | 
|  | /* Image description.  */ | 
|  | CORE_ADDR info; | 
|  | /* Notifier (function called when a library is added or removed).  */ | 
|  | CORE_ADDR notifier; | 
|  | }; | 
|  |  | 
|  | /* Current all_image_infos version.  */ | 
|  | #define DYLD_VERSION_MIN 1 | 
|  | #define DYLD_VERSION_MAX 15 | 
|  |  | 
|  | /* Per PSPACE specific data.  */ | 
|  | struct darwin_info | 
|  | { | 
|  | /* Address of structure dyld_all_image_infos in inferior.  */ | 
|  | CORE_ADDR all_image_addr = 0; | 
|  |  | 
|  | /* Gdb copy of dyld_all_info_infos.  */ | 
|  | struct gdb_dyld_all_image_infos all_image {}; | 
|  | }; | 
|  |  | 
|  | /* Per-program-space data key.  */ | 
|  | static const registry<program_space>::key<darwin_info> | 
|  | solib_darwin_pspace_data; | 
|  |  | 
|  | /* Get the darwin solib data for PSPACE.  If none is found yet, add it now.  This | 
|  | function always returns a valid object.  */ | 
|  |  | 
|  | static darwin_info * | 
|  | get_darwin_info (program_space *pspace) | 
|  | { | 
|  | darwin_info *info = solib_darwin_pspace_data.get (pspace); | 
|  | if (info != nullptr) | 
|  | return info; | 
|  |  | 
|  | return solib_darwin_pspace_data.emplace (pspace); | 
|  | } | 
|  |  | 
|  | /* Return non-zero if the version in dyld_all_image is known.  */ | 
|  |  | 
|  | static int | 
|  | darwin_dyld_version_ok (const struct darwin_info *info) | 
|  | { | 
|  | return info->all_image.version >= DYLD_VERSION_MIN | 
|  | && info->all_image.version <= DYLD_VERSION_MAX; | 
|  | } | 
|  |  | 
|  | /* Read dyld_all_image from inferior.  */ | 
|  |  | 
|  | static void | 
|  | darwin_load_image_infos (struct darwin_info *info) | 
|  | { | 
|  | gdb_byte buf[24]; | 
|  | bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ()); | 
|  | type *ptr_type | 
|  | = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; | 
|  | int len; | 
|  |  | 
|  | /* If the structure address is not known, don't continue.  */ | 
|  | if (info->all_image_addr == 0) | 
|  | return; | 
|  |  | 
|  | /* The structure has 4 fields: version (4 bytes), count (4 bytes), | 
|  | info (pointer) and notifier (pointer).  */ | 
|  | len = 4 + 4 + 2 * ptr_type->length (); | 
|  | gdb_assert (len <= sizeof (buf)); | 
|  | memset (&info->all_image, 0, sizeof (info->all_image)); | 
|  |  | 
|  | /* Read structure raw bytes from target.  */ | 
|  | if (target_read_memory (info->all_image_addr, buf, len)) | 
|  | return; | 
|  |  | 
|  | /* Extract the fields.  */ | 
|  | info->all_image.version = extract_unsigned_integer (buf, 4, byte_order); | 
|  | if (!darwin_dyld_version_ok (info)) | 
|  | return; | 
|  |  | 
|  | info->all_image.count = extract_unsigned_integer (buf + 4, 4, byte_order); | 
|  | info->all_image.info = extract_typed_address (buf + 8, ptr_type); | 
|  | info->all_image.notifier = extract_typed_address | 
|  | (buf + 8 + ptr_type->length (), ptr_type); | 
|  | } | 
|  |  | 
|  | /* Link map info to include in an allocated solib entry.  */ | 
|  |  | 
|  | struct lm_info_darwin final : public lm_info | 
|  | { | 
|  | explicit lm_info_darwin (CORE_ADDR lm_addr) | 
|  | : lm_addr (lm_addr) | 
|  | {} | 
|  |  | 
|  | /* The target location of lm.  */ | 
|  | CORE_ADDR lm_addr; | 
|  | }; | 
|  |  | 
|  | /* Lookup the value for a specific symbol.  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | lookup_symbol_from_bfd (bfd *abfd, const char *symname) | 
|  | { | 
|  | CORE_ADDR symaddr = 0; | 
|  |  | 
|  | gdb::array_view<asymbol *> symbol_table | 
|  | = gdb_bfd_canonicalize_symtab (abfd, false); | 
|  |  | 
|  | for (const asymbol *sym : symbol_table) | 
|  | { | 
|  | if (strcmp (sym->name, symname) == 0 | 
|  | && (sym->section->flags & (SEC_CODE | SEC_DATA)) != 0) | 
|  | { | 
|  | /* BFD symbols are section relative.  */ | 
|  | symaddr = sym->value + sym->section->vma; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return symaddr; | 
|  | } | 
|  |  | 
|  | /* Return program interpreter string.  */ | 
|  |  | 
|  | static char * | 
|  | find_program_interpreter (void) | 
|  | { | 
|  | char *buf = NULL; | 
|  |  | 
|  | /* If we have an current exec_bfd, get the interpreter from the load | 
|  | commands.  */ | 
|  | if (current_program_space->exec_bfd ()) | 
|  | { | 
|  | bfd_mach_o_load_command *cmd; | 
|  |  | 
|  | if (bfd_mach_o_lookup_command (current_program_space->exec_bfd (), | 
|  | BFD_MACH_O_LC_LOAD_DYLINKER, &cmd) == 1) | 
|  | return cmd->command.dylinker.name_str; | 
|  | } | 
|  |  | 
|  | /* If we didn't find it, read from memory. | 
|  | FIXME: todo.  */ | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | owning_intrusive_list<solib> | 
|  | darwin_solib_ops::current_sos () const | 
|  | { | 
|  | type *ptr_type | 
|  | = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; | 
|  | enum bfd_endian byte_order = type_byte_order (ptr_type); | 
|  | int ptr_len = ptr_type->length (); | 
|  | unsigned int image_info_size; | 
|  | darwin_info *info = get_darwin_info (current_program_space); | 
|  |  | 
|  | /* Be sure image infos are loaded.  */ | 
|  | darwin_load_image_infos (info); | 
|  |  | 
|  | if (!darwin_dyld_version_ok (info)) | 
|  | return {}; | 
|  |  | 
|  | image_info_size = ptr_len * 3; | 
|  |  | 
|  | owning_intrusive_list<solib> sos; | 
|  |  | 
|  | /* Read infos for each solib. | 
|  | The first entry was rumored to be the executable itself, but this is not | 
|  | true when a large number of shared libraries are used (table expanded ?). | 
|  | We now check all entries, but discard executable images.  */ | 
|  | for (int i = 0; i < info->all_image.count; i++) | 
|  | { | 
|  | CORE_ADDR iinfo = info->all_image.info + i * image_info_size; | 
|  | gdb::byte_vector buf (image_info_size); | 
|  | CORE_ADDR load_addr; | 
|  | CORE_ADDR path_addr; | 
|  | struct mach_o_header_external hdr; | 
|  | unsigned long hdr_val; | 
|  |  | 
|  | /* Read image info from inferior.  */ | 
|  | if (target_read_memory (iinfo, buf.data (), image_info_size)) | 
|  | break; | 
|  |  | 
|  | load_addr = extract_typed_address (buf.data (), ptr_type); | 
|  | path_addr = extract_typed_address (buf.data () + ptr_len, ptr_type); | 
|  |  | 
|  | /* Read Mach-O header from memory.  */ | 
|  | if (target_read_memory (load_addr, (gdb_byte *) &hdr, sizeof (hdr) - 4)) | 
|  | break; | 
|  | /* Discard wrong magic numbers.  Shouldn't happen.  */ | 
|  | hdr_val = extract_unsigned_integer | 
|  | (hdr.magic, sizeof (hdr.magic), byte_order); | 
|  | if (hdr_val != BFD_MACH_O_MH_MAGIC && hdr_val != BFD_MACH_O_MH_MAGIC_64) | 
|  | continue; | 
|  | /* Discard executable.  Should happen only once.  */ | 
|  | hdr_val = extract_unsigned_integer | 
|  | (hdr.filetype, sizeof (hdr.filetype), byte_order); | 
|  | if (hdr_val == BFD_MACH_O_MH_EXECUTE) | 
|  | continue; | 
|  |  | 
|  | gdb::unique_xmalloc_ptr<char> file_path | 
|  | = target_read_string (path_addr, SO_NAME_MAX_PATH_SIZE - 1); | 
|  | if (file_path == nullptr) | 
|  | break; | 
|  |  | 
|  | /* Create and fill the new struct solib element.  */ | 
|  | sos.emplace_back (std::make_unique<lm_info_darwin> (load_addr), | 
|  | file_path.get (), file_path.get (), *this); | 
|  | } | 
|  |  | 
|  | return sos; | 
|  | } | 
|  |  | 
|  | /* Check LOAD_ADDR points to a Mach-O executable header.  Return LOAD_ADDR | 
|  | in case of success, 0 in case of failure.  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | darwin_validate_exec_header (CORE_ADDR load_addr) | 
|  | { | 
|  | bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ()); | 
|  | struct mach_o_header_external hdr; | 
|  | unsigned long hdr_val; | 
|  |  | 
|  | /* Read Mach-O header from memory.  */ | 
|  | if (target_read_memory (load_addr, (gdb_byte *) &hdr, sizeof (hdr) - 4)) | 
|  | return 0; | 
|  |  | 
|  | /* Discard wrong magic numbers.  Shouldn't happen.  */ | 
|  | hdr_val = extract_unsigned_integer | 
|  | (hdr.magic, sizeof (hdr.magic), byte_order); | 
|  | if (hdr_val != BFD_MACH_O_MH_MAGIC && hdr_val != BFD_MACH_O_MH_MAGIC_64) | 
|  | return 0; | 
|  |  | 
|  | /* Check executable.  */ | 
|  | hdr_val = extract_unsigned_integer | 
|  | (hdr.filetype, sizeof (hdr.filetype), byte_order); | 
|  | if (hdr_val == BFD_MACH_O_MH_EXECUTE) | 
|  | return load_addr; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Get the load address of the executable using dyld list of images. | 
|  | We assume that the dyld info are correct (which is wrong if the target | 
|  | is stopped at the first instruction).  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | darwin_read_exec_load_addr_from_dyld (struct darwin_info *info) | 
|  | { | 
|  | type *ptr_type | 
|  | = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; | 
|  | int ptr_len = ptr_type->length (); | 
|  | unsigned int image_info_size = ptr_len * 3; | 
|  | int i; | 
|  |  | 
|  | /* Read infos for each solib.  One of them should be the executable.  */ | 
|  | for (i = 0; i < info->all_image.count; i++) | 
|  | { | 
|  | CORE_ADDR iinfo = info->all_image.info + i * image_info_size; | 
|  | gdb::byte_vector buf (image_info_size); | 
|  | CORE_ADDR load_addr; | 
|  |  | 
|  | /* Read image info from inferior.  */ | 
|  | if (target_read_memory (iinfo, buf.data (), image_info_size)) | 
|  | break; | 
|  |  | 
|  | load_addr = extract_typed_address (buf.data (), ptr_type); | 
|  | if (darwin_validate_exec_header (load_addr) == load_addr) | 
|  | return load_addr; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Get the load address of the executable when the PC is at the dyld | 
|  | entry point using parameter passed by the kernel (at SP). */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | darwin_read_exec_load_addr_at_init (struct darwin_info *info) | 
|  | { | 
|  | gdbarch *gdbarch = current_inferior ()->arch (); | 
|  | enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | 
|  | int addr_size = gdbarch_addr_bit (gdbarch) / 8; | 
|  | ULONGEST load_ptr_addr; | 
|  | ULONGEST load_addr; | 
|  | gdb_byte buf[8]; | 
|  |  | 
|  | /* Get SP.  */ | 
|  | if (regcache_cooked_read_unsigned (get_thread_regcache (inferior_thread ()), | 
|  | gdbarch_sp_regnum (gdbarch), | 
|  | &load_ptr_addr) != REG_VALID) | 
|  | return 0; | 
|  |  | 
|  | /* Read value at SP (image load address).  */ | 
|  | if (target_read_memory (load_ptr_addr, buf, addr_size)) | 
|  | return 0; | 
|  |  | 
|  | load_addr = extract_unsigned_integer (buf, addr_size, byte_order); | 
|  |  | 
|  | return darwin_validate_exec_header (load_addr); | 
|  | } | 
|  |  | 
|  | /* A wrapper for bfd_mach_o_fat_extract that handles reference | 
|  | counting properly.  This will either return NULL, or return a new | 
|  | reference to a BFD.  */ | 
|  |  | 
|  | static gdb_bfd_ref_ptr | 
|  | gdb_bfd_mach_o_fat_extract (bfd *abfd, bfd_format format, | 
|  | const bfd_arch_info_type *arch) | 
|  | { | 
|  | bfd *result = bfd_mach_o_fat_extract (abfd, format, arch); | 
|  |  | 
|  | if (result == NULL) | 
|  | return NULL; | 
|  |  | 
|  | if (result == abfd) | 
|  | gdb_bfd_ref (result); | 
|  | else | 
|  | gdb_bfd_mark_parent (result, abfd); | 
|  |  | 
|  | return gdb_bfd_ref_ptr (result); | 
|  | } | 
|  |  | 
|  | /* Return the BFD for the program interpreter.  */ | 
|  |  | 
|  | static gdb_bfd_ref_ptr | 
|  | darwin_get_dyld_bfd () | 
|  | { | 
|  | char *interp_name; | 
|  |  | 
|  | /* This method doesn't work with an attached process.  */ | 
|  | if (current_inferior ()->attach_flag) | 
|  | return NULL; | 
|  |  | 
|  | /* Find the program interpreter.  */ | 
|  | interp_name = find_program_interpreter (); | 
|  | if (!interp_name) | 
|  | return NULL; | 
|  |  | 
|  | /* Create a bfd for the interpreter.  */ | 
|  | gdb_bfd_ref_ptr dyld_bfd (gdb_bfd_open (interp_name, gnutarget)); | 
|  | if (dyld_bfd != NULL) | 
|  | { | 
|  | gdb_bfd_ref_ptr sub | 
|  | (gdb_bfd_mach_o_fat_extract | 
|  | (dyld_bfd.get (), bfd_object, | 
|  | gdbarch_bfd_arch_info (current_inferior ()->arch ()))); | 
|  | dyld_bfd = sub; | 
|  | } | 
|  | return dyld_bfd; | 
|  | } | 
|  |  | 
|  | /* Extract dyld_all_image_addr when the process was just created, assuming the | 
|  | current PC is at the entry of the dynamic linker.  */ | 
|  |  | 
|  | static void | 
|  | darwin_solib_get_all_image_info_addr_at_init (struct darwin_info *info) | 
|  | { | 
|  | CORE_ADDR load_addr = 0; | 
|  | gdb_bfd_ref_ptr dyld_bfd = darwin_get_dyld_bfd (); | 
|  |  | 
|  | if (dyld_bfd == NULL) | 
|  | return; | 
|  |  | 
|  | /* We find the dynamic linker's base address by examining | 
|  | the current pc (which should point at the entry point for the | 
|  | dynamic linker) and subtracting the offset of the entry point.  */ | 
|  | load_addr = (regcache_read_pc (get_thread_regcache (inferior_thread ())) | 
|  | - bfd_get_start_address (dyld_bfd.get ())); | 
|  |  | 
|  | /* Now try to set a breakpoint in the dynamic linker.  */ | 
|  | info->all_image_addr = | 
|  | lookup_symbol_from_bfd (dyld_bfd.get (), "_dyld_all_image_infos"); | 
|  |  | 
|  | if (info->all_image_addr == 0) | 
|  | return; | 
|  |  | 
|  | info->all_image_addr += load_addr; | 
|  | } | 
|  |  | 
|  | /* Extract dyld_all_image_addr reading it from | 
|  | TARGET_OBJECT_DARWIN_DYLD_INFO.  */ | 
|  |  | 
|  | static void | 
|  | darwin_solib_read_all_image_info_addr (struct darwin_info *info) | 
|  | { | 
|  | gdb_byte buf[8]; | 
|  | LONGEST len; | 
|  | type *ptr_type | 
|  | = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; | 
|  |  | 
|  | /* Sanity check.  */ | 
|  | if (ptr_type->length () > sizeof (buf)) | 
|  | return; | 
|  |  | 
|  | len = target_read (current_inferior ()->top_target (), | 
|  | TARGET_OBJECT_DARWIN_DYLD_INFO, | 
|  | NULL, buf, 0, ptr_type->length ()); | 
|  | if (len <= 0) | 
|  | return; | 
|  |  | 
|  | /* The use of BIG endian is intended, as BUF is a raw stream of bytes.  This | 
|  | makes the support of remote protocol easier.  */ | 
|  | info->all_image_addr = extract_unsigned_integer (buf, len, BFD_ENDIAN_BIG); | 
|  | } | 
|  |  | 
|  | void | 
|  | darwin_solib_ops::create_inferior_hook (int from_tty) const | 
|  | { | 
|  | /* Everything below only makes sense if we have a running inferior.  */ | 
|  | if (!target_has_execution ()) | 
|  | return; | 
|  |  | 
|  | darwin_info *info = get_darwin_info (current_program_space); | 
|  | CORE_ADDR load_addr; | 
|  |  | 
|  | info->all_image_addr = 0; | 
|  |  | 
|  | darwin_solib_read_all_image_info_addr (info); | 
|  |  | 
|  | if (info->all_image_addr == 0) | 
|  | darwin_solib_get_all_image_info_addr_at_init (info); | 
|  |  | 
|  | if (info->all_image_addr == 0) | 
|  | return; | 
|  |  | 
|  | darwin_load_image_infos (info); | 
|  |  | 
|  | if (!darwin_dyld_version_ok (info)) | 
|  | { | 
|  | warning (_("unhandled dyld version (%d)"), info->all_image.version); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (info->all_image.count != 0) | 
|  | { | 
|  | /* Possible relocate the main executable (PIE).  */ | 
|  | load_addr = darwin_read_exec_load_addr_from_dyld (info); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Possible issue: | 
|  | Do not break on the notifier if dyld is not initialized (deduced from | 
|  | count == 0).  In that case, dyld hasn't relocated itself and the | 
|  | notifier may point to a wrong address.  */ | 
|  |  | 
|  | load_addr = darwin_read_exec_load_addr_at_init (info); | 
|  | } | 
|  |  | 
|  | if (load_addr != 0 && current_program_space->symfile_object_file != NULL) | 
|  | { | 
|  | CORE_ADDR vmaddr; | 
|  |  | 
|  | /* Find the base address of the executable.  */ | 
|  | vmaddr = bfd_mach_o_get_base_address (current_program_space->exec_bfd ()); | 
|  |  | 
|  | /* Relocate.  */ | 
|  | if (vmaddr != load_addr) | 
|  | objfile_rebase (current_program_space->symfile_object_file, | 
|  | load_addr - vmaddr); | 
|  | } | 
|  |  | 
|  | /* Set solib notifier (to reload list of shared libraries).  */ | 
|  | CORE_ADDR notifier = info->all_image.notifier; | 
|  |  | 
|  | if (info->all_image.count == 0) | 
|  | { | 
|  | /* Dyld hasn't yet relocated itself, so the notifier address may | 
|  | be incorrect (as it has to be relocated).  */ | 
|  | CORE_ADDR start | 
|  | = bfd_get_start_address (current_program_space->exec_bfd ()); | 
|  | if (start == 0) | 
|  | notifier = 0; | 
|  | else | 
|  | { | 
|  | gdb_bfd_ref_ptr dyld_bfd = darwin_get_dyld_bfd (); | 
|  | if (dyld_bfd != NULL) | 
|  | { | 
|  | CORE_ADDR dyld_bfd_start_address; | 
|  | CORE_ADDR dyld_relocated_base_address; | 
|  | CORE_ADDR pc; | 
|  |  | 
|  | dyld_bfd_start_address = bfd_get_start_address (dyld_bfd.get()); | 
|  |  | 
|  | /* We find the dynamic linker's base address by examining | 
|  | the current pc (which should point at the entry point | 
|  | for the dynamic linker) and subtracting the offset of | 
|  | the entry point.  */ | 
|  |  | 
|  | pc = regcache_read_pc (get_thread_regcache (inferior_thread ())); | 
|  | dyld_relocated_base_address = pc - dyld_bfd_start_address; | 
|  |  | 
|  | /* We get the proper notifier relocated address by | 
|  | adding the dyld relocated base address to the current | 
|  | notifier offset value.  */ | 
|  |  | 
|  | notifier += dyld_relocated_base_address; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Add the breakpoint which is hit by dyld when the list of solib is | 
|  | modified.  */ | 
|  | if (notifier != 0) | 
|  | create_solib_event_breakpoint (current_inferior ()->arch (), notifier); | 
|  | } | 
|  |  | 
|  | void | 
|  | darwin_solib_ops::clear_solib (program_space *pspace) const | 
|  | { | 
|  | darwin_info *info = get_darwin_info (pspace); | 
|  |  | 
|  | info->all_image_addr = 0; | 
|  | info->all_image.version = 0; | 
|  | } | 
|  |  | 
|  | /* The section table is built from bfd sections using bfd VMAs. | 
|  | Relocate these VMAs according to solib info.  */ | 
|  |  | 
|  | void | 
|  | darwin_solib_ops::relocate_section_addresses (solib &so, | 
|  | target_section *sec) const | 
|  | { | 
|  | auto *li = gdb::checked_static_cast<lm_info_darwin *> (so.lm_info.get ()); | 
|  |  | 
|  | sec->addr += li->lm_addr; | 
|  | sec->endaddr += li->lm_addr; | 
|  |  | 
|  | /* Best effort to set addr_high/addr_low.  This is used only by | 
|  | 'info sharedlibary'.  */ | 
|  | if (so.addr_high == 0) | 
|  | { | 
|  | so.addr_low = sec->addr; | 
|  | so.addr_high = sec->endaddr; | 
|  | } | 
|  | if (sec->endaddr > so.addr_high) | 
|  | so.addr_high = sec->endaddr; | 
|  | if (sec->addr < so.addr_low) | 
|  | so.addr_low = sec->addr; | 
|  | } | 
|  |  | 
|  | gdb_bfd_ref_ptr | 
|  | darwin_solib_ops::bfd_open (const char *pathname) const | 
|  | { | 
|  | int found_file; | 
|  |  | 
|  | /* Search for shared library file.  */ | 
|  | gdb::unique_xmalloc_ptr<char> found_pathname | 
|  | = solib_find (pathname, &found_file); | 
|  | if (found_pathname == NULL) | 
|  | perror_with_name (pathname); | 
|  |  | 
|  | /* Open bfd for shared library.  */ | 
|  | gdb_bfd_ref_ptr abfd (solib_bfd_fopen (found_pathname.get (), found_file)); | 
|  |  | 
|  | gdb_bfd_ref_ptr res | 
|  | (gdb_bfd_mach_o_fat_extract | 
|  | (abfd.get (), bfd_object, | 
|  | gdbarch_bfd_arch_info (current_inferior ()->arch ()))); | 
|  | if (res == NULL) | 
|  | error (_("`%s': not a shared-library: %s"), | 
|  | bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ())); | 
|  |  | 
|  | /* The current filename for fat-binary BFDs is a name generated | 
|  | by BFD, usually a string containing the name of the architecture. | 
|  | Reset its value to the actual filename.  */ | 
|  | bfd_set_filename (res.get (), pathname); | 
|  |  | 
|  | return res; | 
|  | } |