From patchwork Fri Mar 9 21:20:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 26266 Received: (qmail 36652 invoked by alias); 9 Mar 2018 21:20:42 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 36638 invoked by uid 89); 9 Mar 2018 21:20:41 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.1 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy= X-HELO: mx1.redhat.com Received: from mx3-rdu2.redhat.com (HELO mx1.redhat.com) (66.187.233.73) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 09 Mar 2018 21:20:39 +0000 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DAB2BBD9E for ; Fri, 9 Mar 2018 21:20:37 +0000 (UTC) Received: from [127.0.0.1] (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5B801100727B for ; Fri, 9 Mar 2018 21:20:37 +0000 (UTC) Subject: Re: [PATCH 10/11] Extend GNU ifunc testcases To: GDB Patches References: <20180309211612.12941-1-palves@redhat.com> <20180309211612.12941-11-palves@redhat.com> From: Pedro Alves Message-ID: Date: Fri, 9 Mar 2018 21:20:36 +0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.6.0 MIME-Version: 1.0 In-Reply-To: <20180309211612.12941-11-palves@redhat.com> For convenience, here's the same patch with "git diff -w", easier to read because the patch moves code at global scope to functions. diff --git c/gdb/testsuite/gdb.base/gnu-ifunc-lib.c w/gdb/testsuite/gdb.base/gnu-ifunc-lib.c index b9d446c92fa..7aac81faec6 100644 --- c/gdb/testsuite/gdb.base/gnu-ifunc-lib.c +++ w/gdb/testsuite/gdb.base/gnu-ifunc-lib.c @@ -22,10 +22,14 @@ extern int final (int arg); typedef int (*final_t) (int arg); +#ifndef IFUNC_RESOLVER_ATTR asm (".type gnu_ifunc, %gnu_indirect_function"); - final_t gnu_ifunc (unsigned long hwcap) +#else +final_t +gnu_ifunc_resolver (unsigned long hwcap) +#endif { resolver_hwcap = hwcap; if (! gnu_ifunc_initialized) @@ -33,3 +37,9 @@ gnu_ifunc (unsigned long hwcap) else return final; } + +#ifdef IFUNC_RESOLVER_ATTR +extern int gnu_ifunc (int arg); + +__typeof (gnu_ifunc) gnu_ifunc __attribute__ ((ifunc ("gnu_ifunc_resolver"))); +#endif diff --git c/gdb/testsuite/gdb.base/gnu-ifunc-resd.c w/gdb/testsuite/gdb.base/gnu-ifunc-resd.c new file mode 100644 index 00000000000..ef19fc98fab --- /dev/null +++ w/gdb/testsuite/gdb.base/gnu-ifunc-resd.c @@ -0,0 +1,22 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2009-2018 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 . */ + +int +final (int arg) +{ + return arg + 1; +} diff --git c/gdb/testsuite/gdb.base/gnu-ifunc.c w/gdb/testsuite/gdb.base/gnu-ifunc.c index 78fd30d9fd2..a4abacaccee 100644 --- c/gdb/testsuite/gdb.base/gnu-ifunc.c +++ w/gdb/testsuite/gdb.base/gnu-ifunc.c @@ -23,12 +23,6 @@ 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. */ diff --git c/gdb/testsuite/gdb.base/gnu-ifunc.exp w/gdb/testsuite/gdb.base/gnu-ifunc.exp index 1d0d0401c37..fa1464bec51 100644 --- c/gdb/testsuite/gdb.base/gnu-ifunc.exp +++ w/gdb/testsuite/gdb.base/gnu-ifunc.exp @@ -18,47 +18,177 @@ if {[skip_shlib_tests]} { } standard_testfile .c -set executable ${testfile} -set staticexecutable ${executable}-static +set staticexecutable ${testfile}-static set staticbinfile [standard_output_file ${staticexecutable}] set libfile "${testfile}-lib" set libsrc ${libfile}.c -set lib_so [standard_output_file ${libfile}.so] + +set resdfile "${testfile}-resd" +set resdsrc ${resdfile}.c + +if [get_compiler_info] { + return -1 +} + +# Return the binary suffix appended to program and library names to +# make each testcase variant unique. +proc make_binsuffix {resolver_attr resolver_debug resolved_debug} { + return "$resolver_attr-$resolver_debug-$resolved_debug" +} + +# Compile the testcase. RESOLVER_ATTR is true if we're testing with +# an ifunc resolver that has a different name from the user symbol, +# specified with GCC's __attribute__ ifunc. RESOLVER_DEBUG is true +# iff the resolver was compiled with debug info. RESOLVED_DEBUG is +# true iff the target function was compiled with debug info. +proc build {resolver_attr resolver_debug resolved_debug} { + global srcdir subdir srcfile binfile + global libsrc lib_so libfile + global exec_opts executable + global hex gdb_prompt + global resdfile resdsrc + + set suffix [make_binsuffix $resolver_attr $resolver_debug $resolved_debug] + + set lib_so [standard_output_file ${libfile}-$suffix.so] # $lib_o must not have {debug}, it would override the STT_GNU_IFUNC ELF markers. -set lib_o [standard_output_file ${libfile}.o] + set lib_o [standard_output_file ${libfile}-$suffix.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 exec_opts [list debug shlib=$lib_so] set lib_opts {} -set exec_opts [list debug shlib=$lib_so] + set resd_opts {} -if [get_compiler_info] { - return -1 + if {$resolver_attr} { + lappend lib_opts "additional_flags=-DIFUNC_RESOLVER_ATTR" } -if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc $lib_so $lib_opts] != "" - || [gdb_compile ${srcdir}/${subdir}/$srcfile $binfile executable $exec_opts] != ""} { - untested "failed to compile first testcase" - return -1 + if {$resolver_debug} { + lappend lib_opts "debug" + } + + if {$resolved_debug} { + lappend resd_opts "debug" + } + + set resd_o $resdfile-$suffix.o + + if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc \ + $lib_so $lib_opts] != "" + || [gdb_compile ${srcdir}/${subdir}/$resdsrc \ + $resd_o object $resd_opts] != "" + || [gdb_compile [list ${srcdir}/${subdir}/$srcfile $resd_o] \ + $binfile-$suffix executable $exec_opts] != ""} { + untested "failed to compile testcase" + return 0 + } + + return 1 +} + +# Test setting a breakpoint on a ifunc function before and after the +# ifunc is resolved. RESOLVER_ATTR is true if we're testing with a +# resolver specified with __attribute__ ifunc. RESOLVER_DEBUG is true +# iff the resolver was compiled with debug info. RESOLVED_DEBUG is +# true iff the target function was compiled with debug info. +proc_with_prefix set-break {resolver_attr resolver_debug resolved_debug} { + global binfile libfile lib_so + global hex decimal + global gdb_prompt + + set suffix [make_binsuffix $resolver_attr $resolver_debug $resolved_debug] + + set lib_so [standard_output_file ${libfile}-$suffix.so] + clean_restart $binfile-$suffix + gdb_load_shlib ${lib_so} + + if ![runto_main] then { + fail "can't run to main" + return 1 + } + + set ws "\[ \t\]+" + + if {$resolver_attr} { + set gnu_ifunc_resolver "gnu_ifunc_resolver" + } else { + set gnu_ifunc_resolver "gnu_ifunc" + } + + with_test_prefix "before resolving" { + delete_breakpoints + gdb_test "break gnu_ifunc" \ + "Breakpoint $decimal at gnu-indirect-function resolver at $hex" + gdb_test "info breakpoints" \ + "$decimal${ws}STT_GNU_IFUNC resolver${ws}keep${ws}y${ws}$hex <${gnu_ifunc_resolver}>" + } + + global resdsrc + + with_test_prefix "resolve" { + delete_breakpoints + gdb_breakpoint [gdb_get_line_number "break-at-exit"] + gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*" + } + + with_test_prefix "after resolving" { + delete_breakpoints + + if {!$resolved_debug} { + # Set a breakpoint both at the ifunc, and at the ifunc's + # target. GDB should resolve both to the same address. + # Start with the ifunc's target. + set addr "-" + set test "break final" + # Extract the address without the leading "0x", because + # addresses in "info break" output include leading 0s + # (like "0x0000ADDR"). + set hex_number {[0-9a-fA-F][0-9a-fA-F]*} + gdb_test_multiple $test $test { + -re "Breakpoint .* at 0x($hex_number)\r\n$gdb_prompt $" { + set addr $expect_out(1,string) + pass $test + } + } + + # Now set a break at the ifunc. + gdb_test "break gnu_ifunc" "Breakpoint .* at 0x$addr" + set location "$decimal${ws}breakpoint${ws}keep${ws}y${ws}0x0*$addr${ws}" + } else { + set lineno -1 + set test "break final" + gdb_test_multiple $test $test { + -re "Breakpoint .* at $hex: file .*$resdsrc, line ($decimal)\\.\r\n$gdb_prompt $" { + set lineno $expect_out(1,string) + pass $test + } + } + gdb_test "break gnu_ifunc" "Breakpoint .* at $hex: file .*$resdsrc, line $lineno\\." + set location "$decimal${ws}breakpoint${ws}keep${ws}y${ws}$hex in final at .*$resdsrc:$lineno" + } + gdb_test "info breakpoints" "$location\r\n$location" + } +} + +proc misc_tests {resolver_attr resolver_debug resolved_debug} { + global srcdir subdir srcfile binfile + global libsrc lib_so libfile + global exec_opts executable + global hex gdb_prompt + global resdfile resdsrc + + set suffix [make_binsuffix $resolver_attr $resolver_debug $resolved_debug] + + if {$resolver_attr} { + set gnu_ifunc_resolver "gnu_ifunc_resolver" + } else { + set gnu_ifunc_resolver "gnu_ifunc" } # Start with a fresh gdb. -clean_restart $executable + clean_restart $binfile-$suffix gdb_load_shlib ${lib_so} if ![runto_main] then { @@ -67,14 +197,23 @@ if ![runto_main] then { } # 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-nextcall"] if i && (int) 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. + if {!$resolver_debug && !$resolved_debug} { + gdb_test "p gnu_ifunc()" \ + "'final' has unknown return type; cast the call to its declared return type" + gdb_test "p gnu_ifunc (3)" \ + "'final' has unknown return type; cast the call to its declared return type" + gdb_test "p (int) gnu_ifunc (3)" " = 4" + } else { + gdb_test "p gnu_ifunc()" "Too few arguments in function call\\." gdb_test "p gnu_ifunc (3)" " = 4" + } # Test that the resolver received its argument. @@ -93,7 +232,16 @@ gdb_test "p/x resolver_hwcap" "= $actual_hwcap" "resolver received HWCAP" # Test GDB will skip the gnu_ifunc resolver on first call. + # Even if the resolver has debug info, stepping into an ifunc call + # should skip the resolver. + if {!$resolved_debug} { + # Make GDB stop stepping even if it steps into a function with + # no debug info. + gdb_test_no_output "set step-mode on" + gdb_test "step" "$hex in final \\\(\\\)" + } else { gdb_test "step" "\r\nfinal .*" + } # Test GDB will not break before the final chosen implementation. @@ -104,14 +252,16 @@ gdb_test "step" "\r\nfinal .*" # # Breakpoint 2, main () at ./gdb.base/gnu-ifunc.c:33 -gdb_test "continue" "Continuing.\r\n\r\nBreakpoint .* (at|in) .*break-at-nextcall.*" \ + 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" + 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 @@ -125,25 +275,82 @@ if {[istarget powerpc64-*] && [is_lp64_target]} { 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" + gdb_test "p gnu_ifunc" \ + " = {} 0x\[0-9a-f\]+ <${func_prefix}${gnu_ifunc_resolver}>" \ + "p gnu_ifunc executing" + gdb_test "info sym gnu_ifunc" \ + "${gnu_ifunc_resolver} in section .*" \ + "info sym gnu_ifunc executing" set test "info addr gnu_ifunc" + if {!$resolver_attr && $resolver_debug} { + gdb_test_multiple $test $test { + -re "Symbol \"gnu_ifunc\" is a function at address (0x\[0-9a-f\]+).*$gdb_prompt $" { + pass $test + } + } + } else { 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 " + } + gdb_test "info sym $expect_out(1,string)" \ + "${gnu_ifunc_resolver} in section .*" \ + "info sym " + + # Test calling the resolver directly instead of the ifunc symbol. + # Can only do that if the ifunc and the ifunc resolver have + # different names. + if {$resolver_attr} { + if {$resolver_debug} { + gdb_test "p gnu_ifunc_resolver(0)" \ + " = \\(int \\(\\*\\)\\(int\\)\\) $hex " + } else { + gdb_test "p gnu_ifunc_resolver(0)" \ + "'gnu_ifunc_resolver' has unknown return type; cast the call to its declared return type" + gdb_test "p (void *) gnu_ifunc_resolver(0)" \ + " = \\(void \\*\\) $hex " + } + } +} +# Test all the combinations of: +# +# - An ifunc resolver with the same name as the ifunc symbol vs an +# ifunc resolver with a different name as the ifunc symbol. +# +# - ifunc resolver compiled with and without debug info. This ensures +# that GDB understands that a function not a regular function by +# looking at the STT_GNU_IFUNC type in the elf symbols. DWARF has +# no way to express the STT_GNU_IFUNC type. +# +# - ifunc target function (resolved) compiled with and without debug +# info. +foreach_with_prefix resolver_attr {0 1} { + foreach_with_prefix resolver_debug {0 1} { + foreach_with_prefix resolved_debug {0 1} { + build $resolver_attr $resolver_debug $resolved_debug + misc_tests $resolver_attr $resolver_debug $resolved_debug + set-break $resolver_attr $resolver_debug $resolved_debug + } + } +} # Test statically linked ifunc resolving during inferior start. # https://bugzilla.redhat.com/show_bug.cgi?id=624967 -# Compile $staticbinfile separately as it may exit on error (ld/12595). +with_test_prefix "static" { + # Compile $staticbinfile separately as it may exit on error + # (ld/12595). + set lib_o [standard_output_file ${libfile}.o] + set resd_o [standard_output_file ${resdfile}.o] if { [gdb_compile ${srcdir}/${subdir}/$libsrc $lib_o object {}] != "" - || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o" $staticbinfile executable {debug}] != "" } { + || [gdb_compile ${srcdir}/${subdir}/$resdsrc $resd_o object {}] != "" + || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o $resd_o" \ + $staticbinfile executable {debug}] != "" } { untested "failed to compile second testcase" return -1 } @@ -154,3 +361,4 @@ gdb_breakpoint "gnu_ifunc" gdb_breakpoint "main" gdb_run_cmd gdb_test "" "Breakpoint \[0-9\]*, main .*" "static gnu_ifunc" +}