| /* Copyright (C) 2013-2023 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 "defs.h" | 
 | #include "solib-aix.h" | 
 | #include "solib.h" | 
 | #include "solist.h" | 
 | #include "inferior.h" | 
 | #include "gdb_bfd.h" | 
 | #include "gdbcore.h" | 
 | #include "objfiles.h" | 
 | #include "symtab.h" | 
 | #include "xcoffread.h" | 
 | #include "observable.h" | 
 | #include "gdbcmd.h" | 
 | #include "gdbsupport/scope-exit.h" | 
 |  | 
 | /* Our private data in struct so_list.  */ | 
 |  | 
 | struct lm_info_aix : public lm_info_base | 
 | { | 
 |   /* The name of the file mapped by the loader.  Apart from the entry | 
 |      for the main executable, this is usually a shared library (which, | 
 |      on AIX, is an archive library file, created using the "ar" | 
 |      command).  */ | 
 |   std::string filename; | 
 |  | 
 |   /* The name of the shared object file with the actual dynamic | 
 |      loading dependency.  This may be empty (Eg. main executable).  */ | 
 |   std::string member_name; | 
 |  | 
 |   /* The address in inferior memory where the text section got mapped.  */ | 
 |   CORE_ADDR text_addr = 0; | 
 |  | 
 |   /* The size of the text section, obtained via the loader data.  */ | 
 |   ULONGEST text_size = 0; | 
 |  | 
 |   /* The address in inferior memory where the data section got mapped.  */ | 
 |   CORE_ADDR data_addr = 0; | 
 |  | 
 |   /* The size of the data section, obtained via the loader data.  */ | 
 |   ULONGEST data_size = 0; | 
 | }; | 
 |  | 
 | /* This module's per-inferior data.  */ | 
 |  | 
 | struct solib_aix_inferior_data | 
 | { | 
 |   /* The list of shared libraries. | 
 |  | 
 |      Note that the first element of this list is always the main | 
 |      executable, which is not technically a shared library.  But | 
 |      we need that information to perform its relocation, and | 
 |      the same principles applied to shared libraries also apply | 
 |      to the main executable.  So it's simpler to keep it as part | 
 |      of this list.  */ | 
 |   gdb::optional<std::vector<lm_info_aix>> library_list; | 
 | }; | 
 |  | 
 | /* Key to our per-inferior data.  */ | 
 | static const registry<inferior>::key<solib_aix_inferior_data> | 
 |   solib_aix_inferior_data_handle; | 
 |  | 
 | /* Return this module's data for the given inferior. | 
 |    If none is found, add a zero'ed one now.  */ | 
 |  | 
 | static struct solib_aix_inferior_data * | 
 | get_solib_aix_inferior_data (struct inferior *inf) | 
 | { | 
 |   struct solib_aix_inferior_data *data; | 
 |  | 
 |   data = solib_aix_inferior_data_handle.get (inf); | 
 |   if (data == NULL) | 
 |     data = solib_aix_inferior_data_handle.emplace (inf); | 
 |  | 
 |   return data; | 
 | } | 
 |  | 
 | #if !defined(HAVE_LIBEXPAT) | 
 |  | 
 | /* Dummy implementation if XML support is not compiled in.  */ | 
 |  | 
 | static gdb::optional<std::vector<lm_info_aix>> | 
 | solib_aix_parse_libraries (const char *library) | 
 | { | 
 |   static int have_warned; | 
 |  | 
 |   if (!have_warned) | 
 |     { | 
 |       have_warned = 1; | 
 |       warning (_("Can not parse XML library list; XML support was disabled " | 
 | 		 "at compile time")); | 
 |     } | 
 |  | 
 |   return {}; | 
 | } | 
 |  | 
 | #else /* HAVE_LIBEXPAT */ | 
 |  | 
 | #include "xml-support.h" | 
 |  | 
 | /* Handle the start of a <library> element.  */ | 
 |  | 
 | static void | 
 | library_list_start_library (struct gdb_xml_parser *parser, | 
 | 			    const struct gdb_xml_element *element, | 
 | 			    void *user_data, | 
 | 			    std::vector<gdb_xml_value> &attributes) | 
 | { | 
 |   std::vector<lm_info_aix> *list = (std::vector<lm_info_aix> *) user_data; | 
 |   lm_info_aix item; | 
 |   struct gdb_xml_value *attr; | 
 |  | 
 |   attr = xml_find_attribute (attributes, "name"); | 
 |   item.filename = (const char *) attr->value.get (); | 
 |  | 
 |   attr = xml_find_attribute (attributes, "member"); | 
 |   if (attr != NULL) | 
 |     item.member_name = (const char *) attr->value.get (); | 
 |  | 
 |   attr = xml_find_attribute (attributes, "text_addr"); | 
 |   item.text_addr = * (ULONGEST *) attr->value.get (); | 
 |  | 
 |   attr = xml_find_attribute (attributes, "text_size"); | 
 |   item.text_size = * (ULONGEST *) attr->value.get (); | 
 |  | 
 |   attr = xml_find_attribute (attributes, "data_addr"); | 
 |   item.data_addr = * (ULONGEST *) attr->value.get (); | 
 |  | 
 |   attr = xml_find_attribute (attributes, "data_size"); | 
 |   item.data_size = * (ULONGEST *) attr->value.get (); | 
 |  | 
 |   list->push_back (std::move (item)); | 
 | } | 
 |  | 
 | /* Handle the start of a <library-list-aix> element.  */ | 
 |  | 
 | static void | 
 | library_list_start_list (struct gdb_xml_parser *parser, | 
 | 			 const struct gdb_xml_element *element, | 
 | 			 void *user_data, | 
 | 			 std::vector<gdb_xml_value> &attributes) | 
 | { | 
 |   char *version | 
 |     = (char *) xml_find_attribute (attributes, "version")->value.get (); | 
 |  | 
 |   if (strcmp (version, "1.0") != 0) | 
 |     gdb_xml_error (parser, | 
 | 		   _("Library list has unsupported version \"%s\""), | 
 | 		   version); | 
 | } | 
 |  | 
 | /* The allowed elements and attributes for an AIX library list | 
 |    described in XML format.  The root element is a <library-list-aix>.  */ | 
 |  | 
 | static const struct gdb_xml_attribute library_attributes[] = | 
 | { | 
 |   { "name", GDB_XML_AF_NONE, NULL, NULL }, | 
 |   { "member", GDB_XML_AF_OPTIONAL, NULL, NULL }, | 
 |   { "text_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | 
 |   { "text_size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | 
 |   { "data_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | 
 |   { "data_size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | 
 |   { NULL, GDB_XML_AF_NONE, NULL, NULL } | 
 | }; | 
 |  | 
 | static const struct gdb_xml_element library_list_children[] = | 
 | { | 
 |   { "library", library_attributes, NULL, | 
 |     GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, | 
 |     library_list_start_library, NULL}, | 
 |   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | 
 | }; | 
 |  | 
 | static const struct gdb_xml_attribute library_list_attributes[] = | 
 | { | 
 |   { "version", GDB_XML_AF_NONE, NULL, NULL }, | 
 |   { NULL, GDB_XML_AF_NONE, NULL, NULL } | 
 | }; | 
 |  | 
 | static const struct gdb_xml_element library_list_elements[] = | 
 | { | 
 |   { "library-list-aix", library_list_attributes, library_list_children, | 
 |     GDB_XML_EF_NONE, library_list_start_list, NULL }, | 
 |   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | 
 | }; | 
 |  | 
 | /* Parse LIBRARY, a string containing the loader info in XML format, | 
 |    and return a vector of lm_info_aix objects. | 
 |  | 
 |    Return an empty option if the parsing failed.  */ | 
 |  | 
 | static gdb::optional<std::vector<lm_info_aix>> | 
 | solib_aix_parse_libraries (const char *library) | 
 | { | 
 |   std::vector<lm_info_aix> result; | 
 |  | 
 |   if (gdb_xml_parse_quick (_("aix library list"), "library-list-aix.dtd", | 
 | 			   library_list_elements, library, &result) == 0) | 
 |     return result; | 
 |  | 
 |   return {}; | 
 | } | 
 |  | 
 | #endif /* HAVE_LIBEXPAT */ | 
 |  | 
 | /* Return the loader info for the given inferior (INF), or an empty | 
 |    option if the list could not be computed. | 
 |  | 
 |    Cache the result in per-inferior data, so as to avoid recomputing it | 
 |    each time this function is called. | 
 |  | 
 |    If an error occurs while computing this list, and WARNING_MSG | 
 |    is not NULL, then print a warning including WARNING_MSG and | 
 |    a description of the error.  */ | 
 |  | 
 | static gdb::optional<std::vector<lm_info_aix>> & | 
 | solib_aix_get_library_list (struct inferior *inf, const char *warning_msg) | 
 | { | 
 |   struct solib_aix_inferior_data *data; | 
 |  | 
 |   /* If already computed, return the cached value.  */ | 
 |   data = get_solib_aix_inferior_data (inf); | 
 |   if (data->library_list.has_value ()) | 
 |     return data->library_list; | 
 |  | 
 |   gdb::optional<gdb::char_vector> library_document | 
 |     = target_read_stralloc (current_inferior ()->top_target (), | 
 | 			    TARGET_OBJECT_LIBRARIES_AIX, | 
 | 			    NULL); | 
 |   if (!library_document && warning_msg != NULL) | 
 |     { | 
 |       warning (_("%s (failed to read TARGET_OBJECT_LIBRARIES_AIX)"), | 
 | 	       warning_msg); | 
 |       return data->library_list; | 
 |     } | 
 |  | 
 |   solib_debug_printf ("TARGET_OBJECT_LIBRARIES_AIX = %s", | 
 | 		      library_document->data ()); | 
 |  | 
 |   data->library_list = solib_aix_parse_libraries (library_document->data ()); | 
 |   if (!data->library_list.has_value () && warning_msg != NULL) | 
 |     warning (_("%s (missing XML support?)"), warning_msg); | 
 |  | 
 |   return data->library_list; | 
 | } | 
 |  | 
 | /* If the .bss section's VMA is set to an address located before | 
 |    the end of the .data section, causing the two sections to overlap, | 
 |    return the overlap in bytes.  Otherwise, return zero. | 
 |  | 
 |    Motivation: | 
 |  | 
 |    The GNU linker sometimes sets the start address of the .bss session | 
 |    before the end of the .data section, making the 2 sections overlap. | 
 |    The loader appears to handle this situation gracefully, by simply | 
 |    loading the bss section right after the end of the .data section. | 
 |  | 
 |    This means that the .data and the .bss sections are sometimes | 
 |    no longer relocated by the same amount.  The problem is that | 
 |    the ldinfo data does not contain any information regarding | 
 |    the relocation of the .bss section, assuming that it would be | 
 |    identical to the information provided for the .data section | 
 |    (this is what would normally happen if the program was linked | 
 |    correctly). | 
 |  | 
 |    GDB therefore needs to detect those cases, and make the corresponding | 
 |    adjustment to the .bss section offset computed from the ldinfo data | 
 |    when necessary.  This function returns the adjustment amount  (or | 
 |    zero when no adjustment is needed).  */ | 
 |  | 
 | static CORE_ADDR | 
 | solib_aix_bss_data_overlap (bfd *abfd) | 
 | { | 
 |   struct bfd_section *data_sect, *bss_sect; | 
 |  | 
 |   data_sect = bfd_get_section_by_name (abfd, ".data"); | 
 |   if (data_sect == NULL) | 
 |     return 0; /* No overlap possible.  */ | 
 |  | 
 |   bss_sect = bfd_get_section_by_name (abfd, ".bss"); | 
 |   if (bss_sect == NULL) | 
 |     return 0; /* No overlap possible.  */ | 
 |  | 
 |   /* Assume the problem only occurs with linkers that place the .bss | 
 |      section after the .data section (the problem has only been | 
 |      observed when using the GNU linker, and the default linker | 
 |      script always places the .data and .bss sections in that order).  */ | 
 |   if (bfd_section_vma (bss_sect) < bfd_section_vma (data_sect)) | 
 |     return 0; | 
 |  | 
 |   if (bfd_section_vma (bss_sect) | 
 |       < bfd_section_vma (data_sect) + bfd_section_size (data_sect)) | 
 |     return (bfd_section_vma (data_sect) + bfd_section_size (data_sect) | 
 | 	    - bfd_section_vma (bss_sect)); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Implement the "relocate_section_addresses" target_so_ops method.  */ | 
 |  | 
 | static void | 
 | solib_aix_relocate_section_addresses (struct so_list *so, | 
 | 				      struct target_section *sec) | 
 | { | 
 |   struct bfd_section *bfd_sect = sec->the_bfd_section; | 
 |   bfd *abfd = bfd_sect->owner; | 
 |   const char *section_name = bfd_section_name (bfd_sect); | 
 |   lm_info_aix *info = (lm_info_aix *) so->lm_info; | 
 |  | 
 |   if (strcmp (section_name, ".text") == 0) | 
 |     { | 
 |       sec->addr = info->text_addr; | 
 |       sec->endaddr = sec->addr + info->text_size; | 
 |  | 
 |       /* The text address given to us by the loader contains | 
 | 	 XCOFF headers, so we need to adjust by this much.  */ | 
 |       sec->addr += bfd_sect->filepos; | 
 |     } | 
 |   else if (strcmp (section_name, ".data") == 0) | 
 |     { | 
 |       sec->addr = info->data_addr; | 
 |       sec->endaddr = sec->addr + info->data_size; | 
 |     } | 
 |   else if (strcmp (section_name, ".bss") == 0) | 
 |     { | 
 |       /* The information provided by the loader does not include | 
 | 	 the address of the .bss section, but we know that it gets | 
 | 	 relocated by the same offset as the .data section.  So, | 
 | 	 compute the relocation offset for the .data section, and | 
 | 	 apply it to the .bss section as well.  If the .data section | 
 | 	 is not defined (which seems highly unlikely), do our best | 
 | 	 by assuming no relocation.  */ | 
 |       struct bfd_section *data_sect | 
 | 	= bfd_get_section_by_name (abfd, ".data"); | 
 |       CORE_ADDR data_offset = 0; | 
 |  | 
 |       if (data_sect != NULL) | 
 | 	data_offset = info->data_addr - bfd_section_vma (data_sect); | 
 |  | 
 |       sec->addr = bfd_section_vma (bfd_sect) + data_offset; | 
 |       sec->addr += solib_aix_bss_data_overlap (abfd); | 
 |       sec->endaddr = sec->addr + bfd_section_size (bfd_sect); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* All other sections should not be relocated.  */ | 
 |       sec->addr = bfd_section_vma (bfd_sect); | 
 |       sec->endaddr = sec->addr + bfd_section_size (bfd_sect); | 
 |     } | 
 | } | 
 |  | 
 | /* Implement the "free_so" target_so_ops method.  */ | 
 |  | 
 | static void | 
 | solib_aix_free_so (struct so_list *so) | 
 | { | 
 |   lm_info_aix *li = (lm_info_aix *) so->lm_info; | 
 |  | 
 |   solib_debug_printf ("%s", so->so_name); | 
 |  | 
 |   delete li; | 
 | } | 
 |  | 
 | /* Implement the "clear_solib" target_so_ops method.  */ | 
 |  | 
 | static void | 
 | solib_aix_clear_solib (void) | 
 | { | 
 |   /* Nothing needed.  */ | 
 | } | 
 |  | 
 | /* Compute and return the OBJFILE's section_offset array, using | 
 |    the associated loader info (INFO).  */ | 
 |  | 
 | static section_offsets | 
 | solib_aix_get_section_offsets (struct objfile *objfile, | 
 | 			       lm_info_aix *info) | 
 | { | 
 |   bfd *abfd = objfile->obfd.get (); | 
 |  | 
 |   section_offsets offsets (objfile->section_offsets.size ()); | 
 |  | 
 |   /* .text */ | 
 |  | 
 |   if (objfile->sect_index_text != -1) | 
 |     { | 
 |       struct bfd_section *sect | 
 | 	= objfile->sections[objfile->sect_index_text].the_bfd_section; | 
 |  | 
 |       offsets[objfile->sect_index_text] | 
 | 	= info->text_addr + sect->filepos - bfd_section_vma (sect); | 
 |     } | 
 |  | 
 |   /* .data */ | 
 |  | 
 |   if (objfile->sect_index_data != -1) | 
 |     { | 
 |       struct bfd_section *sect | 
 | 	= objfile->sections[objfile->sect_index_data].the_bfd_section; | 
 |  | 
 |       offsets[objfile->sect_index_data] | 
 | 	= info->data_addr - bfd_section_vma (sect); | 
 |     } | 
 |  | 
 |   /* .bss | 
 |  | 
 |      The offset of the .bss section should be identical to the offset | 
 |      of the .data section.  If no .data section (which seems hard to | 
 |      believe it is possible), assume it is zero.  */ | 
 |  | 
 |   if (objfile->sect_index_bss != -1 | 
 |       && objfile->sect_index_data != -1) | 
 |     { | 
 |       offsets[objfile->sect_index_bss] | 
 | 	= (offsets[objfile->sect_index_data] | 
 | 	   + solib_aix_bss_data_overlap (abfd)); | 
 |     } | 
 |  | 
 |   /* All other sections should not need relocation.  */ | 
 |  | 
 |   return offsets; | 
 | } | 
 |  | 
 | /* Implement the "solib_create_inferior_hook" target_so_ops method.  */ | 
 |  | 
 | static void | 
 | solib_aix_solib_create_inferior_hook (int from_tty) | 
 | { | 
 |   const char *warning_msg = "unable to relocate main executable"; | 
 |  | 
 |   /* We need to relocate the main executable...  */ | 
 |  | 
 |   gdb::optional<std::vector<lm_info_aix>> &library_list | 
 |     = solib_aix_get_library_list (current_inferior (), warning_msg); | 
 |   if (!library_list.has_value ()) | 
 |     return;  /* Warning already printed.  */ | 
 |  | 
 |   if (library_list->empty ()) | 
 |     { | 
 |       warning (_("unable to relocate main executable (no info from loader)")); | 
 |       return; | 
 |     } | 
 |  | 
 |   lm_info_aix &exec_info = (*library_list)[0]; | 
 |   if (current_program_space->symfile_object_file != NULL) | 
 |     { | 
 |       objfile *objf = current_program_space->symfile_object_file; | 
 |       section_offsets offsets = solib_aix_get_section_offsets (objf, | 
 | 							       &exec_info); | 
 |  | 
 |       objfile_relocate (objf, offsets); | 
 |     } | 
 | } | 
 |  | 
 | /* Implement the "current_sos" target_so_ops method.  */ | 
 |  | 
 | static struct so_list * | 
 | solib_aix_current_sos (void) | 
 | { | 
 |   struct so_list *start = NULL, *last = NULL; | 
 |   int ix; | 
 |  | 
 |   gdb::optional<std::vector<lm_info_aix>> &library_list | 
 |     = solib_aix_get_library_list (current_inferior (), NULL); | 
 |   if (!library_list.has_value ()) | 
 |     return NULL; | 
 |  | 
 |   /* Build a struct so_list for each entry on the list. | 
 |      We skip the first entry, since this is the entry corresponding | 
 |      to the main executable, not a shared library.  */ | 
 |   for (ix = 1; ix < library_list->size (); ix++) | 
 |     { | 
 |       struct so_list *new_solib = XCNEW (struct so_list); | 
 |       std::string so_name; | 
 |  | 
 |       lm_info_aix &info = (*library_list)[ix]; | 
 |       if (info.member_name.empty ()) | 
 | 	{ | 
 | 	 /* INFO.FILENAME is probably not an archive, but rather | 
 | 	    a shared object.  Unusual, but it should be possible | 
 | 	    to link a program against a shared object directory, | 
 | 	    without having to put it in an archive first.  */ | 
 | 	 so_name = info.filename; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	 /* This is the usual case on AIX, where the shared object | 
 | 	    is a member of an archive.  Create a synthetic so_name | 
 | 	    that follows the same convention as AIX's ldd tool | 
 | 	    (Eg: "/lib/libc.a(shr.o)").  */ | 
 | 	 so_name = string_printf ("%s(%s)", info.filename.c_str (), | 
 | 				  info.member_name.c_str ()); | 
 | 	} | 
 |       strncpy (new_solib->so_original_name, so_name.c_str (), | 
 | 	       SO_NAME_MAX_PATH_SIZE - 1); | 
 |       new_solib->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; | 
 |       memcpy (new_solib->so_name, new_solib->so_original_name, | 
 | 	      SO_NAME_MAX_PATH_SIZE); | 
 |       new_solib->lm_info = new lm_info_aix (info); | 
 |  | 
 |       /* Add it to the list.  */ | 
 |       if (!start) | 
 | 	last = start = new_solib; | 
 |       else | 
 | 	{ | 
 | 	  last->next = new_solib; | 
 | 	  last = new_solib; | 
 | 	} | 
 |     } | 
 |  | 
 |   return start; | 
 | } | 
 |  | 
 | /* Implement the "open_symbol_file_object" target_so_ops method.  */ | 
 |  | 
 | static int | 
 | solib_aix_open_symbol_file_object (int from_tty) | 
 | { | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Implement the "in_dynsym_resolve_code" target_so_ops method.  */ | 
 |  | 
 | static int | 
 | solib_aix_in_dynsym_resolve_code (CORE_ADDR pc) | 
 | { | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Implement the "bfd_open" target_so_ops method.  */ | 
 |  | 
 | static gdb_bfd_ref_ptr | 
 | solib_aix_bfd_open (const char *pathname) | 
 | { | 
 |   /* The pathname is actually a synthetic filename with the following | 
 |      form: "/path/to/sharedlib(member.o)" (double-quotes excluded). | 
 |      split this into archive name and member name. | 
 |  | 
 |      FIXME: This is a little hacky.  Perhaps we should provide access | 
 |      to the solib's lm_info here?  */ | 
 |   const int path_len = strlen (pathname); | 
 |   const char *sep; | 
 |   int filename_len; | 
 |   int found_file; | 
 |  | 
 |   if (pathname[path_len - 1] != ')') | 
 |     return solib_bfd_open (pathname); | 
 |  | 
 |   /* Search for the associated parens.  */ | 
 |   sep = strrchr (pathname, '('); | 
 |   if (sep == NULL) | 
 |     { | 
 |       /* Should never happen, but recover as best as we can (trying | 
 | 	 to open pathname without decoding, possibly leading to | 
 | 	 a failure), rather than triggering an assert failure).  */ | 
 |       warning (_("missing '(' in shared object pathname: %s"), pathname); | 
 |       return solib_bfd_open (pathname); | 
 |     } | 
 |   filename_len = sep - pathname; | 
 |  | 
 |   std::string filename (string_printf ("%.*s", filename_len, pathname)); | 
 |   std::string member_name (string_printf ("%.*s", path_len - filename_len - 2, | 
 | 					  sep + 1)); | 
 |  | 
 |   /* Calling solib_find makes certain that sysroot path is set properly | 
 |      if program has a dependency on .a archive and sysroot is set via | 
 |      set sysroot command.  */ | 
 |   gdb::unique_xmalloc_ptr<char> found_pathname | 
 |     = solib_find (filename.c_str (), &found_file); | 
 |   if (found_pathname == NULL) | 
 |       perror_with_name (pathname); | 
 |   gdb_bfd_ref_ptr archive_bfd (solib_bfd_fopen (found_pathname.get (), | 
 | 						found_file)); | 
 |   if (archive_bfd == NULL) | 
 |     { | 
 |       warning (_("Could not open `%s' as an executable file: %s"), | 
 | 	       filename.c_str (), bfd_errmsg (bfd_get_error ())); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   if (bfd_check_format (archive_bfd.get (), bfd_object)) | 
 |     return archive_bfd; | 
 |  | 
 |   if (! bfd_check_format (archive_bfd.get (), bfd_archive)) | 
 |     { | 
 |       warning (_("\"%s\": not in executable format: %s."), | 
 | 	       filename.c_str (), bfd_errmsg (bfd_get_error ())); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   gdb_bfd_ref_ptr object_bfd | 
 |     (gdb_bfd_openr_next_archived_file (archive_bfd.get (), NULL)); | 
 |   while (object_bfd != NULL) | 
 |     { | 
 |       if (member_name == bfd_get_filename (object_bfd.get ())) | 
 | 	break; | 
 |  | 
 |       std::string s = bfd_get_filename (object_bfd.get ()); | 
 |  | 
 |       /* For every inferior after first int bfd system we | 
 | 	 will have the pathname instead of the member name | 
 | 	 registered. Hence the below condition exists.  */ | 
 |  | 
 |       if (s.find ('(') != std::string::npos) | 
 | 	{ | 
 | 	  int pos = s.find ('('); | 
 | 	  int len = s.find (')') - s.find ('('); | 
 | 	  if (s.substr (pos+1, len-1) == member_name) | 
 | 	    return object_bfd; | 
 | 	} | 
 |  | 
 |       object_bfd = gdb_bfd_openr_next_archived_file (archive_bfd.get (), | 
 | 						     object_bfd.get ()); | 
 |     } | 
 |  | 
 |   if (object_bfd == NULL) | 
 |     { | 
 |       warning (_("\"%s\": member \"%s\" missing."), filename.c_str (), | 
 | 	       member_name.c_str ()); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   if (! bfd_check_format (object_bfd.get (), bfd_object)) | 
 |     { | 
 |       warning (_("%s(%s): not in object format: %s."), | 
 | 	       filename.c_str (), member_name.c_str (), | 
 | 	       bfd_errmsg (bfd_get_error ())); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   /* Override the returned bfd's name with the name returned from solib_find | 
 |      along with appended parenthesized member name in order to allow commands | 
 |      listing all shared libraries to display.  Otherwise, we would only be | 
 |      displaying the name of the archive member object.  */ | 
 |   std::string fname = string_printf ("%s%s", | 
 | 				     bfd_get_filename (archive_bfd.get ()), | 
 | 				     sep); | 
 |   bfd_set_filename (object_bfd.get (), fname.c_str ()); | 
 |  | 
 |   return object_bfd; | 
 | } | 
 |  | 
 | /* Return the obj_section corresponding to OBJFILE's data section, | 
 |    or NULL if not found.  */ | 
 | /* FIXME: Define in a more general location? */ | 
 |  | 
 | static struct obj_section * | 
 | data_obj_section_from_objfile (struct objfile *objfile) | 
 | { | 
 |   struct obj_section *osect; | 
 |  | 
 |   ALL_OBJFILE_OSECTIONS (objfile, osect) | 
 |     if (strcmp (bfd_section_name (osect->the_bfd_section), ".data") == 0) | 
 |       return osect; | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | /* Return the TOC value corresponding to the given PC address, | 
 |    or raise an error if the value could not be determined.  */ | 
 |  | 
 | CORE_ADDR | 
 | solib_aix_get_toc_value (CORE_ADDR pc) | 
 | { | 
 |   struct obj_section *pc_osect = find_pc_section (pc); | 
 |   struct obj_section *data_osect; | 
 |   CORE_ADDR result; | 
 |  | 
 |   if (pc_osect == NULL) | 
 |     error (_("unable to find TOC entry for pc %s " | 
 | 	     "(no section contains this PC)"), | 
 | 	   core_addr_to_string (pc)); | 
 |  | 
 |   data_osect = data_obj_section_from_objfile (pc_osect->objfile); | 
 |   if (data_osect == NULL) | 
 |     error (_("unable to find TOC entry for pc %s " | 
 | 	     "(%s has no data section)"), | 
 | 	   core_addr_to_string (pc), objfile_name (pc_osect->objfile)); | 
 |  | 
 |   result = data_osect->addr () + xcoff_get_toc_offset (pc_osect->objfile); | 
 |  | 
 |   solib_debug_printf ("pc=%s -> %s", core_addr_to_string (pc), | 
 | 		      core_addr_to_string (result)); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | /* This module's normal_stop observer.  */ | 
 |  | 
 | static void | 
 | solib_aix_normal_stop_observer (struct bpstat *unused_1, int unused_2) | 
 | { | 
 |   struct solib_aix_inferior_data *data | 
 |     = get_solib_aix_inferior_data (current_inferior ()); | 
 |  | 
 |   /* The inferior execution has been resumed, and it just stopped | 
 |      again.  This means that the list of shared libraries may have | 
 |      evolved.  Reset our cached value.  */ | 
 |   data->library_list.reset (); | 
 | } | 
 |  | 
 | /* The target_so_ops for AIX targets.  */ | 
 | const struct target_so_ops solib_aix_so_ops = | 
 | { | 
 |   solib_aix_relocate_section_addresses, | 
 |   solib_aix_free_so, | 
 |   nullptr, | 
 |   solib_aix_clear_solib, | 
 |   solib_aix_solib_create_inferior_hook, | 
 |   solib_aix_current_sos, | 
 |   solib_aix_open_symbol_file_object, | 
 |   solib_aix_in_dynsym_resolve_code, | 
 |   solib_aix_bfd_open, | 
 | }; | 
 |  | 
 | void _initialize_solib_aix (); | 
 | void | 
 | _initialize_solib_aix () | 
 | { | 
 |   gdb::observers::normal_stop.attach (solib_aix_normal_stop_observer, | 
 | 				      "solib-aix"); | 
 | } |