This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
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}