From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 11935 invoked by alias); 8 Feb 2006 05:35:20 -0000 Received: (qmail 11924 invoked by uid 22791); 8 Feb 2006 05:35:18 -0000 X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (66.187.233.31) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 08 Feb 2006 05:35:15 +0000 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.12.11/8.12.11) with ESMTP id k185ZD3E012223 for ; Wed, 8 Feb 2006 00:35:13 -0500 Received: from pobox.corp.redhat.com (pobox.corp.redhat.com [172.16.52.156]) by int-mx1.corp.redhat.com (8.11.6/8.11.6) with ESMTP id k185ZD131683; Wed, 8 Feb 2006 00:35:13 -0500 Received: from livre.oliva.athome.lsd.ic.unicamp.br (vpn50-11.rdu.redhat.com [172.16.50.11]) by pobox.corp.redhat.com (8.12.8/8.12.8) with ESMTP id k185ZBYT010109; Wed, 8 Feb 2006 00:35:12 -0500 Received: from livre.oliva.athome.lsd.ic.unicamp.br (livre.oliva.athome.lsd.ic.unicamp.br [127.0.0.1]) by livre.oliva.athome.lsd.ic.unicamp.br (8.13.5/8.13.5) with ESMTP id k185I2KR015410; Wed, 8 Feb 2006 03:18:02 -0200 Received: (from aoliva@localhost) by livre.oliva.athome.lsd.ic.unicamp.br (8.13.5/8.13.5/Submit) id k185I1Co015409; Wed, 8 Feb 2006 03:18:01 -0200 To: gdb-patches@sources.redhat.com, cagney@redhat.com Subject: cope with varying prelink base addresses From: Alexandre Oliva Date: Wed, 08 Feb 2006 05:35:00 -0000 Message-ID: User-Agent: Gnus/5.1007 (Gnus v5.10.7) XEmacs/21.4.18 (linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2006-02/txt/msg00164.txt.bz2 --=-=-= Content-length: 2280 If you take a core file created on a box whose libraries have been prelinked with random base addresses and try to use it for debugging on another box that has the very same binaries, but prelinked with a different base address, gdb will get all confused. That's because the load_addr of the prelinked binaries will be whatever difference there was between the base address selected by prelink and the base address used at run time by the dynamic loader. If everything is prelinked successfully, the load addresses will all be zero. So when gdb reads the binary prelinked with a different base address, and applies to it the load address read from the core file, it won't get the correct address if the prelinked binary on the debugging host got a different address. This is arguably a change in the binary, so debugging is not expected to work anyway, but it definitely is surprising, especially when prelink is enabled by default. This would render core files useless across different hosts. This patch gets gdb to try to recognize the situation in which a binary got prelinked but is otherwise unchanged, and figure out the difference between the base addresses at core-generating host and at the debugging host. The only piece of information available for this that I could find was the address of the dynamic table in the dynamic loader data structures. The heuristics I used was to check whether the dynamic table address changed but remained at the same position within a page. If so, I assume the difference is caused by prelinking, and then I adjust the load_addr that gdb is going to use for that binary. Otherwise, it will face the same problems you're expected to face when debugging a core file using a different binary. I'd appreciate if whoever reviews this would pay particular attention to the gdb testcase; it's the first time I write one, and I could use some guidance to make sure I'm not making undeserved assumptions. It's bad enough that I have to use compile- and run-time flags that will likely only work with GCC and GNU ld, and that the test requires the prelink program to be in the PATH; I hope I'm coping well with cases in which the flags are not accepted or prelink is not in place. Comments? Ok to install? Tested on amd64-linux-gnu. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=gdb-prelink-core-2.patch Content-length: 15222 for gdb/ChangeLog from Alexandre Oliva * 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. for gdb/testsuite/ChangeLog from Alexandre Oliva * 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-08 00:09:27.000000000 -0200 +++ gdb/mipsnbsd-tdep.c 2006-02-08 00:38:54.000000000 -0200 @@ -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-08 00:09:27.000000000 -0200 +++ gdb/solib-legacy.c 2006-02-08 00:38:54.000000000 -0200 @@ -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-08 00:09:27.000000000 -0200 +++ gdb/solib-svr4.c 2006-02-08 02:58:15.000000000 -0200 @@ -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-08 00:09:27.000000000 -0200 +++ gdb/solib-svr4.h 2006-02-08 00:38:54.000000000 -0200 @@ -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-06 03:44:14.000000000 -0200 +++ gdb/Makefile.in 2006-02-08 01:19:32.000000000 -0200 @@ -2570,7 +2570,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-08 02:42:10.000000000 -0200 @@ -0,0 +1,15 @@ +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-08 02:42:13.000000000 -0200 @@ -0,0 +1,11 @@ +#include + +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-08 03:00:21.000000000 -0200 @@ -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 + +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 + --=-=-= Content-length: 249 -- 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} --=-=-=--