1 /* Copyright (C) 2013-2023 Free Software Foundation, Inc. 2 3 This file is part of GDB. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include "defs.h" 19 #include "solib-aix.h" 20 #include "solib.h" 21 #include "solist.h" 22 #include "inferior.h" 23 #include "gdb_bfd.h" 24 #include "gdbcore.h" 25 #include "objfiles.h" 26 #include "symtab.h" 27 #include "xcoffread.h" 28 #include "observable.h" 29 #include "gdbcmd.h" 30 #include "gdbsupport/scope-exit.h" 31 32 /* Our private data in struct so_list. */ 33 34 struct lm_info_aix : public lm_info_base 35 { 36 /* The name of the file mapped by the loader. Apart from the entry 37 for the main executable, this is usually a shared library (which, 38 on AIX, is an archive library file, created using the "ar" 39 command). */ 40 std::string filename; 41 42 /* The name of the shared object file with the actual dynamic 43 loading dependency. This may be empty (Eg. main executable). */ 44 std::string member_name; 45 46 /* The address in inferior memory where the text section got mapped. */ 47 CORE_ADDR text_addr = 0; 48 49 /* The size of the text section, obtained via the loader data. */ 50 ULONGEST text_size = 0; 51 52 /* The address in inferior memory where the data section got mapped. */ 53 CORE_ADDR data_addr = 0; 54 55 /* The size of the data section, obtained via the loader data. */ 56 ULONGEST data_size = 0; 57 }; 58 59 /* This module's per-inferior data. */ 60 61 struct solib_aix_inferior_data 62 { 63 /* The list of shared libraries. 64 65 Note that the first element of this list is always the main 66 executable, which is not technically a shared library. But 67 we need that information to perform its relocation, and 68 the same principles applied to shared libraries also apply 69 to the main executable. So it's simpler to keep it as part 70 of this list. */ 71 gdb::optional<std::vector<lm_info_aix>> library_list; 72 }; 73 74 /* Key to our per-inferior data. */ 75 static const registry<inferior>::key<solib_aix_inferior_data> 76 solib_aix_inferior_data_handle; 77 78 /* Return this module's data for the given inferior. 79 If none is found, add a zero'ed one now. */ 80 81 static struct solib_aix_inferior_data * 82 get_solib_aix_inferior_data (struct inferior *inf) 83 { 84 struct solib_aix_inferior_data *data; 85 86 data = solib_aix_inferior_data_handle.get (inf); 87 if (data == NULL) 88 data = solib_aix_inferior_data_handle.emplace (inf); 89 90 return data; 91 } 92 93 #if !defined(HAVE_LIBEXPAT) 94 95 /* Dummy implementation if XML support is not compiled in. */ 96 97 static gdb::optional<std::vector<lm_info_aix>> 98 solib_aix_parse_libraries (const char *library) 99 { 100 static int have_warned; 101 102 if (!have_warned) 103 { 104 have_warned = 1; 105 warning (_("Can not parse XML library list; XML support was disabled " 106 "at compile time")); 107 } 108 109 return {}; 110 } 111 112 #else /* HAVE_LIBEXPAT */ 113 114 #include "xml-support.h" 115 116 /* Handle the start of a <library> element. */ 117 118 static void 119 library_list_start_library (struct gdb_xml_parser *parser, 120 const struct gdb_xml_element *element, 121 void *user_data, 122 std::vector<gdb_xml_value> &attributes) 123 { 124 std::vector<lm_info_aix> *list = (std::vector<lm_info_aix> *) user_data; 125 lm_info_aix item; 126 struct gdb_xml_value *attr; 127 128 attr = xml_find_attribute (attributes, "name"); 129 item.filename = (const char *) attr->value.get (); 130 131 attr = xml_find_attribute (attributes, "member"); 132 if (attr != NULL) 133 item.member_name = (const char *) attr->value.get (); 134 135 attr = xml_find_attribute (attributes, "text_addr"); 136 item.text_addr = * (ULONGEST *) attr->value.get (); 137 138 attr = xml_find_attribute (attributes, "text_size"); 139 item.text_size = * (ULONGEST *) attr->value.get (); 140 141 attr = xml_find_attribute (attributes, "data_addr"); 142 item.data_addr = * (ULONGEST *) attr->value.get (); 143 144 attr = xml_find_attribute (attributes, "data_size"); 145 item.data_size = * (ULONGEST *) attr->value.get (); 146 147 list->push_back (std::move (item)); 148 } 149 150 /* Handle the start of a <library-list-aix> element. */ 151 152 static void 153 library_list_start_list (struct gdb_xml_parser *parser, 154 const struct gdb_xml_element *element, 155 void *user_data, 156 std::vector<gdb_xml_value> &attributes) 157 { 158 char *version 159 = (char *) xml_find_attribute (attributes, "version")->value.get (); 160 161 if (strcmp (version, "1.0") != 0) 162 gdb_xml_error (parser, 163 _("Library list has unsupported version \"%s\""), 164 version); 165 } 166 167 /* The allowed elements and attributes for an AIX library list 168 described in XML format. The root element is a <library-list-aix>. */ 169 170 static const struct gdb_xml_attribute library_attributes[] = 171 { 172 { "name", GDB_XML_AF_NONE, NULL, NULL }, 173 { "member", GDB_XML_AF_OPTIONAL, NULL, NULL }, 174 { "text_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, 175 { "text_size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, 176 { "data_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, 177 { "data_size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, 178 { NULL, GDB_XML_AF_NONE, NULL, NULL } 179 }; 180 181 static const struct gdb_xml_element library_list_children[] = 182 { 183 { "library", library_attributes, NULL, 184 GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, 185 library_list_start_library, NULL}, 186 { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } 187 }; 188 189 static const struct gdb_xml_attribute library_list_attributes[] = 190 { 191 { "version", GDB_XML_AF_NONE, NULL, NULL }, 192 { NULL, GDB_XML_AF_NONE, NULL, NULL } 193 }; 194 195 static const struct gdb_xml_element library_list_elements[] = 196 { 197 { "library-list-aix", library_list_attributes, library_list_children, 198 GDB_XML_EF_NONE, library_list_start_list, NULL }, 199 { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } 200 }; 201 202 /* Parse LIBRARY, a string containing the loader info in XML format, 203 and return a vector of lm_info_aix objects. 204 205 Return an empty option if the parsing failed. */ 206 207 static gdb::optional<std::vector<lm_info_aix>> 208 solib_aix_parse_libraries (const char *library) 209 { 210 std::vector<lm_info_aix> result; 211 212 if (gdb_xml_parse_quick (_("aix library list"), "library-list-aix.dtd", 213 library_list_elements, library, &result) == 0) 214 return result; 215 216 return {}; 217 } 218 219 #endif /* HAVE_LIBEXPAT */ 220 221 /* Return the loader info for the given inferior (INF), or an empty 222 option if the list could not be computed. 223 224 Cache the result in per-inferior data, so as to avoid recomputing it 225 each time this function is called. 226 227 If an error occurs while computing this list, and WARNING_MSG 228 is not NULL, then print a warning including WARNING_MSG and 229 a description of the error. */ 230 231 static gdb::optional<std::vector<lm_info_aix>> & 232 solib_aix_get_library_list (struct inferior *inf, const char *warning_msg) 233 { 234 struct solib_aix_inferior_data *data; 235 236 /* If already computed, return the cached value. */ 237 data = get_solib_aix_inferior_data (inf); 238 if (data->library_list.has_value ()) 239 return data->library_list; 240 241 gdb::optional<gdb::char_vector> library_document 242 = target_read_stralloc (current_inferior ()->top_target (), 243 TARGET_OBJECT_LIBRARIES_AIX, 244 NULL); 245 if (!library_document && warning_msg != NULL) 246 { 247 warning (_("%s (failed to read TARGET_OBJECT_LIBRARIES_AIX)"), 248 warning_msg); 249 return data->library_list; 250 } 251 252 solib_debug_printf ("TARGET_OBJECT_LIBRARIES_AIX = %s", 253 library_document->data ()); 254 255 data->library_list = solib_aix_parse_libraries (library_document->data ()); 256 if (!data->library_list.has_value () && warning_msg != NULL) 257 warning (_("%s (missing XML support?)"), warning_msg); 258 259 return data->library_list; 260 } 261 262 /* If the .bss section's VMA is set to an address located before 263 the end of the .data section, causing the two sections to overlap, 264 return the overlap in bytes. Otherwise, return zero. 265 266 Motivation: 267 268 The GNU linker sometimes sets the start address of the .bss session 269 before the end of the .data section, making the 2 sections overlap. 270 The loader appears to handle this situation gracefully, by simply 271 loading the bss section right after the end of the .data section. 272 273 This means that the .data and the .bss sections are sometimes 274 no longer relocated by the same amount. The problem is that 275 the ldinfo data does not contain any information regarding 276 the relocation of the .bss section, assuming that it would be 277 identical to the information provided for the .data section 278 (this is what would normally happen if the program was linked 279 correctly). 280 281 GDB therefore needs to detect those cases, and make the corresponding 282 adjustment to the .bss section offset computed from the ldinfo data 283 when necessary. This function returns the adjustment amount (or 284 zero when no adjustment is needed). */ 285 286 static CORE_ADDR 287 solib_aix_bss_data_overlap (bfd *abfd) 288 { 289 struct bfd_section *data_sect, *bss_sect; 290 291 data_sect = bfd_get_section_by_name (abfd, ".data"); 292 if (data_sect == NULL) 293 return 0; /* No overlap possible. */ 294 295 bss_sect = bfd_get_section_by_name (abfd, ".bss"); 296 if (bss_sect == NULL) 297 return 0; /* No overlap possible. */ 298 299 /* Assume the problem only occurs with linkers that place the .bss 300 section after the .data section (the problem has only been 301 observed when using the GNU linker, and the default linker 302 script always places the .data and .bss sections in that order). */ 303 if (bfd_section_vma (bss_sect) < bfd_section_vma (data_sect)) 304 return 0; 305 306 if (bfd_section_vma (bss_sect) 307 < bfd_section_vma (data_sect) + bfd_section_size (data_sect)) 308 return (bfd_section_vma (data_sect) + bfd_section_size (data_sect) 309 - bfd_section_vma (bss_sect)); 310 311 return 0; 312 } 313 314 /* Implement the "relocate_section_addresses" target_so_ops method. */ 315 316 static void 317 solib_aix_relocate_section_addresses (struct so_list *so, 318 struct target_section *sec) 319 { 320 struct bfd_section *bfd_sect = sec->the_bfd_section; 321 bfd *abfd = bfd_sect->owner; 322 const char *section_name = bfd_section_name (bfd_sect); 323 lm_info_aix *info = (lm_info_aix *) so->lm_info; 324 325 if (strcmp (section_name, ".text") == 0) 326 { 327 sec->addr = info->text_addr; 328 sec->endaddr = sec->addr + info->text_size; 329 330 /* The text address given to us by the loader contains 331 XCOFF headers, so we need to adjust by this much. */ 332 sec->addr += bfd_sect->filepos; 333 } 334 else if (strcmp (section_name, ".data") == 0) 335 { 336 sec->addr = info->data_addr; 337 sec->endaddr = sec->addr + info->data_size; 338 } 339 else if (strcmp (section_name, ".bss") == 0) 340 { 341 /* The information provided by the loader does not include 342 the address of the .bss section, but we know that it gets 343 relocated by the same offset as the .data section. So, 344 compute the relocation offset for the .data section, and 345 apply it to the .bss section as well. If the .data section 346 is not defined (which seems highly unlikely), do our best 347 by assuming no relocation. */ 348 struct bfd_section *data_sect 349 = bfd_get_section_by_name (abfd, ".data"); 350 CORE_ADDR data_offset = 0; 351 352 if (data_sect != NULL) 353 data_offset = info->data_addr - bfd_section_vma (data_sect); 354 355 sec->addr = bfd_section_vma (bfd_sect) + data_offset; 356 sec->addr += solib_aix_bss_data_overlap (abfd); 357 sec->endaddr = sec->addr + bfd_section_size (bfd_sect); 358 } 359 else 360 { 361 /* All other sections should not be relocated. */ 362 sec->addr = bfd_section_vma (bfd_sect); 363 sec->endaddr = sec->addr + bfd_section_size (bfd_sect); 364 } 365 } 366 367 /* Implement the "free_so" target_so_ops method. */ 368 369 static void 370 solib_aix_free_so (struct so_list *so) 371 { 372 lm_info_aix *li = (lm_info_aix *) so->lm_info; 373 374 solib_debug_printf ("%s", so->so_name); 375 376 delete li; 377 } 378 379 /* Implement the "clear_solib" target_so_ops method. */ 380 381 static void 382 solib_aix_clear_solib (void) 383 { 384 /* Nothing needed. */ 385 } 386 387 /* Compute and return the OBJFILE's section_offset array, using 388 the associated loader info (INFO). */ 389 390 static section_offsets 391 solib_aix_get_section_offsets (struct objfile *objfile, 392 lm_info_aix *info) 393 { 394 bfd *abfd = objfile->obfd.get (); 395 396 section_offsets offsets (objfile->section_offsets.size ()); 397 398 /* .text */ 399 400 if (objfile->sect_index_text != -1) 401 { 402 struct bfd_section *sect 403 = objfile->sections[objfile->sect_index_text].the_bfd_section; 404 405 offsets[objfile->sect_index_text] 406 = info->text_addr + sect->filepos - bfd_section_vma (sect); 407 } 408 409 /* .data */ 410 411 if (objfile->sect_index_data != -1) 412 { 413 struct bfd_section *sect 414 = objfile->sections[objfile->sect_index_data].the_bfd_section; 415 416 offsets[objfile->sect_index_data] 417 = info->data_addr - bfd_section_vma (sect); 418 } 419 420 /* .bss 421 422 The offset of the .bss section should be identical to the offset 423 of the .data section. If no .data section (which seems hard to 424 believe it is possible), assume it is zero. */ 425 426 if (objfile->sect_index_bss != -1 427 && objfile->sect_index_data != -1) 428 { 429 offsets[objfile->sect_index_bss] 430 = (offsets[objfile->sect_index_data] 431 + solib_aix_bss_data_overlap (abfd)); 432 } 433 434 /* All other sections should not need relocation. */ 435 436 return offsets; 437 } 438 439 /* Implement the "solib_create_inferior_hook" target_so_ops method. */ 440 441 static void 442 solib_aix_solib_create_inferior_hook (int from_tty) 443 { 444 const char *warning_msg = "unable to relocate main executable"; 445 446 /* We need to relocate the main executable... */ 447 448 gdb::optional<std::vector<lm_info_aix>> &library_list 449 = solib_aix_get_library_list (current_inferior (), warning_msg); 450 if (!library_list.has_value ()) 451 return; /* Warning already printed. */ 452 453 if (library_list->empty ()) 454 { 455 warning (_("unable to relocate main executable (no info from loader)")); 456 return; 457 } 458 459 lm_info_aix &exec_info = (*library_list)[0]; 460 if (current_program_space->symfile_object_file != NULL) 461 { 462 objfile *objf = current_program_space->symfile_object_file; 463 section_offsets offsets = solib_aix_get_section_offsets (objf, 464 &exec_info); 465 466 objfile_relocate (objf, offsets); 467 } 468 } 469 470 /* Implement the "current_sos" target_so_ops method. */ 471 472 static struct so_list * 473 solib_aix_current_sos (void) 474 { 475 struct so_list *start = NULL, *last = NULL; 476 int ix; 477 478 gdb::optional<std::vector<lm_info_aix>> &library_list 479 = solib_aix_get_library_list (current_inferior (), NULL); 480 if (!library_list.has_value ()) 481 return NULL; 482 483 /* Build a struct so_list for each entry on the list. 484 We skip the first entry, since this is the entry corresponding 485 to the main executable, not a shared library. */ 486 for (ix = 1; ix < library_list->size (); ix++) 487 { 488 struct so_list *new_solib = XCNEW (struct so_list); 489 std::string so_name; 490 491 lm_info_aix &info = (*library_list)[ix]; 492 if (info.member_name.empty ()) 493 { 494 /* INFO.FILENAME is probably not an archive, but rather 495 a shared object. Unusual, but it should be possible 496 to link a program against a shared object directory, 497 without having to put it in an archive first. */ 498 so_name = info.filename; 499 } 500 else 501 { 502 /* This is the usual case on AIX, where the shared object 503 is a member of an archive. Create a synthetic so_name 504 that follows the same convention as AIX's ldd tool 505 (Eg: "/lib/libc.a(shr.o)"). */ 506 so_name = string_printf ("%s(%s)", info.filename.c_str (), 507 info.member_name.c_str ()); 508 } 509 strncpy (new_solib->so_original_name, so_name.c_str (), 510 SO_NAME_MAX_PATH_SIZE - 1); 511 new_solib->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; 512 memcpy (new_solib->so_name, new_solib->so_original_name, 513 SO_NAME_MAX_PATH_SIZE); 514 new_solib->lm_info = new lm_info_aix (info); 515 516 /* Add it to the list. */ 517 if (!start) 518 last = start = new_solib; 519 else 520 { 521 last->next = new_solib; 522 last = new_solib; 523 } 524 } 525 526 return start; 527 } 528 529 /* Implement the "open_symbol_file_object" target_so_ops method. */ 530 531 static int 532 solib_aix_open_symbol_file_object (int from_tty) 533 { 534 return 0; 535 } 536 537 /* Implement the "in_dynsym_resolve_code" target_so_ops method. */ 538 539 static int 540 solib_aix_in_dynsym_resolve_code (CORE_ADDR pc) 541 { 542 return 0; 543 } 544 545 /* Implement the "bfd_open" target_so_ops method. */ 546 547 static gdb_bfd_ref_ptr 548 solib_aix_bfd_open (const char *pathname) 549 { 550 /* The pathname is actually a synthetic filename with the following 551 form: "/path/to/sharedlib(member.o)" (double-quotes excluded). 552 split this into archive name and member name. 553 554 FIXME: This is a little hacky. Perhaps we should provide access 555 to the solib's lm_info here? */ 556 const int path_len = strlen (pathname); 557 const char *sep; 558 int filename_len; 559 int found_file; 560 561 if (pathname[path_len - 1] != ')') 562 return solib_bfd_open (pathname); 563 564 /* Search for the associated parens. */ 565 sep = strrchr (pathname, '('); 566 if (sep == NULL) 567 { 568 /* Should never happen, but recover as best as we can (trying 569 to open pathname without decoding, possibly leading to 570 a failure), rather than triggering an assert failure). */ 571 warning (_("missing '(' in shared object pathname: %s"), pathname); 572 return solib_bfd_open (pathname); 573 } 574 filename_len = sep - pathname; 575 576 std::string filename (string_printf ("%.*s", filename_len, pathname)); 577 std::string member_name (string_printf ("%.*s", path_len - filename_len - 2, 578 sep + 1)); 579 580 /* Calling solib_find makes certain that sysroot path is set properly 581 if program has a dependency on .a archive and sysroot is set via 582 set sysroot command. */ 583 gdb::unique_xmalloc_ptr<char> found_pathname 584 = solib_find (filename.c_str (), &found_file); 585 if (found_pathname == NULL) 586 perror_with_name (pathname); 587 gdb_bfd_ref_ptr archive_bfd (solib_bfd_fopen (found_pathname.get (), 588 found_file)); 589 if (archive_bfd == NULL) 590 { 591 warning (_("Could not open `%s' as an executable file: %s"), 592 filename.c_str (), bfd_errmsg (bfd_get_error ())); 593 return NULL; 594 } 595 596 if (bfd_check_format (archive_bfd.get (), bfd_object)) 597 return archive_bfd; 598 599 if (! bfd_check_format (archive_bfd.get (), bfd_archive)) 600 { 601 warning (_("\"%s\": not in executable format: %s."), 602 filename.c_str (), bfd_errmsg (bfd_get_error ())); 603 return NULL; 604 } 605 606 gdb_bfd_ref_ptr object_bfd 607 (gdb_bfd_openr_next_archived_file (archive_bfd.get (), NULL)); 608 while (object_bfd != NULL) 609 { 610 if (member_name == bfd_get_filename (object_bfd.get ())) 611 break; 612 613 object_bfd = gdb_bfd_openr_next_archived_file (archive_bfd.get (), 614 object_bfd.get ()); 615 } 616 617 if (object_bfd == NULL) 618 { 619 warning (_("\"%s\": member \"%s\" missing."), filename.c_str (), 620 member_name.c_str ()); 621 return NULL; 622 } 623 624 if (! bfd_check_format (object_bfd.get (), bfd_object)) 625 { 626 warning (_("%s(%s): not in object format: %s."), 627 filename.c_str (), member_name.c_str (), 628 bfd_errmsg (bfd_get_error ())); 629 return NULL; 630 } 631 632 /* Override the returned bfd's name with the name returned from solib_find 633 along with appended parenthesized member name in order to allow commands 634 listing all shared libraries to display. Otherwise, we would only be 635 displaying the name of the archive member object. */ 636 std::string fname = string_printf ("%s%s", 637 bfd_get_filename (archive_bfd.get ()), 638 sep); 639 bfd_set_filename (object_bfd.get (), fname.c_str ()); 640 641 return object_bfd; 642 } 643 644 /* Return the obj_section corresponding to OBJFILE's data section, 645 or NULL if not found. */ 646 /* FIXME: Define in a more general location? */ 647 648 static struct obj_section * 649 data_obj_section_from_objfile (struct objfile *objfile) 650 { 651 struct obj_section *osect; 652 653 ALL_OBJFILE_OSECTIONS (objfile, osect) 654 if (strcmp (bfd_section_name (osect->the_bfd_section), ".data") == 0) 655 return osect; 656 657 return NULL; 658 } 659 660 /* Return the TOC value corresponding to the given PC address, 661 or raise an error if the value could not be determined. */ 662 663 CORE_ADDR 664 solib_aix_get_toc_value (CORE_ADDR pc) 665 { 666 struct obj_section *pc_osect = find_pc_section (pc); 667 struct obj_section *data_osect; 668 CORE_ADDR result; 669 670 if (pc_osect == NULL) 671 error (_("unable to find TOC entry for pc %s " 672 "(no section contains this PC)"), 673 core_addr_to_string (pc)); 674 675 data_osect = data_obj_section_from_objfile (pc_osect->objfile); 676 if (data_osect == NULL) 677 error (_("unable to find TOC entry for pc %s " 678 "(%s has no data section)"), 679 core_addr_to_string (pc), objfile_name (pc_osect->objfile)); 680 681 result = data_osect->addr () + xcoff_get_toc_offset (pc_osect->objfile); 682 683 solib_debug_printf ("pc=%s -> %s", core_addr_to_string (pc), 684 core_addr_to_string (result)); 685 686 return result; 687 } 688 689 /* This module's normal_stop observer. */ 690 691 static void 692 solib_aix_normal_stop_observer (struct bpstat *unused_1, int unused_2) 693 { 694 struct solib_aix_inferior_data *data 695 = get_solib_aix_inferior_data (current_inferior ()); 696 697 /* The inferior execution has been resumed, and it just stopped 698 again. This means that the list of shared libraries may have 699 evolved. Reset our cached value. */ 700 data->library_list.reset (); 701 } 702 703 /* The target_so_ops for AIX targets. */ 704 const struct target_so_ops solib_aix_so_ops = 705 { 706 solib_aix_relocate_section_addresses, 707 solib_aix_free_so, 708 nullptr, 709 solib_aix_clear_solib, 710 solib_aix_solib_create_inferior_hook, 711 solib_aix_current_sos, 712 solib_aix_open_symbol_file_object, 713 solib_aix_in_dynsym_resolve_code, 714 solib_aix_bfd_open, 715 }; 716 717 void _initialize_solib_aix (); 718 void 719 _initialize_solib_aix () 720 { 721 gdb::observers::normal_stop.attach (solib_aix_normal_stop_observer, 722 "solib-aix"); 723 } 724