--- /dev/null
+/* Handle OSF/1, Digital UNIX, and Tru64 shared libraries
+ for GDB, the GNU Debugger.
+ Copyright 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. */
+
+/* When handling shared libraries, GDB has to find out the pathnames
+ of all shared libraries that are currently loaded (to read in their
+ symbols) and where the shared libraries are loaded in memory
+ (to relocate them properly from their prelinked addresses to the
+ current load address).
+
+ Under OSF/1 there are two possibilities to get at this information:
+
+ 1) Peek around in the runtime loader structures.
+ These are not documented, and they are not defined in the system
+ header files. The definitions below were obtained by experimentation,
+ but they seem stable enough.
+
+ 2) Use the libxproc.a library, which contains the equivalent ldr_*
+ routines. The library is documented in Tru64 5.x, but as of 5.1, it
+ only allows a process to examine itself. On earlier versions, it
+ may require that the GDB executable be dynamically linked and that
+ NAT_CLIBS include -lxproc -Wl,-expect_unresolved,ldr_process_context
+ for GDB and all applications that are using libgdb.
+
+ We will use the peeking approach until libxproc.a works for other
+ processes. */
+
+#include "defs.h"
+
+#include <sys/types.h>
+#include <signal.h>
+#include "gdb_string.h"
+
+#include "bfd.h"
+#include "symtab.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "target.h"
+#include "inferior.h"
+#include "solist.h"
+
+#ifdef USE_LDR_ROUTINES
+# include <loader.h>
+#endif
+
+#ifndef USE_LDR_ROUTINES
+/* Definition of runtime loader structures, found by experimentation. */
+#define RLD_CONTEXT_ADDRESS 0x3ffc0000000
+
+/* Per-module information structure referenced by ldr_context_t.head. */
+
+typedef struct
+ {
+ CORE_ADDR next;
+ CORE_ADDR previous;
+ CORE_ADDR unknown1;
+ CORE_ADDR module_name;
+ CORE_ADDR modinfo_addr; /* used by next_link_map_member() to detect
+ the end of the shared module list */
+ long module_id;
+ CORE_ADDR unknown2;
+ CORE_ADDR unknown3;
+ long region_count;
+ CORE_ADDR regioninfo_addr;
+ }
+ldr_module_info_t;
+
+/* Per-region structure referenced by ldr_module_info_t.regioninfo_addr. */
+
+typedef struct
+ {
+ long unknown1;
+ CORE_ADDR regionname_addr;
+ long protection;
+ CORE_ADDR vaddr;
+ CORE_ADDR mapaddr;
+ long size;
+ long unknown2[5];
+ }
+ldr_region_info_t;
+
+/* Structure at RLD_CONTEXT_ADDRESS specifying the start and finish addresses
+ of the shared module list. */
+
+typedef struct
+ {
+ CORE_ADDR unknown1;
+ CORE_ADDR unknown2;
+ CORE_ADDR head;
+ CORE_ADDR tail;
+ }
+ldr_context_t;
+#endif /* !USE_LDR_ROUTINES */
+
+/* Per-section information, stored in struct lm_info.secs. */
+
+struct lm_sec
+ {
+ CORE_ADDR offset; /* difference between default and actual
+ virtual addresses of section .name */
+ CORE_ADDR nameaddr; /* address in inferior of section name */
+ const char *name; /* name of section, null if not fetched */
+ };
+
+/* Per-module information, stored in struct so_list.lm_info. */
+
+struct lm_info
+ {
+ int isloader; /* whether the module is /sbin/loader */
+ int nsecs; /* length of .secs */
+ struct lm_sec secs[1]; /* variable-length array of sections, sorted
+ by name */
+ };
+
+/* Context for iterating through the inferior's shared module list. */
+
+struct read_map_ctxt
+ {
+#ifdef USE_LDR_ROUTINES
+ ldr_process_t proc;
+ ldr_module_t next;
+#else
+ CORE_ADDR next; /* next element in module list */
+ CORE_ADDR tail; /* last element in module list */
+#endif
+ };
+
+/* Forward declaration for this module's autoinit function. */
+
+extern void _initialize_osf_solib (void);
+
+#ifdef USE_LDR_ROUTINES
+# if 0
+/* This routine is intended to be called by ldr_* routines to read memory from
+ the current target. Usage:
+
+ ldr_process = ldr_core_process ();
+ ldr_set_core_reader (ldr_read_memory);
+ ldr_xdetach (ldr_process);
+ ldr_xattach (ldr_process);
+
+ ldr_core_process() and ldr_read_memory() are neither documented nor
+ declared in system header files. They work with OSF/1 2.x, and they might
+ work with later versions as well. */
+
+static int
+ldr_read_memory (CORE_ADDR memaddr, char *myaddr, int len, int readstring)
+{
+ int result;
+ char *buffer;
+
+ if (readstring)
+ {
+ target_read_string (memaddr, &buffer, len, &result);
+ if (result == 0)
+ strcpy (myaddr, buffer);
+ xfree (buffer);
+ }
+ else
+ result = target_read_memory (memaddr, myaddr, len);
+
+ if (result != 0)
+ result = -result;
+ return result;
+}
+# endif /* 0 */
+#endif /* USE_LDR_ROUTINES */
+
+/* Comparison for qsort() and bsearch(): return -1, 0, or 1 according to
+ whether lm_sec *P1's name is lexically less than, equal to, or greater
+ than that of *P2. */
+
+static int
+lm_sec_cmp (const void *p1, const void *p2)
+{
+ const struct lm_sec *lms1 = p1, *lms2 = p2;
+ return strcmp (lms1->name, lms2->name);
+}
+
+/* Sort LMI->secs so that osf_relocate_section_addresses() can binary-search
+ it. */
+
+static void
+lm_secs_sort (struct lm_info *lmi)
+{
+ qsort (lmi->secs, lmi->nsecs, sizeof *lmi->secs, lm_sec_cmp);
+}
+
+/* Populate name fields of LMI->secs. */
+
+static void
+fetch_sec_names (struct lm_info *lmi)
+{
+#ifndef USE_LDR_ROUTINES
+ int i, errcode;
+ struct lm_sec *lms;
+ char *name;
+
+ for (i = 0; i < lmi->nsecs; i++)
+ {
+ lms = lmi->secs + i;
+ target_read_string (lms->nameaddr, &name, PATH_MAX, &errcode);
+ if (errcode != 0)
+ {
+ warning ("unable to read shared sec name at 0x%lx", lms->nameaddr);
+ name = xstrdup ("");
+ }
+ lms->name = name;
+ }
+ lm_secs_sort (lmi);
+#endif
+}
+
+/* target_so_ops callback. Adjust SEC's addresses after it's been mapped into
+ the process. */
+
+static void
+osf_relocate_section_addresses (struct so_list *so,
+ struct section_table *sec)
+{
+ struct lm_info *lmi;
+ struct lm_sec lms_key, *lms;
+
+ /* Fetch SO's section names if we haven't done so already. */
+ lmi = so->lm_info;
+ if (lmi->nsecs && !lmi->secs[0].name)
+ fetch_sec_names (lmi);
+
+ /* Binary-search for offset information corresponding to SEC. */
+ lms_key.name = sec->the_bfd_section->name;
+ lms = bsearch (&lms_key, lmi->secs, lmi->nsecs, sizeof *lms, lm_sec_cmp);
+ if (lms)
+ {
+ sec->addr += lms->offset;
+ sec->endaddr += lms->offset;
+ }
+}
+
+/* target_so_ops callback. Free parts of SO allocated by this file. */
+
+static void
+osf_free_so (struct so_list *so)
+{
+ int i;
+ const char *name;
+
+ for (i = 0; i < so->lm_info->nsecs; i++)
+ {
+ name = so->lm_info->secs[i].name;
+ if (name)
+ xfree ((void *) name);
+ }
+ xfree (so->lm_info);
+}
+
+/* target_so_ops callback. Discard information accumulated by this file and
+ not freed by osf_free_so(). */
+
+static void
+osf_clear_solib (void)
+{
+ return;
+}
+
+/* target_so_ops callback. Prepare to handle shared libraries after the
+ inferior process has been created but before it's executed any
+ instructions.
+
+ For a statically bound executable, the inferior's first instruction is the
+ one at "_start", or a similar text label. No further processing is needed
+ in that case.
+
+ For a dynamically bound executable, this first instruction is somewhere
+ in the rld, and the actual user executable is not yet mapped in.
+ We continue the inferior again, rld then maps in the actual user
+ executable and any needed shared libraries and then sends
+ itself a SIGTRAP.
+
+ At that point we discover the names of all shared libraries and
+ read their symbols in.
+
+ FIXME
+
+ This code does not properly handle hitting breakpoints which the
+ user might have set in the rld itself. Proper handling would have
+ to check if the SIGTRAP happened due to a kill call.
+
+ Also, what if child has exit()ed? Must exit loop somehow. */
+
+static void
+osf_solib_create_inferior_hook (void)
+{
+ /* Nothing to do for statically bound executables. */
+
+ if (symfile_objfile == NULL
+ || symfile_objfile->obfd == NULL
+ || ((bfd_get_file_flags (symfile_objfile->obfd) & DYNAMIC) == 0))
+ return;
+
+ /* Now run the target. It will eventually get a SIGTRAP, at
+ which point all of the libraries will have been mapped in and we
+ can go groveling around in the rld structures to find
+ out what we need to know about them. */
+
+ clear_proceed_status ();
+ stop_soon_quietly = 1;
+ stop_signal = TARGET_SIGNAL_0;
+ do
+ {
+ target_resume (minus_one_ptid, 0, stop_signal);
+ wait_for_inferior ();
+ }
+ while (stop_signal != TARGET_SIGNAL_TRAP);
+
+ /* solib_add will call reinit_frame_cache.
+ But we are stopped in the runtime loader and we do not have symbols
+ for the runtime loader. So heuristic_proc_start will be called
+ and will put out an annoying warning.
+ Delaying the resetting of stop_soon_quietly until after symbol loading
+ suppresses the warning. */
+ if (auto_solib_add)
+ solib_add ((char *) 0, 0, (struct target_ops *) 0);
+ stop_soon_quietly = 0;
+
+ /* Enable breakpoints disabled (unnecessarily) by clear_solib(). */
+ re_enable_breakpoints_in_shlibs ();
+}
+
+/* target_so_ops callback. Do additional symbol handling, lookup, etc. after
+ symbols for a shared object have been loaded. */
+
+static void
+osf_special_symbol_handling (void)
+{
+ return;
+}
+
+/* Initialize CTXT in preparation for iterating through the inferior's module
+ list using read_map(). Return success. */
+
+static int
+open_map (struct read_map_ctxt *ctxt)
+{
+#ifdef USE_LDR_ROUTINES
+ ctxt->proc = ldr_my_process ();
+ if (ldr_xattach (ctxt->proc) != 0)
+ return 0;
+ ctxt->next = LDR_NULL_MODULE;
+#else
+ CORE_ADDR ldr_context_addr, prev, next;
+ ldr_context_t ldr_context;
+
+ if (target_read_memory ((CORE_ADDR) RLD_CONTEXT_ADDRESS,
+ (char *) &ldr_context_addr,
+ sizeof (CORE_ADDR)) != 0)
+ return 0;
+ if (target_read_memory (ldr_context_addr,
+ (char *) &ldr_context,
+ sizeof (ldr_context_t)) != 0)
+ return 0;
+ ctxt->next = ldr_context.head;
+ ctxt->tail = ldr_context.tail;
+#endif
+ return 1;
+}
+
+/* Initialize SO to have module NAME, /sbin/loader indicator ISLOADR, and
+ space for NSECS sections. */
+
+static void
+init_so (struct so_list *so, char *name, int isloader, int nsecs)
+{
+ int namelen, i;
+
+ /* solib.c requires various fields to be initialized to 0. */
+ memset (so, 0, sizeof *so);
+
+ /* Copy the name. */
+ namelen = strlen (name);
+ if (namelen >= SO_NAME_MAX_PATH_SIZE)
+ namelen = SO_NAME_MAX_PATH_SIZE - 1;
+
+ memcpy (so->so_original_name, name, namelen);
+ so->so_original_name[namelen] = '\0';
+ memcpy (so->so_name, so->so_original_name, namelen + 1);
+
+ /* Allocate section space. */
+ so->lm_info = xmalloc ((unsigned) &(((struct lm_info *)0)->secs) +
+ nsecs * sizeof *so->lm_info);
+ so->lm_info->isloader = isloader;
+ so->lm_info->nsecs = nsecs;
+ for (i = 0; i < nsecs; i++)
+ so->lm_info->secs[i].name = NULL;
+}
+
+/* Initialize SO's section SECIDX with name address NAMEADDR, name string
+ NAME, default virtual address VADDR, and actual virtual address
+ MAPADDR. */
+
+static void
+init_sec (struct so_list *so, int secidx, CORE_ADDR nameaddr,
+ const char *name, CORE_ADDR vaddr, CORE_ADDR mapaddr)
+{
+ struct lm_sec *lms;
+
+ lms = so->lm_info->secs + secidx;
+ lms->nameaddr = nameaddr;
+ lms->name = name;
+ lms->offset = mapaddr - vaddr;
+}
+
+/* If there are more elements starting at CTXT in inferior's module list,
+ store the next element in SO, advance CTXT to the next element, and return
+ 1, else return 0. */
+
+static int
+read_map (struct read_map_ctxt *ctxt, struct so_list *so)
+{
+ ldr_module_info_t minf;
+ ldr_region_info_t rinf;
+
+#ifdef USE_LDR_ROUTINES
+ size_t size;
+ ldr_region_t i;
+
+ /* Retrieve the next element. */
+ if (ldr_next_module (ctxt->proc, &ctxt->next) != 0)
+ return 0;
+ if (ctxt->next == LDR_NULL_MODULE)
+ return 0;
+ if (ldr_inq_module (ctxt->proc, ctxt->next, &minf, sizeof minf, &size) != 0)
+ return 0;
+
+ /* Initialize the module name and section count. */
+ init_so (so, minf.lmi_name, 0, minf.lmi_nregion);
+
+ /* Retrieve section names and offsets. */
+ for (i = 0; i < minf.lmi_nregion; i++)
+ {
+ if (ldr_inq_region (ctxt->proc, ctxt->next, i, &rinf,
+ sizeof rinf, &size) != 0)
+ goto err;
+ init_sec (so, (int) i, 0, xstrdup (rinf.lri_name),
+ (CORE_ADDR) rinf.lri_vaddr, (CORE_ADDR) rinf.lri_mapaddr);
+ }
+ lm_secs_sort (so->lm_info);
+#else
+ char *name;
+ int errcode, i;
+
+ /* Retrieve the next element. */
+ if (!ctxt->next)
+ return 0;
+ if (target_read_memory (ctxt->next, (char *) &minf, sizeof minf) != 0)
+ return 0;
+ if (ctxt->next == ctxt->tail)
+ ctxt->next = 0;
+ else
+ ctxt->next = minf.next;
+
+ /* Initialize the module name and section count. */
+ target_read_string (minf.module_name, &name, PATH_MAX, &errcode);
+ if (errcode != 0)
+ return 0;
+ init_so (so, name, !minf.modinfo_addr, minf.region_count);
+ xfree (name);
+
+ /* Retrieve section names and offsets. */
+ for (i = 0; i < minf.region_count; i++)
+ {
+ if (target_read_memory (minf.regioninfo_addr + i * sizeof rinf,
+ (char *) &rinf, sizeof rinf) != 0)
+ goto err;
+ init_sec (so, i, rinf.regionname_addr, NULL, rinf.vaddr, rinf.mapaddr);
+ }
+#endif /* !USE_LDR_ROUTINES */
+ return 1;
+
+ err:
+ osf_free_so (so);
+ return 0;
+}
+
+/* Free resources allocated by open_map (CTXT). */
+
+static void
+close_map (struct read_map_ctxt *ctxt)
+{
+#ifdef USE_LDR_ROUTINES
+ ldr_xdetach (ctxt->proc);
+#endif
+}
+
+/* target_so_ops callback. Return a list of shared objects currently loaded
+ in the inferior. */
+
+static struct so_list *
+osf_current_sos (void)
+{
+ struct so_list *head = NULL, *tail, *newtail, so;
+ struct read_map_ctxt ctxt;
+ int skipped_main;
+
+ if (!open_map (&ctxt))
+ return NULL;
+
+ /* Read subsequent elements. */
+ for (skipped_main = 0;;)
+ {
+ if (!read_map (&ctxt, &so))
+ break;
+
+ /* Skip the main program module, which is first in the list after
+ /sbin/loader. */
+ if (!so.lm_info->isloader && !skipped_main)
+ {
+ osf_free_so (&so);
+ skipped_main = 1;
+ continue;
+ }
+
+ newtail = xmalloc (sizeof *newtail);
+ if (!head)
+ head = newtail;
+ else
+ tail->next = newtail;
+ tail = newtail;
+
+ memcpy (tail, &so, sizeof so);
+ tail->next = NULL;
+ }
+
+ done:
+ close_map (&ctxt);
+ return head;
+}
+
+/* target_so_ops callback. Attempt to locate and open the main symbol
+ file. */
+
+static int
+osf_open_symbol_file_object (void *from_ttyp)
+{
+ struct read_map_ctxt ctxt;
+ struct so_list so;
+ int found;
+
+ if (symfile_objfile)
+ if (!query ("Attempt to reload symbols from process? "))
+ return 0;
+
+ /* The first module after /sbin/loader is the main program. */
+ if (!open_map (&ctxt))
+ return 0;
+ for (found = 0; !found;)
+ {
+ if (!read_map (&ctxt, &so))
+ break;
+ found = !so.lm_info->isloader;
+ osf_free_so (&so);
+ }
+ close_map (&ctxt);
+
+ if (found)
+ symbol_file_add_main (so.so_name, *(int *) from_ttyp);
+ return found;
+}
+
+/* target_so_ops callback. Return whether PC is in the dynamic linker. */
+
+static int
+osf_in_dynsym_resolve_code (CORE_ADDR pc)
+{
+ return 0;
+}
+
+static struct target_so_ops osf_so_ops;
+
+void
+_initialize_osf_solib (void)
+{
+ osf_so_ops.relocate_section_addresses = osf_relocate_section_addresses;
+ osf_so_ops.free_so = osf_free_so;
+ osf_so_ops.clear_solib = osf_clear_solib;
+ osf_so_ops.solib_create_inferior_hook = osf_solib_create_inferior_hook;
+ osf_so_ops.special_symbol_handling = osf_special_symbol_handling;
+ osf_so_ops.current_sos = osf_current_sos;
+ osf_so_ops.open_symbol_file_object = osf_open_symbol_file_object;
+ osf_so_ops.in_dynsym_resolve_code = osf_in_dynsym_resolve_code;
+
+ /* FIXME: Don't do this here. *_gdbarch_init() should set so_ops. */
+ current_target_so_ops = &osf_so_ops;
+}