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 08/15] PIE: Base functionality


Hi,

this is the basic support for relocating the main executable symbols after the
inferior is started/attached.

Patch does not try to make `-fPIE -pie -static' working as on non-embedded
GNU/Linux targets it has no use and it does not even build for me on
fedora12.x86_64.
	/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/4.4.2/crtbeginT.o: relocation R_X86_64_32 against `__DTOR_END__' can not be used when
making a shared object; recompile with -fPIC
Left there some existing embedded static PIE support intact
(->svr4_static_exec_displacement).


Thanks,
Jan


gdb/
	* 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.
	* symfile.c (syms_from_objfile): Relocate even MAINLINE objfiles.

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

--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -1485,111 +1485,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);
     }
 }
 
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -803,7 +803,7 @@ 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)
+  if (addrs && addrs->other[0].name)
     {
       asection *lower_sect;
       asection *sect;
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-interp.exp
@@ -0,0 +1,413 @@
+# 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*] } {
+    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


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