diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 2f007912f4..19de94b926 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,7 @@ +2001-02-20 Kevin Buettner + + * solib-aix5.c: New file. + 2001-02-20 Martin M. Hunt * solib.c (info_sharedlibrary_command): Don't assume pointers diff --git a/gdb/solib-aix5.c b/gdb/solib-aix5.c new file mode 100644 index 0000000000..48e6b2a7fa --- /dev/null +++ b/gdb/solib-aix5.c @@ -0,0 +1,860 @@ +/* Handle AIX5 shared libraries for GDB, the GNU Debugger. + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, + 2000, 2001 + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" + +#include +#include +#include "gdb_string.h" +#include +#include +#include + +#include "elf/external.h" + +#include "symtab.h" +#include "bfd.h" +#include "symfile.h" +#include "objfiles.h" +#include "gdbcore.h" +#include "command.h" +#include "target.h" +#include "frame.h" +#include "gdb_regex.h" +#include "inferior.h" +#include "environ.h" +#include "language.h" +#include "gdbcmd.h" + +#include "solist.h" +#include "solib-svr4.h" + +/* Link map info to include in an allocated so_list entry */ + +enum maptype { + MT_READONLY = 0, + MT_READWRITE = 1, + MT_LAST = 2 +}; + +struct lm_info + { + struct + { + CORE_ADDR addr; /* base address */ + CORE_ADDR size; /* size of mapped object */ + CORE_ADDR offset; /* offset into mapped object */ + long flags; /* MA_ protection and attribute flags */ + CORE_ADDR gp; /* global pointer value */ + } mapping[MT_LAST]; + char *mapname; /* name in /proc/pid/object */ + char *pathname; /* full pathname to object */ + char *membername; /* member name in archive file */ + }; + +/* On SVR4 systems, a list of symbols in the dynamic linker where + GDB can try to place a breakpoint to monitor shared library + events. + + If none of these symbols are found, or other errors occur, then + SVR4 systems will fall back to using a symbol as the "startup + mapping complete" breakpoint address. */ + +static char *solib_break_names[] = +{ + "r_debug_state", + "_r_debug_state", + "_dl_debug_state", + "rtld_db_dlactivity", + NULL +}; + +static char *bkpt_names[] = +{ +#ifdef SOLIB_BKPT_NAME + SOLIB_BKPT_NAME, /* Prefer configured name if it exists. */ +#endif + "_start", + "main", + NULL +}; + +static void aix5_relocate_main_executable (void); + +/* + + LOCAL FUNCTION + + bfd_lookup_symbol -- lookup the value for a specific symbol + + SYNOPSIS + + CORE_ADDR bfd_lookup_symbol (bfd *abfd, char *symname) + + DESCRIPTION + + An expensive way to lookup the value of a single symbol for + bfd's that are only temporary anyway. This is used by the + shared library support to find the address of the debugger + interface structures in the shared library. + + Note that 0 is specifically allowed as an error return (no + such symbol). + */ + +static CORE_ADDR +bfd_lookup_symbol (bfd *abfd, char *symname) +{ + unsigned int storage_needed; + asymbol *sym; + asymbol **symbol_table; + unsigned int number_of_symbols; + unsigned int i; + struct cleanup *back_to; + CORE_ADDR symaddr = 0; + + storage_needed = bfd_get_symtab_upper_bound (abfd); + + if (storage_needed > 0) + { + symbol_table = (asymbol **) xmalloc (storage_needed); + back_to = make_cleanup (free, (PTR) symbol_table); + number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table); + + for (i = 0; i < number_of_symbols; i++) + { + sym = *symbol_table++; + if (STREQ (sym->name, symname)) + { + /* Bfd symbols are section relative. */ + symaddr = sym->value + sym->section->vma; + break; + } + } + do_cleanups (back_to); + } + + if (symaddr) + return symaddr; + + /* On FreeBSD, the dynamic linker is stripped by default. So we'll + have to check the dynamic string table too. */ + + storage_needed = bfd_get_dynamic_symtab_upper_bound (abfd); +/* FIXME: This problem should be addressed in BFD. */ +#define REASONABLE_LIMIT 0x400000 + if (storage_needed > REASONABLE_LIMIT) + storage_needed = REASONABLE_LIMIT; + + if (storage_needed > 0) + { + symbol_table = (asymbol **) xmalloc (storage_needed); + back_to = make_cleanup (free, (PTR) symbol_table); + number_of_symbols = bfd_canonicalize_dynamic_symtab (abfd, symbol_table); + + for (i = 0; i < number_of_symbols; i++) + { + sym = *symbol_table++; + if (STREQ (sym->name, symname)) + { + /* Bfd symbols are section relative. */ + symaddr = sym->value + sym->section->vma; + break; + } + } + do_cleanups (back_to); + } + + return symaddr; +} + + +/* Read /proc/PID/map and build a list of shared objects such that + the pr_mflags value AND'd with MATCH_MASK is equal to MATCH_VAL. + This gives us a convenient way to find all of the mappings that + don't belong to the main executable or vice versa. Here are + some of the possibilities: + + - Fetch all mappings: + MATCH_MASK: 0 + MATCH_VAL: 0 + - Fetch all mappings except for main executable: + MATCH_MASK: MA_MAINEXEC + MATCH_VAL: 0 + - Fetch only main executable: + MATCH_MASK: MA_MAINEXEC + MATCH_VAL: MA_MAINEXEC + + A cleanup chain for the list allocations done by this function should + be established prior to calling build_so_list_from_mapfile(). */ + +static struct so_list * +build_so_list_from_mapfile (int pid, long match_mask, long match_val) +{ + char *mapbuf = NULL; + struct prmap *prmap; + int mapbuf_size; + struct so_list *sos = NULL; + + { + int mapbuf_allocation_size = 8192; + char map_pathname[64]; + int map_fd; + + /* Open the map file */ + + sprintf (map_pathname, "/proc/%d/map", pid); + map_fd = open (map_pathname, O_RDONLY); + if (map_fd < 0) + return 0; + + /* Read the entire map file in */ + do + { + if (mapbuf) + { + free (mapbuf); + mapbuf_allocation_size *= 2; + lseek (map_fd, 0, SEEK_SET); + } + mapbuf = xmalloc (mapbuf_allocation_size); + mapbuf_size = read (map_fd, mapbuf, mapbuf_allocation_size); + if (mapbuf_size < 0) + { + free (mapbuf); + /* FIXME: This warrants an error or a warning of some sort */ + return 0; + } + } while (mapbuf_size == mapbuf_allocation_size); + + close (map_fd); + } + + for (prmap = (struct prmap *) mapbuf; + (char *) prmap < mapbuf + mapbuf_size; + prmap++) + { + char *mapname, *pathname, *membername; + struct so_list *sop; + enum maptype maptype; + + if (prmap->pr_size == 0) + break; + + /* Skip to the next entry if there's no path associated with the + map, unless we're looking for the kernel text region, in which + case it's okay if there's no path. */ + if ((prmap->pr_pathoff == 0 || prmap->pr_pathoff >= mapbuf_size) + && ((match_mask & MA_KERNTEXT) == 0)) + continue; + + /* Skip to the next entry if our match conditions don't hold. */ + if ((prmap->pr_mflags & match_mask) != match_val) + continue; + + mapname = prmap->pr_mapname; + if (prmap->pr_pathoff == 0) + { + pathname = ""; + membername = ""; + } + else + { + pathname = mapbuf + prmap->pr_pathoff; + membername = pathname + strlen (pathname) + 1; + } + + for (sop = sos; sop != NULL; sop = sop->next) + if (strcmp (pathname, sop->lm_info->pathname) == 0 + && strcmp (membername, sop->lm_info->membername) == 0) + break; + + if (sop == NULL) + { + sop = xcalloc (sizeof (struct so_list), 1); + make_cleanup (free, sop); + sop->lm_info = xcalloc (sizeof (struct lm_info), 1); + make_cleanup (free, sop->lm_info); + sop->lm_info->mapname = xstrdup (mapname); + make_cleanup (free, sop->lm_info->mapname); + /* FIXME: Eliminate the pathname field once length restriction + is lifted on so_name and so_original_name. */ + sop->lm_info->pathname = xstrdup (pathname); + make_cleanup (free, sop->lm_info->pathname); + sop->lm_info->membername = xstrdup (membername); + make_cleanup (free, sop->lm_info->membername); + + strncpy (sop->so_name, pathname, SO_NAME_MAX_PATH_SIZE - 1); + sop->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; + strcpy (sop->so_original_name, sop->so_name); + + sop->next = sos; + sos = sop; + } + + maptype = (prmap->pr_mflags & MA_WRITE) ? MT_READWRITE : MT_READONLY; + sop->lm_info->mapping[maptype].addr = (CORE_ADDR) prmap->pr_vaddr; + sop->lm_info->mapping[maptype].size = prmap->pr_size; + sop->lm_info->mapping[maptype].offset = prmap->pr_off; + sop->lm_info->mapping[maptype].flags = prmap->pr_mflags; + sop->lm_info->mapping[maptype].gp = (CORE_ADDR) prmap->pr_gp; + } + + free (mapbuf); + return sos; +} + +/* + + LOCAL FUNCTION + + open_symbol_file_object + + SYNOPSIS + + void open_symbol_file_object (void *from_tty) + + DESCRIPTION + + If no open symbol file, attempt to locate and open the main symbol + file. + + If FROM_TTYP dereferences to a non-zero integer, allow messages to + be printed. This parameter is a pointer rather than an int because + open_symbol_file_object() is called via catch_errors() and + catch_errors() requires a pointer argument. */ + +static int +open_symbol_file_object (void *from_ttyp) +{ + CORE_ADDR lm, l_name; + char *filename; + int errcode; + int from_tty = *(int *)from_ttyp; + struct cleanup *old_chain = make_cleanup (null_cleanup, 0); + struct so_list *sos; + + sos = build_so_list_from_mapfile (PIDGET (inferior_pid), + MA_MAINEXEC, MA_MAINEXEC); + + + if (sos == NULL) + { + warning ("Could not find name of main executable in map file"); + return 0; + } + + symbol_file_command (sos->lm_info->pathname, from_tty); + + do_cleanups (old_chain); + + aix5_relocate_main_executable (); + + return 1; +} + +/* LOCAL FUNCTION + + aix5_current_sos -- build a list of currently loaded shared objects + + SYNOPSIS + + struct so_list *aix5_current_sos () + + DESCRIPTION + + Build a list of `struct so_list' objects describing the shared + objects currently loaded in the inferior. This list does not + include an entry for the main executable file. + + Note that we only gather information directly available from the + inferior --- we don't examine any of the shared library files + themselves. The declaration of `struct so_list' says which fields + we provide values for. */ + +static struct so_list * +aix5_current_sos (void) +{ + struct cleanup *old_chain = make_cleanup (null_cleanup, 0); + struct so_list *sos; + + /* Fetch the list of mappings, excluding the main executable. */ + sos = build_so_list_from_mapfile (PIDGET (inferior_pid), MA_MAINEXEC, 0); + + /* Reverse the list; it looks nicer when we print it if the mappings + are in the same order as in the map file. */ + if (sos) + { + struct so_list *next = sos->next; + + sos->next = 0; + while (next) + { + struct so_list *prev = sos; + + sos = next; + next = next->next; + sos->next = prev; + } + } + discard_cleanups (old_chain); + return sos; +} + + +/* Return 1 if PC lies in the dynamic symbol resolution code of the + SVR4 run time loader. */ + +static CORE_ADDR interp_text_sect_low; +static CORE_ADDR interp_text_sect_high; +static CORE_ADDR interp_plt_sect_low; +static CORE_ADDR interp_plt_sect_high; + +/* FIXME: Does this belong here? (If it does, it ought to be renamed.) */ +int +in_svr4_dynsym_resolve_code (CORE_ADDR pc) +{ + return ((pc >= interp_text_sect_low && pc < interp_text_sect_high) + || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high) + || in_plt_section (pc, NULL)); +} + +/* + + LOCAL FUNCTION + + enable_break -- arrange for dynamic linker to hit breakpoint + + SYNOPSIS + + int enable_break (void) + + DESCRIPTION + + Both the SunOS and the SVR4 dynamic linkers have, as part of their + debugger interface, support for arranging for the inferior to hit + a breakpoint after mapping in the shared libraries. This function + enables that breakpoint. + + For SunOS, there is a special flag location (in_debugger) which we + set to 1. When the dynamic linker sees this flag set, it will set + a breakpoint at a location known only to itself, after saving the + original contents of that place and the breakpoint address itself, + in it's own internal structures. When we resume the inferior, it + will eventually take a SIGTRAP when it runs into the breakpoint. + We handle this (in a different place) by restoring the contents of + the breakpointed location (which is only known after it stops), + chasing around to locate the shared libraries that have been + loaded, then resuming. + + For SVR4, the debugger interface structure contains a member (r_brk) + which is statically initialized at the time the shared library is + built, to the offset of a function (_r_debug_state) which is guaran- + teed to be called once before mapping in a library, and again when + the mapping is complete. At the time we are examining this member, + it contains only the unrelocated offset of the function, so we have + to do our own relocation. Later, when the dynamic linker actually + runs, it relocates r_brk to be the actual address of _r_debug_state(). + + The debugger interface structure also contains an enumeration which + is set to either RT_ADD or RT_DELETE prior to changing the mapping, + depending upon whether or not the library is being mapped or unmapped, + and then set to RT_CONSISTENT after the library is mapped/unmapped. + */ + +static int +enable_break (void) +{ + int success = 0; + + struct minimal_symbol *msymbol; + char **bkpt_namep; + asection *interp_sect; + + /* First, remove all the solib event breakpoints. Their addresses + may have changed since the last time we ran the program. */ + remove_solib_event_breakpoints (); + + interp_text_sect_low = interp_text_sect_high = 0; + interp_plt_sect_low = interp_plt_sect_high = 0; + + /* Find the .interp section; if not found, warn the user and drop + into the old breakpoint at symbol code. */ + interp_sect = bfd_get_section_by_name (exec_bfd, ".interp"); + if (interp_sect) + { + unsigned int interp_sect_size; + char *buf; + CORE_ADDR load_addr; + bfd *tmp_bfd; + CORE_ADDR sym_addr = 0; + + /* Read the contents of the .interp section into a local buffer; + the contents specify the dynamic linker this program uses. */ + interp_sect_size = bfd_section_size (exec_bfd, interp_sect); + buf = alloca (interp_sect_size); + bfd_get_section_contents (exec_bfd, interp_sect, + buf, 0, interp_sect_size); + + /* Now we need to figure out where the dynamic linker was + loaded so that we can load its symbols and place a breakpoint + in the dynamic linker itself. + + This address is stored on the stack. However, I've been unable + to find any magic formula to find it for Solaris (appears to + be trivial on GNU/Linux). Therefore, we have to try an alternate + mechanism to find the dynamic linker's base address. */ + tmp_bfd = bfd_openr (buf, gnutarget); + if (tmp_bfd == NULL) + goto bkpt_at_symbol; + + /* Make sure the dynamic linker's really a useful object. */ + if (!bfd_check_format (tmp_bfd, bfd_object)) + { + warning ("Unable to grok dynamic linker %s as an object file", buf); + bfd_close (tmp_bfd); + goto bkpt_at_symbol; + } + + /* We find the dynamic linker's base address by examining the + current pc (which point at the entry point for the dynamic + linker) and subtracting the offset of the entry point. */ + load_addr = read_pc () - tmp_bfd->start_address; + + /* Record the relocated start and end address of the dynamic linker + text and plt section for in_aix5_dynsym_resolve_code. */ + interp_sect = bfd_get_section_by_name (tmp_bfd, ".text"); + if (interp_sect) + { + interp_text_sect_low = + bfd_section_vma (tmp_bfd, interp_sect) + load_addr; + interp_text_sect_high = + interp_text_sect_low + bfd_section_size (tmp_bfd, interp_sect); + } + interp_sect = bfd_get_section_by_name (tmp_bfd, ".plt"); + if (interp_sect) + { + interp_plt_sect_low = + bfd_section_vma (tmp_bfd, interp_sect) + load_addr; + interp_plt_sect_high = + interp_plt_sect_low + bfd_section_size (tmp_bfd, interp_sect); + } + + /* Now try to set a breakpoint in the dynamic linker. */ + for (bkpt_namep = solib_break_names; *bkpt_namep != NULL; bkpt_namep++) + { + sym_addr = bfd_lookup_symbol (tmp_bfd, *bkpt_namep); + if (sym_addr != 0) + break; + } + + /* We're done with the temporary bfd. */ + bfd_close (tmp_bfd); + + if (sym_addr != 0) + { + create_solib_event_breakpoint (load_addr + sym_addr); + return 1; + } + + /* For whatever reason we couldn't set a breakpoint in the dynamic + linker. Warn and drop into the old code. */ + bkpt_at_symbol: + warning ("Unable to find dynamic linker breakpoint function.\nGDB will be unable to debug shared library initializers\nand track explicitly loaded dynamic code."); + } + + /* Scan through the list of symbols, trying to look up the symbol and + set a breakpoint there. Terminate loop when we/if we succeed. */ + + for (bkpt_namep = bkpt_names; *bkpt_namep != NULL; bkpt_namep++) + { + msymbol = lookup_minimal_symbol (*bkpt_namep, NULL, symfile_objfile); + if ((msymbol != NULL) && (SYMBOL_VALUE_ADDRESS (msymbol) != 0)) + { + create_solib_event_breakpoint (SYMBOL_VALUE_ADDRESS (msymbol)); + return 1; + } + } + + /* Nothing good happened. */ + success = 0; + + return (success); +} + +/* + + LOCAL FUNCTION + + special_symbol_handling -- additional shared library symbol handling + + SYNOPSIS + + void special_symbol_handling () + + DESCRIPTION + + Once the symbols from a shared object have been loaded in the usual + way, we are called to do any system specific symbol handling that + is needed. + + */ + +static void +aix5_special_symbol_handling (void) +{ + /* Nothing needed (yet) for AIX5. */ +} + +#define SECTMAPMASK (~ (CORE_ADDR) 0x03ffffff) + +static void +aix5_relocate_main_executable (void) +{ + struct so_list *so; + struct section_offsets *new_offsets; + int i; + int changed = 0; + struct cleanup *old_chain = make_cleanup (null_cleanup, 0); + + /* Fetch the mappings for the main executable from the map file. */ + so = build_so_list_from_mapfile (PIDGET (inferior_pid), + MA_MAINEXEC, MA_MAINEXEC); + + /* Make sure we actually have some mappings to work with. */ + if (so == NULL) + { + warning ("Could not find main executable in map file"); + do_cleanups (old_chain); + return; + } + + /* Allocate the data structure which'll contain the new offsets to + relocate by. Initialize it so it contains the current offsets. */ + new_offsets = xcalloc (sizeof (struct section_offsets), + symfile_objfile->num_sections); + make_cleanup (free, new_offsets); + for (i = 0; i < symfile_objfile->num_sections; i++) + ANOFFSET (new_offsets, i) = ANOFFSET (symfile_objfile->section_offsets, i); + + /* Iterate over the mappings in the main executable and compute + the new offset value as appropriate. */ + for (i = 0; i < MT_LAST; i++) + { + CORE_ADDR increment = 0; + struct obj_section *sect; + bfd *obfd = symfile_objfile->obfd; + + ALL_OBJFILE_OSECTIONS (symfile_objfile, sect) + { + int flags = bfd_get_section_flags (obfd, sect->the_bfd_section); + if (flags & SEC_ALLOC) + { + if (((so->lm_info->mapping[i].flags & MA_WRITE) == 0) + == ((flags & SEC_READONLY) != 0)) + { + int idx = sect->the_bfd_section->index; + + if (increment == 0) + increment = so->lm_info->mapping[i].addr + - (bfd_section_vma (obfd, sect->the_bfd_section) + & SECTMAPMASK); + + if (increment != ANOFFSET (new_offsets, idx)) + { + ANOFFSET (new_offsets, idx) = increment; + changed = 1; + } + } + } + } + } + + /* If any of the offsets have changed, then relocate the objfile. */ + if (changed) + objfile_relocate (symfile_objfile, new_offsets); + + /* Free up all the space we've allocated. */ + do_cleanups (old_chain); +} + +/* + + GLOBAL FUNCTION + + aix5_solib_create_inferior_hook -- shared library startup support + + SYNOPSIS + + void aix5_solib_create_inferior_hook() + + DESCRIPTION + + When gdb starts up the inferior, it nurses it along (through the + shell) until it is ready to execute it's first instruction. At this + point, this function gets called via expansion of the macro + SOLIB_CREATE_INFERIOR_HOOK. + + For SVR4 executables, this first instruction is either the first + instruction in the dynamic linker (for dynamically linked + executables) or the instruction at "start" for statically linked + executables. For dynamically linked executables, the system + first exec's /lib/libc.so.N, which contains the dynamic linker, + and starts it running. The dynamic linker maps in any needed + shared libraries, maps in the actual user executable, and then + jumps to "start" in the user executable. + + */ + +static void +aix5_solib_create_inferior_hook (void) +{ + aix5_relocate_main_executable (); + + if (!enable_break ()) + { + warning ("shared library handler failed to enable breakpoint"); + return; + } +} + +static void +aix5_clear_solib (void) +{ +} + +static void +aix5_free_so (struct so_list *so) +{ + free (so->lm_info->mapname); + free (so->lm_info->pathname); + free (so->lm_info->membername); + free (so->lm_info); +} + +static void +aix5_relocate_section_addresses (struct so_list *so, + struct section_table *sec) +{ + int flags = bfd_get_section_flags (sec->bfd, sec->the_bfd_section); + + if (flags & SEC_ALLOC) + { + int idx = (flags & SEC_READONLY) ? MT_READONLY : MT_READWRITE; + CORE_ADDR addr = so->lm_info->mapping[idx].addr; + + sec->addr += addr; + sec->endaddr += addr; + } +} + +/* Find the global pointer for the given function address ADDR. */ + +static CORE_ADDR +aix5_find_global_pointer (CORE_ADDR addr) +{ + struct so_list *sos, *so; + CORE_ADDR global_pointer = 0; + struct cleanup *old_chain = make_cleanup (null_cleanup, 0); + + sos = build_so_list_from_mapfile (PIDGET (inferior_pid), 0, 0); + + for (so = sos; so != NULL; so = so->next) + { + if (so->lm_info->mapping[MT_READONLY].addr <= addr + && addr <= so->lm_info->mapping[MT_READONLY].addr + + so->lm_info->mapping[MT_READONLY].size) + { + global_pointer = so->lm_info->mapping[MT_READWRITE].gp; + break; + } + } + + do_cleanups (old_chain); + + return global_pointer; +} + +/* Find the execute-only kernel region known as the gate page. This + page is where the signal trampoline lives. It may be found by + querying the map file and looking for the MA_KERNTEXT flag. */ +static void +aix5_find_gate_addresses (CORE_ADDR *start, CORE_ADDR *end) +{ + struct so_list *so; + struct cleanup *old_chain = make_cleanup (null_cleanup, 0); + + /* Fetch the mappings for the main executable from the map file. */ + so = build_so_list_from_mapfile (PIDGET (inferior_pid), + MA_KERNTEXT, MA_KERNTEXT); + + /* Make sure we actually have some mappings to work with. */ + if (so == NULL) + { + warning ("Could not find gate page in map file"); + *start = 0; + *end = 0; + do_cleanups (old_chain); + return; + } + + /* There should only be on kernel mapping for the gate page and + it'll be in the read-only (even though it's execute-only) + mapping in the lm_info struct. */ + + *start = so->lm_info->mapping[MT_READONLY].addr; + *end = *start + so->lm_info->mapping[MT_READONLY].size; + + /* Free up all the space we've allocated. */ + do_cleanups (old_chain); +} + +/* From ia64-tdep.c. FIXME: If we end up using this for rs6000 too, + we'll need to make the names match. */ +extern CORE_ADDR (*native_find_global_pointer) (CORE_ADDR); + +/* From ia64-aix-tdep.c. Hook for finding the starting and + ending gate page addresses. The only reason that this hook + is in this file is because this is where the map file reading + code is located. */ +extern void (*aix5_find_gate_addresses_hook) (CORE_ADDR *, CORE_ADDR *); + +static struct target_so_ops aix5_so_ops; + +void +_initialize_aix5_solib (void) +{ + aix5_so_ops.relocate_section_addresses = aix5_relocate_section_addresses; + aix5_so_ops.free_so = aix5_free_so; + aix5_so_ops.clear_solib = aix5_clear_solib; + aix5_so_ops.solib_create_inferior_hook = aix5_solib_create_inferior_hook; + aix5_so_ops.special_symbol_handling = aix5_special_symbol_handling; + aix5_so_ops.current_sos = aix5_current_sos; + aix5_so_ops.open_symbol_file_object = open_symbol_file_object; + + native_find_global_pointer = aix5_find_global_pointer; + aix5_find_gate_addresses_hook = aix5_find_gate_addresses; + + /* FIXME: Don't do this here. *_gdbarch_init() should set so_ops. */ + current_target_so_ops = &aix5_so_ops; +} +