diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 04c20ddae9..fa575d0167 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2011-03-28 Jan Kratochvil + + Test STT_GNU_IFUNC support. + * gdb.base/gnu-ifunc-lib.c: New file. + * gdb.base/gnu-ifunc.c: New file. + * gdb.base/gnu-ifunc.exp: New file. + 2011-03-28 Jan Kratochvil Support a ring of related breakpoints. diff --git a/gdb/testsuite/gdb.base/gnu-ifunc-lib.c b/gdb/testsuite/gdb.base/gnu-ifunc-lib.c new file mode 100644 index 0000000000..dc6710e92c --- /dev/null +++ b/gdb/testsuite/gdb.base/gnu-ifunc-lib.c @@ -0,0 +1,33 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2009, 2010, 2011 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 . */ + +extern volatile int gnu_ifunc_initialized; +extern int init_stub (int arg); +extern int final (int arg); + +typedef int (*final_t) (int arg); + +asm (".type gnu_ifunc, @gnu_indirect_function"); + +final_t +gnu_ifunc (void) +{ + if (! gnu_ifunc_initialized) + return init_stub; + else + return final; +} diff --git a/gdb/testsuite/gdb.base/gnu-ifunc.c b/gdb/testsuite/gdb.base/gnu-ifunc.c new file mode 100644 index 0000000000..0ee741f4f6 --- /dev/null +++ b/gdb/testsuite/gdb.base/gnu-ifunc.c @@ -0,0 +1,61 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2009, 2010, 2011 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 +init_stub (int arg) +{ + return 0; +} + +int +final (int arg) +{ + return arg + 1; +} + +/* Make differentiation of how the gnu_ifunc call resolves before and after + calling gnu_ifunc_pre. This ensures the resolved function address is not + being cached anywhere for the debugging purposes. */ + +volatile int gnu_ifunc_initialized; + +static void +gnu_ifunc_pre (void) +{ + assert (!gnu_ifunc_initialized); + + gnu_ifunc_initialized = 1; +} + +extern int gnu_ifunc (int arg); + +int +main (void) +{ + int i; + + gnu_ifunc_pre (); + + i = gnu_ifunc (1); /* break-at-call */ + assert (i == 2); + + gnu_ifunc (2); /* break-at-nextcall */ + + return 0; /* break-at-exit */ +} diff --git a/gdb/testsuite/gdb.base/gnu-ifunc.exp b/gdb/testsuite/gdb.base/gnu-ifunc.exp new file mode 100644 index 0000000000..4fcc3bfc74 --- /dev/null +++ b/gdb/testsuite/gdb.base/gnu-ifunc.exp @@ -0,0 +1,146 @@ +# Copyright (C) 2009, 2010, 2011 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 . + +if {[skip_shlib_tests]} { + return 0 +} + +set testfile "gnu-ifunc" +set executable ${testfile} +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${executable} +set staticexecutable ${executable}-static +set staticbinfile ${objdir}/${subdir}/${staticexecutable} + +set libfile "${testfile}-lib" +set libsrc ${libfile}.c +set lib_so ${objdir}/${subdir}/${libfile}.so +# $lib_o must not have {debug}, it would override the STT_GNU_IFUNC ELF markers. +set lib_o ${objdir}/${subdir}/${libfile}.o + +# We need DWARF for the "final" function as we "step" into the function and GDB +# would step-over the "final" function if there would be no line number debug +# information (DWARF) available. +# +# We must not have DWARF for the "gnu_ifunc" function as DWARF has no way to +# express the STT_GNU_IFUNC type and it would be considered as a regular +# function due to DWARF by GDB. In ELF gnu-ifunc is expressed by the +# STT_GNU_IFUNC type. +# +# Both functions need to be in the same shared library file but +# gdb_compile_shlib has no way to specify source-specific compilation options. +# +# Therefore $libfile contains only the STT_GNU_IFUNC function with no DWARF +# referencing all the other parts from the main executable with DWARF. + +set lib_opts {} +set exec_opts [list debug shlib=$lib_so] + +if [get_compiler_info ${binfile}] { + return -1 +} + +if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc $lib_so $lib_opts] != "" + || [gdb_compile ${srcdir}/${subdir}/$srcfile $binfile executable $exec_opts] != ""} { + untested "Could not compile dynamic executable $binfile." + return -1 +} + +# Start with a fresh gdb. + +clean_restart $executable +gdb_load_shlibs ${lib_so} + +if ![runto_main] then { + fail "Can't run to main" + return 1; +} + +# The "if" condition is artifical to test regression of a former patch. +gdb_breakpoint "[gdb_get_line_number "break-at-nextcall"] if i && gnu_ifunc (i) != 42" + +gdb_breakpoint [gdb_get_line_number "break-at-call"] +gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*" + +# Test GDB will automatically indirect the call. + +gdb_test "p gnu_ifunc (3)" " = 4" + +# Test GDB will skip the gnu_ifunc resolver on first call. + +gdb_test "step" "\r\nfinal .*" + +# Test GDB will not break before the final chosen implementation. + +# Also test a former patch regression: +# Continuing. +# Error in testing breakpoint condition: +# Attempt to take address of value not located in memory. +# +# Breakpoint 2, main () at ./gdb.base/gnu-ifunc.c:33 + +gdb_test "continue" "Continuing.\r\n\r\nBreakpoint .* (at|in) .*break-at-nextcall.*" \ + "continue to break-at-nextcall" + +gdb_breakpoint "gnu_ifunc" + +gdb_continue_to_breakpoint "nextcall gnu_ifunc" + +gdb_test "frame" "#0 +(0x\[0-9a-f\]+ in +)?final \\(.*" "nextcall gnu_ifunc skipped" + + +# Check any commands not doing an inferior call access the address of the +# STT_GNU_IFUNC resolver, not the target function. + +if {[istarget powerpc64-*] && [is_lp64_target]} { + # With only minimal symbols GDB provides the function descriptors. With + # full debug info the function code would be displayed. + set func_prefix {\.} +} else { + set func_prefix {} +} + +gdb_test "p gnu_ifunc" " = {} 0x\[0-9a-f\]+ <${func_prefix}gnu_ifunc>" "p gnu_ifunc executing" +gdb_test "info sym gnu_ifunc" "gnu_ifunc in section .*" "info sym gnu_ifunc executing" + +set test "info addr gnu_ifunc" +gdb_test_multiple $test $test { + -re "Symbol \"gnu_ifunc\" is at (0x\[0-9a-f\]+) in .*$gdb_prompt $" { + pass $test + } +} +gdb_test "info sym $expect_out(1,string)" "gnu_ifunc in section .*" "info sym " + + +# Test statically linked ifunc resolving during inferior start. +# https://bugzilla.redhat.com/show_bug.cgi?id=624967 + +if ![target_info exists gdb_stub] { + + # Compile $staticbinfile separately as it may exit on error (ld/12595). + + if { [gdb_compile ${srcdir}/${subdir}/$libsrc $lib_o object {}] != "" + || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o" $staticbinfile executable {debug}] != "" } { + untested "Could not compile static executable $staticbinfile." + return -1 + } + + clean_restart $staticexecutable + + gdb_breakpoint "gnu_ifunc" + gdb_breakpoint "main" + gdb_run_cmd + gdb_test "" "Breakpoint \[0-9\]*, main .*" "static gnu_ifunc" +}