]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/solib-aix.c
gdb: merge solib-frv aix-solib debug options into "set/show debug solib"
[thirdparty/binutils-gdb.git] / gdb / solib-aix.c
1 /* Copyright (C) 2013-2022 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 }