diff options
Diffstat (limited to 'gdb/pa64solib.c')
-rw-r--r-- | gdb/pa64solib.c | 1327 |
1 files changed, 1327 insertions, 0 deletions
diff --git a/gdb/pa64solib.c b/gdb/pa64solib.c new file mode 100644 index 0000000..aa11f24 --- /dev/null +++ b/gdb/pa64solib.c @@ -0,0 +1,1327 @@ +/* Handle HP ELF shared libraries for GDB, the GNU Debugger. + Copyright 1999 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. + + HP in their infinite stupidity choose not to use standard ELF dynamic + linker interfaces. They also choose not to make their ELF dymamic + linker interfaces compatible with the SOM dynamic linker. The + net result is we can not use either of the existing somsolib.c or + solib.c. What a crock. + + Even more disgusting. This file depends on functions provided only + in certain PA64 libraries. Thus this file is supposed to only be + used native. When will HP ever learn that they need to provide the + same functionality in all their libraries! */ + +#include <dlfcn.h> +#include <elf.h> +#include <elf_hp.h> + +#include "defs.h" + +#include "frame.h" +#include "bfd.h" +#include "libhppa.h" +#include "gdbcore.h" +#include "symtab.h" +#include "breakpoint.h" +#include "symfile.h" +#include "objfiles.h" +#include "inferior.h" +#include "gdb-stabs.h" +#include "gdb_stat.h" +#include "gdbcmd.h" +#include "assert.h" +#include "language.h" + +#include <fcntl.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* Defined in exec.c; used to prevent dangling pointer bug. */ +extern struct target_ops exec_ops; + +static CORE_ADDR + bfd_lookup_symbol PARAMS ((bfd *, char *)); +/* This lives in hppa-tdep.c. */ +extern struct unwind_table_entry *find_unwind_entry PARAMS ((CORE_ADDR pc)); + +/* These ought to be defined in some public interface, but aren't. They + identify dynamic linker events. */ +#define DLD_CB_LOAD 1 +#define DLD_CB_UNLOAD 0 + +/* A structure to keep track of all the known shared objects. */ +struct so_list + { + bfd *abfd; + char *name; + struct so_list *next; + struct objfile *objfile; + CORE_ADDR pa64_solib_desc_addr; + struct load_module_desc pa64_solib_desc; + struct section_table *sections; + struct section_table *sections_end; + boolean loaded; + }; + +static struct so_list *so_list_head; + +/* This is the cumulative size in bytes of the symbol tables of all + shared objects on the so_list_head list. (When we say size, here + we mean of the information before it is brought into memory and + potentially expanded by GDB.) When adding a new shlib, this value + is compared against the threshold size, held by auto_solib_add + (in megabytes). If adding symbols for the new shlib would cause + the total size to exceed the threshold, then the new shlib's symbols + are not loaded. */ +static LONGEST pa64_solib_total_st_size; + +/* When the threshold is reached for any shlib, we refuse to add + symbols for subsequent shlibs, even if those shlibs' symbols would + be small enough to fit under the threshold. (Although this may + result in one, early large shlib preventing the loading of later, + smalller shlibs' symbols, it allows us to issue one informational + message. The alternative, to issue a message for each shlib whose + symbols aren't loaded, could be a big annoyance where the threshold + is exceeded due to a very large number of shlibs.) */ +static int pa64_solib_st_size_threshold_exceeded; + +/* When adding fields, be sure to clear them in _initialize_pa64_solib. */ +typedef struct + { + CORE_ADDR dld_flags_addr; + long long dld_flags; + sec_ptr dyninfo_sect; + boolean have_read_dld_descriptor; + boolean is_valid; + CORE_ADDR load_map; + CORE_ADDR load_map_addr; + struct load_module_desc dld_desc; + } +dld_cache_t; + +static dld_cache_t dld_cache; + +static void pa64_sharedlibrary_info_command PARAMS ((char *, int)); + +static void pa64_solib_sharedlibrary_command PARAMS ((char *, int)); + +static void * pa64_target_read_memory PARAMS ((void *, CORE_ADDR, size_t, int)); + +static boolean read_dld_descriptor PARAMS ((struct target_ops *)); + +static boolean read_dynamic_info PARAMS ((asection *, dld_cache_t *)); + +static void add_to_solist PARAMS ((boolean, char *, struct load_module_desc *, + CORE_ADDR, struct target_ops *)); + +/* When examining the shared library for debugging information we have to + look for HP debug symbols, stabs and dwarf2 debug symbols. */ +static char *pa64_debug_section_names[] = { + ".debug_header", ".debug_gntt", ".debug_lntt", ".debug_slt", ".debug_vt", + ".stabs", ".stabstr", ".debug_info", ".debug_abbrev", ".debug_aranges", + ".debug_macinfo", ".debug_line", ".debug_loc", ".debug_pubnames", + ".debug_str", NULL +}; + +/* Return a ballbark figure for the amount of memory GDB will need to + allocate to read in the debug symbols from FILENAME. */ +static LONGEST +pa64_solib_sizeof_symbol_table (filename) + char *filename; +{ + bfd *abfd; + int i; + int desc; + char *absolute_name; + LONGEST st_size = (LONGEST) 0; + asection *sect; + + /* We believe that filename was handed to us by the dynamic linker, and + is therefore always an absolute path. */ + desc = openp (getenv ("PATH"), 1, filename, O_RDONLY | O_BINARY, + 0, &absolute_name); + if (desc < 0) + { + perror_with_name (filename); + } + filename = absolute_name; + + abfd = bfd_fdopenr (filename, gnutarget, desc); + if (!abfd) + { + close (desc); + make_cleanup (free, filename); + error ("\"%s\": can't open to read symbols: %s.", filename, + bfd_errmsg (bfd_get_error ())); + } + + if (!bfd_check_format (abfd, bfd_object)) + { + bfd_close (abfd); + make_cleanup (free, filename); + error ("\"%s\": can't read symbols: %s.", filename, + bfd_errmsg (bfd_get_error ())); + } + + /* Sum the sizes of the various sections that compose debug info. */ + for (i = 0; pa64_debug_section_names[i] != NULL; i++) + { + asection *sect; + + sect = bfd_get_section_by_name (abfd, pa64_debug_section_names[i]); + if (sect) + st_size += (LONGEST)bfd_section_size (abfd, sect); + } + + bfd_close (abfd); + free (filename); + + /* Unfortunately, just summing the sizes of various debug info + sections isn't a very accurate measurement of how much heap + space the debugger will need to hold them. It also doesn't + account for space needed by linker (aka "minimal") symbols. + + Anecdotal evidence suggests that just summing the sizes of + debug-info-related sections understates the heap space needed + to represent it internally by about an order of magnitude. + + Since it's not exactly brain surgery we're doing here, rather + than attempt to more accurately measure the size of a shlib's + symbol table in GDB's heap, we'll just apply a 10x fudge- + factor to the debug info sections' size-sum. No, this doesn't + account for minimal symbols in non-debuggable shlibs. But it + all roughly washes out in the end. */ + return st_size * (LONGEST) 10; +} + +/* Add a shared library to the objfile list and load its symbols into + GDB's symbol table. */ +static void +pa64_solib_add_solib_objfile (so, name, from_tty, text_addr) + struct so_list *so; + char *name; + int from_tty; + CORE_ADDR text_addr; +{ + bfd *tmp_bfd; + asection *sec; + obj_private_data_t *obj_private; + + /* We need the BFD so that we can look at its sections. We open up the + file temporarily, then close it when we are done. */ + tmp_bfd = bfd_openr (name, gnutarget); + if (tmp_bfd == NULL) + { + perror_with_name (name); + return; + } + + if (!bfd_check_format (tmp_bfd, bfd_object)) + { + bfd_close (tmp_bfd); + error ("\"%s\" is not an object file: %s", name, + bfd_errmsg (bfd_get_error ())); + } + + + /* Undo some braindamage from symfile.c. + + First, symfile.c will subtract the VMA of the first .text section + in the shared library that it finds. Undo that. */ + sec = bfd_get_section_by_name (tmp_bfd, ".text"); + text_addr += bfd_section_vma (tmp_bfd, sec); + + /* Now find the true lowest section in the shared library. */ + sec = NULL; + bfd_map_over_sections (tmp_bfd, find_lowest_section, (PTR) &sec); + + if (sec) + { + /* Subtract out the VMA of the lowest section. */ + text_addr -= bfd_section_vma (tmp_bfd, sec); + + /* ??? Add back in the filepos of that lowest section. */ + text_addr += sec->filepos; + } + + /* We are done with the temporary bfd. Get rid of it and make sure + nobody else can us it. */ + bfd_close (tmp_bfd); + tmp_bfd = NULL; + + /* Now let the generic code load up symbols for this library. */ + so->objfile = symbol_file_add (name, from_tty, text_addr, 0, 0, 0, 0, 1); + so->abfd = so->objfile->obfd; + + /* Mark this as a shared library and save private data. */ + so->objfile->flags |= OBJF_SHARED; + + if (so->objfile->obj_private == NULL) + { + obj_private = (obj_private_data_t *) + obstack_alloc (&so->objfile->psymbol_obstack, + sizeof (obj_private_data_t)); + obj_private->unwind_info = NULL; + obj_private->so_info = NULL; + so->objfile->obj_private = (PTR) obj_private; + } + + obj_private = (obj_private_data_t *) so->objfile->obj_private; + obj_private->so_info = so; + obj_private->dp = so->pa64_solib_desc.linkage_ptr; +} + +/* Load debugging information for a shared library. TARGET may be + NULL if we are not attaching to a process or reading a core file. */ + +static void +pa64_solib_load_symbols (so, name, from_tty, text_addr, target) + struct so_list *so; + char *name; + int from_tty; + CORE_ADDR text_addr; + struct target_ops *target; +{ + struct section_table *p; + asection *sec; + int status; + char buf[4]; + CORE_ADDR presumed_data_start; + + if (text_addr == 0) + text_addr = so->pa64_solib_desc.text_base; + + pa64_solib_add_solib_objfile (so, name, from_tty, text_addr); + + /* Now we need to build a section table for this library since + we might be debugging a core file from a dynamically linked + executable in which the libraries were not privately mapped. */ + if (build_section_table (so->abfd, + &so->sections, + &so->sections_end)) + { + error ("Unable to build section table for shared library\n."); + return; + } + + ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT) + = so->pa64_solib_desc.text_base; + ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA) + = so->pa64_solib_desc.data_base; + + /* Relocate all the sections based on where they got loaded. */ + for (p = so->sections; p < so->sections_end; p++) + { + if (p->the_bfd_section->flags & SEC_CODE) + { + p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT); + p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT); + } + else if (p->the_bfd_section->flags & SEC_DATA) + { + p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA); + p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA); + } + } + + /* Now see if we need to map in the text and data for this shared + library (for example debugging a core file which does not use + private shared libraries.). + + Carefully peek at the first text address in the library. If the + read succeeds, then the libraries were privately mapped and were + included in the core dump file. + + If the peek failed, then the libraries were not privately mapped + and are not in the core file, we'll have to read them in ourselves. */ + status = target_read_memory (text_addr, buf, 4); + if (status != 0) + { + int old, new; + int update_coreops; + int update_execops; + + /* We must update the to_sections field in the core_ops structure + here, otherwise we dereference a potential dangling pointer + for each call to target_read/write_memory within this routine. */ + update_coreops = core_ops.to_sections == target->to_sections; + + /* Ditto exec_ops (this was a bug). */ + update_execops = exec_ops.to_sections == target->to_sections; + + new = so->sections_end - so->sections; + /* Add sections from the shared library to the core target. */ + if (target->to_sections) + { + old = target->to_sections_end - target->to_sections; + target->to_sections = (struct section_table *) + xrealloc ((char *) target->to_sections, + ((sizeof (struct section_table)) * (old + new))); + } + else + { + old = 0; + target->to_sections = (struct section_table *) + xmalloc ((sizeof (struct section_table)) * new); + } + target->to_sections_end = (target->to_sections + old + new); + + /* Update the to_sections field in the core_ops structure + if needed, ditto exec_ops. */ + if (update_coreops) + { + core_ops.to_sections = target->to_sections; + core_ops.to_sections_end = target->to_sections_end; + } + + if (update_execops) + { + exec_ops.to_sections = target->to_sections; + exec_ops.to_sections_end = target->to_sections_end; + } + + /* Copy over the old data before it gets clobbered. */ + memcpy ((char *) (target->to_sections + old), + so->sections, + ((sizeof (struct section_table)) * new)); + } +} + + +/* Add symbols from shared libraries into the symtab list, unless the + size threshold (specified by auto_solib_add, in megabytes) would + be exceeded. */ + +void +pa64_solib_add (arg_string, from_tty, target) + char *arg_string; + int from_tty; + struct target_ops *target; +{ + struct minimal_symbol *msymbol; + CORE_ADDR addr; + asection *shlib_info; + int status; + unsigned int dld_flags; + char buf[4], *re_err; + int threshold_warning_given = 0; + int dll_index; + struct load_module_desc dll_desc; + char *dll_path; + + /* First validate our arguments. */ + if ((re_err = re_comp (arg_string ? arg_string : ".")) != NULL) + { + error ("Invalid regexp: %s", re_err); + } + + /* If we're debugging a core file, or have attached to a running + process, then pa64_solib_create_inferior_hook will not have been + called. + + We need to first determine if we're dealing with a dynamically + linked executable. If not, then return without an error or warning. + + We also need to examine __dld_flags to determine if the shared library + list is valid and to determine if the libraries have been privately + mapped. */ + if (symfile_objfile == NULL) + return; + + /* First see if the objfile was dynamically linked. */ + shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, ".dynamic"); + if (!shlib_info) + return; + + /* It's got a .dynamic section, make sure it's not empty. */ + if (bfd_section_size (symfile_objfile->obfd, shlib_info) == 0) + return; + + /* Read in the load map pointer if we have not done so already. */ + if (! dld_cache.have_read_dld_descriptor) + if (! read_dld_descriptor (target)) + return; + + /* If the libraries were not mapped private, warn the user. */ + if ((dld_cache.dld_flags & DT_HP_DEBUG_PRIVATE) == 0) + warning ("The shared libraries were not privately mapped; setting a\nbreakpoint in a shared library will not work until you rerun the program.\n"); + + /* For each shaerd library, add it to the shared library list. */ + for (dll_index = 1; ; dll_index++) + { + /* Read in the load module descriptor. */ + if (dlgetmodinfo (dll_index, &dll_desc, sizeof (dll_desc), + pa64_target_read_memory, 0, dld_cache.load_map) + == 0) + return; + + /* Get the name of the shared library. */ + dll_path = (char *)dlgetname (&dll_desc, sizeof (dll_desc), + pa64_target_read_memory, + 0, dld_cache.load_map); + + if (!dll_path) + error ("pa64_solib_add, unable to read shared library path."); + + add_to_solist (from_tty, dll_path, &dll_desc, 0, target); + } +} + + +/* This hook gets called just before the first instruction in the + inferior process is executed. + + This is our opportunity to set magic flags in the inferior so + that GDB can be notified when a shared library is mapped in and + to tell the dynamic linker that a private copy of the library is + needed (so GDB can set breakpoints in the library). + + We need to set two flag bits in this routine. + + DT_HP_DEBUG_PRIVATE to indicate that shared libraries should be + mapped private. + + DT_HP_DEBUG_CALLBACK to indicate that we want the dynamic linker to + call the breakpoint routine for significant events. */ + +void +pa64_solib_create_inferior_hook () +{ + struct minimal_symbol *msymbol; + unsigned int dld_flags, status; + asection *shlib_info, *interp_sect; + char buf[4]; + struct objfile *objfile; + CORE_ADDR anaddr; + + /* First, remove all the solib event breakpoints. Their addresses + may have changed since the last time we ran the program. */ + remove_solib_event_breakpoints (); + + if (symfile_objfile == NULL) + return; + + /* First see if the objfile was dynamically linked. */ + shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, ".dynamic"); + if (!shlib_info) + return; + + /* It's got a .dynamic section, make sure it's not empty. */ + if (bfd_section_size (symfile_objfile->obfd, shlib_info) == 0) + return; + + /* Slam the pid of the process into __d_pid; failing is only a warning! */ + msymbol = lookup_minimal_symbol ("__d_pid", NULL, symfile_objfile); + if (msymbol == NULL) + { + warning ("Unable to find __d_pid symbol in object file."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track explicit library load/unload calls"); + goto keep_going; + } + + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + store_unsigned_integer (buf, 4, inferior_pid); + status = target_write_memory (anaddr, buf, 4); + if (status != 0) + { + warning ("Unable to write __d_pid"); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track explicit library load/unload calls"); + goto keep_going; + } + +keep_going: + + /* Read in the .dynamic section. */ + if (! read_dynamic_info (shlib_info, &dld_cache)) + error ("Unable to read the .dynamic section."); + + /* Turn on the flags we care about. */ + dld_cache.dld_flags |= DT_HP_DEBUG_PRIVATE; + /* ?!? Right now GDB is not recognizing hitting the callback breakpoint + as a shared library event. Fix that and remove the #if0 code. */ +#if 0 + dld_cache.dld_flags |= DT_HP_DEBUG_CALLBACK; +#endif + status = target_write_memory (dld_cache.dld_flags_addr, + (char *) &dld_cache.dld_flags, + sizeof (dld_cache.dld_flags)); + if (status != 0) + error ("Unable to modify dynamic linker flags."); + + /* Now we have to create a shared library breakpoint in the dynamic + linker. This can be somewhat tricky since the symbol is inside + the dynamic linker (for which we do not have symbols or a base + load address! Luckily I wrote this code for solib.c years ago. */ + 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 get_out; + + /* 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 get_out; + } + + /* 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. + + Also note the breakpoint is the second instruction in the + routine. */ + load_addr = read_pc () - tmp_bfd->start_address; + sym_addr = bfd_lookup_symbol (tmp_bfd, "__dld_break"); + sym_addr = load_addr + sym_addr + 4; + + /* Create the shared library breakpoint. */ + create_solib_event_breakpoint (sym_addr); + + /* We're done with the temporary bfd. */ + bfd_close (tmp_bfd); + } + +get_out: + /* Wipe out all knowledge of old shared libraries since their + mapping can change from one exec to another! */ + while (so_list_head) + { + struct so_list *temp; + + temp = so_list_head; + free (so_list_head); + so_list_head = temp->next; + } + clear_symtab_users (); +} + +/* This operation removes the "hook" between GDB and the dynamic linker, + which causes the dld to notify GDB of shared library events. + + After this operation completes, the dld will no longer notify GDB of + shared library events. To resume notifications, GDB must call + pa64_solib_create_inferior_hook. + + This operation does not remove any knowledge of shared libraries which + GDB may already have been notified of. */ + +void +pa64_solib_remove_inferior_hook (pid) + int pid; +{ + /* Turn off the DT_HP_DEBUG_CALLBACK bit in the dynamic linker flags. */ + dld_cache.dld_flags &= ~DT_HP_DEBUG_CALLBACK; + target_write_memory (dld_cache.dld_flags_addr, + (char *)&dld_cache.dld_flags, + sizeof (dld_cache.dld_flags)); +} + +/* This function creates a breakpoint on the dynamic linker hook, which + is called when e.g., a shl_load or shl_unload call is made. This + breakpoint will only trigger when a shl_load call is made. + + If filename is NULL, then loads of any dll will be caught. Else, + only loads of the file whose pathname is the string contained by + filename will be caught. + + Undefined behaviour is guaranteed if this function is called before + pa64_solib_create_inferior_hook. */ + +void +pa64_solib_create_catch_load_hook (pid, tempflag, filename, cond_string) + int pid; + int tempflag; + char *filename; + char *cond_string; +{ + create_solib_load_event_breakpoint ("", tempflag, filename, cond_string); +} + +/* This function creates a breakpoint on the dynamic linker hook, which + is called when e.g., a shl_load or shl_unload call is made. This + breakpoint will only trigger when a shl_unload call is made. + + If filename is NULL, then unloads of any dll will be caught. Else, + only unloads of the file whose pathname is the string contained by + filename will be caught. + + Undefined behaviour is guaranteed if this function is called before + pa64_solib_create_inferior_hook. */ + +void +pa64_solib_create_catch_unload_hook (pid, tempflag, filename, cond_string) + int pid; + int tempflag; + char *filename; + char *cond_string; +{ + create_solib_unload_event_breakpoint ("", tempflag, filename, cond_string); +} + +/* Return nonzero if the dynamic linker has reproted that a library + has been loaded. */ + +int +pa64_solib_have_load_event (pid) + int pid; +{ + CORE_ADDR event_kind; + + event_kind = read_register (ARG0_REGNUM); + return (event_kind == DLD_CB_LOAD); +} + +/* Return nonzero if the dynamic linker has reproted that a library + has been unloaded. */ +int +pa64_solib_have_unload_event (pid) + int pid; +{ + CORE_ADDR event_kind; + + event_kind = read_register (ARG0_REGNUM); + return (event_kind == DLD_CB_UNLOAD); +} + +/* Return a pointer to a string indicating the pathname of the most + recently loaded library. + + The caller is reposible for copying the string before the inferior is + restarted. */ + +char * +pa64_solib_loaded_library_pathname (pid) + int pid; +{ + static char dll_path[MAXPATHLEN]; + CORE_ADDR dll_path_addr = read_register (ARG3_REGNUM); + read_memory_string (dll_path_addr, dll_path, MAXPATHLEN); + return dll_path; +} + +/* Return a pointer to a string indicating the pathname of the most + recently unloaded library. + + The caller is reposible for copying the string before the inferior is + restarted. */ + +char * +pa64_solib_unloaded_library_pathname (pid) + int pid; +{ + static char dll_path[MAXPATHLEN]; + CORE_ADDR dll_path_addr = read_register (ARG3_REGNUM); + read_memory_string (dll_path_addr, dll_path, MAXPATHLEN); + return dll_path; +} + +/* Return nonzero if PC is an address inside the dynamic linker. */ + +int +pa64_solib_in_dynamic_linker (pid, pc) + int pid; + CORE_ADDR pc; +{ + asection *shlib_info; + + if (symfile_objfile == NULL) + return 0; + + if (!dld_cache.have_read_dld_descriptor) + if (!read_dld_descriptor (¤t_target)) + return 0; + + return (pc >= dld_cache.dld_desc.text_base + && pc < dld_cache.dld_desc.text_base + dld_cache.dld_desc.text_size); +} + + +/* Return the GOT value for the shared library in which ADDR belongs. If + ADDR isn't in any known shared library, return zero. */ + +CORE_ADDR +pa64_solib_get_got_by_pc (addr) + CORE_ADDR addr; +{ + struct so_list *so_list = so_list_head; + CORE_ADDR got_value = 0; + + while (so_list) + { + if (so_list->pa64_solib_desc.text_base <= addr + && ((so_list->pa64_solib_desc.text_base + + so_list->pa64_solib_desc.text_size) + > addr)) + { + got_value = so_list->pa64_solib_desc.linkage_ptr; + break; + } + so_list = so_list->next; + } + return got_value; +} + +/* Return the address of the handle of the shared library in which ADDR + belongs. If ADDR isn't in any known shared library, return zero. + + This function is used in hppa_fix_call_dummy in hppa-tdep.c. */ + +CORE_ADDR +pa64_solib_get_solib_by_pc (addr) + CORE_ADDR addr; +{ + struct so_list *so_list = so_list_head; + CORE_ADDR retval = 0; + + while (so_list) + { + if (so_list->pa64_solib_desc.text_base <= addr + && ((so_list->pa64_solib_desc.text_base + + so_list->pa64_solib_desc.text_size) + > addr)) + { + retval = so_list->pa64_solib_desc_addr; + break; + } + so_list = so_list->next; + } + return retval; +} + +/* Dump information about all the currently loaded shared libraries. */ + +static void +pa64_sharedlibrary_info_command (ignore, from_tty) + char *ignore; + int from_tty; +{ + struct so_list *so_list = so_list_head; + + if (exec_bfd == NULL) + { + printf_unfiltered ("no exec file.\n"); + return; + } + + if (so_list == NULL) + { + printf_unfiltered ("No shared libraries loaded at this time.\n"); + return; + } + + printf_unfiltered ("Shared Object Libraries\n"); + printf_unfiltered (" %-19s%-19s%-19s%-19s\n", + " tstart", " tend", + " dstart", " dend"); + while (so_list) + { + unsigned int flags; + + printf_unfiltered ("%s", so_list->name); + if (so_list->objfile == NULL) + printf_unfiltered (" (symbols not loaded)"); + if (so_list->loaded == 0) + printf_unfiltered (" (shared library unloaded)"); + printf_unfiltered (" %-18s", + local_hex_string_custom (so_list->pa64_solib_desc.linkage_ptr, + "016l")); + printf_unfiltered ("\n"); + printf_unfiltered ("%-18s", + local_hex_string_custom (so_list->pa64_solib_desc.text_base, + "016l")); + printf_unfiltered (" %-18s", + local_hex_string_custom ((so_list->pa64_solib_desc.text_base, + + so_list->pa64_solib_desc.text_size), + "016l")); + printf_unfiltered (" %-18s", + local_hex_string_custom (so_list->pa64_solib_desc.data_base, + "016l")); + printf_unfiltered (" %-18s\n", + local_hex_string_custom ((so_list->pa64_solib_desc.data_base, + + so_list->pa64_solib_desc.data_size), + "016l")); + so_list = so_list->next; + } +} + +/* Load up one or more shared libraries as directed by the user. */ + +static void +pa64_solib_sharedlibrary_command (args, from_tty) + char *args; + int from_tty; +{ + dont_repeat (); + pa64_solib_add (args, from_tty, (struct target_ops *) 0); +} + +/* Return the name of the shared library containing ADDR or NULL if ADDR + is not contained in any known shared library. */ + +char * +pa64_solib_address (addr) + CORE_ADDR addr; +{ + struct so_list *so = so_list_head; + + while (so) + { + /* Is this address within this shlib's text range? If so, + return the shlib's name. */ + if (addr >= so->pa64_solib_desc.text_base + && addr < (so->pa64_solib_desc.text_base + | so->pa64_solib_desc.text_size)) + return so->name; + + /* Nope, keep looking... */ + so = so->next; + } + + /* No, we couldn't prove that the address is within a shlib. */ + return NULL; +} + +/* We are killing the inferior and restarting the program. */ + +void +pa64_solib_restart () +{ + struct so_list *sl = so_list_head; + + /* Before the shlib info vanishes, use it to disable any breakpoints + that may still be active in those shlibs. */ + disable_breakpoints_in_shlibs (0); + + /* Discard all the shlib descriptors. */ + while (sl) + { + struct so_list *next_sl = sl->next; + free (sl); + sl = next_sl; + } + so_list_head = NULL; + + pa64_solib_total_st_size = (LONGEST) 0; + pa64_solib_st_size_threshold_exceeded = 0; + + dld_cache.is_valid = 0; + dld_cache.have_read_dld_descriptor = 0; + dld_cache.dld_flags_addr = 0; + dld_cache.load_map = 0; + dld_cache.load_map_addr = 0; + dld_cache.dld_desc.data_base = 0; + dld_cache.dld_flags = 0; + dld_cache.dyninfo_sect = 0; +} + +void +_initialize_pa64_solib () +{ + add_com ("sharedlibrary", class_files, pa64_solib_sharedlibrary_command, + "Load shared object library symbols for files matching REGEXP."); + add_info ("sharedlibrary", pa64_sharedlibrary_info_command, + "Status of loaded shared object libraries."); + add_show_from_set + (add_set_cmd ("auto-solib-add", class_support, var_zinteger, + (char *) &auto_solib_add, + "Set autoloading size threshold (in megabytes) of shared library symbols.\n\ +If nonzero, symbols from all shared object libraries will be loaded\n\ +automatically when the inferior begins execution or when the dynamic linker\n\ +informs gdb that a new library has been loaded, until the symbol table\n\ +of the program and libraries exceeds this threshold.\n\ +Otherwise, symbols must be loaded manually, using `sharedlibrary'.", + &setlist), + &showlist); + + /* ??rehrauer: On HP-UX, the kernel parameter MAXDSIZ limits how much + data space a process can use. We ought to be reading MAXDSIZ and + setting auto_solib_add to some large fraction of that value. If + not that, we maybe ought to be setting it smaller than the default + for MAXDSIZ (that being 64Mb, I believe). However, [1] this threshold + is only crudely approximated rather than actually measured, and [2] + 50 Mbytes is too small for debugging gdb itself. Thus, the arbitrary + 100 figure. + */ + auto_solib_add = 100; /* Megabytes */ + + pa64_solib_restart (); +} + +/* Get some HPUX-specific data from a shared lib. */ +CORE_ADDR +so_lib_thread_start_addr (so) + struct so_list *so; +{ + return so->pa64_solib_desc.tls_start_addr; +} + +/* Read the dynamic linker's internal shared library descriptor. + + This must happen after dld starts running, so we can't do it in + read_dynamic_info. Record the fact that we have loaded the + descriptor. If the library is archive bound, then return zero, else + return nonzero. */ + +static boolean +read_dld_descriptor (target) + struct target_ops *target; +{ + char *dll_path; + asection *dyninfo_sect; + + /* If necessary call read_dynamic_info to extract the contents of the + .dynamic section from the shared library. */ + if (!dld_cache.is_valid) + { + if (symfile_objfile == NULL) + error ("No object file symbols."); + + dyninfo_sect = bfd_get_section_by_name (symfile_objfile->obfd, + ".dynamic"); + if (!dyninfo_sect) + { + return 0; + } + + if (!read_dynamic_info (dyninfo_sect, &dld_cache)) + error ("Unable to read in .dynamic section information."); + } + + /* Read the load map pointer. */ + if (target_read_memory (dld_cache.load_map_addr, + (char*) &dld_cache.load_map, + sizeof(dld_cache.load_map)) + != 0) + { + error ("Error while reading in load map pointer."); + } + + /* Read in the dld load module descriptor */ + if (dlgetmodinfo (-1, + &dld_cache.dld_desc, + sizeof(dld_cache.dld_desc), + pa64_target_read_memory, + 0, + dld_cache.load_map) + == 0) + { + error ("Error trying to get information about dynamic linker."); + } + + /* Indicate that we have loaded the dld descriptor. */ + dld_cache.have_read_dld_descriptor = 1; + + /* Add dld.sl to the list of known shared libraries so that we can + do unwind, etc. + + ?!? This may not be correct. Consider of dld.sl contains symbols + which are also referenced/defined by the user program or some user + shared library. We need to make absolutely sure that we do not + pollute the namespace from GDB's point of view. */ + dll_path = dlgetname (&dld_cache.dld_desc, + sizeof(dld_cache.dld_desc), + pa64_target_read_memory, + 0, + dld_cache.load_map); + add_to_solist(0, dll_path, &dld_cache.dld_desc, 0, target); + + return 1; +} + +/* Read the .dynamic section and extract the information of interest, + which is stored in dld_cache. The routine elf_locate_base in solib.c + was used as a model for this. */ + +static boolean +read_dynamic_info (dyninfo_sect, dld_cache_p) + asection *dyninfo_sect; + dld_cache_t *dld_cache_p; +{ + char *buf; + char *bufend; + CORE_ADDR dyninfo_addr; + int dyninfo_sect_size; + CORE_ADDR entry_addr; + + /* Read in .dynamic section, silently ignore errors. */ + dyninfo_addr = bfd_section_vma (symfile_objfile->obfd, dyninfo_sect); + dyninfo_sect_size = bfd_section_size (exec_bfd, dyninfo_sect); + buf = alloca (dyninfo_sect_size); + if (target_read_memory (dyninfo_addr, buf, dyninfo_sect_size)) + return 0; + + /* Scan the .dynamic section and record the items of interest. + In particular, DT_HP_DLD_FLAGS */ + for (bufend = buf + dyninfo_sect_size, entry_addr = dyninfo_addr; + buf < bufend; + buf += sizeof (Elf64_Dyn), entry_addr += sizeof (Elf64_Dyn)) + { + Elf64_Dyn *x_dynp = (Elf64_Dyn*)buf; + Elf64_Sxword dyn_tag; + CORE_ADDR dyn_ptr; + char pbuf[TARGET_PTR_BIT / HOST_CHAR_BIT]; + + dyn_tag = bfd_h_get_64 (symfile_objfile->obfd, + (bfd_byte*) &x_dynp->d_tag); + + /* We can't use a switch here because dyn_tag is 64 bits and HP's + lame comiler does not handle 64bit items in switch statements. */ + if (dyn_tag == DT_NULL) + break; + else if (dyn_tag == DT_HP_DLD_FLAGS) + { + /* Set dld_flags_addr and dld_flags in *dld_cache_p */ + dld_cache_p->dld_flags_addr = entry_addr + offsetof(Elf64_Dyn, d_un); + if (target_read_memory (dld_cache_p->dld_flags_addr, + (char*) &dld_cache_p->dld_flags, + sizeof(dld_cache_p->dld_flags)) + != 0) + { + error ("Error while reading in .dynamic section of the program."); + } + } + else if (dyn_tag == DT_HP_LOAD_MAP) + { + /* Dld will place the address of the load map at load_map_addr + after it starts running. */ + if (target_read_memory (entry_addr + offsetof(Elf64_Dyn, + d_un.d_ptr), + (char*) &dld_cache_p->load_map_addr, + sizeof(dld_cache_p->load_map_addr)) + != 0) + { + error ("Error while reading in .dynamic section of the program."); + } + } + else + { + /* tag is not of interest */ + } + } + + /* Record other information and set is_valid to 1. */ + dld_cache_p->dyninfo_sect = dyninfo_sect; + + /* Verify that we read in required info. These fields are re-set to zero + in pa64_solib_restart. */ + + if (dld_cache_p->dld_flags_addr != 0 && dld_cache_p->load_map_addr != 0) + dld_cache_p->is_valid = 1; + else + return 0; + + return 1; +} + +/* Wrapper for target_read_memory to make dlgetmodinfo happy. */ + +static void * +pa64_target_read_memory (buffer, ptr, bufsiz, ident) + void *buffer; + CORE_ADDR ptr; + size_t bufsiz; + int ident; +{ + if (target_read_memory (ptr, buffer, bufsiz) != 0) + return 0; + return buffer; +} + +/* Called from handle_dynlink_load_event and pa64_solib_add to add + a shared library to so_list_head list and possibly to read in the + debug information for the library. + + If load_module_desc_p is NULL, then the load module descriptor must + be read from the inferior process at the address load_module_desc_addr. */ + +static void +add_to_solist (from_tty, dll_path, load_module_desc_p, + load_module_desc_addr, target) + boolean from_tty; + char *dll_path; + struct load_module_desc *load_module_desc_p; + CORE_ADDR load_module_desc_addr; + struct target_ops *target; +{ + struct so_list *new_so, *so_list_tail; + int pa64_solib_st_size_threshhold_exceeded; + LONGEST st_size; + + if (symfile_objfile == NULL) + return; + + so_list_tail = so_list_head; + /* Find the end of the list of shared objects. */ + while (so_list_tail && so_list_tail->next) + { + if (strcmp (so_list_tail->name, dll_path) == 0) + return; + so_list_tail = so_list_tail->next; + } + + if (so_list_tail && strcmp (so_list_tail->name, dll_path) == 0) + return; + + /* Add the shared library to the so_list_head list */ + new_so = (struct so_list *) xmalloc (sizeof (struct so_list)); + memset ((char *)new_so, 0, sizeof (struct so_list)); + if (so_list_head == NULL) + { + so_list_head = new_so; + so_list_tail = new_so; + } + else + { + so_list_tail->next = new_so; + so_list_tail = new_so; + } + + /* Initialize the new_so */ + if (load_module_desc_p) + { + new_so->pa64_solib_desc = *load_module_desc_p; + } + else + { + if (target_read_memory (load_module_desc_addr, + (char*) &new_so->pa64_solib_desc, + sizeof(struct load_module_desc)) + != 0) + { + error ("Error while reading in dynamic library %s", dll_path); + } + } + + new_so->pa64_solib_desc_addr = load_module_desc_addr; + new_so->loaded = 1; + new_so->name = obsavestring (dll_path, strlen(dll_path), + &symfile_objfile->symbol_obstack); + + /* If we are not going to load the library, tell the user if we + haven't already and return. */ + + st_size = pa64_solib_sizeof_symbol_table (dll_path); + pa64_solib_st_size_threshhold_exceeded = + !from_tty + && ( (st_size + pa64_solib_total_st_size) + > (auto_solib_add * (LONGEST)1000000)); + if (pa64_solib_st_size_threshhold_exceeded) + { + pa64_solib_add_solib_objfile (new_so, dll_path, from_tty, 1); + return; + } + + /* Now read in debug info. */ + pa64_solib_total_st_size += st_size; + + /* This fills in new_so->objfile, among others. */ + pa64_solib_load_symbols (new_so, + dll_path, + from_tty, + 0); + return; +} + + +/* + 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 (abfd, symname) + 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); + } + return (symaddr); +} + |