Fix attaching to PIEs prelinked on the disk after the process was
	started.
	* 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/
	* 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.
This commit is contained in:
Jan Kratochvil
2010-07-05 17:57:50 +00:00
parent d7d9a9f820
commit 0a1e94c73e
4 changed files with 304 additions and 46 deletions

View File

@ -1,3 +1,12 @@
2010-07-05 Jan Kratochvil <jan.kratochvil@redhat.com>
Joel Brobecker <brobecker@adacore.com>
Fix attaching to PIEs prelinked on the disk after the process was
started.
* 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.
2010-07-02 Tom Tromey <tromey@redhat.com>
PR exp/11780:

View File

@ -1775,13 +1775,187 @@ 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 after the executable has been loaded.
Moreover the address of placement in target memory can be
different from what the program headers in target memory say - this
is the goal of PIE.
Detected DISPLACEMENT covers both the offsets of PIE placement and
possible new prelink performed after 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 more easily by the difference of
ehdr2->e_entry. But we haven't read the ehdr yet, and we
already have enough information to compute that displacement
with what we've read. */
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 is an exception by being never relocated by
prelink as its addresses are always zero. */
if (memcmp (phdrp, phdr2p, sizeof (*phdrp)) == 0)
continue;
/* Check also other adjustment combinations - PR 11786. */
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 more easily by the difference of
ehdr2->e_entry. But we haven't read the ehdr yet, and we
already have enough information to compute that displacement
with what we've read. */
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 is an exception by being never relocated by
prelink as its addresses are always zero. */
if (memcmp (phdrp, phdr2p, sizeof (*phdrp)) == 0)
continue;
/* Check also other adjustment combinations - PR 11786. */
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);

View File

@ -1,3 +1,14 @@
2010-07-05 Jan Kratochvil <jan.kratochvil@redhat.com>
Joel Brobecker <brobecker@adacore.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.
2010-07-02 Tom Tromey <tromey@redhat.com>
* gdb.base/bitops.exp: Remove extraneous "pass".

View File

@ -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"
@ -325,38 +331,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
@ -364,9 +344,13 @@ proc test_attach {file displacement} {
# Print the "PIE (Position Independent Executable) displacement" message.
gdb_test_no_output "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
}
@ -402,11 +386,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
@ -615,7 +644,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"} {
@ -634,7 +666,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} {
@ -682,16 +714,48 @@ 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 and 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 means that executables and libraries have
# been modified after they have been run. They
# cannot be reused for problem reproducibility after
# the testcase ends in the ATTACH case. Therefore
# they are rather deleted not to confuse after the
# run finishes.
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
}
}
}