diff options
-rw-r--r-- | gdb/aarch64-tdep.c | 8 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-mops-single-step.c | 73 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp | 98 | ||||
-rw-r--r-- | gdb/testsuite/lib/gdb.exp | 99 |
4 files changed, 275 insertions, 3 deletions
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 8d0553f..05ecd42 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -3808,10 +3808,12 @@ aarch64_displaced_step_copy_insn (struct gdbarch *gdbarch, if (aarch64_decode_insn (insn, &inst, 1, NULL) != 0) return NULL; - /* Look for a Load Exclusive instruction which begins the sequence. */ - if (inst.opcode->iclass == ldstexcl && bit (insn, 22)) + /* Look for a Load Exclusive instruction which begins the sequence, + or for a MOPS instruction. */ + if ((inst.opcode->iclass == ldstexcl && bit (insn, 22)) + || AARCH64_CPU_HAS_FEATURE (*inst.opcode->avariant, MOPS)) { - /* We can't displaced step atomic sequences. */ + /* We can't displaced step atomic sequences nor MOPS instructions. */ return NULL; } diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c new file mode 100644 index 0000000..4a27867 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c @@ -0,0 +1,73 @@ +/* This file is part of GDB, the GNU debugger. + + Copyright 2024 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 <http://www.gnu.org/licenses/>. */ + +#define TEST_STRING "Just a test string." +#define BUF_SIZE sizeof(TEST_STRING) + +int +main (void) +{ + char source[BUF_SIZE] = TEST_STRING; + char dest[BUF_SIZE]; + char *p, *q; + long size, zero; + + /* Note: The prfm instruction in the asm statements below is there just + to allow the testcase to recognize when the PC is at the instruction + right after the MOPS sequence. */ + + p = dest; + size = sizeof (dest); + zero = 0; + /* Break memset. */ + /* memset implemented in MOPS instructions. */ + __asm__ volatile ("setp [%0]!, %1!, %2\n\t" + "setm [%0]!, %1!, %2\n\t" + "sete [%0]!, %1!, %2\n\t" + "prfm pldl3keep, [%0, #0]\n\t" + : "+&r"(p), "+&r"(size) + : "r"(zero) + : "memory"); + + p = dest; + q = source; + size = sizeof (dest); + /* Break memcpy. */ + /* memcpy implemented in MOPS instructions. */ + __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t" + "cpyfm [%0]!, [%1]!, %2!\n\t" + "cpyfe [%0]!, [%1]!, %2!\n\t" + "prfm pldl3keep, [%0, #0]\n\t" + : "+&r" (p), "+&r" (q), "+&r" (size) + : + : "memory"); + + p = dest; + q = source; + size = sizeof (dest); + /* Break memmove. */ + /* memmove implemented in MOPS instructions. */ + __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t" + "cpym [%0]!, [%1]!, %2!\n\t" + "cpye [%0]!, [%1]!, %2!\n\t" + "prfm pldl3keep, [%0, #0]\n\t" + : "+&r" (p), "+&r" (q), "+&r" (size) + : + : "memory"); + + return 0; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp new file mode 100644 index 0000000..2cb5fdc --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp @@ -0,0 +1,98 @@ +# Copyright 2024 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 <http://www.gnu.org/licenses/>. +# +# This file is part of the GDB testsuite. + +# Test single stepping through MOPS (memory operations) instruction sequences. + +require allow_aarch64_mops_tests + +standard_testfile +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + [list debug additional_flags=-march=armv9.3-a]] } { + return -1 +} + +# If the inferior is rescheduled to another CPU while a main or epilogue +# instruction is executed, the OS resets the inferior back to the prologue +# instruction, so we need to allow for that possibility. +proc step_through_sequence { prefix } { + set count 0 + + while { [is_at_instruction ${prefix}p] == 1 && $count < 50 } { + incr count + + # The stepi output isn't useful to detect whether we stepped over + # the instruction. + gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}p" + if { [is_at_instruction ${prefix}m] == 1 } { + pass "stepped over ${prefix}p" + } else { + fail "stepped over ${prefix}e" + return 0 + } + + gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}m" + if { [is_at_instruction ${prefix}e] == 1 } { + pass "stepped over ${prefix}m" + } elseif { [is_at_instruction ${prefix}p] == 1 } { + # The inferior was rescheduled to another CPU. + pass "${prefix}m: reset back to prologue" + continue + } else { + fail "stepped over ${prefix}m" + return 0 + } + + gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}e" + if { [is_at_instruction prfm] == 1 } { + pass "stepped over ${prefix}e" + return 1 + } elseif { [is_at_instruction ${prefix}p] == 1 } { + # The inferior was rescheduled to another CPU. + pass "${prefix}e: reset back to prologue" + continue + } + } + + fail "step through $prefix sequence" + return 0 +} + +if ![runto_main] { + return -1 +} + +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memset"] +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memcpy"] +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memmove"] + +gdb_continue_to_breakpoint "memset breakpoint" + +if { [arrive_at_instruction setp] } { + step_through_sequence set +} + +gdb_continue_to_breakpoint "memcpy breakpoint" + +if { [arrive_at_instruction cpyfp] } { + step_through_sequence cpyf +} + +gdb_continue_to_breakpoint "memmove breakpoint" + +if { [arrive_at_instruction cpyp] } { + step_through_sequence cpy +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index cdc3721..ac68e66 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -848,6 +848,44 @@ proc gdb_continue_to_breakpoint {name {location_pattern .*}} { }] } +# Check whether GDB is stopped at the given instruction. +# INSTRUCTION should be just its mnemonic, without any arguments. + +proc is_at_instruction { instruction } { + global gdb_prompt hex + + set test "pc points to $instruction" + gdb_test_multiple {x/i $pc} $test { + -re -wrap "=> $hex \[^\r\n\]+:\t$instruction\t\[^\r\n\]+" { + return 1 + } + -re "\r\n$gdb_prompt $" { + return 0 + } + } + + return 0 +} + +# Single-steps GDB until it arrives at the given instruction. +# INSTRUCTION should be just its mnemonic, without any arguments. + +proc arrive_at_instruction { instruction } { + set count 0 + + while { [is_at_instruction $instruction] != 1 } { + gdb_test -nopass "stepi" "\[^\r\n\]+" \ + "stepi #$count to reach $instruction" + incr count + + if { $count > 50 } { + fail "didn't reach $instruction" + return 0 + } + } + + return 1 +} # gdb_internal_error_resync: # @@ -4496,6 +4534,67 @@ proc aarch64_supports_sme_svl { length } { return 1 } +# Run a test on the target to see if it supports AArch64 MOPS (Memory +# Operations) extensions. Return 1 if so, 0 if it does not. Note this +# causes a restart of GDB. + +gdb_caching_proc allow_aarch64_mops_tests {} { + global srcdir subdir gdb_prompt inferior_exited_re + + set me "allow_aarch64_mops_tests" + + if { ![is_aarch64_target]} { + return 0 + } + + # ARMv9.3-A contains the MOPS extension. The test program doesn't use it, + # but take the opportunity to check whether the toolchain knows about MOPS. + set compile_flags "{additional_flags=-march=armv9.3-a}" + + # Compile a program that tests the MOPS feature. + set src { + #include <stdbool.h> + #include <sys/auxv.h> + + #ifndef HWCAP2_MOPS + #define HWCAP2_MOPS (1UL << 43) + #endif + + int main() { + bool mops_supported = getauxval (AT_HWCAP2) & HWCAP2_MOPS; + + return !mops_supported; + } + } + + if {![gdb_simple_compile $me $src executable $compile_flags]} { + return 0 + } + + # Compilation succeeded so now run it via gdb. + clean_restart $obj + gdb_run_cmd + gdb_expect { + -re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" { + verbose -log "\n$me mops support not detected" + set allow_mops_tests 0 + } + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { + verbose -log "\n$me: mops support detected" + set allow_mops_tests 1 + } + default { + warning "\n$me: default case taken" + set allow_mops_tests 0 + } + } + gdb_exit + remote_file build delete $obj + + verbose "$me: returning $allow_mops_tests" 2 + return $allow_mops_tests +} + # A helper that compiles a test case to see if __int128 is supported. proc gdb_int128_helper {lang} { return [gdb_can_simple_compile "i128-for-$lang" { |