This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch 00/15] PIE: Position Independent Executable support


Hi,

PIEs are built using `gcc -fPIE -pie' and their code is like for shared
libraries (.so) using -fPIC but they are not shared libraries.

Currently GDB will fail on such files:
	(gdb) start
	Temporary breakpoint 1 at 0x700: file pie.c, line 6.
	Starting program: .../pie 
	Error in re-setting breakpoint 1: Cannot access memory at address 0x6fc

there was a working patch from Jeff Johnston but it does not catch various
corner cases of ld.so debugging and some prelink/sepdebug cases:
	http://cvs.fedora.redhat.com/viewvc/rpms/gdb/devel/gdb-6.3-pie-20050110.patch?content-type=text%2Fplain&view=co
	http://cvs.fedora.redhat.com/viewvc/rpms/gdb/devel/gdb-6.3-test-pie-20050107.patch?content-type=text%2Fplain&view=co

The patchset is based on objfile_relocate() not existing before which makes
the whole operation more straightforward as suggested by Ulrich Weigand:
	http://sourceware.org/ml/gdb/2009-08/msg00039.html

The patchset does not introduce nothing much new and it is more a general set
of miscellaneous fixes merged together to make a special inferior case of PIE
working.  At least some of the small fixes should be easy to get accepted.

It is a fresh work, it has never been tested outside of my machine.  It is
going to be pushed now for testing at least in Fedora Rawhide.

The patch below is a merge of the patchset for better testing convenience.

Expecting it should be extended for gdbserver, I did not check it more.

Regression tested only as this merge on
{x86_64,x86_64-m32,i686}-fedora12-linux-gnu.


Thanks,
Jan


--- a/gdb/auxv.c
+++ b/gdb/auxv.c
@@ -25,6 +25,7 @@
 #include "inferior.h"
 #include "valprint.h"
 #include "gdb_assert.h"
+#include "gdbcore.h"
 
 #include "auxv.h"
 #include "elf/common.h"
@@ -33,15 +34,11 @@
 #include <fcntl.h>
 
 
-/* This function is called like a to_xfer_partial hook, but must be
-   called with TARGET_OBJECT_AUXV.  It handles access via
-   /proc/PID/auxv, which is a common method for native targets.  */
+/* This function handles access via /proc/PID/auxv, which is a common method
+   for native targets.  */
 
-LONGEST
-procfs_xfer_auxv (struct target_ops *ops,
-		  enum target_object object,
-		  const char *annex,
-		  gdb_byte *readbuf,
+static LONGEST
+procfs_xfer_auxv (gdb_byte *readbuf,
 		  const gdb_byte *writebuf,
 		  ULONGEST offset,
 		  LONGEST len)
@@ -50,9 +47,6 @@ procfs_xfer_auxv (struct target_ops *ops,
   int fd;
   LONGEST n;
 
-  gdb_assert (object == TARGET_OBJECT_AUXV);
-  gdb_assert (readbuf || writebuf);
-
   pathname = xstrprintf ("/proc/%d/auxv", PIDGET (inferior_ptid));
   fd = open (pathname, writebuf != NULL ? O_WRONLY : O_RDONLY);
   xfree (pathname);
@@ -72,6 +66,143 @@ procfs_xfer_auxv (struct target_ops *ops,
   return n;
 }
 
+/* This function handles access via ld.so's symbol `_dl_auxv'.  */
+
+static LONGEST
+ld_so_xfer_auxv (gdb_byte *readbuf,
+		 const gdb_byte *writebuf,
+		 ULONGEST offset,
+		 LONGEST len)
+{
+  struct minimal_symbol *msym;
+  CORE_ADDR data_address, pointer_address;
+  struct type *ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+  size_t ptr_size = TYPE_LENGTH (ptr_type);
+  size_t auxv_pair_size = 2 * ptr_size;
+  gdb_byte *ptr_buf = alloca (ptr_size);
+  LONGEST retval;
+  size_t block;
+
+  msym = lookup_minimal_symbol ("_dl_auxv", NULL, NULL);
+  if (msym == NULL)
+    return -1;
+
+  if (MSYMBOL_SIZE (msym) != ptr_size)
+    return -1;
+
+  /* POINTER_ADDRESS is a location where the `_dl_auxv' variable resides.
+     DATA_ADDRESS is the inferior value present in `_dl_auxv', therefore the
+     real inferior AUXV address.  */
+
+  pointer_address = SYMBOL_VALUE_ADDRESS (msym);
+
+  data_address = read_memory_typed_address (pointer_address, ptr_type);
+
+  /* Possibly still not initialized such as during an inferior startup.  */
+  if (data_address == 0)
+    return -1;
+
+  data_address += offset;
+
+  if (writebuf != NULL)
+    {
+      if (target_write_memory (data_address, writebuf, len) == 0)
+	return len;
+      else
+	return -1;
+    }
+
+  /* Stop if trying to read past the existing AUXV block.  The final AT_NULL
+     was already returned before.  */
+
+  if (offset >= auxv_pair_size)
+    {
+      if (target_read_memory (data_address - auxv_pair_size, ptr_buf,
+			      ptr_size) != 0)
+	return -1;
+
+      if (extract_typed_address (ptr_buf, ptr_type) == AT_NULL)
+	return 0;
+    }
+
+  retval = 0;
+  block = 0x400;
+  gdb_assert (block % auxv_pair_size == 0);
+
+  while (len > 0)
+    {
+      if (block > len)
+	block = len;
+
+      /* Reading sizes smaller than AUXV_PAIR_SIZE is not supported.  Tails
+	 unaligned to AUXV_PAIR_SIZE will not be read during a call (they
+	 should be completed during next read with new/extended buffer).  */
+
+      block &= -auxv_pair_size;
+      if (block == 0)
+	return retval;
+
+      if (target_read_memory (data_address, readbuf, block) != 0)
+	{
+	  if (block <= auxv_pair_size)
+	    return retval;
+
+	  block = auxv_pair_size;
+	  continue;
+	}
+
+      data_address += block;
+      len -= block;
+
+      /* Check terminal AT_NULL.  This function is being called indefinitely
+         being extended its READBUF until it returns EOF (0).  */
+
+      while (block >= auxv_pair_size)
+	{
+	  retval += auxv_pair_size;
+
+	  if (extract_typed_address (readbuf, ptr_type) == AT_NULL)
+	    return retval;
+
+	  readbuf += auxv_pair_size;
+	  block -= auxv_pair_size;
+	}
+    }
+
+  return retval;
+}
+
+/* This function is called like a to_xfer_partial hook, but must be
+   called with TARGET_OBJECT_AUXV.  It handles access to AUXV.  */
+
+LONGEST
+memory_xfer_auxv (struct target_ops *ops,
+		  enum target_object object,
+		  const char *annex,
+		  gdb_byte *readbuf,
+		  const gdb_byte *writebuf,
+		  ULONGEST offset,
+		  LONGEST len)
+{
+  gdb_assert (object == TARGET_OBJECT_AUXV);
+  gdb_assert (readbuf || writebuf);
+
+   /* ld_so_xfer_auxv is the only function safe for virtual executables being
+      executed by valgrind's memcheck.  As using ld_so_xfer_auxv is problematic
+      during inferior startup GDB does call it only for attached processes.  */
+
+  if (current_inferior ()->attach_flag != 0)
+    {
+      LONGEST retval;
+
+      retval = ld_so_xfer_auxv (readbuf, writebuf, offset, len);
+      if (retval != -1)
+	return retval;
+    }
+
+  return procfs_xfer_auxv (readbuf, writebuf, offset, len);
+}
+
 /* Read one auxv entry from *READPTR, not reading locations >= ENDPTR.
    Return 0 if *READPTR is already at the end of the buffer.
    Return -1 if there is insufficient buffer for a whole entry.
--- a/gdb/auxv.h
+++ b/gdb/auxv.h
@@ -43,11 +43,7 @@ extern int target_auxv_search (struct target_ops *ops,
 /* Print the contents of the target's AUXV on the specified file. */
 extern int fprint_target_auxv (struct ui_file *file, struct target_ops *ops);
 
-/* This function is called like a to_xfer_partial hook, but must be
-   called with TARGET_OBJECT_AUXV.  It handles access via
-   /proc/PID/auxv, which is a common method for native targets.  */
-
-extern LONGEST procfs_xfer_auxv (struct target_ops *ops,
+extern LONGEST memory_xfer_auxv (struct target_ops *ops,
 				 enum target_object object,
 				 const char *annex,
 				 gdb_byte *readbuf,
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -4559,9 +4559,15 @@ static int
 breakpoint_address_match (struct address_space *aspace1, CORE_ADDR addr1,
 			  struct address_space *aspace2, CORE_ADDR addr2)
 {
+  int addr_bit = gdbarch_addr_bit (target_gdbarch);
+  CORE_ADDR addr_mask = CORE_ADDR_MAX;
+
+  if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
+    addr_mask = ((CORE_ADDR) 1 << addr_bit) - 1;
+
   return ((gdbarch_has_global_breakpoints (target_gdbarch)
 	   || aspace1 == aspace2)
-	  && addr1 == addr2);
+	  && (addr1 & addr_mask) == (addr2 & addr_mask));
 }
 
 static void
@@ -4994,9 +5000,10 @@ disable_breakpoints_in_shlibs (void)
        becomes enabled, or the duplicate is removed, gdb will try to insert
        all breakpoints.  If we don't set shlib_disabled here, we'll try
        to insert those breakpoints and fail.  */
-    if (((b->type == bp_breakpoint)
-	 || (b->type == bp_hardware_breakpoint)
-	 || (b->type == bp_tracepoint))
+    if ((b->type == bp_breakpoint
+	 || b->type == bp_hardware_breakpoint
+	 || b->type == bp_tracepoint
+	 || b->type == bp_shlib_event)
 	&& loc->pspace == current_program_space
 	&& !loc->shlib_disabled
 #ifdef PC_SOLIB
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -98,7 +98,20 @@
 /* A byte from the program being debugged.  */
 typedef bfd_byte gdb_byte;
 
-/* An address in the program being debugged.  Host byte order.  */
+/* An address in the program being debugged.  Host byte order.
+
+   Its width is the maximum width of all the supported targets.  That means
+   32-bit target will run on such GDB using 64-bit CORE_ADDR cluttering the
+   bits 32...63 with random data from internal GDB calculations.  GDB currently
+   in general truncates the address width only when it is being presented/used
+   externally (such as by the paddress function).
+
+   FIXME: This is still not right as any GDB internal comparisons (such as >=)
+   of CORE_ADDR do not use the properly truncated width.  As converting all the
+   CORE_ADDR operations to width-aware functions is not feasible the way out
+   could be a width-aware C++ class CORE_ADDR referencing gdbarch as its
+   constructor parameter.  */
+
 typedef bfd_vma CORE_ADDR;
 
 /* The largest CORE_ADDR value.  */
--- a/gdb/exec.c
+++ b/gdb/exec.c
@@ -674,8 +674,36 @@ print_section_info (struct target_section_table *t, bfd *abfd)
   wrap_here ("        ");
   printf_filtered (_("file type %s.\n"), bfd_get_target (abfd));
   if (abfd == exec_bfd)
-    printf_filtered (_("\tEntry point: %s\n"),
-                     paddress (gdbarch, bfd_get_start_address (abfd)));
+    {
+      bfd_vma displacement;
+
+      for (p = t->sections; p < t->sections_end; p++)
+	{
+	  asection *asect = p->the_bfd_section;
+
+	  if ((bfd_get_section_flags (abfd, asect) & (SEC_ALLOC | SEC_LOAD))
+	      != (SEC_ALLOC | SEC_LOAD))
+	    continue;
+
+	  if (bfd_get_section_vma (abfd, asect) <= abfd->start_address
+	      && abfd->start_address < bfd_get_section_vma (abfd, asect)
+				       + bfd_get_section_size (asect))
+	    {
+	      displacement = p->addr - bfd_get_section_vma (abfd, asect);
+	      break;
+	    }
+	}
+      if (p == t->sections_end)
+	{
+	  warning (_("Cannot find section for the entry point of %s.\n"),
+		   bfd_get_filename (abfd));
+	  displacement = 0;
+	}
+
+      printf_filtered (_("\tEntry point: %s\n"),
+		       paddress (gdbarch, bfd_get_start_address (abfd)
+					  + displacement));
+    }
   for (p = t->sections; p < t->sections_end; p++)
     {
       printf_filtered ("\t%s", hex_string_custom (p->addr, wid));
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -395,22 +395,6 @@ post_create_inferior (struct target_ops *target, int from_tty)
   /* Now that we know the register layout, retrieve current PC.  */
   stop_pc = regcache_read_pc (get_current_regcache ());
 
-  /* If the solist is global across processes, there's no need to
-     refetch it here.  */
-  if (exec_bfd && !gdbarch_has_global_solist (target_gdbarch))
-    {
-      /* Sometimes the platform-specific hook loads initial shared
-	 libraries, and sometimes it doesn't.  Try to do so first, so
-	 that we can add them with the correct value for FROM_TTY.
-	 If we made all the inferior hook methods consistent,
-	 this call could be removed.  */
-#ifdef SOLIB_ADD
-      SOLIB_ADD (NULL, from_tty, target, auto_solib_add);
-#else
-      solib_add (NULL, from_tty, target, auto_solib_add);
-#endif
-    }
-
   if (exec_bfd)
     {
       /* Create the hooks to handle shared library load and unload
@@ -418,7 +402,25 @@ post_create_inferior (struct target_ops *target, int from_tty)
 #ifdef SOLIB_CREATE_INFERIOR_HOOK
       SOLIB_CREATE_INFERIOR_HOOK (PIDGET (inferior_ptid));
 #else
-      solib_create_inferior_hook ();
+      solib_create_inferior_hook (from_tty);
+#endif
+    }
+
+  /* If the solist is global across processes, there's no need to
+     refetch it here.  */
+  if (exec_bfd && !gdbarch_has_global_solist (target_gdbarch))
+    {
+      /* Sometimes the platform-specific hook loads initial shared
+	 libraries, and sometimes it doesn't.  If it doesn't FROM_TTY will be
+	 incorrectly 0 but such solib targets should be fixed anyway.  If we
+	 made all the inferior hook methods consistent, this call could be
+	 removed.  Call it only after the solib target has been initialized by
+	 solib_create_inferior_hook.  */
+
+#ifdef SOLIB_ADD
+      SOLIB_ADD (NULL, 0, target, auto_solib_add);
+#else
+      solib_add (NULL, 0, target, auto_solib_add);
 #endif
     }
 
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -764,7 +764,7 @@ follow_exec (ptid_t pid, char *execd_pathname)
 #ifdef SOLIB_CREATE_INFERIOR_HOOK
   SOLIB_CREATE_INFERIOR_HOOK (PIDGET (inferior_ptid));
 #else
-  solib_create_inferior_hook ();
+  solib_create_inferior_hook (0);
 #endif
 
   jit_inferior_created_hook ();
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -769,7 +769,7 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
 		 breakpoint.  If a "cloned-VM" event was propagated
 		 better throughout the core, this wouldn't be
 		 required.  */
-	      solib_create_inferior_hook ();
+	      solib_create_inferior_hook (0);
 	    }
 
 	  /* Let the thread_db layer learn about this new process.  */
@@ -951,7 +951,7 @@ Attaching after process %d fork to child process %d.\n"),
 	     shared libraries, and install the solib event breakpoint.
 	     If a "cloned-VM" event was propagated better throughout
 	     the core, this wouldn't be required.  */
-	  solib_create_inferior_hook ();
+	  solib_create_inferior_hook (0);
 	}
 
       /* Let the thread_db layer learn about this new process.  */
@@ -4928,7 +4928,7 @@ linux_xfer_partial (struct target_ops *ops, enum target_object object,
   LONGEST xfer;
 
   if (object == TARGET_OBJECT_AUXV)
-    return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf,
+    return memory_xfer_auxv (ops, object, annex, readbuf, writebuf,
 			     offset, len);
 
   if (object == TARGET_OBJECT_OSDATA)
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -18,13 +18,10 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
-#include "gdbcore.h"
 #include "gdbtypes.h"
 #include "linux-tdep.h"
-#include "observer.h"
 #include "auxv.h"
 #include "target.h"
-#include "elf-bfd.h"
 #include "elf/common.h"
 
 /* This function is suitable for architectures that don't
@@ -155,33 +152,3 @@ linux_has_shared_address_space (void)
 
   return target_is_uclinux;
 }
-
-/* Observer for the executable_changed event, to check whether the new
-   exec binary is a PIE (Position Independent Executable) specimen, which
-   is currently unsupported.  */
-
-static void
-check_is_pie_binary (void)
-{
-  Elf_Internal_Ehdr *elf_hdr;
-
-  if (!exec_bfd)
-    return;
-  else if (bfd_get_flavour (exec_bfd) != bfd_target_elf_flavour)
-    return;
-
-  if (elf_tdata (exec_bfd)->elf_header->e_type == ET_DYN)
-    warning (_("\
-The current binary is a PIE (Position Independent Executable), which\n\
-GDB does NOT currently support.  Most debugger features will fail if used\n\
-in this session.\n"));
-}
-
-/* Provide a prototype to silence -Wmissing-prototypes.  */
-extern initialize_file_ftype _initialize_linux_tdep;
-
-void
-_initialize_linux_tdep (void)
-{
-  observer_attach_executable_changed (check_is_pie_binary);
-}
--- a/gdb/nto-procfs.c
+++ b/gdb/nto-procfs.c
@@ -653,7 +653,7 @@ static void
 procfs_post_attach (pid_t pid)
 {
   if (exec_bfd)
-    solib_create_inferior_hook ();
+    solib_create_inferior_hook (0);
 }
 
 static ptid_t
@@ -1214,7 +1214,7 @@ procfs_create_inferior (struct target_ops *ops, char *exec_file,
 
   if (exec_bfd != NULL
       || (symfile_objfile != NULL && symfile_objfile->obfd != NULL))
-    solib_create_inferior_hook ();
+    solib_create_inferior_hook (0);
 }
 
 static void
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -600,9 +600,10 @@ free_all_objfiles (void)
 }
 
 /* Relocate OBJFILE to NEW_OFFSETS.  There should be OBJFILE->NUM_SECTIONS
-   entries in new_offsets.  */
-void
-objfile_relocate (struct objfile *objfile, struct section_offsets *new_offsets)
+   entries in new_offsets.  SEPARATE_DEBUG_OBJFILE is not touched here.  */
+
+static void
+objfile_relocate1 (struct objfile *objfile, struct section_offsets *new_offsets)
 {
   struct obj_section *s;
   struct section_offsets *delta =
@@ -680,6 +681,10 @@ objfile_relocate (struct objfile *objfile, struct section_offsets *new_offsets)
     }
   }
 
+  if (objfile->psymtabs_addrmap)
+    addrmap_relocate (objfile->psymtabs_addrmap,
+		      ANOFFSET (delta, SECT_OFF_TEXT (objfile)));
+
   {
     struct partial_symtab *p;
 
@@ -752,6 +757,49 @@ objfile_relocate (struct objfile *objfile, struct section_offsets *new_offsets)
       exec_set_section_address (bfd_get_filename (objfile->obfd), idx,
 				obj_section_addr (s));
     }
+}
+
+/* Relocate OBJFILE to NEW_OFFSETS.  There should be OBJFILE->NUM_SECTIONS
+   entries in new_offsets.  Process also OBJFILE's SEPARATE_DEBUG_OBJFILE.
+
+   The number and ordering of sections does differ between the two objfiles.
+   Only their names match.  Also the file offsets will differ (objfile being
+   possibly prelinked but separate_debug_objfile is probably not prelinked) but
+   the in-memory absolute address as specified by NEW_OFFSETS must match both
+   files.  */
+
+void
+objfile_relocate (struct objfile *objfile, struct section_offsets *new_offsets)
+{
+  objfile_relocate1 (objfile, new_offsets);
+
+  if (objfile->separate_debug_objfile != NULL)
+    {
+      struct objfile *debug_objfile = objfile->separate_debug_objfile;
+      struct section_addr_info *objfile_addrs;
+      struct section_offsets *new_debug_offsets;
+      int new_debug_num_sections;
+      struct cleanup *my_cleanups;
+
+      objfile_addrs = build_section_addr_info_from_objfile (objfile);
+      my_cleanups = make_cleanup (xfree, objfile_addrs);
+
+      /* Here OBJFILE_ADDRS contain the correct absolute addresses, the
+	 relative ones must be already created according to debug_objfile.  */
+
+      addr_info_make_relative (objfile_addrs, debug_objfile->obfd);
+
+      gdb_assert (debug_objfile->num_sections
+		  == bfd_count_sections (debug_objfile->obfd));
+      new_debug_offsets = alloca (SIZEOF_N_SECTION_OFFSETS
+						 (debug_objfile->num_sections));
+      relative_addr_info_to_section_offsets (new_debug_offsets,
+					     debug_objfile->num_sections,
+					     objfile_addrs);
+      do_cleanups (my_cleanups);
+
+      objfile_relocate1 (debug_objfile, new_debug_offsets);
+    }
 
   /* Relocate breakpoints as necessary, after things are relocated. */
   breakpoint_re_set ();
--- a/gdb/procfs.c
+++ b/gdb/procfs.c
@@ -4377,7 +4377,7 @@ procfs_xfer_partial (struct target_ops *ops, enum target_object object,
 
 #ifdef NEW_PROC_API
     case TARGET_OBJECT_AUXV:
-      return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf,
+      return memory_xfer_auxv (ops, object, annex, readbuf, writebuf,
 			       offset, len);
 #endif
 
--- a/gdb/solib-darwin.c
+++ b/gdb/solib-darwin.c
@@ -295,7 +295,7 @@ darwin_special_symbol_handling (void)
 /* Shared library startup support.  See documentation in solib-svr4.c  */
 
 static void
-darwin_solib_create_inferior_hook (void)
+darwin_solib_create_inferior_hook (int from_tty)
 {
   struct minimal_symbol *msymbol;
   char **bkpt_namep;
--- a/gdb/solib-frv.c
+++ b/gdb/solib-frv.c
@@ -984,7 +984,7 @@ frv_relocate_main_executable (void)
  */
 
 static void
-frv_solib_create_inferior_hook (void)
+frv_solib_create_inferior_hook (int from_tty)
 {
   /* Relocate main executable.  */
   frv_relocate_main_executable ();
--- a/gdb/solib-irix.c
+++ b/gdb/solib-irix.c
@@ -392,7 +392,7 @@ enable_break (void)
 
    SYNOPSIS
 
-   void solib_create_inferior_hook ()
+   void solib_create_inferior_hook (int from_tty)
 
    DESCRIPTION
 
@@ -437,7 +437,7 @@ enable_break (void)
  */
 
 static void
-irix_solib_create_inferior_hook (void)
+irix_solib_create_inferior_hook (int from_tty)
 {
   struct inferior *inf;
   struct thread_info *tp;
--- a/gdb/solib-null.c
+++ b/gdb/solib-null.c
@@ -32,7 +32,7 @@ null_special_symbol_handling (void)
 }
 
 static void
-null_solib_create_inferior_hook (void)
+null_solib_create_inferior_hook (int from_tty)
 {
 }
 
--- a/gdb/solib-osf.c
+++ b/gdb/solib-osf.c
@@ -306,7 +306,7 @@ osf_clear_solib (void)
    Also, what if child has exit()ed?  Must exit loop somehow.  */
 
 static void
-osf_solib_create_inferior_hook (void)
+osf_solib_create_inferior_hook (int from_tty)
 {
   struct inferior *inf;
   struct thread_info *tp;
--- a/gdb/solib-pa64.c
+++ b/gdb/solib-pa64.c
@@ -329,7 +329,7 @@ bfd_lookup_symbol (bfd *abfd, char *symname)
    with shared libraries mapped shareable.  */
 
 static void
-pa64_solib_create_inferior_hook (void)
+pa64_solib_create_inferior_hook (int from_tty)
 {
   struct minimal_symbol *msymbol;
   unsigned int dld_flags, status;
--- a/gdb/solib-som.c
+++ b/gdb/solib-som.c
@@ -182,7 +182,7 @@ struct {
    means running until the "_start" is called.  */
 
 static void
-som_solib_create_inferior_hook (void)
+som_solib_create_inferior_hook (int from_tty)
 {
   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
   struct minimal_symbol *msymbol;
--- a/gdb/solib-spu.c
+++ b/gdb/solib-spu.c
@@ -52,25 +52,19 @@
 static void
 spu_relocate_main_executable (int spufs_fd)
 {
-  struct objfile *objfile;
-  struct cleanup *old_chain;
   struct section_offsets *new_offsets;
   int i;
 
-  for (objfile = symfile_objfile;
-       objfile;
-       objfile = objfile->separate_debug_objfile)
-    {
-      new_offsets = xcalloc (objfile->num_sections,
-			     sizeof (struct section_offsets));
-      old_chain = make_cleanup (xfree, new_offsets);
+  if (symfile_objfile == NULL)
+    return;
 
-      for (i = 0; i < objfile->num_sections; i++)
-        new_offsets->offsets[i] = SPUADDR (spufs_fd, 0);
+  new_offsets = alloca (symfile_objfile->num_sections
+			* sizeof (struct section_offsets));
 
-      objfile_relocate (objfile, new_offsets);
-      do_cleanups (old_chain);
-    }
+  for (i = 0; i < symfile_objfile->num_sections; i++)
+    new_offsets->offsets[i] = SPUADDR (spufs_fd, 0);
+
+  objfile_relocate (symfile_objfile, new_offsets);
 }
 
 /* When running a stand-alone SPE executable, we may need to skip one more
@@ -373,7 +367,7 @@ spu_enable_break (struct objfile *objfile)
 
 /* Create inferior hook.  */
 static void
-spu_solib_create_inferior_hook (void)
+spu_solib_create_inferior_hook (int from_tty)
 {
   /* Remove all previously installed solib breakpoints.  Both the SVR4
      code and us will re-install all required breakpoints.  */
@@ -404,7 +398,7 @@ spu_solib_create_inferior_hook (void)
     }
 
   /* Call SVR4 hook -- this will re-insert the SVR4 solib breakpoints.  */
-  svr4_so_ops.solib_create_inferior_hook ();
+  svr4_so_ops.solib_create_inferior_hook (from_tty);
 
   /* If the inferior is statically linked against libspe, we need to install
      our own solib breakpoint right now.  Otherwise, it will be installed by
--- a/gdb/solib-sunos.c
+++ b/gdb/solib-sunos.c
@@ -741,7 +741,7 @@ sunos_special_symbol_handling (void)
  */
 
 static void
-sunos_solib_create_inferior_hook (void)
+sunos_solib_create_inferior_hook (int from_tty)
 {
   struct thread_info *tp;
   struct inferior *inf;
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -50,6 +50,7 @@
 
 static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
 static int svr4_have_link_map_offsets (void);
+static void svr4_relocate_main_executable (void);
 
 /* Link map info to include in an allocated so_list entry */
 
@@ -568,11 +569,12 @@ scan_dyntag (int dyntag, bfd *abfd, CORE_ADDR *ptr)
 {
   int arch_size, step, sect_size;
   long dyn_tag;
-  CORE_ADDR dyn_ptr, dyn_addr;
+  CORE_ADDR dyn_ptr;
   gdb_byte *bufend, *bufstart, *buf;
   Elf32_External_Dyn *x_dynp_32;
   Elf64_External_Dyn *x_dynp_64;
   struct bfd_section *sect;
+  struct target_section *target_section;
 
   if (abfd == NULL)
     return 0;
@@ -588,7 +590,13 @@ scan_dyntag (int dyntag, bfd *abfd, CORE_ADDR *ptr)
   sect = bfd_get_section_by_name (abfd, ".dynamic");
   if (sect == NULL)
     return 0;
-  dyn_addr = bfd_section_vma (abfd, sect);
+
+  for (target_section = current_target_sections->sections;
+       target_section < current_target_sections->sections_end;
+       target_section++)
+    if (sect == target_section->the_bfd_section)
+      break;
+  gdb_assert (target_section < current_target_sections->sections_end);
 
   /* Read in .dynamic from the BFD.  We will get the actual value
      from memory later.  */
@@ -630,7 +638,7 @@ scan_dyntag (int dyntag, bfd *abfd, CORE_ADDR *ptr)
 	     CORE_ADDR ptr_addr;
 
 	     ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
-	     ptr_addr = dyn_addr + (buf - bufstart) + arch_size / 8;
+	     ptr_addr = target_section->addr + (buf - bufstart) + arch_size / 8;
 	     if (target_read_memory (ptr_addr, ptr_buf, arch_size / 8) == 0)
 	       dyn_ptr = extract_typed_address (ptr_buf, ptr_type);
 	     *ptr = dyn_ptr;
@@ -1221,7 +1229,7 @@ exec_entry_point (struct bfd *abfd, struct target_ops *targ)
  */
 
 static int
-enable_break (struct svr4_info *info)
+enable_break (struct svr4_info *info, int from_tty)
 {
   struct minimal_symbol *msymbol;
   char **bkpt_namep;
@@ -1241,7 +1249,7 @@ enable_break (struct svr4_info *info)
      mean r_brk has already been relocated.  Assume the dynamic linker
      is the object containing r_brk.  */
 
-  solib_add (NULL, 0, &current_target, auto_solib_add);
+  solib_add (NULL, from_tty, &current_target, auto_solib_add);
   sym_addr = 0;
   if (info->debug_base && solib_svr4_r_map (info) != 0)
     sym_addr = solib_svr4_r_brk (info);
@@ -1368,7 +1376,7 @@ enable_break (struct svr4_info *info)
 	  info->debug_loader_name = xstrdup (interp_name);
 	  info->debug_loader_offset_p = 1;
 	  info->debug_loader_offset = load_addr;
-	  solib_add (NULL, 0, &current_target, auto_solib_add);
+	  solib_add (NULL, from_tty, &current_target, auto_solib_add);
 	}
 
       /* Record the relocated start and end address of the dynamic linker
@@ -1483,113 +1491,131 @@ enable_break (struct svr4_info *info)
 static void
 svr4_special_symbol_handling (void)
 {
+  svr4_relocate_main_executable ();
 }
 
-/* Relocate the main executable.  This function should be called upon
-   stopping the inferior process at the entry point to the program. 
-   The entry point from BFD is compared to the PC and if they are
-   different, the main executable is relocated by the proper amount. 
+/* Decide if the objfile needs to be relocated.  As indicated above,
+   we will only be here when execution is stopped at the beginning
+   of the program.  Relocation is necessary if the address at which
+   we are presently stopped differs from the start address stored in
+   the executable AND there's no interpreter section.  The condition
+   regarding the interpreter section is very important because if
+   there *is* an interpreter section, execution will begin there
+   instead.  When there is an interpreter section, the start address
+   is (presumably) used by the interpreter at some point to start
+   execution of the program.
+
+   If there is an interpreter, it is normal for it to be set to an
+   arbitrary address at the outset.  The job of finding it is
+   handled in enable_break().
+
+   So, to summarize, relocations are necessary when there is no
+   interpreter section and the start address obtained from the
+   executable is different from the address at which GDB is
+   currently stopped.
    
-   As written it will only attempt to relocate executables which
-   lack interpreter sections.  It seems likely that only dynamic
-   linker executables will get relocated, though it should work
-   properly for a position-independent static executable as well.  */
+   [ The astute reader will note that we also test to make sure that
+     the executable in question has the DYNAMIC flag set.  It is my
+     opinion that this test is unnecessary (undesirable even).  It
+     was added to avoid inadvertent relocation of an executable
+     whose e_type member in the ELF header is not ET_DYN.  There may
+     be a time in the future when it is desirable to do relocations
+     on other types of files as well in which case this condition
+     should either be removed or modified to accomodate the new file
+     type.  (E.g, an ET_EXEC executable which has been built to be
+     position-independent could safely be relocated by the OS if
+     desired.  It is true that this violates the ABI, but the ABI
+     has been known to be bent from time to time.)  - Kevin, Nov 2000. ]
+   */
 
-static void
-svr4_relocate_main_executable (void)
+static CORE_ADDR
+svr4_static_exec_displacement (void)
 {
   asection *interp_sect;
   struct regcache *regcache
     = get_thread_arch_regcache (inferior_ptid, target_gdbarch);
   CORE_ADDR pc = regcache_read_pc (regcache);
 
-  /* Decide if the objfile needs to be relocated.  As indicated above,
-     we will only be here when execution is stopped at the beginning
-     of the program.  Relocation is necessary if the address at which
-     we are presently stopped differs from the start address stored in
-     the executable AND there's no interpreter section.  The condition
-     regarding the interpreter section is very important because if
-     there *is* an interpreter section, execution will begin there
-     instead.  When there is an interpreter section, the start address
-     is (presumably) used by the interpreter at some point to start
-     execution of the program.
-
-     If there is an interpreter, it is normal for it to be set to an
-     arbitrary address at the outset.  The job of finding it is
-     handled in enable_break().
-
-     So, to summarize, relocations are necessary when there is no
-     interpreter section and the start address obtained from the
-     executable is different from the address at which GDB is
-     currently stopped.
-     
-     [ The astute reader will note that we also test to make sure that
-       the executable in question has the DYNAMIC flag set.  It is my
-       opinion that this test is unnecessary (undesirable even).  It
-       was added to avoid inadvertent relocation of an executable
-       whose e_type member in the ELF header is not ET_DYN.  There may
-       be a time in the future when it is desirable to do relocations
-       on other types of files as well in which case this condition
-       should either be removed or modified to accomodate the new file
-       type.  (E.g, an ET_EXEC executable which has been built to be
-       position-independent could safely be relocated by the OS if
-       desired.  It is true that this violates the ABI, but the ABI
-       has been known to be bent from time to time.)  - Kevin, Nov 2000. ]
-     */
-
   interp_sect = bfd_get_section_by_name (exec_bfd, ".interp");
   if (interp_sect == NULL 
       && (bfd_get_file_flags (exec_bfd) & DYNAMIC) != 0
       && (exec_entry_point (exec_bfd, &exec_ops) != pc))
+    return pc - exec_entry_point (exec_bfd, &exec_ops);
+
+  return 0;
+}
+
+/* We relocate all of the sections by the same amount.  This
+   behavior is mandated by recent editions of the System V ABI. 
+   According to the System V Application Binary Interface,
+   Edition 4.1, page 5-5:
+
+     ...  Though the system chooses virtual addresses for
+     individual processes, it maintains the segments' relative
+     positions.  Because position-independent code uses relative
+     addressesing between segments, the difference between
+     virtual addresses in memory must match the difference
+     between virtual addresses in the file.  The difference
+     between the virtual address of any segment in memory and
+     the corresponding virtual address in the file is thus a
+     single constant value for any one executable or shared
+     object in a given process.  This difference is the base
+     address.  One use of the base address is to relocate the
+     memory image of the program during dynamic linking.
+
+   The same language also appears in Edition 4.0 of the System V
+   ABI and is left unspecified in some of the earlier editions.  */
+
+static CORE_ADDR
+svr4_exec_displacement (void)
+{
+  int found;
+  CORE_ADDR entry_point;
+
+  if (exec_bfd == NULL)
+    return 0;
+
+  if (target_auxv_search (&current_target, AT_ENTRY, &entry_point) == 1)
+    return entry_point - exec_entry_point (exec_bfd, &current_target);
+
+  return svr4_static_exec_displacement ();
+}
+
+/* Relocate the main executable.  This function should be called upon
+   stopping the inferior process at the entry point to the program. 
+   The entry point from BFD is compared to the AT_ENTRY of AUXV and if they are
+   different, the main executable is relocated by the proper amount.  */
+
+static void
+svr4_relocate_main_executable (void)
+{
+  CORE_ADDR displacement = svr4_exec_displacement ();
+
+  /* Even if DISPLACEMENT is 0 still try to relocate it as this is a new
+     difference of in-memory vs. in-file addresses and we could already
+     relocate the executable at this function to improper address before.  */
+
+  if (symfile_objfile)
     {
-      struct cleanup *old_chain;
       struct section_offsets *new_offsets;
-      int i, changed;
-      CORE_ADDR displacement;
-      
-      /* It is necessary to relocate the objfile.  The amount to
-	 relocate by is simply the address at which we are stopped
-	 minus the starting address from the executable.
-
-	 We relocate all of the sections by the same amount.  This
-	 behavior is mandated by recent editions of the System V ABI. 
-	 According to the System V Application Binary Interface,
-	 Edition 4.1, page 5-5:
-
-	   ...  Though the system chooses virtual addresses for
-	   individual processes, it maintains the segments' relative
-	   positions.  Because position-independent code uses relative
-	   addressesing between segments, the difference between
-	   virtual addresses in memory must match the difference
-	   between virtual addresses in the file.  The difference
-	   between the virtual address of any segment in memory and
-	   the corresponding virtual address in the file is thus a
-	   single constant value for any one executable or shared
-	   object in a given process.  This difference is the base
-	   address.  One use of the base address is to relocate the
-	   memory image of the program during dynamic linking.
-
-	 The same language also appears in Edition 4.0 of the System V
-	 ABI and is left unspecified in some of the earlier editions.  */
-
-      displacement = pc - exec_entry_point (exec_bfd, &exec_ops);
-      changed = 0;
-
-      new_offsets = xcalloc (symfile_objfile->num_sections,
-			     sizeof (struct section_offsets));
-      old_chain = make_cleanup (xfree, new_offsets);
+      int i;
+
+      new_offsets = alloca (symfile_objfile->num_sections
+			    * sizeof (*new_offsets));
 
       for (i = 0; i < symfile_objfile->num_sections; i++)
-	{
-	  if (displacement != ANOFFSET (symfile_objfile->section_offsets, i))
-	    changed = 1;
-	  new_offsets->offsets[i] = displacement;
-	}
+	new_offsets->offsets[i] = displacement;
 
-      if (changed)
-	objfile_relocate (symfile_objfile, new_offsets);
+      objfile_relocate (symfile_objfile, new_offsets);
+    }
+  else if (exec_bfd)
+    {
+      asection *asect;
 
-      do_cleanups (old_chain);
+      for (asect = exec_bfd->sections; asect != NULL; asect = asect->next)
+	exec_set_section_address (bfd_get_filename (exec_bfd), asect->index,
+				  bfd_section_vma (exec_bfd, asect)
+				  + displacement);
     }
 }
 
@@ -1601,7 +1627,7 @@ svr4_relocate_main_executable (void)
 
    SYNOPSIS
 
-   void svr4_solib_create_inferior_hook ()
+   void svr4_solib_create_inferior_hook (int from_tty)
 
    DESCRIPTION
 
@@ -1646,7 +1672,7 @@ svr4_relocate_main_executable (void)
  */
 
 static void
-svr4_solib_create_inferior_hook (void)
+svr4_solib_create_inferior_hook (int from_tty)
 {
   struct inferior *inf;
   struct thread_info *tp;
@@ -1655,12 +1681,13 @@ svr4_solib_create_inferior_hook (void)
   info = get_svr4_info ();
 
   /* Relocate the main executable if necessary.  */
-  svr4_relocate_main_executable ();
+  if (current_inferior ()->attach_flag == 0)
+    svr4_relocate_main_executable ();
 
   if (!svr4_have_link_map_offsets ())
     return;
 
-  if (!enable_break (info))
+  if (!enable_break (info, from_tty))
     return;
 
 #if defined(_SCO_DS)
@@ -1884,8 +1911,19 @@ elf_lookup_lib_symbol (const struct objfile *objfile,
 		       const char *linkage_name,
 		       const domain_enum domain)
 {
-  if (objfile->obfd == NULL
-     || scan_dyntag (DT_SYMBOLIC, objfile->obfd, NULL) != 1)
+  bfd *abfd;
+
+  if (objfile == symfile_objfile)
+    abfd = exec_bfd;
+  else
+    {
+      /* OBJFILE should have been passed as the non-debug one.  */
+      gdb_assert (objfile->separate_debug_objfile_backlink == NULL);
+
+      abfd = objfile->obfd;
+    }
+
+  if (abfd == NULL || scan_dyntag (DT_SYMBOLIC, abfd, NULL) != 1)
     return NULL;
 
   return lookup_global_symbol_from_objfile
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -306,7 +306,7 @@ solib_target_special_symbol_handling (void)
 }
 
 static void
-solib_target_solib_create_inferior_hook (void)
+solib_target_solib_create_inferior_hook (int from_tty)
 {
   /* Nothing needed.  */
 }
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -998,7 +998,7 @@ clear_solib (void)
 
    SYNOPSIS
 
-   void solib_create_inferior_hook ()
+   void solib_create_inferior_hook (int from_tty)
 
    DESCRIPTION
 
@@ -1008,10 +1008,10 @@ clear_solib (void)
    SOLIB_CREATE_INFERIOR_HOOK.  */
 
 void
-solib_create_inferior_hook (void)
+solib_create_inferior_hook (int from_tty)
 {
   struct target_so_ops *ops = solib_ops (target_gdbarch);
-  ops->solib_create_inferior_hook();
+  ops->solib_create_inferior_hook (from_tty);
 }
 
 /* GLOBAL FUNCTION
@@ -1087,7 +1087,6 @@ reload_shared_libraries (char *ignored, int from_tty,
 			 struct cmd_list_element *e)
 {
   no_shared_libraries (NULL, from_tty);
-  solib_add (NULL, from_tty, NULL, auto_solib_add);
   /* Creating inferior hooks here has two purposes. First, if we reload 
      shared libraries then the address of solib breakpoint we've computed
      previously might be no longer valid.  For example, if we forgot to set
@@ -1102,9 +1101,19 @@ reload_shared_libraries (char *ignored, int from_tty,
 #ifdef SOLIB_CREATE_INFERIOR_HOOK
       SOLIB_CREATE_INFERIOR_HOOK (PIDGET (inferior_ptid));
 #else
-      solib_create_inferior_hook ();
+      solib_create_inferior_hook (from_tty);
 #endif
     }
+
+  /* Sometimes the platform-specific hook loads initial shared
+     libraries, and sometimes it doesn't.  If it doesn't FROM_TTY will be
+     incorrectly 0 but such solib targets should be fixed anyway.  If we
+     made all the inferior hook methods consistent, this call could be
+     removed.  Call it only after the solib target has been initialized by
+     solib_create_inferior_hook.  */
+
+  solib_add (NULL, 0, NULL, auto_solib_add);
+
   /* We have unloaded and then reloaded debug info for all shared libraries.
      However, frames may still reference them, for example a frame's 
      unwinder might still point of DWARF FDE structures that are now freed.
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -42,7 +42,7 @@ extern int solib_read_symbols (struct so_list *, int);
    addresses to which they are linked, and sufficient information to
    read in their symbols at a later time.  */
 
-extern void solib_create_inferior_hook (void);
+extern void solib_create_inferior_hook (int from_tty);
 
 /* If ADDR lies in a shared library, return its name.  */
 
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -90,7 +90,7 @@ struct target_so_ops
     void (*clear_solib) (void);
 
     /* Target dependent code to run after child process fork.  */
-    void (*solib_create_inferior_hook) (void);
+    void (*solib_create_inferior_hook) (int from_tty);
 
     /* Do additional symbol handling, lookup, etc. after symbols
        for a shared object have been loaded.  */
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -536,40 +536,151 @@ place_section (bfd *abfd, asection *sect, void *obj)
   arg->lowest = start_addr + bfd_get_section_size (sect);
 }
 
-/* Parse the user's idea of an offset for dynamic linking, into our idea
-   of how to represent it for fast symbol reading.  This is the default
-   version of the sym_fns.sym_offsets function for symbol readers that
-   don't need to do anything special.  It allocates a section_offsets table
-   for the objectfile OBJFILE and stuffs ADDR into all of the offsets.  */
+/* Build (allocate and populate) struct section_addr_info with absolute
+   addresses from OBJFILE->OBFD and OBJFILE->SECTION_OFFSETS.  */
+
+struct section_addr_info *
+build_section_addr_info_from_objfile (struct objfile *objfile)
+{
+  struct target_section *sections = NULL, *sections_end;
+  struct target_section *p;
+  int addr_bit = gdbarch_addr_bit (objfile->gdbarch);
+  CORE_ADDR mask = CORE_ADDR_MAX;
+  struct section_addr_info *retval;
+  struct cleanup *my_cleanups;
+
+  if (build_section_table (objfile->obfd, &sections, &sections_end))
+    error (_("Can't find the file sections in `%s': %s"),
+	   bfd_get_filename (objfile->obfd), bfd_errmsg (bfd_get_error ()));
+  my_cleanups = make_cleanup (xfree, sections);
+
+  if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
+    mask = ((CORE_ADDR) 1 << addr_bit) - 1;
+
+  for (p = sections; p < sections_end; p++)
+    {
+      CORE_ADDR baseaddr;
+
+      gdb_assert (p->the_bfd_section->index < objfile->num_sections);
+      baseaddr = ANOFFSET (objfile->section_offsets,
+			   p->the_bfd_section->index);
+
+      p->addr = (p->addr + baseaddr) & mask;
+      p->endaddr = (p->endaddr + baseaddr) & mask;
+    }
+
+  retval = build_section_addr_info_from_section_table (sections, sections_end);
+
+  do_cleanups (my_cleanups);
+
+  return retval;
+}
+
+/* Store struct section_addr_info as prepared (made relative and with SECTINDEX
+   filled-in) by addr_info_make_relative into SECTION_OFFSETS of NUM_SECTIONS
+   entries.  */
 
 void
-default_symfile_offsets (struct objfile *objfile,
-			 struct section_addr_info *addrs)
+relative_addr_info_to_section_offsets (struct section_offsets *section_offsets,
+				       int num_sections,
+				       struct section_addr_info *addrs)
 {
   int i;
 
-  objfile->num_sections = bfd_count_sections (objfile->obfd);
-  objfile->section_offsets = (struct section_offsets *)
-    obstack_alloc (&objfile->objfile_obstack,
-		   SIZEOF_N_SECTION_OFFSETS (objfile->num_sections));
-  memset (objfile->section_offsets, 0,
-	  SIZEOF_N_SECTION_OFFSETS (objfile->num_sections));
+  memset (section_offsets, 0, SIZEOF_N_SECTION_OFFSETS (num_sections));
 
-  /* Now calculate offsets for section that were specified by the
-     caller. */
+  /* Now calculate offsets for section that were specified by the caller. */
   for (i = 0; i < addrs->num_sections && addrs->other[i].name; i++)
     {
-      struct other_sections *osp ;
+      struct other_sections *osp;
 
-      osp = &addrs->other[i] ;
+      osp = &addrs->other[i];
       if (osp->addr == 0)
   	continue;
 
       /* Record all sections in offsets */
       /* The section_offsets in the objfile are here filled in using
          the BFD index. */
-      (objfile->section_offsets)->offsets[osp->sectindex] = osp->addr;
+      section_offsets->offsets[osp->sectindex] = osp->addr;
+    }
+}
+
+/* Relativize absolute addresses in ADDRS into offsets based on ABFD.  Fill-in
+   also SECTINDEXes there.  */
+
+void
+addr_info_make_relative (struct section_addr_info *addrs, bfd *abfd)
+{
+  asection *lower_sect;
+  asection *sect;
+  CORE_ADDR lower_offset;
+  int i;
+
+  /* Find lowest loadable section to be used as starting point for
+     continguous sections. FIXME!! won't work without call to find
+     .text first, but this assumes text is lowest section. */
+  lower_sect = bfd_get_section_by_name (abfd, ".text");
+  if (lower_sect == NULL)
+    bfd_map_over_sections (abfd, find_lowest_section, &lower_sect);
+  if (lower_sect == NULL)
+    {
+      warning (_("no loadable sections found in added symbol-file %s"),
+	       bfd_get_filename (abfd));
+      lower_offset = 0;
+    }
+  else
+    lower_offset = bfd_section_vma (bfd_get_filename (abfd), lower_sect);
+
+  /* Calculate offsets for the loadable sections.
+     FIXME! Sections must be in order of increasing loadable section
+     so that contiguous sections can use the lower-offset!!!
+
+     Adjust offsets if the segments are not contiguous.
+     If the section is contiguous, its offset should be set to
+     the offset of the highest loadable section lower than it
+     (the loadable section directly below it in memory).
+     this_offset = lower_offset = lower_addr - lower_orig_addr */
+
+  for (i = 0; i < addrs->num_sections && addrs->other[i].name; i++)
+    {
+      if (addrs->other[i].addr != 0)
+	{
+	  sect = bfd_get_section_by_name (abfd, addrs->other[i].name);
+	  if (sect)
+	    {
+	      addrs->other[i].addr -= bfd_section_vma (abfd, sect);
+	      lower_offset = addrs->other[i].addr;
+	      /* This is the index used by BFD. */
+	      addrs->other[i].sectindex = sect->index;
+	    }
+	  else
+	    {
+	      warning (_("section %s not found in %s"), addrs->other[i].name,
+		       bfd_get_filename (abfd));
+	      addrs->other[i].addr = 0;
+	    }
+	}
+      else
+	addrs->other[i].addr = lower_offset;
     }
+}
+
+/* Parse the user's idea of an offset for dynamic linking, into our idea
+   of how to represent it for fast symbol reading.  This is the default
+   version of the sym_fns.sym_offsets function for symbol readers that
+   don't need to do anything special.  It allocates a section_offsets table
+   for the objectfile OBJFILE and stuffs ADDR into all of the offsets.  */
+
+void
+default_symfile_offsets (struct objfile *objfile,
+			 struct section_addr_info *addrs)
+{
+  objfile->num_sections = bfd_count_sections (objfile->obfd);
+  objfile->section_offsets = (struct section_offsets *)
+    obstack_alloc (&objfile->objfile_obstack,
+		   SIZEOF_N_SECTION_OFFSETS (objfile->num_sections));
+  relative_addr_info_to_section_offsets (objfile->section_offsets,
+					 objfile->num_sections, addrs);
 
   /* For relocatable files, all loadable sections will start at zero.
      The zero is meaningless, so try to pick arbitrary addresses such
@@ -803,65 +914,8 @@ syms_from_objfile (struct objfile *objfile,
 
      We no longer warn if the lowest section is not a text segment (as
      happens for the PA64 port.  */
-  if (!mainline && addrs && addrs->other[0].name)
-    {
-      asection *lower_sect;
-      asection *sect;
-      CORE_ADDR lower_offset;
-      int i;
-
-      /* Find lowest loadable section to be used as starting point for
-         continguous sections. FIXME!! won't work without call to find
-	 .text first, but this assumes text is lowest section. */
-      lower_sect = bfd_get_section_by_name (objfile->obfd, ".text");
-      if (lower_sect == NULL)
-	bfd_map_over_sections (objfile->obfd, find_lowest_section,
-			       &lower_sect);
-      if (lower_sect == NULL)
-	{
-	  warning (_("no loadable sections found in added symbol-file %s"),
-		   objfile->name);
-	  lower_offset = 0;
-	}
-      else
-	lower_offset = bfd_section_vma (objfile->obfd, lower_sect);
-
-      /* Calculate offsets for the loadable sections.
- 	 FIXME! Sections must be in order of increasing loadable section
- 	 so that contiguous sections can use the lower-offset!!!
-
-         Adjust offsets if the segments are not contiguous.
-         If the section is contiguous, its offset should be set to
- 	 the offset of the highest loadable section lower than it
- 	 (the loadable section directly below it in memory).
- 	 this_offset = lower_offset = lower_addr - lower_orig_addr */
-
-        for (i = 0; i < addrs->num_sections && addrs->other[i].name; i++)
-          {
-            if (addrs->other[i].addr != 0)
-              {
-                sect = bfd_get_section_by_name (objfile->obfd,
-                                                addrs->other[i].name);
-                if (sect)
-                  {
-                    addrs->other[i].addr
-                      -= bfd_section_vma (objfile->obfd, sect);
-                    lower_offset = addrs->other[i].addr;
-                    /* This is the index used by BFD. */
-                    addrs->other[i].sectindex = sect->index ;
-                  }
-                else
-                  {
-                    warning (_("section %s not found in %s"),
-                             addrs->other[i].name,
-                             objfile->name);
-                    addrs->other[i].addr = 0;
-                  }
-              }
-            else
-              addrs->other[i].addr = lower_offset;
-          }
-    }
+  if (addrs && addrs->other[0].name)
+    addr_info_make_relative (addrs, objfile->obfd);
 
   /* Initialize symbol reading routines for this objfile, allow complaints to
      appear for this new file, and record how verbose to be, then do the
@@ -948,7 +1002,6 @@ symbol_file_add_with_addrs_or_offsets (bfd *abfd,
   struct objfile *objfile;
   struct partial_symtab *psymtab;
   char *debugfile = NULL;
-  struct section_addr_info *orig_addrs = NULL;
   struct cleanup *my_cleanups;
   const char *name = bfd_get_filename (abfd);
   const int from_tty = add_flags & SYMFILE_VERBOSE;
@@ -967,12 +1020,6 @@ symbol_file_add_with_addrs_or_offsets (bfd *abfd,
   objfile = allocate_objfile (abfd, flags);
   discard_cleanups (my_cleanups);
 
-  if (addrs)
-    {
-      orig_addrs = copy_section_addr_info (addrs);
-      make_cleanup_free_section_addr_info (orig_addrs);
-    }
-
   /* We either created a new mapped symbol table, mapped an existing
      symbol table file which has not had initial symbol reading
      performed, or need to read an unmapped symbol table. */
@@ -1017,18 +1064,17 @@ symbol_file_add_with_addrs_or_offsets (bfd *abfd,
      `.gnu_debuglink' may no longer be present with `.note.gnu.build-id'.  */
   if (objfile->psymtabs == NULL)
     debugfile = find_separate_debug_file (objfile);
+
   if (debugfile)
     {
-      if (addrs != NULL)
-	{
-	  objfile->separate_debug_objfile
-            = symbol_file_add (debugfile, add_flags, orig_addrs, flags);
-	}
-      else
-	{
-	  objfile->separate_debug_objfile
-            = symbol_file_add (debugfile, add_flags, NULL, flags);
-	}
+      struct section_addr_info *objfile_addrs;
+
+      objfile_addrs = build_section_addr_info_from_objfile (objfile);
+      make_cleanup (xfree, objfile_addrs);
+
+      objfile->separate_debug_objfile = symbol_file_add (debugfile, add_flags,
+							 objfile_addrs, flags);
+
       objfile->separate_debug_objfile->separate_debug_objfile_backlink
         = objfile;
 
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -175,6 +175,16 @@ struct sym_fns
 
 };
 
+extern struct section_addr_info *
+		 build_section_addr_info_from_objfile (struct objfile *objfile);
+
+extern void relative_addr_info_to_section_offsets
+  (struct section_offsets *section_offsets, int num_sections,
+   struct section_addr_info *addrs);
+
+extern void addr_info_make_relative (struct section_addr_info *addrs,
+				     bfd *abfd);
+
 /* The default version of sym_fns.sym_offsets for readers that don't
    do anything special.  */
 
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -1408,7 +1408,12 @@ lookup_objfile_from_block (const struct block *block)
   /* Go through SYMTABS.  */
   ALL_SYMTABS (obj, s)
     if (block == BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), GLOBAL_BLOCK))
-      return obj;
+      {
+	if (obj->separate_debug_objfile_backlink)
+	  obj = obj->separate_debug_objfile_backlink;
+
+	return obj;
+      }
 
   return NULL;
 }
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-interp-lib.c
@@ -0,0 +1,40 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>.  */
+
+#include <signal.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+
+void
+libfunc (const char *action)
+{
+  assert (action != NULL);
+
+  if (strcmp (action, "segv") == 0)
+    raise (SIGSEGV);
+
+  if (strcmp (action, "sleep") == 0)
+    {
+      puts ("sleeping");
+      fflush (stdout);
+
+      sleep (60);
+    }
+
+  assert (0);
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-interp-main.c
@@ -0,0 +1,30 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+
+extern void libfunc (const char *action);
+
+int
+main (int argc, char **argv)
+{
+  assert (argc == 2);
+
+  libfunc (argv[1]);
+
+  return 0;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-interp.exp
@@ -0,0 +1,542 @@
+# Copyright 2009 Free Software Foundation, Inc.
+
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This test only works on GNU/Linux.
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] || [skip_shlib_tests]} {
+    continue
+}
+
+set test "break-interp"
+set binprefix ${objdir}/${subdir}/${test}
+# Only to get the $interp_system name.
+set srcfile_test "start.c"
+set binfile_test ${test}-test
+set binfile_lib ${objdir}/${subdir}/${test}.so
+set srcfile "${test}-main.c"
+set srcfile_lib "${test}-lib.c"
+
+if [get_compiler_info ${binfile_lib}] {
+    return -1
+}
+
+# Use -soname so that it is listed with " => " by ldd and this testcase makes
+# a copy of ${binfile_lib} for each prelink variant.
+
+if {[gdb_compile_shlib ${srcdir}/${subdir}/${srcfile_lib} ${binfile_lib} [list debug additional_flags=-Wl,-soname,${test}.so]] != ""} {
+    return -1
+}
+
+if {[build_executable ${test}.exp $binfile_test ${srcfile_test} {}] == -1} {
+    return -1
+}
+
+# Return the interpreter filename string.
+# Return "" if no interpreter was found.
+proc section_get {exec section} {
+    global objdir
+    global subdir
+    set tmp "${objdir}/${subdir}/break-interp.interp"
+    set objcopy_program [transform objcopy]
+
+    set command "exec $objcopy_program -O binary --set-section-flags $section=A --change-section-address $section=0 -j $section $exec $tmp"
+    verbose -log "command is $command"
+    set result [catch $command output]
+    verbose -log "result is $result"
+    verbose -log "output is $output"
+    if {$result == 1} {
+	return ""
+    }
+    set fi [open $tmp]
+    fconfigure $fi -translation binary
+    set data [read $fi]
+    close $fi
+    #file delete $tmp
+    # .interp has size $len + 1 but .gnu_debuglink contains garbage after \000.
+    set len [string first \000 $data]
+    if {$len < 0} {
+	verbose -log "section $section not found"
+	return ""
+    }
+    set retval [string range $data 0 [expr $len - 1]]
+    verbose -log "section $section is <$retval>"
+    return $retval
+}
+
+# Note: The separate debug info file content build-id/crc32 are not verified
+# contrary to the GDB search algorithm skipping non-matching ones.
+proc system_debug_get {exec} {
+    global debug_root
+
+    set exec_build_id_debug [build_id_debug_filename_get $exec]
+    set debug_base "[file tail $exec].debug"
+    set exec_dir [file dirname $exec]
+
+    # isfile returns 1 even for symlinks to files.
+    set retval $debug_root/$exec_build_id_debug
+    if [file isfile $retval] {
+	return $retval
+    }
+    set retval $exec_dir/$debug_base
+    if [file isfile $retval] {
+	return $retval
+    }
+    set retval $exec_dir/.debug/$debug_base
+    if [file isfile $retval] {
+	return $retval
+    }
+    set retval $debug_root/$exec_dir/$debug_base
+    if [file isfile $retval] {
+	return $retval
+    }
+    return ""
+}
+
+gdb_exit
+gdb_start
+set debug_root ""
+set test "show debug-file-directory"
+gdb_test_multiple $test $test {
+    -re "The directory where separate debug symbols are searched for is \"(.*)\".\r\n$gdb_prompt $" {
+	set debug_root $expect_out(1,string)
+    }
+}
+
+set interp_system [section_get ${objdir}/${subdir}/$binfile_test .interp]
+set interp_system_debug [system_debug_get $interp_system]
+verbose -log "$interp_system has debug $interp_system_debug"
+
+proc prelinkNO_run {arg} {
+    set command "exec /usr/sbin/prelink -uN $arg"
+    verbose -log "command is $command"
+    set result [catch $command output]
+    verbose -log "result is $result"
+    verbose -log "output is $output"
+    return [list $result $output]
+}
+
+proc prelinkNO {arg {name {}}} {
+    if {$name == ""} {
+	set name [file tail $arg]
+    }
+    set test "unprelink $name"
+    set run [prelinkNO_run $arg]
+    set result [lindex $run 0]
+    set output [lindex $run 1]
+    if {$result == 0 && $output == ""} {
+	verbose -log "$name has been now unprelinked"
+	set run [prelinkNO_run $arg]
+	set result [lindex $run 0]
+	set output [lindex $run 1]
+    }
+    # Last line does miss the trailing \n.
+    if {$result == 1 && [regexp {^(/usr/sbin/prelink: [^ ]* does not have .gnu.prelink_undo section\n?)*$} $output]} {
+	pass $test
+	return 1
+    } else {
+	fail $test
+	return 0
+    }
+}
+
+proc prelinkYES {arg {name ""}} {
+    if {$name == ""} {
+	set name [file tail $arg]
+    }
+    set test "prelink $name"
+    set command "exec /usr/sbin/prelink -qNR --no-exec-shield $arg"
+    verbose -log "command is $command"
+    set result [catch $command output]
+    verbose -log "result is $result"
+    verbose -log "output is $output"
+    if {$result == 0 && $output == ""} {
+	pass $test
+	return 1
+    } else {
+	fail $test
+	return 0
+    }
+}
+
+# Resolve symlinks.
+proc symlink_resolve {file} {
+    set loop 0
+    while {[file type $file] == "link"} {
+	set target [file readlink $file]
+	if {[file pathtype $target] == "relative"} {
+	    set src2 [file dirname $file]/$target
+	} else {
+	    set src2 $target
+	}
+	verbose -log "Resolved symlink $file targetting $target as $src2"
+	set file $src2
+
+	set loop [expr $loop + 1]
+	if {$loop > 30} {
+	    fail "Looping symlink resolution for $file"
+	    return ""
+	}
+    }
+    return $file
+}
+
+proc copy {src dest} {
+    set src [symlink_resolve $src]
+    # Test name would contain build-id hash for symlink-unresolved $src.
+    set test "copy [file tail $src] to [file tail $dest]"
+    set command "file copy -force $src $dest"
+    verbose -log "command is $command"
+    if [catch $command] {
+	fail $test
+	return 0
+    } else {
+    	pass $test
+	return 1
+    }
+}
+
+proc strip_debug {dest} {
+    set test "strip [file tail $dest]"
+    set strip_program [transform strip]
+    set command "exec $strip_program --strip-debug $dest"
+    verbose -log "command is $command"
+    if [catch $command] {
+	fail $test
+	return 0
+    } else {
+    	pass $test
+	return 1
+    }
+}
+
+# `runto' does not check we stopped really at the function we specified.
+proc reach {func command} {
+    global gdb_prompt
+
+    if [gdb_breakpoint $func allow-pending] {
+	set test "reach $func"
+	gdb_test_multiple $command $test {
+	    -re "Breakpoint \[0-9\]+, $func \\(.*\\) at .*:\[0-9\]+\r\n.*$gdb_prompt $" {
+		pass $test
+	    }
+	    -re "Breakpoint \[0-9\]+, \[0-9xa-f\]+ in $func \\(\\)( from .*)?\r\n$gdb_prompt $" { 
+		pass $test
+	    }
+	}
+    }
+}
+
+proc test_core {file} {
+    global srcdir subdir gdb_prompt
+
+    set corefile [core_find $file {} "segv"]
+    if {$corefile == ""} {
+	return
+    }
+
+    gdb_exit
+    gdb_start
+    # Clear it to never find any separate debug infos in $debug_root.
+    gdb_test "set debug-file-directory" "" "set debug-file-directory for core"
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load $file
+
+    # Do not check the binary filename as it may be truncated.
+    gdb_test "core-file $corefile" "Core was generated by .*\r\n#0 .*" "core loaded"
+
+    gdb_test "bt" "#\[0-9\]+ +\[^\r\n\]*\\mlibfunc\\M\[^\r\n\]*\r\n#\[0-9\]+ +\[^\r\n\]*\\mmain\\M.*" "core main bt"
+}
+
+proc test_attach {file} {
+    global board_info
+
+    gdb_exit
+
+    set test "sleep function started"
+
+    set command "${file} sleep"
+    set res [remote_spawn host $command];
+    if { $res < 0 || $res == "" } {
+	perror "Spawning $command failed."
+	fail $test
+	return
+    }
+    set pid [exp_pid -i $res]
+    gdb_expect {
+	-re "sleeping\r\n" {
+	    pass $test
+	}
+	eof {
+	    fail "$test (eof)"
+	    return
+	}
+	timeout {
+	    fail "$test (timeout)"
+	    return
+	}
+    }
+
+    gdb_exit
+    gdb_start
+    gdb_test "attach $pid" "Attaching to process $pid\r\n.*" "attach"
+    gdb_test "bt" "#\[0-9\]+ +\[^\r\n\]*\\mlibfunc\\M\[^\r\n\]*\r\n#\[0-9\]+ +\[^\r\n\]*\\mmain\\M.*" "attach main bt"
+    gdb_exit
+
+    remote_exec host "kill -9 $pid"
+}
+
+proc test_ld {file ifmain trynosym} {
+    global srcdir subdir gdb_prompt
+
+    # First test normal `file'-command loaded $FILE with symbols.
+
+    gdb_exit
+    gdb_start
+    # Clear it to never find any separate debug infos in $debug_root.
+    gdb_test "set debug-file-directory"
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load $file
+
+    reach "dl_main" "run segv"
+
+    gdb_test "bt" "#0 +\[^\r\n\]*\\mdl_main\\M.*" "dl bt"
+
+    if $ifmain {
+	reach "main" continue
+
+	reach "libfunc" continue
+
+	gdb_test "bt" "#0 +\[^\r\n\]*\\mlibfunc\\M\[^\r\n\]*\r\n#1 +\[^\r\n\]*\\mmain\\M.*" "main bt"
+
+	test_core $file
+
+	test_attach $file
+    }
+
+    if !$trynosym {
+	return
+    }
+
+    global pf_prefix
+    set old_ldprefix $pf_prefix
+    lappend pf_prefix "symbol-less:"
+
+    # Test also `exec-file'-command loaded $FILE - therefore without symbols.
+    # SYMBOL_OBJFILE is not available and only EXEC_BFD must be used.
+
+    gdb_exit
+    gdb_start
+    # Clear it to never find any separate debug infos in $debug_root.
+    gdb_test "set debug-file-directory"
+    gdb_reinitialize_dir $srcdir/$subdir
+
+    # Test no (error) message has been printed by `exec-file'.
+    set escapedfile [string_to_regexp $file]
+    gdb_test "exec-file $file" "exec-file $escapedfile" "load"
+
+    if $ifmain {
+	reach "dl_main" run
+
+	set test "info files"
+	set entrynohex ""
+	gdb_test_multiple $test $test {
+	    -re "\r\n\[\t \]*Entry point:\[\t \]*0x(\[0-9a-f\]+)\r\n.*$gdb_prompt $" {
+		set entrynohex $expect_out(1,string) 
+		pass $test
+	    }
+	}
+	if {$entrynohex != ""} {
+	    gdb_test "break *0x$entrynohex" "" "break at entry point"
+	    gdb_test "continue" "\r\nBreakpoint \[0-9\]+, 0x0*$entrynohex in .*" "entry point reached"
+	}
+    } else {
+	# There is no symbol to break at ld.so.  Moreover it can exit with an
+	# error code.
+	gdb_test "run" "Program exited (normally|with code \[0-9\]+)\\." "ld.so exit"
+    }
+
+    set pf_prefix $old_ldprefix
+}
+
+# Create separate binaries for each testcase - to make the possible reported
+# problem reproducible after the whole test run finishes.
+
+set old_ldprefix $pf_prefix
+foreach ldprelink {NO YES} {
+    foreach ldsepdebug {NO IN SEP} {
+	# Skip running the ldsepdebug test if we do not have system separate
+	# debug info available.
+	if {$interp_system_debug == "" && $ldsepdebug == "SEP"} {
+	    continue
+	}
+
+	set ldname "LDprelink${ldprelink}debug${ldsepdebug}"
+	set interp $binprefix-$ldname
+
+	# prelink needs to always prelink all the dependencies to do any file
+	# modifications of its files.  ld.so also needs all the dependencies to
+	# be prelinked to omit the relocation process.  In-memory file offsets
+	# are not dependent whether ld.so went the prelink way or through the
+	# relocation process.
+	#
+	# For GDB we are not interested whether prelink succeeds as it is
+	# transparent to GDB.  GDB is being tested for differences of file
+	# offsets vs. in-memory offsets.  So we have to prelink even ld.so for
+	# the BIN modification to happen but we need to restore the original
+	# possibly unprelinked ld.so to test all the combinations for GDB.
+	set interp_saved ${interp}-saved
+
+	set pf_prefix $old_ldprefix
+	lappend pf_prefix "$ldname:"
+
+	if {$ldsepdebug == "NO"} {
+	    copy $interp_system $interp
+	    # Never call strip-debug before unprelink:
+	    # prelink: ...: Section .note.gnu.build-id created after prelinking
+	    if ![prelinkNO $interp] {
+		continue
+	    }
+	    strip_debug $interp
+	} elseif {$ldsepdebug == "IN" && $interp_system_debug == ""} {
+	    copy $interp_system $interp
+	} elseif {$ldsepdebug == "IN" && $interp_system_debug != ""} {
+	    copy $interp_system $interp
+	    copy $interp_system_debug "${interp}.debug"
+	    # eu-unstrip: DWARF data in '...' not adjusted for prelinking bias; consider prelink -u
+	    if {![prelinkNO $interp] || ![prelinkNO "${interp}.debug"]} {
+		continue
+	    }
+	    set test "eu-unstrip unprelinked:[file tail $interp_system] + [file tail $interp_system_debug] to [file tail $interp]"
+	    set command "exec eu-unstrip -o $interp $interp ${interp}.debug"
+	    verbose -log "command is $command"
+	    if [catch $command] {
+		setup_xfail *-*-*
+		fail $test
+		continue
+	    } else {
+		pass $test
+	    }
+	} elseif {$ldsepdebug == "SEP" && $interp_system_debug == ""} {
+	    copy $interp_system $interp
+	    # eu-unstrip: DWARF data in '...' not adjusted for prelinking bias; consider prelink -u
+	    if ![prelinkNO $interp] {
+		continue
+	    }
+	    gdb_gnu_strip_debug $interp
+	} elseif {$ldsepdebug == "SEP" && $interp_system_debug != ""} {
+	    copy $interp_system $interp
+	    copy $interp_system_debug "${interp}.debug"
+	}
+
+	if {$ldsepdebug == "SEP"} {
+	    if ![prelinkNO "${interp}.debug"] {
+		continue
+	    }
+	} else {
+	    file delete "${interp}.debug"
+	}
+
+	if ![prelink$ldprelink $interp] {
+	    continue
+	}
+	test_ld $interp 0 [expr {$ldsepdebug == "NO"}]
+
+	if ![copy $interp $interp_saved] {
+	    continue
+	}
+	set old_binprefix $pf_prefix
+	foreach binprelink {NO YES} {
+	    foreach binsepdebug {NO IN SEP} {
+		foreach binpie {NO YES} {
+		    # This combination is not possible, non-PIE (fixed address)
+		    # binary cannot be prelinked to any (other) address.
+		    if {$binprelink == "YES" && $binpie == "NO"} {
+			continue
+		    }
+
+		    set binname "BINprelink${binprelink}debug${binsepdebug}pie${binpie}"
+		    set exec $binprefix-$binname
+		    set dir ${exec}.d
+
+		    set pf_prefix $old_binprefix
+		    lappend pf_prefix "$binname:"
+
+		    set opts "additional_flags=-Wl,--dynamic-linker,$interp,-rpath,$dir"
+		    lappend opts "additional_flags=-Wl,$binfile_lib,-rpath,[file dirname $binfile_lib]"
+		    if {$binsepdebug != "NO"} {
+			lappend opts {debug}
+		    }
+		    if {$binpie == "YES"} {
+			lappend opts {additional_flags=-fPIE -pie}
+		    }
+		    if {[build_executable ${test}.exp [file tail $exec] $srcfile $opts] == -1} {
+			continue;
+		    }
+		    if {$binsepdebug == "SEP"} {
+			gdb_gnu_strip_debug $exec
+			# Just a sanity check.  As gdb_gnu_strip_debug uses the
+			# "[file dirname $exec]/.debug/[file tail $exec].debug"
+			# variant delete the higher-priority exec.debug file.
+			file delete "$exec.debug"
+		    }
+
+		    # Supply a self-sufficent directory $dir with the required
+		    # libraries.  To make an executable properly prelinked all
+		    # its dependencies on libraries must be also prelinked.  If
+		    # some of the system libraries is currently not prelinked
+		    # we have no right to prelink (modify it) at its current
+		    # system place.
+
+		    file delete -force $dir
+		    file mkdir $dir
+
+		    set command "ldd $exec"
+		    set result [catch "exec $command" output]
+		    verbose -log "result of $command is $result"
+		    verbose -log "output of $command is $output"
+		    if {$result != 0 || $output == ""} {
+			fail $command
+		    } else {
+			pass $command
+		    }
+
+		    # gdb testsuite will put there also needless -lm.
+		    set test "$command output contains libc"
+		    set libc [regexp -all -inline -line {^.* => (/[^ ]+).*$} $output]
+		    if {[llength $libc] == 0} {
+			fail $test
+		    } else {
+			pass $test
+		    }
+
+		    set dests {}
+		    for {set i 1} {$i < [llength $libc]} {incr i 2} {
+			set abspath [lindex $libc $i]
+			set dest "$dir/[file tail $abspath]"
+			copy $abspath $dest
+			lappend dests $dest
+		    }
+
+		    if {[prelink$binprelink "--dynamic-linker=$interp --ld-library-path=$dir $exec $interp [concat $dests]" $exec]
+		        && [copy $interp_saved $interp]} {
+			test_ld $exec 1 [expr {$binsepdebug == "NO"}]
+		    }
+		}
+	    }
+	}
+
+	file delete $interp_saved
+    }
+}
+set pf_prefix $old_ldprefix
--- a/gdb/testsuite/gdb.base/corefile.exp
+++ b/gdb/testsuite/gdb.base/corefile.exp
@@ -42,65 +42,12 @@ if [get_compiler_info ${binfile}] {
     return -1;
 }
 
-# Create a core file named "corefile" rather than just "core", to
-# avoid problems with sys admin types that like to regularly prune all
-# files named "core" from the system.
-#
-# Arbitrarily try setting the core size limit to "unlimited" since
-# this does not hurt on systems where the command does not work and
-# allows us to generate a core on systems where it does.
-#
-# Some systems append "core" to the name of the program; others append
-# the name of the program to "core"; still others (like Linux, as of
-# May 2003) create cores named "core.PID".  In the latter case, we
-# could have many core files lying around, and it may be difficult to
-# tell which one is ours, so let's run the program in a subdirectory.
-set found 0
-set coredir "${objdir}/${subdir}/coredir.[getpid]"
-file mkdir $coredir
-catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile}; true) >/dev/null 2>&1\""
-#      remote_exec host "${binfile}"
-foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" {
-    if [remote_file build exists $i] {
-	remote_exec build "mv $i ${objdir}/${subdir}/corefile"
-	set found 1
-    }
-}
-# Check for "core.PID".
-if { $found == 0 } {
-    set names [glob -nocomplain -directory $coredir core.*]
-    if {[llength $names] == 1} {
-        set corefile [file join $coredir [lindex $names 0]]
-        remote_exec build "mv $corefile ${objdir}/${subdir}/corefile"
-        set found 1
-    }
-}
-if { $found == 0 } {
-    # The braindamaged HPUX shell quits after the ulimit -c above
-    # without executing ${binfile}.  So we try again without the
-    # ulimit here if we didn't find a core file above.
-    # Oh, I should mention that any "braindamaged" non-Unix system has
-    # the same problem. I like the cd bit too, it's really neat'n stuff.
-    catch "system \"(cd ${objdir}/${subdir}; ${binfile}; true) >/dev/null 2>&1\""
-    foreach i "${objdir}/${subdir}/core ${objdir}/${subdir}/core.coremaker.c ${binfile}.core" {
-	if [remote_file build exists $i] {
-	    remote_exec build "mv $i ${objdir}/${subdir}/corefile"
-	    set found 1
-	}
-    }
+set corefile [core_find $binfile {coremmap.data}]
+if {$corefile == ""} {
+    return 0;
 }
 
-# Try to clean up after ourselves. 
-remote_file build delete [file join $coredir coremmap.data]
-remote_exec build "rmdir $coredir"
-    
-if { $found == 0  } {
-    warning "can't generate a core file - core tests suppressed - check ulimit -c"
-    return 0
-}
-
-#
-# Test that we can simply startup with a "-core=corefile" command line arg
+# Test that we can simply startup with a "-core=$corefile" command line arg
 # and recognize that the core file is a valid, usable core file.
 # To do this, we must shutdown the currently running gdb and restart
 # with the -core args.  We can't use gdb_start because it looks for
@@ -114,27 +61,27 @@ if { $found == 0  } {
 
 gdb_exit
 if $verbose>1 then {
-    send_user "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS -core=$objdir/$subdir/corefile\n"
+    send_user "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS -core=$corefile\n"
 }
 
 set oldtimeout $timeout
 set timeout [expr "$timeout + 60"]
 verbose "Timeout is now $timeout seconds" 2
-eval "spawn $GDB $INTERNAL_GDBFLAGS $GDBFLAGS -core=$objdir/$subdir/corefile"
+eval "spawn $GDB $INTERNAL_GDBFLAGS $GDBFLAGS -core=$corefile"
 expect {
     -re "Couldn't find .* registers in core file.*$gdb_prompt $" {
-        fail "args: -core=corefile (couldn't find regs)"
+        fail "args: -core=[file tail $corefile] (couldn't find regs)"
     }
     -re "Core was generated by .*coremaker.*\r\n\#0  .*\(\).*\r\n$gdb_prompt $" {
-	pass "args: -core=corefile"
+	pass "args: -core=[file tail $corefile]"
     }
     -re "Core was generated by .*\r\n\#0  .*\(\).*\r\n$gdb_prompt $" {
-	pass "args: -core=corefile (with bad program name)"
+	pass "args: -core=[file tail $corefile] (with bad program name)"
     }
     -re ".*registers from core file: File in wrong format.* $" {
-	fail "args: -core=corefile (could not read registers from core file)"
+	fail "args: -core=[file tail $corefile] (could not read registers from core file)"
     }
-    -re ".*$gdb_prompt $"	{ fail "args: -core=corefile" }
+    -re ".*$gdb_prompt $"	{ fail "args: -core=[file tail $corefile]" }
     timeout 		{ fail "(timeout) starting with -core" }
 }
 
@@ -147,22 +94,22 @@ expect {
 close;
 
 if $verbose>1 then {
-    send_user "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS $binfile -core=$objdir/$subdir/corefile\n"
+    send_user "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS $binfile -core=$corefile\n"
 }
 
 
-eval "spawn $GDB $INTERNAL_GDBFLAGS $GDBFLAGS $binfile -core=$objdir/$subdir/corefile";
+eval "spawn $GDB $INTERNAL_GDBFLAGS $GDBFLAGS $binfile -core=$corefile";
 expect {
     -re "Core was generated by .*coremaker.*\r\n\#0  .*\(\).*\r\n$gdb_prompt $" {
-	pass "args: execfile -core=corefile"
+	pass "args: execfile -core=[file tail $corefile]"
     }
     -re "Core was generated by .*\r\n\#0  .*\(\).*\r\n$gdb_prompt $"	 {
-	pass "args: execfile -core=corefile (with bad program name)"
+	pass "args: execfile -core=[file tail $corefile] (with bad program name)"
     }
     -re ".*registers from core file: File in wrong format.* $" {
-	fail "args: execfile -core=corefile (could not read registers from core file)"
+	fail "args: execfile -core=[file tail $corefile] (could not read registers from core file)"
     }
-    -re ".*$gdb_prompt $"	{ fail "args: execfile -core=corefile" }
+    -re ".*$gdb_prompt $"	{ fail "args: execfile -core=[file tail $corefile]" }
     timeout 		{ fail "(timeout) starting with -core" }
 }
 set timeout $oldtimeout
@@ -178,7 +125,7 @@ gdb_load ${binfile}
 
 # Test basic corefile recognition via core-file command.
 
-send_gdb "core-file $objdir/$subdir/corefile\n"
+send_gdb "core-file $corefile\n"
 gdb_expect {
     -re ".* program is being debugged already.*y or n. $" {
 	# gdb_load may connect us to a gdbserver.
--- a/gdb/testsuite/gdb.base/pie-support.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* This testcase is part of GDB, the GNU debugger.
-
-   Copyright 2009 Free Software Foundation, Inc.
-
-   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 3 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, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdio.h>
-
-void
-f1 (int a)
-{
-  printf ("a = %d\n", a);
-}
-
-int
-main (int argc, char *argv[])
-{
-  f1 (1);
-
-  return 0;
-}
--- a/gdb/testsuite/gdb.base/pie-support.exp
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2009 Free Software Foundation, Inc.
-
-# 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 3 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, see <http://www.gnu.org/licenses/>.
-
-set testfile pie-support
-set srcfile ${testfile}.c
-set objfile ${objdir}/${subdir}/${testfile}.o
-set binfile ${objdir}/${subdir}/${testfile}
-
-if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${objfile}" object {quiet debug additional_flags=-fpie}] != "" } {
-    untested "Couldn't compile test PIE object file."
-    return -1
-}
-if  { [gdb_compile "${objfile}" "${binfile}" executable {quiet debug additional_flags=-pie}] != "" } {
-    untested "Couldn't compile test PIE binary."
-    return -1
-}
-
-# Get things started.
-
-gdb_exit
-gdb_start
-gdb_reinitialize_dir $srcdir/$subdir
-
-if [is_remote host] {
-  set binfile [remote_download host $binfile]
-    if { $binfile == "" } {
-      untested "Couldn't download remote test binary."
-      return -1
-    }
-}
-
-# The file command used to kill the remote target.  For the benefit
-# of the testsuite, preserve this behavior.
-send_gdb "kill\n"
-gdb_expect 120 {
-    -re "Kill the program being debugged. .y or n. $" {
-	send_gdb "y\n"
-	verbose "\t\tKilling previous program being debugged"
-	exp_continue
-    }
-    -re "$gdb_prompt $" {
-	# OK.
-    }
-}
-
-gdb_test "file $binfile" "current binary is a PIE.*" "correctly detected PIE binary"
--- /dev/null
+++ b/gdb/testsuite/gdb.base/valgrind-db-attach.c
@@ -0,0 +1,30 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+
+int main()
+{
+  void *p;
+
+  p = malloc (1);
+  if (p == NULL)
+    return 1;
+  free (p);
+  free (p);	/* double-free */
+  return 0;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.base/valgrind-db-attach.exp
@@ -0,0 +1,74 @@
+# Copyright 2009 Free Software Foundation, Inc.
+
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+set test valgrind-db-attach
+set srcfile $test.c
+set executable $test
+set binfile ${objdir}/${subdir}/${executable}
+if {[build_executable $test.exp $executable $srcfile {debug}] == -1} {
+    return -1
+}
+
+gdb_exit
+
+# remote_spawn breaks the command on each whitespace despite possible quoting.
+# Use backslash-escaped whitespace there instead:
+
+set db_command "--db-command=$GDB $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts] %f %p"
+regsub -all " " $db_command "\\ " db_command
+
+set test "spawn valgrind"
+set cmd "valgrind --db-attach=yes $db_command $binfile"
+set res [remote_spawn host $cmd];
+if { $res < 0 || $res == "" } {
+    verbose -log "Spawning $cmd failed."
+    setup_xfail *-*-*
+    fail $test
+    return -1
+}
+pass $test
+# Declare GDB now as running.
+set gdb_spawn_id -1
+
+set test "valgrind started"
+# The trailing '.' differs for different memcheck versions.
+gdb_test_multiple "" $test {
+    -re "Memcheck, a memory error detector\\.?\r\n" {
+	pass $test
+    }
+    -re "valgrind: failed to start tool 'memcheck' for platform '.*': No such file or directory" {
+	setup_xfail *-*-*
+	fail $test
+	return -1
+    }
+}
+
+set double_free [gdb_get_line_number "double-free"]
+
+gdb_test_multiple "" $test {
+    -re "Invalid free\\(\\) / delete / delete\\\[\\\]\r\n.*: main \\(${srcfile}:$double_free\\)\r\n.*---- Attach to debugger \\? --- \[^\r\n\]* ---- " {
+	send_gdb "y\r"
+    }
+    -re "---- Attach to debugger \\? --- \[^\r\n\]* ---- " {
+	send_gdb "n\r"
+	exp_continue
+    }
+}
+
+# Initialization from default_gdb_start.
+gdb_test "set height 0"
+gdb_test "set width 0"
+
+gdb_test "bt" "in main \\(.*\\) at .*${srcfile}:$double_free"
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges.exp
@@ -55,6 +55,12 @@ gdb_start
 gdb_reinitialize_dir $srcdir/$subdir
 gdb_load ${binfile}
 
+# Test also objfile->psymtabs_addrmap relocations for -fPIE -pie builds below.
+# On some targets it may possibly fail but the program is being started only
+# for the PIE build so try it anyway.
+
+runto_main
+
 # Correct output:
 # 	Line 39 of "../.././gdb/testsuite/gdb.dwarf2/dw2-ranges.S" starts at address 0x4 and ends at 0x8.
 # Wrong output:
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -2780,6 +2780,11 @@ proc gdb_gnu_strip_debug { dest args } {
       return 1
     }
 
+    # Workaround PR binutils/10802:
+    # Preserve the 'x' bit also for PIEs (Position Independent Executables).
+    set perm [file attributes ${dest} -permissions]
+    file attributes ${stripped_file} -permissions $perm
+
     # Get rid of everything but the debug info, and store result in debug_file
     # This will be in the .debug subdirectory, see above.
     set result [catch "exec $strip_to_file_program --only-keep-debug ${dest} -o ${debug_file}" output]
@@ -2816,7 +2821,12 @@ proc gdb_gnu_strip_debug { dest args } {
       return 1
     }
 
-   return 0
+    # Workaround PR binutils/10802:
+    # Preserve the 'x' bit also for PIEs (Position Independent Executables).
+    set perm [file attributes ${stripped_file} -permissions]
+    file attributes ${dest} -permissions $perm
+
+    return 0
 }
 
 # Test the output of GDB_COMMAND matches the pattern obtained
@@ -3045,3 +3055,70 @@ if {[info exists TRANSCRIPT]} {
     return [uplevel real_send_gdb $args]
   }
 }
+
+proc core_find {binfile {deletefiles {}} {arg ""}} {
+    global objdir subdir
+
+    set destcore "$binfile.core"
+    file delete $destcore
+
+    # Create a core file named "$destcore" rather than just "core", to
+    # avoid problems with sys admin types that like to regularly prune all
+    # files named "core" from the system.
+    #
+    # Arbitrarily try setting the core size limit to "unlimited" since
+    # this does not hurt on systems where the command does not work and
+    # allows us to generate a core on systems where it does.
+    #
+    # Some systems append "core" to the name of the program; others append
+    # the name of the program to "core"; still others (like Linux, as of
+    # May 2003) create cores named "core.PID".  In the latter case, we
+    # could have many core files lying around, and it may be difficult to
+    # tell which one is ours, so let's run the program in a subdirectory.
+    set found 0
+    set coredir "${objdir}/${subdir}/coredir.[getpid]"
+    file mkdir $coredir
+    catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >/dev/null 2>&1\""
+    #      remote_exec host "${binfile}"
+    foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" {
+	if [remote_file build exists $i] {
+	    remote_exec build "mv $i $destcore"
+	    set found 1
+	}
+    }
+    # Check for "core.PID".
+    if { $found == 0 } {
+	set names [glob -nocomplain -directory $coredir core.*]
+	if {[llength $names] == 1} {
+	    set corefile [file join $coredir [lindex $names 0]]
+	    remote_exec build "mv $corefile $destcore"
+	    set found 1
+	}
+    }
+    if { $found == 0 } {
+	# The braindamaged HPUX shell quits after the ulimit -c above
+	# without executing ${binfile}.  So we try again without the
+	# ulimit here if we didn't find a core file above.
+	# Oh, I should mention that any "braindamaged" non-Unix system has
+	# the same problem. I like the cd bit too, it's really neat'n stuff.
+	catch "system \"(cd ${objdir}/${subdir}; ${binfile}; true) >/dev/null 2>&1\""
+	foreach i "${objdir}/${subdir}/core ${objdir}/${subdir}/core.coremaker.c ${binfile}.core" {
+	    if [remote_file build exists $i] {
+		remote_exec build "mv $i $destcore"
+		set found 1
+	    }
+	}
+    }
+
+    # Try to clean up after ourselves. 
+    foreach deletefile $deletefiles {
+	remote_file build delete [file join $coredir $deletefile]
+    }
+    remote_exec build "rmdir $coredir"
+	
+    if { $found == 0  } {
+	warning "can't generate a core file - core tests suppressed - check ulimit -c"
+	return ""
+    }
+    return $destcore
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]