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]

Re: cope with varying prelink base addresses


On Feb 20, 2006, Daniel Jacobowitz <drow@false.org> wrote:

> The testcase behaves on Solaris, where there's no prelink binary.  I'm
> a little worried about it being noisy where -shared doesn't work,
> and with various non-GCC tools.  Maybe move the gcc_compiled check
> further up?

Isn't gcc_compiled only set when you actually try to compile
something?  That was my impression when I tried the test earlier,
before I submitted the first version of this patch.

> Oh, and please add copyright notices to the testsuite files; they're
> pretty trivial, but we're trying to be consistent about that.

Done, thanks for the info, I didn't realize that because other
testcases I looked at didn't have any.

Kevin, ok to install this one?

for gdb/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	* solib-svr4.h (struct link_map_offsets): Add l_ld_offset and
	l_ld_size fields.
	* solib-svr4.c (struct lm_info): Add l_addr field.
	(LM_ADDR_FROM_LINK_MAP): Renamed from LM_ADDR.
	(HAS_LM_DYNAMIC_FROM_LINK_MAP): New.
	(LM_DYNAMIC_FROM_LINK_MAP): New.
	(LM_ADDR_CHECK): New.  Use it instead of LM_ADDR.
	(svr4_current_sos): Initialize l_addr.  Adjust.
	(svr4_relocate_section_addresses): Adjust.
	(svr4_ilp32_fetch_link_map_offsets): Define new members.
	(svr4_lp64_fetch_link_map_offsets): Likewise.
	* solib-legacy.c (legacy_svr4_fetch_link_map_offsets): Likewise.
	* mipsnbsd-tdep.c (mipsnbsd_ilp32_fetch_link_map_offsets): Likewise.
	(mipsnbsd_lp64_fetch_link_map_offsets): Likewise.
	* Makefile.in (solib-svr4.o): Depend on $(elf_bfd_h).

for gdb/testsuite/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	* gdb.base/prelink.exp: New test.
	* gdb.base/prelink.c, gdb.base/prelink-lib.c: New sources.

Index: gdb/mipsnbsd-tdep.c
===================================================================
--- gdb/mipsnbsd-tdep.c.orig	2006-02-14 16:30:44.000000000 -0200
+++ gdb/mipsnbsd-tdep.c	2006-02-23 17:07:50.000000000 -0300
@@ -339,6 +339,8 @@ mipsnbsd_ilp32_fetch_link_map_offsets (v
       lmo.l_addr_size = 4;
       lmo.l_name_offset = 8;
       lmo.l_name_size = 4;
+      lmo.l_ld_offset = 12;
+      lmo.l_ld_size = 4;
       lmo.l_next_offset = 16;
       lmo.l_next_size = 4;
       lmo.l_prev_offset = 20;
@@ -369,6 +371,8 @@ mipsnbsd_lp64_fetch_link_map_offsets (vo
       lmo.l_addr_size = 8;
       lmo.l_name_offset = 16; 
       lmo.l_name_size = 8;
+      lmo.l_ld_offset = 24;
+      lmo.l_ld_size = 8;
       lmo.l_next_offset = 32;
       lmo.l_next_size = 8;
       lmo.l_prev_offset = 40;
Index: gdb/solib-legacy.c
===================================================================
--- gdb/solib-legacy.c.orig	2006-02-14 16:30:44.000000000 -0200
+++ gdb/solib-legacy.c	2006-02-23 17:07:50.000000000 -0300
@@ -69,6 +69,9 @@ legacy_svr4_fetch_link_map_offsets (void
       lmo.l_next_offset = offsetof (struct link_map, l_next);
       lmo.l_next_size = fieldsize (struct link_map, l_next);
 
+      lmo.l_ld_offset = offsetof (struct link_map, l_ld);
+      lmo.l_ld_size = fieldsize (struct link_map, l_ld);
+
       lmo.l_prev_offset = offsetof (struct link_map, l_prev);
       lmo.l_prev_size = fieldsize (struct link_map, l_prev);
 
@@ -84,6 +87,10 @@ legacy_svr4_fetch_link_map_offsets (void
       lmo.l_next_offset = offsetof (struct link_map, lm_next);
       lmo.l_next_size = fieldsize (struct link_map, lm_next);
 
+      /* FIXME: Is this the right field name, or is it available at all?  */
+      lmo.l_ld_offset = offsetof (struct link_map, lm_ld);
+      lmo.l_ld_size = fieldsize (struct link_map, lm_ld);
+
       lmo.l_name_offset = offsetof (struct link_map, lm_name);
       lmo.l_name_size = fieldsize (struct link_map, lm_name);
 #else /* !defined(HAVE_STRUCT_LINK_MAP_WITH_LM_MEMBERS) */
@@ -98,6 +105,10 @@ legacy_svr4_fetch_link_map_offsets (void
 
       lmo.l_name_offset = offsetof (struct so_map, som_path);
       lmo.l_name_size = fieldsize (struct so_map, som_path);
+
+      /* FIXME: Is the address of the dynamic table available?  */
+      lmo.l_ld_offset = 0;
+      lmo.l_ld_size = 0;
 #endif /* HAVE_STRUCT_SO_MAP_WITH_SOM_MEMBERS */
 #endif /* HAVE_STRUCT_LINK_MAP_WITH_LM_MEMBERS */
 #endif /* HAVE_STRUCT_LINK_MAP_WITH_L_MEMBERS */
Index: gdb/solib-svr4.c
===================================================================
--- gdb/solib-svr4.c.orig	2006-02-14 16:30:44.000000000 -0200
+++ gdb/solib-svr4.c	2006-02-23 17:07:50.000000000 -0300
@@ -42,6 +42,7 @@
 #include "solib-svr4.h"
 
 #include "bfd-target.h"
+#include "elf-bfd.h"
 #include "exec.h"
 
 static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
@@ -59,6 +60,13 @@ struct lm_info
        rather than void *, so that we may use byte offsets to find the
        various fields without the need for a cast.  */
     gdb_byte *lm;
+
+    /* Amount by which addresses in the binary should be relocated to
+       match the inferior.  This could most often be taken directly
+       from lm, but when prelinking is involved and the prelink base
+       address changes, we may need a different offset, we want to
+       warn about the difference and compute it only once.  */
+    CORE_ADDR l_addr;
   };
 
 /* On SVR4 systems, a list of symbols in the dynamic linker where
@@ -127,14 +135,101 @@ static char *main_name_list[] =
 /* link map access functions */
 
 static CORE_ADDR
-LM_ADDR (struct so_list *so)
+LM_ADDR_FROM_LINK_MAP (struct so_list *so)
 {
   struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
 
-  return (CORE_ADDR) extract_signed_integer (so->lm_info->lm + lmo->l_addr_offset, 
+  return (CORE_ADDR) extract_signed_integer (so->lm_info->lm
+					     + lmo->l_addr_offset,
 					     lmo->l_addr_size);
 }
 
+static int
+HAS_LM_DYNAMIC_FROM_LINK_MAP ()
+{
+  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+
+  return (lmo->l_ld_size != 0);
+}
+
+static CORE_ADDR
+LM_DYNAMIC_FROM_LINK_MAP (struct so_list *so)
+{
+  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+
+  gdb_assert (lmo->l_ld_size != 0);
+
+  return (CORE_ADDR) extract_signed_integer (so->lm_info->lm
+					     + lmo->l_ld_offset,
+					     lmo->l_ld_size);
+}
+
+static CORE_ADDR
+LM_ADDR_CHECK (struct so_list *so, bfd *abfd)
+{
+  if (so->lm_info->l_addr == (CORE_ADDR)-1)
+    {
+      struct bfd_section *dyninfo_sect;
+      CORE_ADDR l_addr, l_dynaddr, dynaddr, align = 0x1000;
+
+      l_addr = LM_ADDR_FROM_LINK_MAP (so);
+
+      if (! abfd || ! HAS_LM_DYNAMIC_FROM_LINK_MAP ())
+	goto set_addr;
+
+      l_dynaddr = LM_DYNAMIC_FROM_LINK_MAP (so);
+
+      dyninfo_sect = bfd_get_section_by_name (abfd, ".dynamic");
+      if (dyninfo_sect == NULL)
+	goto set_addr;
+
+      dynaddr = bfd_section_vma (abfd, dyninfo_sect);
+
+      if (dynaddr + l_addr != l_dynaddr)
+	{
+	  warning (_(".dynamic section for \"%s\" "
+		     "is not at the expected address"), so->so_name);
+
+	  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+	    {
+	      Elf_Internal_Ehdr *ehdr = elf_tdata (abfd)->elf_header;
+	      Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr;
+	      int i;
+
+	      align = 1;
+
+	      for (i = 0; i < ehdr->e_phnum; i++)
+		if (phdr[i].p_type == PT_LOAD && phdr[i].p_align > align)
+		  align = phdr[i].p_align;
+	    }
+
+	  /* Turn it into a mask.  */
+	  align--;
+
+	  /* If the changes match the alignment requirements, we
+	     assume we're using a core file that was generated by the
+	     same binary, just prelinked with a different base offset.
+	     If it doesn't match, we may have a different binary, the
+	     same binary with the dynamic table loaded at an unrelated
+	     location, or anything, really.  To avoid regressions,
+	     don't adjust the base offset in the latter case, although
+	     odds are that, if things really changed, debugging won't
+	     quite work.  */
+	  if ((l_addr & align) == 0 && ((dynaddr - l_dynaddr) & align) == 0)
+	    {
+	      l_addr = l_dynaddr - dynaddr;
+	      warning (_("difference appears to be caused by prelink, "
+			 "adjusting expectations"));
+	    }
+	}
+
+    set_addr:
+      so->lm_info->l_addr = l_addr;
+    }
+
+  return so->lm_info->l_addr;
+}
+
 static CORE_ADDR
 LM_NEXT (struct so_list *so)
 {
@@ -649,6 +744,8 @@ svr4_current_sos (void)
 	    free_so (new);
 	  else
 	    {
+	      new->lm_info->l_addr = (CORE_ADDR)-1;
+
 	      new->next = 0;
 	      *link_ptr = new;
 	      link_ptr = &new->next;
@@ -912,7 +1009,7 @@ enable_break (void)
 	  if (strcmp (buf, so->so_original_name) == 0)
 	    {
 	      load_addr_found = 1;
-	      load_addr = LM_ADDR (so);
+	      load_addr = LM_ADDR_CHECK (so, tmp_bfd);
 	      break;
 	    }
 	  so = so->next;
@@ -1272,8 +1369,10 @@ static void
 svr4_relocate_section_addresses (struct so_list *so,
                                  struct section_table *sec)
 {
-  sec->addr    = svr4_truncate_ptr (sec->addr    + LM_ADDR (so));
-  sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR (so));
+  sec->addr    = svr4_truncate_ptr (sec->addr    + LM_ADDR_CHECK (so,
+								  sec->bfd));
+  sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR_CHECK (so,
+								  sec->bfd));
 }
 
 
@@ -1362,6 +1461,8 @@ svr4_ilp32_fetch_link_map_offsets (void)
       lmo.l_addr_size = 4;
       lmo.l_name_offset = 4;
       lmo.l_name_size = 4;
+      lmo.l_ld_offset = 8;
+      lmo.l_ld_size = 4;
       lmo.l_next_offset = 12;
       lmo.l_next_size = 4;
       lmo.l_prev_offset = 16;
@@ -1395,6 +1496,8 @@ svr4_lp64_fetch_link_map_offsets (void)
       lmo.l_addr_size = 8;
       lmo.l_name_offset = 8;
       lmo.l_name_size = 8;
+      lmo.l_ld_offset = 16;
+      lmo.l_ld_size = 8;
       lmo.l_next_offset = 24;
       lmo.l_next_size = 8;
       lmo.l_prev_offset = 32;
Index: gdb/solib-svr4.h
===================================================================
--- gdb/solib-svr4.h.orig	2006-02-14 16:30:44.000000000 -0200
+++ gdb/solib-svr4.h	2006-02-23 17:07:50.000000000 -0300
@@ -50,6 +50,12 @@ struct link_map_offsets
     /* Size of l_addr field in struct link_map.  */
     int l_addr_size;
 
+    /* Offset to l_ld field in struct link_map.  */
+    int l_ld_offset;
+
+    /* Size of l_ld field in struct link_map.  */
+    int l_ld_size;
+
     /* Offset to l_next field in struct link_map.  */
     int l_next_offset;
 
Index: gdb/Makefile.in
===================================================================
--- gdb/Makefile.in.orig	2006-02-23 17:06:06.000000000 -0300
+++ gdb/Makefile.in	2006-02-23 17:07:50.000000000 -0300
@@ -2582,7 +2582,8 @@ solib-sunos.o: solib-sunos.c $(defs_h) $
 solib-svr4.o: solib-svr4.c $(defs_h) $(elf_external_h) $(elf_common_h) \
 	$(elf_mips_h) $(symtab_h) $(bfd_h) $(symfile_h) $(objfiles_h) \
 	$(gdbcore_h) $(target_h) $(inferior_h) $(gdb_assert_h) \
-	$(solist_h) $(solib_h) $(solib_svr4_h) $(bfd_target_h) $(exec_h)
+	$(solist_h) $(solib_h) $(solib_svr4_h) $(bfd_target_h) $(elf_bfd_h) \
+	$(exec_h)
 sol-thread.o: sol-thread.c $(defs_h) $(gdbthread_h) $(target_h) \
 	$(inferior_h) $(gdb_stat_h) $(gdbcmd_h) $(gdbcore_h) $(regcache_h) \
 	$(solib_h) $(symfile_h) $(gdb_string_h) $(gregset_h)
Index: gdb/testsuite/gdb.base/prelink-lib.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdb/testsuite/gdb.base/prelink-lib.c	2006-02-23 17:14:55.000000000 -0300
@@ -0,0 +1,34 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2006  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 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.
+*/
+
+int
+g (void (*p)(void))
+{
+  p ();
+}
+
+void
+f(void (*p)(void)) {
+  g (p);
+}
+
+void (*h (void)) (void (*p)(void))
+{
+  return f;
+}
Index: gdb/testsuite/gdb.base/prelink.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdb/testsuite/gdb.base/prelink.c	2006-02-23 17:14:58.000000000 -0300
@@ -0,0 +1,30 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2006  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 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdio.h>
+
+extern void (*h (void)) (void (*)(void));
+
+int
+main (void)
+{
+  void (*f) (void (*)(void)) = h ();
+  printf ("%p\n", f);
+  f (0);
+}
Index: gdb/testsuite/gdb.base/prelink.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdb/testsuite/gdb.base/prelink.exp	2006-02-23 17:07:50.000000000 -0300
@@ -0,0 +1,128 @@
+# Copyright 2006 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 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.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@prep.ai.mit.edu
+
+# This file was written by Alexandre Oliva <aoliva@redhat.com>
+
+if $tracelevel then {
+	strace $tracelevel
+	}
+
+set prms_id 0
+set bug_id 0
+
+# are we on a target board
+if ![isnative] then {
+    return
+}
+
+set testfile "prelink"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+set libsrcfile ${testfile}-lib.c
+set libfile ${objdir}/${subdir}/${testfile}.so
+if { [gdb_compile "${srcdir}/${subdir}/${libsrcfile}" "${libfile}" executable [list debug "additional_flags=-fpic -shared -nodefaultlibs"]] != ""} {
+    # If creating the shared library fails, maybe we don't have the right tools
+    return -1
+}
+
+if {[catch "system \"prelink -NR ${libfile}\""] != 0} {
+    # Maybe we don't have prelink.
+    return -1
+}
+
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile} ${libfile}" "${binfile}" executable [list debug "additional_flags=-Wl,-rpath,${objdir}/${subdir}"]] != ""} {
+    return -1;
+}
+
+if [get_compiler_info ${binfile}] {
+    return -1
+}
+
+if {$gcc_compiled == 0} {
+    return -1
+}
+
+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\""
+
+foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" {
+    if [remote_file build exists $i] {
+	remote_exec build "mv $i ${objdir}/${subdir}/prelink.core"
+	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}/prelink.core"
+        set found 1
+    }
+}
+
+catch "system \"prelink -u ${libfile}\""
+catch "system \"prelink -NR ${libfile}\""
+
+# 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 - prelink tests suppressed - check ulimit -c"
+    return 0
+}
+
+# Start with a fresh gdb
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set oldtimeout $timeout
+set timeout [expr "$timeout + 60"]
+verbose "Timeout is now $timeout seconds" 2
+send_gdb "core-file $objdir/$subdir/prelink.core\n"
+gdb_expect {
+    -re "warning: \.dynamic section.*not at the expected address" {
+	pass "changed base address"
+    }
+    -re ".*$gdb_prompt $"	{ fail "changed base address" }
+    timeout 		{ fail "(timeout) changed base address" }
+}
+gdb_expect {
+    -re "warning: difference.*caused by prelink, adjusting" {
+	pass "prelink adjustment"
+    }
+    -re ".*$gdb_prompt $"	{ fail "prelink adjustment" }
+    timeout 		{ fail "(timeout) prelink adjustment" }
+}
+set timeout $oldtimeout
+verbose "Timeout is now $timeout seconds" 2
+
+gdb_exit
+
+return 0
+
-- 
Alexandre Oliva         http://www.lsd.ic.unicamp.br/~oliva/
Secretary for FSF Latin America        http://www.fsfla.org/
Red Hat Compiler Engineer   aoliva@{redhat.com, gcc.gnu.org}
Free Software Evangelist  oliva@{lsd.ic.unicamp.br, gnu.org}

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