diff mbox series

[v2] gdb/testsuite: Add gdb.arch/aarch64-sve-sigunwind.exp

Message ID 20250412050338.2260505-1-thiago.bauermann@linaro.org
State New
Headers show
Series [v2] gdb/testsuite: Add gdb.arch/aarch64-sve-sigunwind.exp | expand

Commit Message

Thiago Jung Bauermann April 12, 2025, 5:03 a.m. UTC
There's currently no test for unwinding the SVE registers from a signal
frame, so add one.

Tested on aarch64-linux-gnu native.
---
 .../gdb.arch/aarch64-sve-sigunwind.c          | 205 ++++++++++++++++++
 .../gdb.arch/aarch64-sve-sigunwind.exp        | 106 +++++++++
 2 files changed, 311 insertions(+)
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp

Hello,

This version addresses Luis' comments from v1.

By using the testsuite's vector list, it's easy to return early in the .exp
file if the system supports only one vector length, and I don't hit the
corner case that Luis described anymore.

I decided to make the test more complete by also checking the contents of
all registers in the SVE context of the signal frame.  Unfortunately I get
the following failures:

Running /path/to/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp ...
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z0.b.u contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z1.b.u contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z2.b.u contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z3.b.u contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z4.b.u contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z5.b.u contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p0 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p1 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p2 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p3 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p4 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p5 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p6 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p7 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p8 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p9 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p10 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p11 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p12 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p13 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p14 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p15 contents were correctly unwound
FAIL: gdb.arch/aarch64-sve-sigunwind.exp: ffr contents were correctly unwound

                === gdb Summary ===

# of expected passes            144
# of unexpected failures        23

After some investigation, I suspect this is a bug in the kernel though. To
take GDB out of the picture, I wrote the following test program:

https://people.linaro.org/~thiago.bauermann/sve/sve-sigunwind.c

Running it in a QEMU VM with VL of 128 bits, I get:

SVE context: vl = 16
SVE context: flags = 0
SVE context: Z0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
SVE context: Z1 = 0 0 0 0 0 0 3 232 0 0 0 0 0 0 3 232
SVE context: Z2 = 0 0 0 0 0 0 0 0 0 0 235 17 78 62 96 0
SVE context: Z3 = 0 0 0 0 0 0 0 0 0 0 202 159 237 8 8 128
SVE context: Z4 = 0 0 0 0 0 0 0 56 0 0 202 159 237 8 0 64
SVE context: Z5 = 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 9
SVE context: Z6 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z7 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z8 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z9 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z10 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z11 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z12 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z13 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z14 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z15 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z16 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z17 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z18 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z19 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z20 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z21 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z22 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z23 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z24 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z25 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z26 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z27 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z28 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z29 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z30 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: Z31 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
SVE context: P0 = 0 0
SVE context: P1 = 0 0
SVE context: P2 = 0 0
SVE context: P3 = 0 0
SVE context: P4 = 0 0
SVE context: P5 = 0 0
SVE context: P6 = 0 0
SVE context: P7 = 0 0
SVE context: P8 = 0 0
SVE context: P9 = 0 0
SVE context: P10 = 0 0
SVE context: P11 = 0 0
SVE context: P12 = 0 0
SVE context: P13 = 0 0
SVE context: P14 = 0 0
SVE context: P15 = 0 0
SVE context: FFR = 0 0

Maybe there's a bug in my test program as well? I couldn't find it, but
it's interesting that the registers with wrong contents are the same ones
that GDB gets.

I'll investigate some more and report the problem on the kernel mailing
list if I don't find anything. In any case, I think it's worth to add the
test even with these failures.

Changes in v2:
- Use vector length list detected by the testsuite's helper to find two
  valid vector lengths that the test can use (suggested by Luis).
- Test unwinding Z, P and FFR register contents, not just VG.
- Rename test files to aarch64-sve-sigunwind.{c,exp}.


base-commit: 4fa9476ad23d5868c2bc4c02dc1a73acaefbcbc2

Comments

Luis Machado April 14, 2025, 8:59 a.m. UTC | #1
On 4/12/25 06:03, Thiago Jung Bauermann wrote:
> There's currently no test for unwinding the SVE registers from a signal
> frame, so add one.
> 
> Tested on aarch64-linux-gnu native.
> ---
>  .../gdb.arch/aarch64-sve-sigunwind.c          | 205 ++++++++++++++++++
>  .../gdb.arch/aarch64-sve-sigunwind.exp        | 106 +++++++++
>  2 files changed, 311 insertions(+)
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp
> 
> Hello,
> 
> This version addresses Luis' comments from v1.
> 
> By using the testsuite's vector list, it's easy to return early in the .exp
> file if the system supports only one vector length, and I don't hit the
> corner case that Luis described anymore.
> 
> I decided to make the test more complete by also checking the contents of
> all registers in the SVE context of the signal frame.  Unfortunately I get
> the following failures:
> 
> Running /path/to/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp ...
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z0.b.u contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z1.b.u contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z2.b.u contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z3.b.u contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z4.b.u contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z5.b.u contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p0 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p1 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p2 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p3 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p4 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p5 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p6 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p7 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p8 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p9 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p10 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p11 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p12 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p13 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p14 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p15 contents were correctly unwound
> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: ffr contents were correctly unwound
> 
>                 === gdb Summary ===
> 
> # of expected passes            144
> # of unexpected failures        23
> 
> After some investigation, I suspect this is a bug in the kernel though. To
> take GDB out of the picture, I wrote the following test program:
> 
> https://people.linaro.org/~thiago.bauermann/sve/sve-sigunwind.c
> 
> Running it in a QEMU VM with VL of 128 bits, I get:
> 
> SVE context: vl = 16
> SVE context: flags = 0
> SVE context: Z0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
> SVE context: Z1 = 0 0 0 0 0 0 3 232 0 0 0 0 0 0 3 232
> SVE context: Z2 = 0 0 0 0 0 0 0 0 0 0 235 17 78 62 96 0
> SVE context: Z3 = 0 0 0 0 0 0 0 0 0 0 202 159 237 8 8 128
> SVE context: Z4 = 0 0 0 0 0 0 0 56 0 0 202 159 237 8 0 64
> SVE context: Z5 = 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 9
> SVE context: Z6 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z7 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z8 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z9 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z10 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z11 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z12 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z13 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z14 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z15 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z16 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z17 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z18 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z19 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z20 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z21 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z22 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z23 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z24 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z25 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z26 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z27 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z28 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z29 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z30 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: Z31 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
> SVE context: P0 = 0 0
> SVE context: P1 = 0 0
> SVE context: P2 = 0 0
> SVE context: P3 = 0 0
> SVE context: P4 = 0 0
> SVE context: P5 = 0 0
> SVE context: P6 = 0 0
> SVE context: P7 = 0 0
> SVE context: P8 = 0 0
> SVE context: P9 = 0 0
> SVE context: P10 = 0 0
> SVE context: P11 = 0 0
> SVE context: P12 = 0 0
> SVE context: P13 = 0 0
> SVE context: P14 = 0 0
> SVE context: P15 = 0 0
> SVE context: FFR = 0 0
> 
> Maybe there's a bug in my test program as well? I couldn't find it, but
> it's interesting that the registers with wrong contents are the same ones
> that GDB gets.
> 
> I'll investigate some more and report the problem on the kernel mailing
> list if I don't find anything. In any case, I think it's worth to add the
> test even with these failures.

Interesting. I wonder if gdb is getting confused with the register set to use (FPSIMD/SVE) or
with the payload size, and reading some garbage for the first 5 Z registers and the
predicate ones.

Might be worth checking what exactly the payload looks like when we force-update the vector length
in the signal handler. Odd case anyway.

In any case...

> 
> Changes in v2:
> - Use vector length list detected by the testsuite's helper to find two
>   valid vector lengths that the test can use (suggested by Luis).
> - Test unwinding Z, P and FFR register contents, not just VG.
> - Rename test files to aarch64-sve-sigunwind.{c,exp}.
> 
> diff --git a/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c
> new file mode 100644
> index 000000000000..c86beaf5dda0
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c
> @@ -0,0 +1,205 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 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 <http://www.gnu.org/licenses/>.  */
> +
> +/* Exercise unwinding AArch64's SVE registers from a signal frame.   */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <signal.h>
> +#include <sys/prctl.h>
> +#include <unistd.h>
> +
> +static int second_vl = 0;
> +
> +static void
> +initialize_sve_state_main ()
> +{
> +  __asm __volatile ("dup z0.b, -1");
> +  __asm __volatile ("dup z1.b, -1");
> +  __asm __volatile ("dup z2.b, -1");
> +  __asm __volatile ("dup z3.b, -1");
> +  __asm __volatile ("dup z4.b, -1");
> +  __asm __volatile ("dup z5.b, -1");
> +  __asm __volatile ("dup z6.b, -1");
> +  __asm __volatile ("dup z7.b, -1");
> +  __asm __volatile ("dup z8.b, -1");
> +  __asm __volatile ("dup z9.b, -1");
> +  __asm __volatile ("dup z10.b, -1");
> +  __asm __volatile ("dup z11.b, -1");
> +  __asm __volatile ("dup z12.b, -1");
> +  __asm __volatile ("dup z13.b, -1");
> +  __asm __volatile ("dup z14.b, -1");
> +  __asm __volatile ("dup z15.b, -1");
> +  __asm __volatile ("dup z16.b, -1");
> +  __asm __volatile ("dup z17.b, -1");
> +  __asm __volatile ("dup z18.b, -1");
> +  __asm __volatile ("dup z19.b, -1");
> +  __asm __volatile ("dup z20.b, -1");
> +  __asm __volatile ("dup z21.b, -1");
> +  __asm __volatile ("dup z22.b, -1");
> +  __asm __volatile ("dup z23.b, -1");
> +  __asm __volatile ("dup z24.b, -1");
> +  __asm __volatile ("dup z25.b, -1");
> +  __asm __volatile ("dup z26.b, -1");
> +  __asm __volatile ("dup z27.b, -1");
> +  __asm __volatile ("dup z28.b, -1");
> +  __asm __volatile ("dup z29.b, -1");
> +  __asm __volatile ("dup z30.b, -1");
> +  __asm __volatile ("dup z31.b, -1");
> +  __asm __volatile ("ptrue p0.d");
> +  __asm __volatile ("ptrue p1.d");
> +  __asm __volatile ("ptrue p2.d");
> +  __asm __volatile ("ptrue p3.d");
> +  __asm __volatile ("ptrue p4.d");
> +  __asm __volatile ("ptrue p5.d");
> +  __asm __volatile ("ptrue p6.d");
> +  __asm __volatile ("ptrue p7.d");
> +  __asm __volatile ("ptrue p8.d");
> +  __asm __volatile ("ptrue p9.d");
> +  __asm __volatile ("ptrue p10.d");
> +  __asm __volatile ("ptrue p11.d");
> +  __asm __volatile ("ptrue p12.d");
> +  __asm __volatile ("ptrue p13.d");
> +  __asm __volatile ("ptrue p14.d");
> +  __asm __volatile ("ptrue p15.d");
> +  __asm __volatile ("setffr");
> +}
> +
> +static void
> +initialize_sve_state_sighandler ()
> +{
> +  __asm __volatile ("dup z0.b, -2");
> +  __asm __volatile ("dup z1.b, -2");
> +  __asm __volatile ("dup z2.b, -2");
> +  __asm __volatile ("dup z3.b, -2");
> +  __asm __volatile ("dup z4.b, -2");
> +  __asm __volatile ("dup z5.b, -2");
> +  __asm __volatile ("dup z6.b, -2");
> +  __asm __volatile ("dup z7.b, -2");
> +  __asm __volatile ("dup z8.b, -2");
> +  __asm __volatile ("dup z9.b, -2");
> +  __asm __volatile ("dup z10.b, -2");
> +  __asm __volatile ("dup z11.b, -2");
> +  __asm __volatile ("dup z12.b, -2");
> +  __asm __volatile ("dup z13.b, -2");
> +  __asm __volatile ("dup z14.b, -2");
> +  __asm __volatile ("dup z15.b, -2");
> +  __asm __volatile ("dup z16.b, -2");
> +  __asm __volatile ("dup z17.b, -2");
> +  __asm __volatile ("dup z18.b, -2");
> +  __asm __volatile ("dup z19.b, -2");
> +  __asm __volatile ("dup z20.b, -2");
> +  __asm __volatile ("dup z21.b, -2");
> +  __asm __volatile ("dup z22.b, -2");
> +  __asm __volatile ("dup z23.b, -2");
> +  __asm __volatile ("dup z24.b, -2");
> +  __asm __volatile ("dup z25.b, -2");
> +  __asm __volatile ("dup z26.b, -2");
> +  __asm __volatile ("dup z27.b, -2");
> +  __asm __volatile ("dup z28.b, -2");
> +  __asm __volatile ("dup z29.b, -2");
> +  __asm __volatile ("dup z30.b, -2");
> +  __asm __volatile ("dup z31.b, -2");
> +  __asm __volatile ("pfalse p0.b");
> +  __asm __volatile ("pfalse p1.b");
> +  __asm __volatile ("pfalse p2.b");
> +  __asm __volatile ("pfalse p3.b");
> +  __asm __volatile ("pfalse p4.b");
> +  __asm __volatile ("pfalse p5.b");
> +  __asm __volatile ("pfalse p6.b");
> +  __asm __volatile ("pfalse p7.b");
> +  __asm __volatile ("pfalse p8.b");
> +  __asm __volatile ("pfalse p9.b");
> +  __asm __volatile ("pfalse p10.b");
> +  __asm __volatile ("pfalse p11.b");
> +  __asm __volatile ("pfalse p12.b");
> +  __asm __volatile ("pfalse p13.b");
> +  __asm __volatile ("pfalse p14.b");
> +  __asm __volatile ("pfalse p15.b");
> +  __asm __volatile ("setffr");
> +}
> +
> +/* Set new value for the SVE vector length.
> +   Return the value that was set.  */
> +
> +static int
> +set_vl (int vl)
> +{
> +  int rc;
> +
> +  rc = prctl (PR_SVE_SET_VL, vl, 0, 0, 0);
> +  if (rc < 0)
> +    {
> +      perror ("FAILED to PR_SVE_SET_VL");
> +      exit (EXIT_FAILURE);
> +    }
> +
> +  return rc & PR_SVE_VL_LEN_MASK;
> +}
> +
> +static void
> +sighandler (int sig, siginfo_t *info, void *ucontext)
> +{
> +  /* Set vector length to the second value.  */
> +  second_vl = set_vl (second_vl);
> +  initialize_sve_state_sighandler ();
> +  printf ("sighandler: second_vl = %d\n", second_vl); /* Break here.  */
> +}
> +
> +int
> +main (int argc, char *argv[])
> +{
> +  if (argc != 3)
> +    {
> +      fprintf (stderr, "Usage: %s <first vl> <second vl>\n", argv[0]);
> +      return 1;
> +    }
> +
> +  int first_vl = atoi (argv[1]);
> +  second_vl = atoi (argv[2]);
> +
> +  if (first_vl == 0 || second_vl == 0)
> +    {
> +      fprintf (stderr, "Invalid vector length.\n");
> +      return 1;
> +    }
> +
> +  /* Set vector length to the first value.  */
> +  first_vl = set_vl (first_vl);
> +
> +  printf ("main: first_vl = %d\n", first_vl);
> +
> +  unsigned char buf[256];
> +
> +  /* Use an SVE register to make the kernel start saving the SVE bank.  */
> +  asm volatile ("mov z0.b, #255\n\t"
> +		"str z0, %0"
> +		:
> +		: "m" (buf)
> +		: "z0", "memory");
> +
> +  initialize_sve_state_main ();
> +
> +  struct sigaction sigact;
> +  sigact.sa_sigaction = sighandler;
> +  sigact.sa_flags = SA_SIGINFO;
> +  sigaction (SIGUSR1, &sigact, NULL);
> +
> +  kill (getpid (), SIGUSR1);
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp
> new file mode 100644
> index 000000000000..32340bb53a33
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp
> @@ -0,0 +1,106 @@
> +# Copyright 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 <http://www.gnu.org/licenses/>.
> +
> +# Exercise unwinding AArch64's SVE registers from a signal frame.
> +
> +require allow_aarch64_sve_tests
> +# Remote targets can't communicate vector length changes to GDB via the RSP.
> +require !gdb_protocol_is_remote
> +
> +set first_vl 0
> +set second_vl 0
> +
> +# Find two valid VL values to use in the test.
> +# The minimum supported VL is 16 bytes, maximum is 256 bytes, and VL can change
> +# in increments of at least 16 bytes.
> +for {set i 16} {$i <= 256} {incr i 16} {
> +    if {![aarch64_supports_sve_vl $i]} {
> +	continue
> +    }
> +
> +    if {$first_vl == 0} {
> +	set first_vl $i
> +    } elseif {$second_vl == 0} {
> +	set second_vl $i
> +	break
> +    }
> +}
> +
> +if {$first_vl == 0 || $second_vl == 0} {
> +    untested "test needs to support at least two vector lengths"
> +    return
> +}
> +
> +standard_testfile
> +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> +	  [list debug additional_flags=-march=armv9-a]] } {
> +    return
> +}
> +
> +# We want SIGUSR1 to be delivered normally.
> +gdb_test "handle SIGUSR1 nostop" \
> +    [multi_line {Signal        Stop	Print	Pass to program	Description} \
> +		{SIGUSR1       No	Yes	Yes		User defined signal 1}] \
> +    "don't stop for SIGUSR1"
> +
> +set linespec ${srcfile}:[gdb_get_line_number "Break here."]
> +gdb_test_no_output "set args $first_vl $second_vl"
> +
> +if ![runto ${linespec}] {
> +    return
> +}
> +
> +set first_vg [expr $first_vl/8]
> +set second_vg [expr $second_vl/8]
> +
> +gdb_test "print \$vg" ". = $second_vg" "vg was changed"
> +
> +for {set row 0} {$row < 32} {incr row} {
> +    set register_name "\$z${row}\.b\.u"
> +    gdb_test "print sizeof $register_name" " = $second_vl" \
> +	"size of $register_name in the signal handler"
> +    gdb_test "print $register_name" ". = \\{254 <repeats $second_vl times>\\}" \
> +	"$register_name contents in signal handler"
> +}
> +
> +for {set row 0} {$row < 16} {incr row} {
> +    set register_name "\$p${row}"
> +    gdb_test "print $register_name" ". = \\{(0, ){[expr $second_vl/8 - 1]}0\\}" \
> +	"$register_name contents in signal handler"
> +}
> +gdb_test "print \$ffr" ". = \\{(255, ){[expr $second_vl/8 - 1]}255\\}" \
> +    "ffr contents in signal handler"
> +
> +gdb_test "frame function main" \
> +    [multi_line "#$decimal  $hex in main \[^\r\n\]+" \
> +		"$decimal\[ \t\]+kill \\(getpid \\(\\), SIGUSR1\\);"]
> +
> +gdb_test "print \$vg" ". = $first_vg" "vg was correctly unwound"
> +
> +for {set row 0} {$row < 32} {incr row} {
> +    set register_name "\$z${row}\.b\.u"
> +    gdb_test "print sizeof $register_name" " = $first_vl" \
> +	"size of $register_name was correctly unwound"
> +    gdb_test "print $register_name" ". = \\{255 <repeats $first_vl times>\\}" \
> +	"$register_name contents were correctly unwound"
> +}
> +
> +for {set row 0} {$row < 16} {incr row} {
> +    set register_name "\$p${row}"
> +    gdb_test "print $register_name" ". = \\{(1, ){[expr $first_vl/8 - 1]}1\\}" \
> +	"$register_name contents were correctly unwound"
> +}
> +gdb_test "print \$ffr" ". = \\{(255, ){[expr $first_vl/8 - 1]}255\\}" \
> +    "ffr contents were correctly unwound"
> 
> base-commit: 4fa9476ad23d5868c2bc4c02dc1a73acaefbcbc2

... this patch looks good to me as is. Thanks for the update and fixup.

Tested-By: Luis Machado <luis.machado@arm.com>
Approved-By: Luis Machado <luis.machado@arm.com>
Thiago Jung Bauermann April 14, 2025, 3:20 p.m. UTC | #2
Luis Machado <luis.machado@arm.com> writes:

> On 4/12/25 06:03, Thiago Jung Bauermann wrote:
>> I decided to make the test more complete by also checking the contents of
>> all registers in the SVE context of the signal frame.  Unfortunately I get
>> the following failures:
>> 
>> Running /path/to/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp ...
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z0.b.u contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z1.b.u contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z2.b.u contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z3.b.u contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z4.b.u contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $z5.b.u contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p0 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p1 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p2 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p3 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p4 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p5 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p6 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p7 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p8 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p9 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p10 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p11 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p12 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p13 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p14 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: $p15 contents were correctly unwound
>> FAIL: gdb.arch/aarch64-sve-sigunwind.exp: ffr contents were correctly unwound
>> 
>>                 === gdb Summary ===
>> 
>> # of expected passes            144
>> # of unexpected failures        23
>> 
>> After some investigation, I suspect this is a bug in the kernel though. To
>> take GDB out of the picture, I wrote the following test program:
>> 
>> https://people.linaro.org/~thiago.bauermann/sve/sve-sigunwind.c
>> 
>> Running it in a QEMU VM with VL of 128 bits, I get:
>> 
>> SVE context: vl = 16
>> SVE context: flags = 0
>> SVE context: Z0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
>> SVE context: Z1 = 0 0 0 0 0 0 3 232 0 0 0 0 0 0 3 232
>> SVE context: Z2 = 0 0 0 0 0 0 0 0 0 0 235 17 78 62 96 0
>> SVE context: Z3 = 0 0 0 0 0 0 0 0 0 0 202 159 237 8 8 128
>> SVE context: Z4 = 0 0 0 0 0 0 0 56 0 0 202 159 237 8 0 64
>> SVE context: Z5 = 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 9
>> SVE context: Z6 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z7 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z8 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z9 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z10 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z11 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z12 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z13 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z14 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z15 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z16 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z17 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z18 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z19 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z20 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z21 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z22 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z23 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z24 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z25 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z26 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z27 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z28 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z29 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z30 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: Z31 = 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
>> SVE context: P0 = 0 0
>> SVE context: P1 = 0 0
>> SVE context: P2 = 0 0
>> SVE context: P3 = 0 0
>> SVE context: P4 = 0 0
>> SVE context: P5 = 0 0
>> SVE context: P6 = 0 0
>> SVE context: P7 = 0 0
>> SVE context: P8 = 0 0
>> SVE context: P9 = 0 0
>> SVE context: P10 = 0 0
>> SVE context: P11 = 0 0
>> SVE context: P12 = 0 0
>> SVE context: P13 = 0 0
>> SVE context: P14 = 0 0
>> SVE context: P15 = 0 0
>> SVE context: FFR = 0 0
>> 
>> Maybe there's a bug in my test program as well? I couldn't find it, but
>> it's interesting that the registers with wrong contents are the same ones
>> that GDB gets.
>> 
>> I'll investigate some more and report the problem on the kernel mailing
>> list if I don't find anything. In any case, I think it's worth to add the
>> test even with these failures.
>
> Interesting. I wonder if gdb is getting confused with the register set to use (FPSIMD/SVE) or
> with the payload size, and reading some garbage for the first 5 Z registers and the
> predicate ones.

If that is the case then I made the same mistake in my test program.
I still haven't spotted it, but I'll keep looking. And in my program,
I'm using the kernel's access macros from <asm/sigcontext.h> to rule
out a mistake in GDB's hard-coded offsets.

> Might be worth checking what exactly the payload looks like when we force-update the vector length
> in the signal handler. Odd case anyway.
>
> In any case...

<snip>

> ... this patch looks good to me as is. Thanks for the update and fixup.
>
> Tested-By: Luis Machado <luis.machado@arm.com>
> Approved-By: Luis Machado <luis.machado@arm.com>

Thank you! Pushed as commit f844b66ad075.
diff mbox series

Patch

diff --git a/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c
new file mode 100644
index 000000000000..c86beaf5dda0
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.c
@@ -0,0 +1,205 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 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 <http://www.gnu.org/licenses/>.  */
+
+/* Exercise unwinding AArch64's SVE registers from a signal frame.   */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+static int second_vl = 0;
+
+static void
+initialize_sve_state_main ()
+{
+  __asm __volatile ("dup z0.b, -1");
+  __asm __volatile ("dup z1.b, -1");
+  __asm __volatile ("dup z2.b, -1");
+  __asm __volatile ("dup z3.b, -1");
+  __asm __volatile ("dup z4.b, -1");
+  __asm __volatile ("dup z5.b, -1");
+  __asm __volatile ("dup z6.b, -1");
+  __asm __volatile ("dup z7.b, -1");
+  __asm __volatile ("dup z8.b, -1");
+  __asm __volatile ("dup z9.b, -1");
+  __asm __volatile ("dup z10.b, -1");
+  __asm __volatile ("dup z11.b, -1");
+  __asm __volatile ("dup z12.b, -1");
+  __asm __volatile ("dup z13.b, -1");
+  __asm __volatile ("dup z14.b, -1");
+  __asm __volatile ("dup z15.b, -1");
+  __asm __volatile ("dup z16.b, -1");
+  __asm __volatile ("dup z17.b, -1");
+  __asm __volatile ("dup z18.b, -1");
+  __asm __volatile ("dup z19.b, -1");
+  __asm __volatile ("dup z20.b, -1");
+  __asm __volatile ("dup z21.b, -1");
+  __asm __volatile ("dup z22.b, -1");
+  __asm __volatile ("dup z23.b, -1");
+  __asm __volatile ("dup z24.b, -1");
+  __asm __volatile ("dup z25.b, -1");
+  __asm __volatile ("dup z26.b, -1");
+  __asm __volatile ("dup z27.b, -1");
+  __asm __volatile ("dup z28.b, -1");
+  __asm __volatile ("dup z29.b, -1");
+  __asm __volatile ("dup z30.b, -1");
+  __asm __volatile ("dup z31.b, -1");
+  __asm __volatile ("ptrue p0.d");
+  __asm __volatile ("ptrue p1.d");
+  __asm __volatile ("ptrue p2.d");
+  __asm __volatile ("ptrue p3.d");
+  __asm __volatile ("ptrue p4.d");
+  __asm __volatile ("ptrue p5.d");
+  __asm __volatile ("ptrue p6.d");
+  __asm __volatile ("ptrue p7.d");
+  __asm __volatile ("ptrue p8.d");
+  __asm __volatile ("ptrue p9.d");
+  __asm __volatile ("ptrue p10.d");
+  __asm __volatile ("ptrue p11.d");
+  __asm __volatile ("ptrue p12.d");
+  __asm __volatile ("ptrue p13.d");
+  __asm __volatile ("ptrue p14.d");
+  __asm __volatile ("ptrue p15.d");
+  __asm __volatile ("setffr");
+}
+
+static void
+initialize_sve_state_sighandler ()
+{
+  __asm __volatile ("dup z0.b, -2");
+  __asm __volatile ("dup z1.b, -2");
+  __asm __volatile ("dup z2.b, -2");
+  __asm __volatile ("dup z3.b, -2");
+  __asm __volatile ("dup z4.b, -2");
+  __asm __volatile ("dup z5.b, -2");
+  __asm __volatile ("dup z6.b, -2");
+  __asm __volatile ("dup z7.b, -2");
+  __asm __volatile ("dup z8.b, -2");
+  __asm __volatile ("dup z9.b, -2");
+  __asm __volatile ("dup z10.b, -2");
+  __asm __volatile ("dup z11.b, -2");
+  __asm __volatile ("dup z12.b, -2");
+  __asm __volatile ("dup z13.b, -2");
+  __asm __volatile ("dup z14.b, -2");
+  __asm __volatile ("dup z15.b, -2");
+  __asm __volatile ("dup z16.b, -2");
+  __asm __volatile ("dup z17.b, -2");
+  __asm __volatile ("dup z18.b, -2");
+  __asm __volatile ("dup z19.b, -2");
+  __asm __volatile ("dup z20.b, -2");
+  __asm __volatile ("dup z21.b, -2");
+  __asm __volatile ("dup z22.b, -2");
+  __asm __volatile ("dup z23.b, -2");
+  __asm __volatile ("dup z24.b, -2");
+  __asm __volatile ("dup z25.b, -2");
+  __asm __volatile ("dup z26.b, -2");
+  __asm __volatile ("dup z27.b, -2");
+  __asm __volatile ("dup z28.b, -2");
+  __asm __volatile ("dup z29.b, -2");
+  __asm __volatile ("dup z30.b, -2");
+  __asm __volatile ("dup z31.b, -2");
+  __asm __volatile ("pfalse p0.b");
+  __asm __volatile ("pfalse p1.b");
+  __asm __volatile ("pfalse p2.b");
+  __asm __volatile ("pfalse p3.b");
+  __asm __volatile ("pfalse p4.b");
+  __asm __volatile ("pfalse p5.b");
+  __asm __volatile ("pfalse p6.b");
+  __asm __volatile ("pfalse p7.b");
+  __asm __volatile ("pfalse p8.b");
+  __asm __volatile ("pfalse p9.b");
+  __asm __volatile ("pfalse p10.b");
+  __asm __volatile ("pfalse p11.b");
+  __asm __volatile ("pfalse p12.b");
+  __asm __volatile ("pfalse p13.b");
+  __asm __volatile ("pfalse p14.b");
+  __asm __volatile ("pfalse p15.b");
+  __asm __volatile ("setffr");
+}
+
+/* Set new value for the SVE vector length.
+   Return the value that was set.  */
+
+static int
+set_vl (int vl)
+{
+  int rc;
+
+  rc = prctl (PR_SVE_SET_VL, vl, 0, 0, 0);
+  if (rc < 0)
+    {
+      perror ("FAILED to PR_SVE_SET_VL");
+      exit (EXIT_FAILURE);
+    }
+
+  return rc & PR_SVE_VL_LEN_MASK;
+}
+
+static void
+sighandler (int sig, siginfo_t *info, void *ucontext)
+{
+  /* Set vector length to the second value.  */
+  second_vl = set_vl (second_vl);
+  initialize_sve_state_sighandler ();
+  printf ("sighandler: second_vl = %d\n", second_vl); /* Break here.  */
+}
+
+int
+main (int argc, char *argv[])
+{
+  if (argc != 3)
+    {
+      fprintf (stderr, "Usage: %s <first vl> <second vl>\n", argv[0]);
+      return 1;
+    }
+
+  int first_vl = atoi (argv[1]);
+  second_vl = atoi (argv[2]);
+
+  if (first_vl == 0 || second_vl == 0)
+    {
+      fprintf (stderr, "Invalid vector length.\n");
+      return 1;
+    }
+
+  /* Set vector length to the first value.  */
+  first_vl = set_vl (first_vl);
+
+  printf ("main: first_vl = %d\n", first_vl);
+
+  unsigned char buf[256];
+
+  /* Use an SVE register to make the kernel start saving the SVE bank.  */
+  asm volatile ("mov z0.b, #255\n\t"
+		"str z0, %0"
+		:
+		: "m" (buf)
+		: "z0", "memory");
+
+  initialize_sve_state_main ();
+
+  struct sigaction sigact;
+  sigact.sa_sigaction = sighandler;
+  sigact.sa_flags = SA_SIGINFO;
+  sigaction (SIGUSR1, &sigact, NULL);
+
+  kill (getpid (), SIGUSR1);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp
new file mode 100644
index 000000000000..32340bb53a33
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-sve-sigunwind.exp
@@ -0,0 +1,106 @@ 
+# Copyright 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 <http://www.gnu.org/licenses/>.
+
+# Exercise unwinding AArch64's SVE registers from a signal frame.
+
+require allow_aarch64_sve_tests
+# Remote targets can't communicate vector length changes to GDB via the RSP.
+require !gdb_protocol_is_remote
+
+set first_vl 0
+set second_vl 0
+
+# Find two valid VL values to use in the test.
+# The minimum supported VL is 16 bytes, maximum is 256 bytes, and VL can change
+# in increments of at least 16 bytes.
+for {set i 16} {$i <= 256} {incr i 16} {
+    if {![aarch64_supports_sve_vl $i]} {
+	continue
+    }
+
+    if {$first_vl == 0} {
+	set first_vl $i
+    } elseif {$second_vl == 0} {
+	set second_vl $i
+	break
+    }
+}
+
+if {$first_vl == 0 || $second_vl == 0} {
+    untested "test needs to support at least two vector lengths"
+    return
+}
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+	  [list debug additional_flags=-march=armv9-a]] } {
+    return
+}
+
+# We want SIGUSR1 to be delivered normally.
+gdb_test "handle SIGUSR1 nostop" \
+    [multi_line {Signal        Stop	Print	Pass to program	Description} \
+		{SIGUSR1       No	Yes	Yes		User defined signal 1}] \
+    "don't stop for SIGUSR1"
+
+set linespec ${srcfile}:[gdb_get_line_number "Break here."]
+gdb_test_no_output "set args $first_vl $second_vl"
+
+if ![runto ${linespec}] {
+    return
+}
+
+set first_vg [expr $first_vl/8]
+set second_vg [expr $second_vl/8]
+
+gdb_test "print \$vg" ". = $second_vg" "vg was changed"
+
+for {set row 0} {$row < 32} {incr row} {
+    set register_name "\$z${row}\.b\.u"
+    gdb_test "print sizeof $register_name" " = $second_vl" \
+	"size of $register_name in the signal handler"
+    gdb_test "print $register_name" ". = \\{254 <repeats $second_vl times>\\}" \
+	"$register_name contents in signal handler"
+}
+
+for {set row 0} {$row < 16} {incr row} {
+    set register_name "\$p${row}"
+    gdb_test "print $register_name" ". = \\{(0, ){[expr $second_vl/8 - 1]}0\\}" \
+	"$register_name contents in signal handler"
+}
+gdb_test "print \$ffr" ". = \\{(255, ){[expr $second_vl/8 - 1]}255\\}" \
+    "ffr contents in signal handler"
+
+gdb_test "frame function main" \
+    [multi_line "#$decimal  $hex in main \[^\r\n\]+" \
+		"$decimal\[ \t\]+kill \\(getpid \\(\\), SIGUSR1\\);"]
+
+gdb_test "print \$vg" ". = $first_vg" "vg was correctly unwound"
+
+for {set row 0} {$row < 32} {incr row} {
+    set register_name "\$z${row}\.b\.u"
+    gdb_test "print sizeof $register_name" " = $first_vl" \
+	"size of $register_name was correctly unwound"
+    gdb_test "print $register_name" ". = \\{255 <repeats $first_vl times>\\}" \
+	"$register_name contents were correctly unwound"
+}
+
+for {set row 0} {$row < 16} {incr row} {
+    set register_name "\$p${row}"
+    gdb_test "print $register_name" ". = \\{(1, ){[expr $first_vl/8 - 1]}1\\}" \
+	"$register_name contents were correctly unwound"
+}
+gdb_test "print \$ffr" ". = \\{(255, ){[expr $first_vl/8 - 1]}255\\}" \
+    "ffr contents were correctly unwound"