gdb/
authorJan Kratochvil <jan.kratochvil@redhat.com>
Thu, 14 Jan 2010 20:48:26 +0000 (20:48 +0000)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Thu, 14 Jan 2010 20:48:26 +0000 (20:48 +0000)
* solib-svr4.c (svr4_relocate_main_executable): Move the static exec
code part to ...
(svr4_static_exec_displacement): ... a new function.
(svr4_exec_displacement): New function.
(svr4_relocate_main_executable): Call svr4_exec_displacement.  Allocate
new_offsets using alloca now.  Remove variable old_chain and changed.
Call objfile_relocate unconditionally now.

gdb/testsuite/
* gdb.base/break-interp.exp: New file.

gdb/ChangeLog
gdb/solib-svr4.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/break-interp.exp [new file with mode: 0644]

index 8ed5d3bf167304767b0b71ad00df280d7186ec4f..ffcbe7414c9310c624cc366688f98b5f98c42072 100644 (file)
@@ -1,3 +1,13 @@
+2010-01-14  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       * solib-svr4.c (svr4_relocate_main_executable): Move the static exec
+       code part to ...
+       (svr4_static_exec_displacement): ... a new function.
+       (svr4_exec_displacement): New function.
+       (svr4_relocate_main_executable): Call svr4_exec_displacement.  Allocate
+       new_offsets using alloca now.  Remove variable old_chain and changed.
+       Call objfile_relocate unconditionally now.
+
 2010-01-14  Doug Evans  <dje@google.com>
 
        * gdbtypes.c (arch_flags_type): Fix comment.
index 00e16b000a7c83857078dca599882da8a3cd4b07..3ad067ab7e14c51970ad86ec2bbfb0cfafe2851e 100644 (file)
@@ -1535,111 +1535,119 @@ svr4_special_symbol_handling (void)
 {
 }
 
-/* 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;
 
-      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 = alloca (symfile_objfile->num_sections
+                           * sizeof (*new_offsets));
 
-      if (changed)
-       objfile_relocate (symfile_objfile, new_offsets);
+      for (i = 0; i < symfile_objfile->num_sections; i++)
+       new_offsets->offsets[i] = displacement;
 
-      do_cleanups (old_chain);
+      objfile_relocate (symfile_objfile, new_offsets);
     }
 }
 
index 0d440c6e249f9bb7de1fce488c1312e7716b136d..5caf2a999a0c35a2063d1650af86f2a8b5242c3a 100644 (file)
@@ -1,3 +1,7 @@
+2010-01-14  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       * gdb.base/break-interp.exp: New file.
+
 2010-01-13  Phil Muldoon  <pmuldoon@redhat.com>
 
        * gdb.python/py-value.exp (test_lazy_strings): Add lazy string test.
diff --git a/gdb/testsuite/gdb.base/break-interp.exp b/gdb/testsuite/gdb.base/break-interp.exp
new file mode 100644 (file)
index 0000000..d225b6c
--- /dev/null
@@ -0,0 +1,413 @@
+# Copyright 2010 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*] } {
+    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 srcfile "start.c"
+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_ld {file ifmain} {
+    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
+    if $ifmain {
+       reach "main" continue
+    }
+}
+
+# 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
+
+       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"
+                   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
+                   }
+               }
+           }
+       }
+
+       file delete $interp_saved
+    }
+}
+set pf_prefix $old_ldprefix