From 9f2982ff0bf5e33d54b085b98970ab15ba4fac46 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Thu, 14 Jan 2010 21:15:00 +0000 Subject: [PATCH] gdb/ Support Valgrind attachments broken by the PIE support. * auxv.c: Include gdbcore.h. (procfs_xfer_auxv): Make static. Reduce its comment. Drop its parameters ops, object and annex. Remove their assertions. (ld_so_xfer_auxv, memory_xfer_auxv): New function. * auxv.h (procfs_xfer_auxv): Remove comment. Rename to ... (memory_xfer_auxv): ... here. * linux-nat.c (linux_xfer_partial): Rename procfs_xfer_auxv to memory_xfer_auxv. * procfs.c (procfs_xfer_partial): Likewise. * solib-svr4.c (svr4_relocate_main_executable): New prototype. (svr4_special_symbol_handling): Call svr4_relocate_main_executable. (svr4_solib_create_inferior_hook): Conditionalize the svr4_relocate_main_executable call. gdb/testsuite/ * gdb.base/valgrind-db-attach.exp, gdb.base/valgrind-db-attach.c: New. --- gdb/ChangeLog | 17 ++ gdb/auxv.c | 153 ++++++++++++++++-- gdb/auxv.h | 6 +- gdb/linux-nat.c | 2 +- gdb/procfs.c | 2 +- gdb/solib-svr4.c | 5 +- gdb/testsuite/ChangeLog | 4 + gdb/testsuite/gdb.base/valgrind-db-attach.c | 30 ++++ gdb/testsuite/gdb.base/valgrind-db-attach.exp | 76 +++++++++ 9 files changed, 276 insertions(+), 19 deletions(-) create mode 100644 gdb/testsuite/gdb.base/valgrind-db-attach.c create mode 100644 gdb/testsuite/gdb.base/valgrind-db-attach.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 324268f2480..fe3340969af 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +2010-01-14 Jan Kratochvil + + Support Valgrind attachments broken by the PIE support. + * auxv.c: Include gdbcore.h. + (procfs_xfer_auxv): Make static. Reduce its comment. Drop its + parameters ops, object and annex. Remove their assertions. + (ld_so_xfer_auxv, memory_xfer_auxv): New function. + * auxv.h (procfs_xfer_auxv): Remove comment. Rename to ... + (memory_xfer_auxv): ... here. + * linux-nat.c (linux_xfer_partial): Rename procfs_xfer_auxv to + memory_xfer_auxv. + * procfs.c (procfs_xfer_partial): Likewise. + * solib-svr4.c (svr4_relocate_main_executable): New prototype. + (svr4_special_symbol_handling): Call svr4_relocate_main_executable. + (svr4_solib_create_inferior_hook): Conditionalize the + svr4_relocate_main_executable call. + 2010-01-14 Jan Kratochvil * solib-svr4.c (scan_dyntag): Remove variable dyn_addr. New variable diff --git a/gdb/auxv.c b/gdb/auxv.c index 7c9dfc000d1..cf2dc7d695f 100644 --- a/gdb/auxv.c +++ b/gdb/auxv.c @@ -25,6 +25,7 @@ #include "inferior.h" #include "valprint.h" #include "gdb_assert.h" +#include "gdbcore.h" #include "auxv.h" #include "elf/common.h" @@ -33,15 +34,11 @@ #include -/* This function is called like a to_xfer_partial hook, but must be - called with TARGET_OBJECT_AUXV. It handles access via - /proc/PID/auxv, which is a common method for native targets. */ +/* This function handles access via /proc/PID/auxv, which is a common method + for native targets. */ -LONGEST -procfs_xfer_auxv (struct target_ops *ops, - enum target_object object, - const char *annex, - gdb_byte *readbuf, +static LONGEST +procfs_xfer_auxv (gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) @@ -50,9 +47,6 @@ procfs_xfer_auxv (struct target_ops *ops, int fd; LONGEST n; - gdb_assert (object == TARGET_OBJECT_AUXV); - gdb_assert (readbuf || writebuf); - pathname = xstrprintf ("/proc/%d/auxv", PIDGET (inferior_ptid)); fd = open (pathname, writebuf != NULL ? O_WRONLY : O_RDONLY); xfree (pathname); @@ -72,6 +66,143 @@ procfs_xfer_auxv (struct target_ops *ops, return n; } +/* This function handles access via ld.so's symbol `_dl_auxv'. */ + +static LONGEST +ld_so_xfer_auxv (gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, + LONGEST len) +{ + struct minimal_symbol *msym; + CORE_ADDR data_address, pointer_address; + struct type *ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr; + size_t ptr_size = TYPE_LENGTH (ptr_type); + size_t auxv_pair_size = 2 * ptr_size; + gdb_byte *ptr_buf = alloca (ptr_size); + LONGEST retval; + size_t block; + + msym = lookup_minimal_symbol ("_dl_auxv", NULL, NULL); + if (msym == NULL) + return -1; + + if (MSYMBOL_SIZE (msym) != ptr_size) + return -1; + + /* POINTER_ADDRESS is a location where the `_dl_auxv' variable resides. + DATA_ADDRESS is the inferior value present in `_dl_auxv', therefore the + real inferior AUXV address. */ + + pointer_address = SYMBOL_VALUE_ADDRESS (msym); + + data_address = read_memory_typed_address (pointer_address, ptr_type); + + /* Possibly still not initialized such as during an inferior startup. */ + if (data_address == 0) + return -1; + + data_address += offset; + + if (writebuf != NULL) + { + if (target_write_memory (data_address, writebuf, len) == 0) + return len; + else + return -1; + } + + /* Stop if trying to read past the existing AUXV block. The final AT_NULL + was already returned before. */ + + if (offset >= auxv_pair_size) + { + if (target_read_memory (data_address - auxv_pair_size, ptr_buf, + ptr_size) != 0) + return -1; + + if (extract_typed_address (ptr_buf, ptr_type) == AT_NULL) + return 0; + } + + retval = 0; + block = 0x400; + gdb_assert (block % auxv_pair_size == 0); + + while (len > 0) + { + if (block > len) + block = len; + + /* Reading sizes smaller than AUXV_PAIR_SIZE is not supported. Tails + unaligned to AUXV_PAIR_SIZE will not be read during a call (they + should be completed during next read with new/extended buffer). */ + + block &= -auxv_pair_size; + if (block == 0) + return retval; + + if (target_read_memory (data_address, readbuf, block) != 0) + { + if (block <= auxv_pair_size) + return retval; + + block = auxv_pair_size; + continue; + } + + data_address += block; + len -= block; + + /* Check terminal AT_NULL. This function is being called indefinitely + being extended its READBUF until it returns EOF (0). */ + + while (block >= auxv_pair_size) + { + retval += auxv_pair_size; + + if (extract_typed_address (readbuf, ptr_type) == AT_NULL) + return retval; + + readbuf += auxv_pair_size; + block -= auxv_pair_size; + } + } + + return retval; +} + +/* This function is called like a to_xfer_partial hook, but must be + called with TARGET_OBJECT_AUXV. It handles access to AUXV. */ + +LONGEST +memory_xfer_auxv (struct target_ops *ops, + enum target_object object, + const char *annex, + gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, + LONGEST len) +{ + gdb_assert (object == TARGET_OBJECT_AUXV); + gdb_assert (readbuf || writebuf); + + /* ld_so_xfer_auxv is the only function safe for virtual executables being + executed by valgrind's memcheck. As using ld_so_xfer_auxv is problematic + during inferior startup GDB does call it only for attached processes. */ + + if (current_inferior ()->attach_flag != 0) + { + LONGEST retval; + + retval = ld_so_xfer_auxv (readbuf, writebuf, offset, len); + if (retval != -1) + return retval; + } + + return procfs_xfer_auxv (readbuf, writebuf, offset, len); +} + /* Read one auxv entry from *READPTR, not reading locations >= ENDPTR. Return 0 if *READPTR is already at the end of the buffer. Return -1 if there is insufficient buffer for a whole entry. diff --git a/gdb/auxv.h b/gdb/auxv.h index bcab0944cff..679bf17cec2 100644 --- a/gdb/auxv.h +++ b/gdb/auxv.h @@ -43,11 +43,7 @@ extern int target_auxv_search (struct target_ops *ops, /* Print the contents of the target's AUXV on the specified file. */ extern int fprint_target_auxv (struct ui_file *file, struct target_ops *ops); -/* This function is called like a to_xfer_partial hook, but must be - called with TARGET_OBJECT_AUXV. It handles access via - /proc/PID/auxv, which is a common method for native targets. */ - -extern LONGEST procfs_xfer_auxv (struct target_ops *ops, +extern LONGEST memory_xfer_auxv (struct target_ops *ops, enum target_object object, const char *annex, gdb_byte *readbuf, diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index bf0a5f1bdc5..03563cd797b 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -5009,7 +5009,7 @@ linux_xfer_partial (struct target_ops *ops, enum target_object object, LONGEST xfer; if (object == TARGET_OBJECT_AUXV) - return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf, + return memory_xfer_auxv (ops, object, annex, readbuf, writebuf, offset, len); if (object == TARGET_OBJECT_OSDATA) diff --git a/gdb/procfs.c b/gdb/procfs.c index 9278bcb5abc..c6f50d28481 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -4387,7 +4387,7 @@ procfs_xfer_partial (struct target_ops *ops, enum target_object object, #ifdef NEW_PROC_API case TARGET_OBJECT_AUXV: - return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf, + return memory_xfer_auxv (ops, object, annex, readbuf, writebuf, offset, len); #endif diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 0957c446e8b..6daf0dcca6d 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -50,6 +50,7 @@ static struct link_map_offsets *svr4_fetch_link_map_offsets (void); static int svr4_have_link_map_offsets (void); +static void svr4_relocate_main_executable (void); /* Link map info to include in an allocated so_list entry */ @@ -1540,6 +1541,7 @@ enable_break (struct svr4_info *info, int from_tty) static void svr4_special_symbol_handling (void) { + svr4_relocate_main_executable (); } /* Decide if the objfile needs to be relocated. As indicated above, @@ -1729,7 +1731,8 @@ svr4_solib_create_inferior_hook (int from_tty) info = get_svr4_info (); /* Relocate the main executable if necessary. */ - svr4_relocate_main_executable (); + if (current_inferior ()->attach_flag == 0) + svr4_relocate_main_executable (); if (!svr4_have_link_map_offsets ()) return; diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 57dc1aecf68..c9405ae628d 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2010-01-14 Jan Kratochvil + + * gdb.base/valgrind-db-attach.exp, gdb.base/valgrind-db-attach.c: New. + 2010-01-14 Jan Kratochvil * gdb.base/break-interp-lib.c: Include unistd.h, assert.h and stdio.h. diff --git a/gdb/testsuite/gdb.base/valgrind-db-attach.c b/gdb/testsuite/gdb.base/valgrind-db-attach.c new file mode 100644 index 00000000000..5faaaac4e37 --- /dev/null +++ b/gdb/testsuite/gdb.base/valgrind-db-attach.c @@ -0,0 +1,30 @@ +/* This testcase is part of GDB, the GNU debugger. + + 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 . */ + +#include + +int main() +{ + void *p; + + p = malloc (1); + if (p == NULL) + return 1; + free (p); + free (p); /* double-free */ + return 0; +} diff --git a/gdb/testsuite/gdb.base/valgrind-db-attach.exp b/gdb/testsuite/gdb.base/valgrind-db-attach.exp new file mode 100644 index 00000000000..2aa22c1e5ec --- /dev/null +++ b/gdb/testsuite/gdb.base/valgrind-db-attach.exp @@ -0,0 +1,76 @@ +# 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 . + +set test valgrind-db-attach +set srcfile $test.c +set executable $test +set binfile ${objdir}/${subdir}/${executable} +if {[build_executable $test.exp $executable $srcfile {debug}] == -1} { + return -1 +} + +gdb_exit + +# remote_spawn breaks the command on each whitespace despite possible quoting. +# Use backslash-escaped whitespace there instead: + +set db_command "--db-command=$GDB $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts] %f %p" +regsub -all " " $db_command "\\ " db_command + +set test "spawn valgrind" +set cmd "valgrind --db-attach=yes $db_command $binfile" +set res [remote_spawn host $cmd]; +if { $res < 0 || $res == "" } { + verbose -log "Spawning $cmd failed." + setup_xfail *-*-* + fail $test + return -1 +} +pass $test +# Declare GDB now as running. +set gdb_spawn_id -1 + +set test "valgrind started" +# The trailing '.' differs for different memcheck versions. +gdb_test_multiple "" $test { + -re "Memcheck, a memory error detector\\.?\r\n" { + pass $test + } + -re "valgrind: failed to start tool 'memcheck' for platform '.*': No such file or directory" { + setup_xfail *-*-* + fail $test + return -1 + } +} + +set double_free [gdb_get_line_number "double-free"] + +gdb_test_multiple "" $test { + -re "Invalid free\\(\\) / delete / delete\\\[\\\]\r\n.*: main \\(${srcfile}:$double_free\\)\r\n.*---- Attach to debugger \\? --- \[^\r\n\]* ---- " { + send_gdb "y\r" + } + -re "---- Attach to debugger \\? --- \[^\r\n\]* ---- " { + send_gdb "n\r" + exp_continue + } +} + +gdb_test "" "" "eat first prompt" + +# Initialization from default_gdb_start. +gdb_test "set height 0" +gdb_test "set width 0" + +gdb_test "bt" "in main \\(.*\\) at .*${srcfile}:$double_free" -- 2.30.2