From 3f342d0ee9bbe0d3e7e1b3598b6f312fa2273ecf Mon Sep 17 00:00:00 2001 From: Ezra Sitorus Date: Mon, 17 Nov 2025 12:45:58 +0000 Subject: [PATCH] gdb/aarch64: Tests for fpmr Add tests for FPMR support in gdb/gdbserver. These tests check availability of FPMR, reading/writing to FPMR, core file generation and preservation under sighandler frame unwinding. A run of the full gdb testsuite has been done on aarch64-none-linux-gnu without FPMR support. The gdb.arch tests were run on Shrinkwrap with FPMR support. Approved-By: Luis Machado --- gdb/testsuite/gdb.arch/aarch64-fpmr-core.c | 40 ++++++ gdb/testsuite/gdb.arch/aarch64-fpmr-core.exp | 97 ++++++++++++++ .../gdb.arch/aarch64-fpmr-sighandler.c | 55 ++++++++ .../gdb.arch/aarch64-fpmr-sighandler.exp | 74 +++++++++++ gdb/testsuite/gdb.arch/aarch64-fpmr.c | 118 ++++++++++++++++++ gdb/testsuite/gdb.arch/aarch64-fpmr.exp | 99 +++++++++++++++ gdb/testsuite/lib/gdb.exp | 66 ++++++++++ 7 files changed, 549 insertions(+) create mode 100644 gdb/testsuite/gdb.arch/aarch64-fpmr-core.c create mode 100644 gdb/testsuite/gdb.arch/aarch64-fpmr-core.exp create mode 100644 gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.c create mode 100644 gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.exp create mode 100644 gdb/testsuite/gdb.arch/aarch64-fpmr.c create mode 100644 gdb/testsuite/gdb.arch/aarch64-fpmr.exp diff --git a/gdb/testsuite/gdb.arch/aarch64-fpmr-core.c b/gdb/testsuite/gdb.arch/aarch64-fpmr-core.c new file mode 100644 index 00000000000..045f36b5351 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-fpmr-core.c @@ -0,0 +1,40 @@ +/* This file is part of GDB, the GNU debugger. + + Copyright 2008-2025 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 + +uint64_t crash_address = 0; + +void +set_fpmr (uint64_t value) +{ + register uint64_t x0_val asm ("x0") = value; + /* msr fpmr, x0 */ + __asm__ volatile (".inst 0xd51b4440" : : ); +} + +int +main (void) +{ + set_fpmr (0x3fff7fc049); + + /* Check FPMR. */ + + *((uint64_t *) crash_address) = 0xDEAD; /* crash point */ + + return 1; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-fpmr-core.exp b/gdb/testsuite/gdb.arch/aarch64-fpmr-core.exp new file mode 100644 index 00000000000..5ab2072c30f --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-fpmr-core.exp @@ -0,0 +1,97 @@ +# Copyright (C) 2018-2025 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 . + +# This file is part of the gdb testsuite. + +# Test generating and reading a core file with FPMR. + +proc check_fpmr_core_file {core_filename} { + # Load the core file. + if { + [gdb_test "core $core_filename" \ + [multi_line \ + "Core was generated by .*" \ + "Program terminated with signal SIGSEGV, Segmentation fault\\." \ + "#0 ${::hex} in main \\(.*\\) at .*" \ + ".* \\*\\(\\(uint64_t \\*\\) crash_address\\) = 0xDEAD.*"] \ + "load core file"] + } { + untested "failed to generate core file" + return -1 + } + + # Check the value of FPMR in the core file. + gdb_test "print/x \$fpmr" " = 0x3fff7fc049" \ + "fpmr contents from core file" +} + +require is_aarch64_target +require allow_aarch64_fpmr_tests + +standard_testfile +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { + return -1 +} + +set binfile [standard_output_file ${testfile}] + +if {![runto_main]} { + return -1 +} + +set crash_breakpoint "crash point" +gdb_breakpoint [gdb_get_line_number $crash_breakpoint] +gdb_continue_to_breakpoint $crash_breakpoint + +gdb_test "print/x \$fpmr" " = 0x3fff7fc049" "fpmr contents from core file" + +gdb_test "continue" \ + [multi_line \ + "Program received signal SIGSEGV, Segmentation fault\\." \ + "${::hex} in main \\(\\).* at .*" \ + ".* \\*\\(\\(uint64_t \\*\\) crash_address\\) = 0xDEAD.*"] \ + "run to crash" + +# Generate the gcore core file. +set gcore_filename [standard_output_file "${testfile}.gcore"] +set gcore_generated [gdb_gcore_cmd "$gcore_filename" "generate gcore file"] + +# Generate a native core file. +set core_filename [core_find ${binfile}] +set core_generated [expr {$core_filename != ""}] + +# At this point we have a couple core files, the gcore one generated by GDB +# and the native one generated by the Linux Kernel. Make sure GDB can read +# both correctly. + +if {$gcore_generated} { + clean_restart + gdb_load ${binfile} + with_test_prefix "gcore corefile" { + check_fpmr_core_file $gcore_filename + } +} else { + fail "gcore corefile not generated" +} + +if {$core_generated} { + clean_restart + gdb_load ${binfile} + with_test_prefix "native corefile" { + check_fpmr_core_file $core_filename + } +} else { + untested "native corefile not generated" +} diff --git a/gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.c b/gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.c new file mode 100644 index 00000000000..a561e7654a3 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.c @@ -0,0 +1,55 @@ +/* This file is part of GDB, the GNU debugger. + + Copyright 2008-2025 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 +#include +#include +#include + +void +set_fpmr (uint64_t value) +{ + register uint64_t x0_val asm ("x0") = value; + /* msr fpmr, x0 */ + __asm__ volatile (".inst 0xd51b4440" : : ); +} + +void +handler (int sig) +{ + set_fpmr (0xff008041); + exit (0); +} + +int +main () +{ + /* Ensure no signals are blocked. */ + sigset_t newset; + sigemptyset (&newset); + sigprocmask (SIG_SETMASK, &newset, NULL); + + signal (SIGILL, handler); + + set_fpmr (0x3fff7fc049); + + /* 0x06000000 : Causes an illegal instruction. Value undefined as per ARM + Architecture Reference Manual ARMv8, Section C4.1. */ + __asm __volatile (".inst 0x06000000"); + + return 0; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.exp b/gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.exp new file mode 100644 index 00000000000..ca8f29ec9e4 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-fpmr-sighandler.exp @@ -0,0 +1,74 @@ +# Copyright 2018-2025 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 . +# +# This file is part of the gdb testsuite. +# +# Test FPMR register set is properly preserved when unwinding sighandler frames. + +require is_aarch64_target +require allow_aarch64_fpmr_tests + +standard_testfile +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +set reg_main_value "0x3fff7fc049" +set reg_handler_value "0xff008041" + +proc check_fpmr {value} { + gdb_test "print /x \$fpmr" ".* = {?$value}?" \ + "check register \$fpmr has value $value" +} + +# Run until end of signal handler. + +gdb_test "continue" "Continuing.*Program received signal SIGILL.*" \ + "continue until signal" + +gdb_breakpoint [gdb_get_line_number "exit (0)"] +gdb_continue_to_breakpoint "exit" ".*exit.*" + +set handlerframe [get_current_frame_number] +set mainframe [expr {$handlerframe + 2}] + +# Check register values. + +with_test_prefix "handler frame 1st" { + check_fpmr $reg_handler_value +} + +# Switch to the frame for main (), and check register values. + +gdb_test "frame $mainframe" "#$mainframe.*main ().*" \ + "set to main frame" + +with_test_prefix "main frame" { + check_fpmr $reg_main_value +} + +# Switch back to the signal handler frame, and check register values. + +gdb_test "frame $handlerframe" \ + "#$handlerframe.*handler \\\(sig=4\\\).*" \ + "set to signal handler frame" + +with_test_prefix "handler frame 2nd" { + check_fpmr $reg_handler_value +} diff --git a/gdb/testsuite/gdb.arch/aarch64-fpmr.c b/gdb/testsuite/gdb.arch/aarch64-fpmr.c new file mode 100644 index 00000000000..89298d1c6a8 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-fpmr.c @@ -0,0 +1,118 @@ +/* This file is part of GDB, the GNU debugger. + + Copyright 2008-2025 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 + +enum FPM_FORMAT +{ + E5M2, + E4M3, +}; + +enum FPM_OVERFLOW +{ + INFNAN, + SATURATE, +}; + +void +set_fpmr (uint64_t value) +{ + register uint64_t x0_val asm ("x0") = value; + __asm__ volatile (".inst 0xd51b4440" : : ); + /* msr fpmr, x0 */ +} + +uint64_t +modify_src1_fmt (uint64_t fpmr, uint64_t fmt) +{ + return (fpmr & ~(0x7)) | (fmt & 0x7); +} + +uint64_t +modify_src2_fmt (uint64_t fpmr, uint64_t fmt) +{ + return (fpmr & ~((0x7) << 3)) | ((fmt & 0x7) << 3); +} + +uint64_t +modify_dst_fmt (uint64_t fpmr, uint64_t fmt) +{ + return (fpmr & ~((0x7) << 6)) | ((fmt & 0x7) << 6); +} + +uint64_t +modify_osm (uint64_t fpmr, uint64_t overflow) +{ + return (fpmr & ~((0x1) << 14)) | ((overflow & 0x1) << 14); +} + +uint64_t +modify_osc (uint64_t fpmr, uint64_t overflow) +{ + return (fpmr & ~((0x1) << 15)) | ((overflow & 0x1) << 15); +} + +uint64_t +modify_lscale (uint64_t fpmr, uint64_t scale) +{ + return (fpmr & ~((0x7f) << 16)) | ((scale & 0x7f) << 16); +} + +uint64_t +modify_nscale (uint64_t fpmr, uint64_t scale) +{ + return (fpmr & ~((0xff) << 24)) | ((scale & 0xff) << 24); +} + +uint64_t +modify_lscale2 (uint64_t fpmr, uint64_t scale) +{ + return (fpmr & ~((uint64_t)(0x3f) << 32)) | ((uint64_t)(scale & 0x3f) << 32); +} + +int +main (void) +{ + uint64_t fpmr = 0; + + fpmr = modify_src1_fmt (fpmr, E4M3); + set_fpmr (fpmr); /* SRC1 */ + + fpmr = modify_src2_fmt (fpmr, E4M3); + set_fpmr (fpmr); /* SRC2 */ + + fpmr = modify_dst_fmt (fpmr, E4M3); + set_fpmr (fpmr); /* DST */ + + fpmr = modify_osm (fpmr, SATURATE); + set_fpmr (fpmr); /* OSM */ + + fpmr = modify_osc (fpmr, SATURATE); + set_fpmr (fpmr); /* OSC */ + + fpmr = modify_lscale (fpmr, -1); + set_fpmr (fpmr); /* LSCALE */ + + fpmr = modify_nscale (fpmr, -1); + set_fpmr (fpmr); /* NSCALE */ + + fpmr = modify_lscale2 (fpmr, -1); + set_fpmr (fpmr); /* LSCALE2 */ + + return 1; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-fpmr.exp b/gdb/testsuite/gdb.arch/aarch64-fpmr.exp new file mode 100644 index 00000000000..142d8172332 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-fpmr.exp @@ -0,0 +1,99 @@ +# Copyright 2023-2025 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 . */ + +# Exercise reading/writing FPMR when it is present. + +require is_aarch64_target +require allow_aarch64_fpmr_tests + +standard_testfile +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +gdb_test_multiple "info register \$fpmr" "Test FPMR SRC1 E5M2" { + -re ".*\r\n.*\[ \ + F8S1=E5M2 \ + F8S2=E5M2 \ + F8D=E5M2 \ + OSM=Inf \ + OSC=Inf/NaN \ + LSCALE=0 \ + NSCALE=0 \ + LSCALE2=0 \]" { + pass "FPMR SRC1 matches E5M2" + } +} + +set breakpoints \ + [list \ + "SRC1" \ + "SRC2" \ + "DST" \ + "OSM" \ + "OSC" \ + "LSCALE" \ + "NSCALE" \ + "LSCALE2"] + +set reg_values \ + [list \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E5M2 F8D=E5M2 OSM=Inf OSC=Inf/NaN LSCALE=0 NSCALE=0 LSCALE2=0 \]" \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E4M3 F8D=E5M2 OSM=Inf OSC=Inf/NaN LSCALE=0 NSCALE=0 LSCALE2=0 \]" \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E4M3 F8D=E4M3 OSM=Inf OSC=Inf/NaN LSCALE=0 NSCALE=0 LSCALE2=0 \]" \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E4M3 F8D=E4M3 OSM=MaxNormal OSC=Inf/NaN LSCALE=0 NSCALE=0 LSCALE2=0 \]" \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E4M3 F8D=E4M3 OSM=MaxNormal OSC=MaxNormal LSCALE=0 NSCALE=0 LSCALE2=0 \]" \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E4M3 F8D=E4M3 OSM=MaxNormal OSC=MaxNormal LSCALE=127 NSCALE=0 LSCALE2=0 \]" \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E4M3 F8D=E4M3 OSM=MaxNormal OSC=MaxNormal LSCALE=127 NSCALE=255 LSCALE2=0 \]" \ + ".*\r\n.*\[ F8S1=E4M3 F8S2=E4M3 F8D=E4M3 OSM=MaxNormal OSC=MaxNormal LSCALE=127 NSCALE=255 LSCALE2=63 \]"] + +set pass_messages \ + [list \ + "FPMR SRC1 matches E4M3" \ + "FPMR SRC2 matches E4M3" \ + "FPMR DST matches E4M3" \ + "FPMR OSM matches MaxNormal" \ + "FPMR OSC matches MaxNormal" \ + "FPMR LSCALE matches" \ + "FPMR NSCALE matches" \ + "FPMR LSCALE2 matches"] + +for {set i 0} {$i < 8} {incr i} { + set bp [lindex $breakpoints $i] + gdb_breakpoint [gdb_get_line_number $bp] + gdb_continue_to_breakpoint $bp + + gdb_test_multiple "info register \$fpmr" "" { + -re [lindex $reg_values $i] { + pass [lindex $pass_messages $i] + } + } +} + +gdb_test_multiple "set \$fpmr=0x0" "" { + -re ".*\r\n.*\[ F8S1=E5M2 F8S2=E5M2 F8D=E5M2 OSM=Inf OSC=Inf/NaN LSCALE=0 NSCALE=0 LSCALE2=0 \]" { + pass "Reset FPMR to 0 from GDB" + } +} + +gdb_test_multiple "set \$fpmr=0x3f007f4008" "" { + -re ".*\r\n.*\[ F8S1=E5M2 F8S2=E4M3 F8D=E5M2 OSM=MaxNormal OSC=Inf/NaN LSCALE=127 NSCALE=0 LSCALE2=63 \]" { + pass "Write to FPMR from GDB" + } +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 856c96771c6..a424086007e 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -5207,6 +5207,72 @@ proc aarch64_supports_sme_svl { length } { return 1 } +# Run a test on the target to see if it supports the AArch64 FPMR feature. +# Return 1 if so, 0 if it does not. Note this causes a restart of GDB. + +gdb_caching_proc allow_aarch64_fpmr_tests {} { + global srcdir subdir gdb_prompt inferior_exited_re + + set me "allow_aarch64_fpmr_tests" + + if { ![is_aarch64_target]} { + return 0 + } + + set compile_flags "{additional_flags=-march=armv8-a}" + + # Compile a test program reading FPMR. + set src { + int main() { + asm volatile ("mrs x0, fpmr"); + return 0; + } + } + if {![gdb_simple_compile $me $src executable $compile_flags]} { + # Try again, but with a raw hex instruction so we don't rely on + # assembler support for FPMR. + + set compile_flags "{additional_flags=-march=armv8-a}" + + # Compile a test program reading FPMR. + set src { + int main() { + asm volatile (".word 0xD53B4440"); + return 0; + } + } + + if {![gdb_simple_compile $me $src executable $compile_flags]} { + return 0 + } + } + + # Compilation succeeded so now run it via gdb. + clean_restart + gdb_load $obj + gdb_run_cmd + + gdb_expect { + -re ".*Illegal instruction.*${gdb_prompt} $" { + verbose -log "\n$me fpmr support not detected" + set allow_fpmr_tests 0 + } + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { + verbose -log "\n$me: fpmr support detected" + set allow_fpmr_tests 1 + } + default { + warning "\n$me: default case taken" + set allow_fpmr_tests 0 + } + } + gdb_exit + remote_file build delete $obj + + verbose "$me: returning $allow_fpmr_tests" 2 + return $allow_fpmr_tests +} + # 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. -- 2.47.3