From 0a1e94c73edc44cacedc14eb14829d60a1c6dbbd Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Mon, 5 Jul 2010 17:57:50 +0000 Subject: [PATCH] gdb/ 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. --- gdb/ChangeLog | 9 ++ gdb/solib-svr4.c | 184 +++++++++++++++++++++++- gdb/testsuite/ChangeLog | 11 ++ gdb/testsuite/gdb.base/break-interp.exp | 146 +++++++++++++------ 4 files changed, 304 insertions(+), 46 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 4ce53316701..ceaff6cd0d8 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2010-07-05 Jan Kratochvil + Joel Brobecker + + 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 PR exp/11780: diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index d031303ec37..4a837caa000 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -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); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7d08148b5fa..a9da37fc26a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2010-07-05 Jan Kratochvil + Joel Brobecker + + * 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 * gdb.base/bitops.exp: Remove extraneous "pass". diff --git a/gdb/testsuite/gdb.base/break-interp.exp b/gdb/testsuite/gdb.base/break-interp.exp index 4742889aa47..a1f810550ca 100644 --- 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" @@ -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 } } } -- 2.30.2