444 lines
14 KiB
Diff
444 lines
14 KiB
Diff
http://sourceware.org/ml/gdb-patches/2010-03/msg01008.html
|
|
Subject: [patch 1/6]: PIE: Attach binary even after re-prelinked underneath
|
|
|
|
Hi,
|
|
|
|
there is a regression (against previous unreleased commits) by:
|
|
Re: RFC: Verify AT_ENTRY before using it
|
|
http://sourceware.org/ml/gdb-patches/2010-03/msg00395.html
|
|
|
|
for loading PIE executables which have changed on the disk since started.
|
|
There are in fact 3 different addresses one has to properly deal with.
|
|
|
|
This patch uses explicit "file" so it is not dependent on pending:
|
|
[patch] Attach to running but deleted executable
|
|
http://sourceware.org/ml/gdb-patches/2010-03/msg00950.html
|
|
|
|
The two copy-pasted blocks for elf32 and elf64 are "not nice" but this is the
|
|
current style in GDB.
|
|
|
|
No regressions on {x86_64,x86_64-m32,i686}-fedora12-linux-gnu for the whole
|
|
patch series together.
|
|
|
|
|
|
Thanks,
|
|
Jan
|
|
|
|
|
|
gdb/
|
|
2010-03-29 Jan Kratochvil <jan.kratochvil@redhat.com>
|
|
|
|
Fix attaching to PIEs prelinked on the disk since their start.
|
|
* solib-svr4.c (svr4_exec_displacement): New variable arch_size.
|
|
Verify it against bfd_get_arch_size. Try to match arbitrary
|
|
displacement for the phdrs comparison.
|
|
|
|
gdb/testsuite/
|
|
2010-03-29 Jan Kratochvil <jan.kratochvil@redhat.com>
|
|
|
|
* gdb.base/break-interp.exp: Run $binpie with new value "ATTACH", new
|
|
code for it. New variable relink_args.
|
|
(prelinkYES): Call prelinkNO.
|
|
(test_attach): Accept new parameter relink_args. Re-prelink the binary
|
|
in such case. Move the core code to ...
|
|
(test_attach_gdb): ... a new function. Send GDB command "file".
|
|
Extend expected "Attaching to " string.
|
|
|
|
--- a/gdb/solib-svr4.c
|
|
+++ b/gdb/solib-svr4.c
|
|
@@ -1750,13 +1750,183 @@ svr4_exec_displacement (CORE_ADDR *displacementp)
|
|
really do not match. */
|
|
int phdrs_size, phdrs2_size, ok = 1;
|
|
gdb_byte *buf, *buf2;
|
|
+ int arch_size;
|
|
|
|
- buf = read_program_header (-1, &phdrs_size, NULL);
|
|
+ buf = read_program_header (-1, &phdrs_size, &arch_size);
|
|
buf2 = read_program_headers_from_bfd (exec_bfd, &phdrs2_size);
|
|
- if (buf != NULL && buf2 != NULL
|
|
- && (phdrs_size != phdrs2_size
|
|
- || memcmp (buf, buf2, phdrs_size) != 0))
|
|
- ok = 0;
|
|
+ if (buf != NULL && buf2 != NULL)
|
|
+ {
|
|
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
|
|
+
|
|
+ /* We are dealing with three different addresses. EXEC_BFD
|
|
+ represents current address in on-disk file. target memory content
|
|
+ may be different from EXEC_BFD as the file may have been prelinked
|
|
+ to a different address since the executable has been loaded.
|
|
+ Moreover the address of placement in target memory can be
|
|
+ different from what say the target memory program headers - this
|
|
+ is the goal of PIE.
|
|
+
|
|
+ Detected DISPLACEMENT covers both the offsets of PIE placement and
|
|
+ possible new prelink since start of the program. Here relocate
|
|
+ BUF and BUF2 just by the EXEC_BFD vs. target memory content offset
|
|
+ for the verification purpose. */
|
|
+
|
|
+ if (phdrs_size != phdrs2_size
|
|
+ || bfd_get_arch_size (exec_bfd) != arch_size)
|
|
+ ok = 0;
|
|
+ else if (arch_size == 32 && phdrs_size >= sizeof (Elf32_External_Phdr)
|
|
+ && phdrs_size % sizeof (Elf32_External_Phdr) == 0)
|
|
+ {
|
|
+ Elf_Internal_Ehdr *ehdr2 = elf_tdata (exec_bfd)->elf_header;
|
|
+ Elf_Internal_Phdr *phdr2 = elf_tdata (exec_bfd)->phdr;
|
|
+ CORE_ADDR displacement = 0;
|
|
+ int i;
|
|
+
|
|
+ /* DISPLACEMENT could be found easier by the difference of
|
|
+ ehdr2->e_entry but already read BUF does not contain ehdr. */
|
|
+
|
|
+ for (i = 0; i < ehdr2->e_phnum; i++)
|
|
+ if (phdr2[i].p_type == PT_LOAD)
|
|
+ {
|
|
+ Elf32_External_Phdr *phdrp;
|
|
+ gdb_byte *buf_vaddr_p, *buf_paddr_p;
|
|
+ CORE_ADDR vaddr, paddr;
|
|
+ CORE_ADDR displacement_vaddr = 0;
|
|
+ CORE_ADDR displacement_paddr = 0;
|
|
+
|
|
+ phdrp = &((Elf32_External_Phdr *) buf)[i];
|
|
+ buf_vaddr_p = (gdb_byte *) &phdrp->p_vaddr;
|
|
+ buf_paddr_p = (gdb_byte *) &phdrp->p_paddr;
|
|
+
|
|
+ vaddr = extract_unsigned_integer (buf_vaddr_p, 4,
|
|
+ byte_order);
|
|
+ displacement_vaddr = vaddr - phdr2[i].p_vaddr;
|
|
+
|
|
+ paddr = extract_unsigned_integer (buf_paddr_p, 4,
|
|
+ byte_order);
|
|
+ displacement_paddr = paddr - phdr2[i].p_paddr;
|
|
+
|
|
+ if (displacement_vaddr == displacement_paddr)
|
|
+ displacement = displacement_vaddr;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Now compare BUF and BUF2 with optional DISPLACEMENT. */
|
|
+
|
|
+ for (i = 0; i < phdrs_size / sizeof (Elf32_External_Phdr); i++)
|
|
+ {
|
|
+ Elf32_External_Phdr *phdrp;
|
|
+ Elf32_External_Phdr *phdr2p;
|
|
+ gdb_byte *buf_vaddr_p, *buf_paddr_p;
|
|
+ CORE_ADDR vaddr, paddr;
|
|
+
|
|
+ phdrp = &((Elf32_External_Phdr *) buf)[i];
|
|
+ buf_vaddr_p = (gdb_byte *) &phdrp->p_vaddr;
|
|
+ buf_paddr_p = (gdb_byte *) &phdrp->p_paddr;
|
|
+ phdr2p = &((Elf32_External_Phdr *) buf2)[i];
|
|
+
|
|
+ /* PT_GNU_STACK addresses are left as zero not being
|
|
+ relocated by prelink, their displacing would create false
|
|
+ verification failure. Feel free to test the unrelocated
|
|
+ comparison for any segment type. */
|
|
+
|
|
+ if (memcmp (phdrp, phdr2p, sizeof (*phdrp)) == 0)
|
|
+ continue;
|
|
+
|
|
+ vaddr = extract_unsigned_integer (buf_vaddr_p, 4, byte_order);
|
|
+ vaddr -= displacement;
|
|
+ store_unsigned_integer (buf_vaddr_p, 4, byte_order, vaddr);
|
|
+
|
|
+ paddr = extract_unsigned_integer (buf_paddr_p, 4, byte_order);
|
|
+ paddr -= displacement;
|
|
+ store_unsigned_integer (buf_paddr_p, 4, byte_order, paddr);
|
|
+
|
|
+ if (memcmp (phdrp, phdr2p, sizeof (*phdrp)) == 0)
|
|
+ continue;
|
|
+
|
|
+ ok = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ else if (arch_size == 64 && phdrs_size >= sizeof (Elf64_External_Phdr)
|
|
+ && phdrs_size % sizeof (Elf64_External_Phdr) == 0)
|
|
+ {
|
|
+ Elf_Internal_Ehdr *ehdr2 = elf_tdata (exec_bfd)->elf_header;
|
|
+ Elf_Internal_Phdr *phdr2 = elf_tdata (exec_bfd)->phdr;
|
|
+ CORE_ADDR displacement = 0;
|
|
+ int i;
|
|
+
|
|
+ /* DISPLACEMENT could be found easier by the difference of
|
|
+ ehdr2->e_entry but already read BUF does not contain ehdr. */
|
|
+
|
|
+ for (i = 0; i < ehdr2->e_phnum; i++)
|
|
+ if (phdr2[i].p_type == PT_LOAD)
|
|
+ {
|
|
+ Elf64_External_Phdr *phdrp;
|
|
+ gdb_byte *buf_vaddr_p, *buf_paddr_p;
|
|
+ CORE_ADDR vaddr, paddr;
|
|
+ CORE_ADDR displacement_vaddr = 0;
|
|
+ CORE_ADDR displacement_paddr = 0;
|
|
+
|
|
+ phdrp = &((Elf64_External_Phdr *) buf)[i];
|
|
+ buf_vaddr_p = (gdb_byte *) &phdrp->p_vaddr;
|
|
+ buf_paddr_p = (gdb_byte *) &phdrp->p_paddr;
|
|
+
|
|
+ vaddr = extract_unsigned_integer (buf_vaddr_p, 8,
|
|
+ byte_order);
|
|
+ displacement_vaddr = vaddr - phdr2[i].p_vaddr;
|
|
+
|
|
+ paddr = extract_unsigned_integer (buf_paddr_p, 8,
|
|
+ byte_order);
|
|
+ displacement_paddr = paddr - phdr2[i].p_paddr;
|
|
+
|
|
+ if (displacement_vaddr == displacement_paddr)
|
|
+ displacement = displacement_vaddr;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Now compare BUF and BUF2 with optional DISPLACEMENT. */
|
|
+
|
|
+ for (i = 0; i < phdrs_size / sizeof (Elf64_External_Phdr); i++)
|
|
+ {
|
|
+ Elf64_External_Phdr *phdrp;
|
|
+ Elf64_External_Phdr *phdr2p;
|
|
+ gdb_byte *buf_vaddr_p, *buf_paddr_p;
|
|
+ CORE_ADDR vaddr, paddr;
|
|
+
|
|
+ phdrp = &((Elf64_External_Phdr *) buf)[i];
|
|
+ buf_vaddr_p = (gdb_byte *) &phdrp->p_vaddr;
|
|
+ buf_paddr_p = (gdb_byte *) &phdrp->p_paddr;
|
|
+ phdr2p = &((Elf64_External_Phdr *) buf2)[i];
|
|
+
|
|
+ /* PT_GNU_STACK addresses are left as zero not being
|
|
+ relocated by prelink, their displacing would create false
|
|
+ verification failure. Feel free to test the unrelocated
|
|
+ comparison for any segment type. */
|
|
+
|
|
+ if (memcmp (phdrp, phdr2p, sizeof (*phdrp)) == 0)
|
|
+ continue;
|
|
+
|
|
+ vaddr = extract_unsigned_integer (buf_vaddr_p, 8, byte_order);
|
|
+ vaddr -= displacement;
|
|
+ store_unsigned_integer (buf_vaddr_p, 8, byte_order, vaddr);
|
|
+
|
|
+ paddr = extract_unsigned_integer (buf_paddr_p, 8, byte_order);
|
|
+ paddr -= displacement;
|
|
+ store_unsigned_integer (buf_paddr_p, 8, byte_order, paddr);
|
|
+
|
|
+ if (memcmp (phdrp, phdr2p, sizeof (*phdrp)) == 0)
|
|
+ continue;
|
|
+
|
|
+ ok = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ ok = 0;
|
|
+ }
|
|
|
|
xfree (buf);
|
|
xfree (buf2);
|
|
--- a/gdb/testsuite/gdb.base/break-interp.exp
|
|
+++ b/gdb/testsuite/gdb.base/break-interp.exp
|
|
@@ -154,6 +154,12 @@ proc prelinkYES {arg {name ""}} {
|
|
if {$name == ""} {
|
|
set name [file tail $arg]
|
|
}
|
|
+
|
|
+ # Try to unprelink it first so that if it has been already prelinked before
|
|
+ # we get different address now and the result is not affected by the
|
|
+ # previous $arg state..
|
|
+ prelinkNO $arg "$name pre-unprelink"
|
|
+
|
|
set test "prelink $name"
|
|
set command "exec /usr/sbin/prelink -qNR --no-exec-shield $arg"
|
|
verbose -log "command is $command"
|
|
@@ -319,38 +325,12 @@ proc test_core {file displacement} {
|
|
set pf_prefix $old_ldprefix
|
|
}
|
|
|
|
-proc test_attach {file displacement} {
|
|
- global board_info gdb_prompt expect_out
|
|
-
|
|
- gdb_exit
|
|
-
|
|
- set test "sleep function started"
|
|
-
|
|
- set command "${file} sleep"
|
|
- set res [remote_spawn host $command];
|
|
- if { $res < 0 || $res == "" } {
|
|
- perror "Spawning $command failed."
|
|
- fail $test
|
|
- return
|
|
- }
|
|
- set pid [exp_pid -i $res]
|
|
- gdb_expect {
|
|
- -re "sleeping\r\n" {
|
|
- pass $test
|
|
- }
|
|
- eof {
|
|
- fail "$test (eof)"
|
|
- return
|
|
- }
|
|
- timeout {
|
|
- fail "$test (timeout)"
|
|
- return
|
|
- }
|
|
- }
|
|
+proc test_attach_gdb {file pid displacement prefix} {
|
|
+ global gdb_prompt expect_out
|
|
|
|
global pf_prefix
|
|
set old_ldprefix $pf_prefix
|
|
- lappend pf_prefix "attach:"
|
|
+ lappend pf_prefix "$prefix:"
|
|
|
|
gdb_exit
|
|
gdb_start
|
|
@@ -358,9 +338,13 @@ proc test_attach {file displacement} {
|
|
# Print the "PIE (Position Independent Executable) displacement" message.
|
|
gdb_test "set verbose on"
|
|
|
|
+ if {$file != ""} {
|
|
+ gdb_test "file $file" "Reading symbols from .*done\\." "file"
|
|
+ }
|
|
+
|
|
set test "attach"
|
|
gdb_test_multiple "attach $pid" $test {
|
|
- -re "Attaching to process $pid\r\n" {
|
|
+ -re "Attaching to (program: .*, )?process $pid\r\n" {
|
|
# Missing "$gdb_prompt $" is intentional.
|
|
pass $test
|
|
}
|
|
@@ -396,11 +380,56 @@ proc test_attach {file displacement} {
|
|
gdb_test "bt" "#\[0-9\]+ +\[^\r\n\]*\\mlibfunc\\M\[^\r\n\]*\r\n#\[0-9\]+ +\[^\r\n\]*\\mmain\\M.*" "attach main bt"
|
|
gdb_exit
|
|
|
|
- remote_exec host "kill -9 $pid"
|
|
-
|
|
set pf_prefix $old_ldprefix
|
|
}
|
|
|
|
+proc test_attach {file displacement {relink_args ""}} {
|
|
+ global board_info
|
|
+
|
|
+ gdb_exit
|
|
+
|
|
+ set test "sleep function started"
|
|
+
|
|
+ set command "${file} sleep"
|
|
+ set res [remote_spawn host $command];
|
|
+ if { $res < 0 || $res == "" } {
|
|
+ perror "Spawning $command failed."
|
|
+ fail $test
|
|
+ return
|
|
+ }
|
|
+ set pid [exp_pid -i $res]
|
|
+ gdb_expect {
|
|
+ -re "sleeping\r\n" {
|
|
+ pass $test
|
|
+ }
|
|
+ eof {
|
|
+ fail "$test (eof)"
|
|
+ return
|
|
+ }
|
|
+ timeout {
|
|
+ fail "$test (timeout)"
|
|
+ return
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if {$relink_args == ""} {
|
|
+ test_attach_gdb "" $pid $displacement "attach"
|
|
+ } else {
|
|
+ # These could be rather passed as arguments.
|
|
+ global exec interp_saved interp
|
|
+
|
|
+ foreach relink {YES NO} {
|
|
+ if {[prelink$relink $relink_args [file tail $exec]]
|
|
+ && [copy $interp_saved $interp]} {
|
|
+ # /proc/PID/exe cannot be loaded as it is "EXECNAME (deleted)".
|
|
+ test_attach_gdb $exec $pid $displacement "attach-relink$relink"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ remote_exec host "kill -9 $pid"
|
|
+}
|
|
+
|
|
proc test_ld {file ifmain trynosym displacement} {
|
|
global srcdir subdir gdb_prompt expect_out
|
|
|
|
@@ -609,7 +638,10 @@ foreach ldprelink {NO YES} {
|
|
set old_binprefix $pf_prefix
|
|
foreach binprelink {NO YES} {
|
|
foreach binsepdebug {NO IN SEP} {
|
|
- foreach binpie {NO YES} {
|
|
+ # "ATTACH" is like "YES" but it is modified during run.
|
|
+ # It cannot be used for problem reproducibility after the
|
|
+ # testcase ends.
|
|
+ foreach binpie {NO YES ATTACH} {
|
|
# This combination is not possible, non-PIE (fixed address)
|
|
# binary cannot be prelinked to any (other) address.
|
|
if {$binprelink == "YES" && $binpie == "NO"} {
|
|
@@ -628,7 +660,7 @@ foreach ldprelink {NO YES} {
|
|
if {$binsepdebug != "NO"} {
|
|
lappend opts {debug}
|
|
}
|
|
- if {$binpie == "YES"} {
|
|
+ if {$binpie != "NO"} {
|
|
lappend opts {additional_flags=-fPIE -pie}
|
|
}
|
|
if {[build_executable ${test}.exp [file tail $exec] $srcfile $opts] == -1} {
|
|
@@ -680,16 +712,45 @@ foreach ldprelink {NO YES} {
|
|
lappend dests $dest
|
|
}
|
|
|
|
- if {[prelink$binprelink "--dynamic-linker=$interp --ld-library-path=$dir $exec $interp [concat $dests]" [file tail $exec]]
|
|
+ if {$binpie == "NO"} {
|
|
+ set displacement "NONE"
|
|
+ } elseif {$binprelink == "NO"} {
|
|
+ set displacement "NONZERO"
|
|
+ } else {
|
|
+ set displacement "ZERO"
|
|
+ }
|
|
+
|
|
+ set relink_args "--dynamic-linker=$interp --ld-library-path=$dir $exec $interp [concat $dests]"
|
|
+ if {[prelink$binprelink $relink_args [file tail $exec]]
|
|
&& [copy $interp_saved $interp]} {
|
|
- if {$binpie == "NO"} {
|
|
- set displacement "NONE"
|
|
- } elseif {$binprelink == "NO"} {
|
|
- set displacement "NONZERO"
|
|
+ if {$binpie != "ATTACH"} {
|
|
+ test_ld $exec 1 [expr {$binsepdebug == "NO"}] $displacement
|
|
} else {
|
|
- set displacement "ZERO"
|
|
+ # If the file has been randomly prelinked it must
|
|
+ # be "NONZERO". We could see "ZERO" only if it was
|
|
+ # unprelinked na it is now running at the same
|
|
+ # address - which is 0 but executable can never run
|
|
+ # at address 0.
|
|
+
|
|
+ set displacement "NONZERO"
|
|
+ test_attach $exec $displacement $relink_args
|
|
+
|
|
+ # ATTACH executables + libraries get modified since
|
|
+ # they have been run. They cannot be used for
|
|
+ # problem reproducibility after the testcase ends.
|
|
+ set exec_debug [system_debug_get $exec]
|
|
+ if {$exec_debug != ""} {
|
|
+ # `file delete [glob "${exec_debug}*"]' does not work.
|
|
+ foreach f [glob "${exec_debug}*"] {
|
|
+ file delete $f
|
|
+ }
|
|
+ }
|
|
+ file delete -force $dir
|
|
+ # `file delete [glob "${exec}*"]' does not work.
|
|
+ foreach f [glob "${exec}*"] {
|
|
+ file delete $f
|
|
+ }
|
|
}
|
|
- test_ld $exec 1 [expr {$binsepdebug == "NO"}] $displacement
|
|
}
|
|
}
|
|
}
|
|
|