* [PATCH v5 1/5] gdb/aarch64: Add POR_EL0 register support for FEAT_S1POE
2026-09-16 9:17 [PATCH v5 0/5] gdb/aarch64: Add POR_EL0 register support for FEAT_S1POE srinath.parvathaneni
@ 2026-09-16 9:17 ` srinath.parvathaneni
2026-09-16 9:18 ` [PATCH v5 2/5] gdb: Improve SIGSEGV diagnostics for POE faults srinath.parvathaneni
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: srinath.parvathaneni @ 2026-09-16 9:17 UTC (permalink / raw)
To: gdb-patches
Cc: simark, luis.machado.foss, thiago.bauermann, guinevere,
Ezra.Sitorus, Matthieu.Longo, peter.maydell,
Srinath Parvathaneni
From: Srinath Parvathaneni <srinath.parvathaneni@arm.com>
Add support for the FEAT_S1POE POR_EL0 register on AArch64.
This patch adds POR_EL0 to the AArch64 register set and reads/writes it
using the NT_ARM_POE ptrace regset.
It also adds POE pseudo-registers por_p0 through por_p15, with each
pseudo-register mapping to a 4-bit field in POR_EL0, starting from the
least-significant nibble.
POR_EL0 and the POE pseudo-registers are added to the "por" register
group, allowing them to be displayed together using the
"info registers por" command.
With this change, POR_EL0 is accessible through:
* info registers
* info registers por_el0
* info registers por
* p $por_el0
* p/x $por_el0
* set $por_el0 = <value>
The individual POE fields can also be accessed and modified through
$por_p0 to $por_p15.
Example:
(gdb) info register por_el0
por_el0 0x7 [ P0=rwx ]
(gdb) set $por_el0=0xffffffff77777777
(gdb) info registers por_el0
por_el0 0xffffffff77777777 [ P15=??? P14=??? P13=??? P12=???
P11=??? P10=??? P9=??? P8=???
P7=rwx P6=rwx P5=rwx P4=rwx
P3=rwx P2=rwx P1=rwx P0=rwx ]
(gdb) info registers por
por_el0 0xffffffff77777777 [ P15=??? P14=??? P13=??? P12=???
P11=??? P10=??? P9=??? P8=???
P7=rwx P6=rwx P5=rwx P4=rwx
P3=rwx P2=rwx P1=rwx P0=rwx ]
por_p0 0x7 rwx
por_p1 0x7 rwx
por_p2 0x7 rwx
por_p3 0x7 rwx
por_p4 0x7 rwx
por_p5 0x7 rwx
por_p6 0x7 rwx
por_p7 0x7 rwx
por_p8 0xf ???
por_p9 0xf ???
por_p10 0xf ???
por_p11 0xf ???
por_p12 0xf ???
por_p13 0xf ???
por_p14 0xf ???
por_p15 0xf ???
Individual POE pseudo-registers can be written independently, with the
corresponding 4-bit field in POR_EL0 updated accordingly. For example:
(gdb) set $por_el0=0x7
(gdb) info registers por_el0
por_el0 0x0000000000000007 [ P0=rwx ]
(gdb) set $por_p1=0x7
(gdb) info registers por_el0
por_el0 0x0000000000000077 [ P1=rwx P0=rwx ]
---
gdb/Makefile.in | 1 +
gdb/aarch64-linux-nat.c | 64 +++++++++
gdb/aarch64-tdep.c | 268 +++++++++++++++++++++++++++++++++++
gdb/aarch64-tdep.h | 12 ++
gdb/arch/aarch64.c | 4 +
gdb/arch/aarch64.h | 8 +-
gdb/doc/gdb.texinfo | 38 +++++
gdb/features/Makefile | 1 +
gdb/features/aarch64-poe.c | 14 ++
gdb/features/aarch64-poe.xml | 11 ++
gdb/nat/aarch64-poe-linux.h | 30 ++++
11 files changed, 450 insertions(+), 1 deletion(-)
create mode 100644 gdb/features/aarch64-poe.c
create mode 100644 gdb/features/aarch64-poe.xml
create mode 100644 gdb/nat/aarch64-poe-linux.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d1574ec2d2c..6909123e863 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1545,6 +1545,7 @@ HFILES_NO_SRCDIR = \
nat/aarch64-linux-hw-point.h \
nat/aarch64-mte-linux-ptrace.h \
nat/aarch64-pauth-linux.h \
+ nat/aarch64-poe-linux.h \
nat/aarch64-scalable-linux-ptrace.h \
nat/aarch64-scalable-linux-sigcontext.h \
nat/amd64-linux.h \
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 52ace4aab41..f1de070695d 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -34,6 +34,7 @@
#include "arch/arm.h"
#include "nat/aarch64-fpmr-linux.h"
#include "nat/aarch64-gcs-linux.h"
+#include "nat/aarch64-poe-linux.h"
#include "nat/aarch64-linux.h"
#include "nat/aarch64-linux-hw-point.h"
#include "nat/aarch64-mte-linux-ptrace.h"
@@ -602,6 +603,54 @@ store_gcsregs_to_thread (regcache *regcache)
perror_with_name (_("Unable to store GCS registers"));
}
+/* Fill GDB's register array with the POE register value from the current
+ thread. */
+
+static void
+fetch_poeregs_from_thread (regcache *regcache)
+{
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+ gdb_assert (tdep->has_poe ());
+
+ uint64_t user_poe;
+ iovec iovec;
+
+ iovec.iov_base = &user_poe;
+ iovec.iov_len = sizeof (user_poe);
+
+ int tid = get_ptrace_pid (regcache->ptid ());
+ if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_POE, &iovec) != 0)
+ perror_with_name (_("Unable to fetch POE register"));
+
+ regcache->raw_supply (tdep->poe_regnum, &user_poe);
+}
+
+/* Store the NT_ARM_POE register value from GDB's REGCACHE to the thread
+ associated with REGCACHE. */
+
+static void
+store_poeregs_to_thread (regcache *regcache)
+{
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+ gdb_assert (tdep->has_poe ());
+
+ int tid = regcache->ptid ().lwp ();
+
+ iovec iovec;
+ uint64_t user_poe;
+ iovec.iov_base = &user_poe;
+ iovec.iov_len = sizeof (user_poe);
+
+ regcache->raw_collect (tdep->poe_regnum, &user_poe);
+
+ if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_POE, &iovec) != 0)
+ perror_with_name (_("Unable to store POE register"));
+}
+
/* Fill GDB's REGCACHE with the FPMR register set content from the
thread associated with REGCACHE. */
@@ -683,6 +732,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
if (tdep->has_gcs_linux ())
fetch_gcsregs_from_thread (regcache);
+ if (tdep->has_poe ())
+ fetch_poeregs_from_thread (regcache);
+
if (tdep->has_fpmr ())
fetch_fpmr_from_thread (regcache);
}
@@ -722,6 +774,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
&& (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base
|| regno == tdep->gcs_linux_reg_base + 1))
fetch_gcsregs_from_thread (regcache);
+ /* POE register? */
+ else if (tdep->has_poe () && (regno == tdep->poe_regnum))
+ fetch_poeregs_from_thread (regcache);
/* FPMR? */
else if (tdep->has_fpmr () && (regno == tdep->fpmr_regnum))
fetch_fpmr_from_thread (regcache);
@@ -802,6 +857,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
if (tdep->has_fpmr ())
store_fpmr_to_thread (regcache);
+
+ if (tdep->has_poe ())
+ store_poeregs_to_thread (regcache);
}
/* General purpose register? */
else if (regno < AARCH64_V0_REGNUM)
@@ -836,6 +894,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
/* FPMR? */
else if (tdep->has_fpmr () && regno == tdep->fpmr_regnum)
store_fpmr_to_thread (regcache);
+ /* POE register? */
+ else if (tdep->has_poe () && regno == tdep->poe_regnum)
+ store_poeregs_to_thread (regcache);
/* PAuth registers are read-only. */
}
@@ -1024,6 +1085,9 @@ aarch64_linux_nat_target::read_description ()
/* Check for FPMR. */
features.fpmr = hwcap2 & HWCAP2_FPMR;
+ /* Check for POE support. */
+ features.poe = hwcap2 & HWCAP2_POE;
+
return aarch64_read_description (features);
}
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 950ad4f6aae..10b74ff1438 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -164,6 +164,11 @@ static const char *const aarch64_gcs_register_names[] = {
"gcspr"
};
+static const char *const aarch64_poe_register_names[] = {
+ /* Permission Overlay Extension Register. */
+ "por_el0"
+};
+
static const char *const aarch64_gcs_linux_register_names[] = {
/* Field in struct user_gcs. */
"gcs_features_enabled",
@@ -2892,6 +2897,21 @@ is_w_pseudo_register (struct gdbarch *gdbarch, int regnum)
return false;
}
+/* Return TRUE if REGNUM is a POE pseudo-register number. Return FALSE
+ otherwise. */
+
+static bool
+is_poe_pseudo_register (gdbarch *gdbarch, int regnum)
+{
+ aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+
+ if (tdep->poe_pseudo_base <= regnum
+ && regnum < tdep->poe_pseudo_base + tdep->poe_pseudo_count)
+ return true;
+
+ return false;
+}
+
/* Return TRUE if REGNUM is a SME pseudo-register number. Return FALSE
otherwise. */
@@ -3088,6 +3108,21 @@ aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
if (tdep->has_pauth () && regnum == tdep->ra_sign_state_regnum)
return "";
+ if (tdep->has_poe ())
+ {
+ static const char *const poe_name[] =
+ {
+ "por_p0", "por_p1", "por_p2", "por_p3",
+ "por_p4", "por_p5", "por_p6", "por_p7",
+ "por_p8", "por_p9", "por_p10", "por_p11",
+ "por_p12", "por_p13", "por_p14", "por_p15",
+ };
+
+ /* POE pseudo-registers. */
+ if (is_poe_pseudo_register (gdbarch, regnum))
+ return poe_name[regnum - tdep->poe_pseudo_base];
+ }
+
internal_error (_("aarch64_pseudo_register_name: bad register number %d"),
p_regnum);
}
@@ -3130,10 +3165,130 @@ aarch64_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
if (tdep->has_pauth () && regnum == tdep->ra_sign_state_regnum)
return builtin_type (gdbarch)->builtin_uint64;
+ /* POE pseudo-registers are 8-bit. */
+ if (is_poe_pseudo_register (gdbarch, regnum))
+ return builtin_type (gdbarch)->builtin_uint8;
+
internal_error (_("aarch64_pseudo_register_type: bad register number %d"),
p_regnum);
}
+/* Convert a POR_EL0 Perm<m> overlay permission encoding into rwx-style string.
+ For example:
+ 0b0011 -> "r-x" (3)
+ 0b0101 -> "rw-" (5)
+ 0b0111 -> "rwx" (7)
+ 0b0000 -> "---" (0)
+ Reserved encodings (0b1xxx) are returned as "???". */
+
+static const char *
+aarch64_perm_overlay_decode (unsigned int perm)
+{
+ switch (perm)
+ {
+ case 0: return "---";
+ case 1: return "r--";
+ case 2: return "--x";
+ case 3: return "r-x";
+ case 4: return "-w-";
+ case 5: return "rw-";
+ case 6: return "-wx";
+ case 7: return "rwx";
+ default: return "???";
+ }
+}
+
+/* Display POE register POR_EL0 in the following format for the 'info registers'
+ and 'info all-registers' commands:
+ <register-name> <hex-value> [<decoded per-protection-key permissions>] */
+
+static void
+aarch64_print_poe_register_info (ui_file *file, int regnum, const char *name,
+ const frame_info_ptr &frame)
+{
+ value *val = value_of_register (regnum, get_next_frame_sentinel_okay (frame));
+ ULONGEST por_el0 = (ULONGEST) value_as_long (val);
+ gdb_printf (file, "%-14s 0x%s [ ", name, phex (por_el0, 8));
+ const int line_wrap_count = 36;
+
+ for (int i = 15, line_wrap = 0; i >= 0; --i)
+ {
+ unsigned int perm = (por_el0 >> (i * 4)) & 0xf;
+ if (perm)
+ {
+ if (line_wrap != 0 && (line_wrap % 4) == 0)
+ gdb_printf (file, "\n%*s", line_wrap_count, "");
+ gdb_printf (file, "P%d=%s ", i, aarch64_perm_overlay_decode (perm));
+ line_wrap++;
+ }
+ }
+ gdb_puts ("]\n", file);
+}
+
+/* Custom display for POE pseudo registers. */
+
+static void
+aarch64_print_poe_pseudo_register_info (ui_file *file, int regnum,
+ const char *name,
+ const frame_info_ptr &frame)
+{
+ value *val = value_of_register (regnum, get_next_frame_sentinel_okay (frame));
+ gdbarch *gdbarch = get_frame_arch (frame);
+ unsigned int perm = extract_unsigned_integer (val->contents (),
+ gdbarch_byte_order (gdbarch));
+
+ /* Extract only the least significant 4 bits. */
+ perm &= 0xf;
+
+ gdb_printf (file, "%-14s 0x%-16x %s\n", name, perm,
+ aarch64_perm_overlay_decode (perm));
+}
+
+/* For 'info registers' and 'info all-registers', print POE POR_EL0 register and
+ POE pseudo registers using the custom register printer and all other
+ registers using the default register printer. */
+
+static void
+aarch64_print_registers_info (gdbarch *gdbarch, ui_file *file,
+ const frame_info_ptr &frame,
+ int regnum,
+ bool print_all)
+{
+ const int numregs = gdbarch_num_cooked_regs (gdbarch);
+ aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+
+ /* When no register is specified. */
+ if (regnum == -1)
+ {
+ for (int i = 0; i < numregs; i++)
+ {
+ if (i == tdep->poe_regnum)
+ {
+ aarch64_print_poe_register_info
+ (file, i, gdbarch_register_name (gdbarch, i), frame);
+ continue;
+ }
+ else if (is_poe_pseudo_register (gdbarch, i))
+ {
+ aarch64_print_poe_pseudo_register_info
+ (file, i, gdbarch_register_name (gdbarch, i), frame);
+ continue;
+ }
+ default_print_registers_info (gdbarch, file, frame, i, print_all);
+ }
+ }
+ /* When POE por_el0 register is specified. */
+ else if (regnum == tdep->poe_regnum)
+ aarch64_print_poe_register_info
+ (file, regnum, gdbarch_register_name (gdbarch, regnum), frame);
+ /* When individual POE pseudo register is specified. */
+ else if (is_poe_pseudo_register (gdbarch, regnum))
+ aarch64_print_poe_pseudo_register_info
+ (file, regnum, gdbarch_register_name (gdbarch, regnum), frame);
+ else
+ default_print_registers_info (gdbarch, file, frame, regnum, print_all);
+}
+
/* Implement the "pseudo_register_reggroup_p" tdesc_arch_data method. */
static bool
@@ -3161,6 +3316,8 @@ aarch64_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
return group == all_reggroup || group == vector_reggroup;
else if (is_sme_pseudo_register (gdbarch, regnum))
return group == all_reggroup || group == vector_reggroup;
+ else if (is_poe_pseudo_register (gdbarch, regnum))
+ return group == all_reggroup || group == reggroup_find (gdbarch, "por");
/* RA_STATE is used for unwinding only. Do not assign it to any groups. */
if (tdep->has_pauth () && regnum == tdep->ra_sign_state_regnum)
return false;
@@ -3290,7 +3447,36 @@ aarch64_sme_pseudo_register_read (gdbarch *gdbarch, const frame_info_ptr &next_f
za_value->contents_copy (result, dst_offset, src_offset,
offsets.chunk_size);
}
+ return result;
+}
+
+/* Given REGNUM, a POE pseudo-register number, return its value in RESULT. */
+
+static value *
+aarch64_poe_pseudo_register_read (gdbarch *gdbarch,
+ const frame_info_ptr &next_frame,
+ const int pseudo_reg_num)
+{
+ aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+
+ gdb_assert (tdep->has_poe ());
+ gdb_assert (tdep->poe_pseudo_base <= pseudo_reg_num);
+ gdb_assert (pseudo_reg_num < tdep->poe_pseudo_base + tdep->poe_pseudo_count);
+
+ unsigned int pkey = pseudo_reg_num - tdep->poe_pseudo_base;
+ unsigned int shift = pkey * 4;
+
+ value *por_value = value_of_register (tdep->poe_regnum, next_frame);
+ value *result = value::allocate_register (next_frame, pseudo_reg_num);
+ ULONGEST por_el0
+ = extract_unsigned_integer (por_value->contents (),
+ gdbarch_byte_order (gdbarch));
+
+ ULONGEST pseudo_reg_value = (por_el0 >> shift) & 0xf;
+
+ store_unsigned_integer (result->contents_raw (), gdbarch_byte_order (gdbarch),
+ pseudo_reg_value);
return result;
}
@@ -3322,6 +3508,9 @@ aarch64_pseudo_read_value (gdbarch *gdbarch, const frame_info_ptr &next_frame,
else if (is_sme_pseudo_register (gdbarch, pseudo_reg_num))
return aarch64_sme_pseudo_register_read (gdbarch, next_frame,
pseudo_reg_num);
+ else if (is_poe_pseudo_register (gdbarch, pseudo_reg_num))
+ return aarch64_poe_pseudo_register_read (gdbarch, next_frame,
+ pseudo_reg_num);
/* Offset in the "pseudo-register space". */
int pseudo_offset = pseudo_reg_num - gdbarch_num_regs (gdbarch);
@@ -3428,6 +3617,48 @@ aarch64_sme_pseudo_register_write (gdbarch *gdbarch, const frame_info_ptr &next_
za_value->contents_raw ());
}
+/* Given PSEUDO_REG_NUM, a POE pseudo-register number, store DATA in the
+ corresponding field of POR_EL0. */
+
+static void
+aarch64_poe_pseudo_register_write (gdbarch *gdbarch,
+ const frame_info_ptr &next_frame,
+ const int pseudo_reg_num,
+ gdb::array_view<const gdb_byte> data)
+{
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+
+ gdb_assert (tdep->has_poe ());
+ gdb_assert (tdep->poe_pseudo_base <= pseudo_reg_num);
+ gdb_assert (pseudo_reg_num < tdep->poe_pseudo_base + tdep->poe_pseudo_count);
+
+ unsigned int pkey = pseudo_reg_num - tdep->poe_pseudo_base;
+ unsigned int shift = pkey * 4;
+
+ ULONGEST pseudo_reg_value
+ = extract_unsigned_integer (data, gdbarch_byte_order (gdbarch));
+
+ if (pseudo_reg_value > 0xf)
+ error (_("POE pseudo-register value must be between 0 and 15."));
+
+ /* Fetch the current POR_EL0 value. */
+ value *por_value = value_of_register (tdep->poe_regnum, next_frame);
+
+ ULONGEST por_el0
+ = extract_unsigned_integer (por_value->contents (),
+ gdbarch_byte_order (gdbarch));
+
+ ULONGEST mask = ULONGEST (0xf) << shift;
+
+ por_el0 = (por_el0 & ~mask) | (pseudo_reg_value << shift);
+
+ store_unsigned_integer (por_value->contents_writeable (),
+ gdbarch_byte_order (gdbarch), por_el0);
+
+ put_frame_register (next_frame, tdep->poe_regnum, por_value->contents_raw ());
+}
+
/* Implement the "pseudo_register_write" gdbarch method. */
static void
@@ -3464,6 +3695,12 @@ aarch64_pseudo_write (gdbarch *gdbarch, const frame_info_ptr &next_frame,
buf);
return;
}
+ else if (is_poe_pseudo_register (gdbarch, pseudo_reg_num))
+ {
+ aarch64_poe_pseudo_register_write (gdbarch, next_frame, pseudo_reg_num,
+ buf);
+ return;
+ }
/* Offset in the "pseudo-register space". */
int pseudo_offset = pseudo_reg_num - gdbarch_num_regs (gdbarch);
@@ -4140,6 +4377,10 @@ aarch64_features_from_target_desc (const struct target_desc *tdesc)
features.fpmr = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpmr")
!= nullptr);
+ /* Check for POE feature. */
+ features.poe = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.poe")
+ != nullptr);
+
return features;
}
@@ -4560,6 +4801,24 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
fpmr_regnum, "fpmr");
}
+ int poe_regnum = -1;
+ int first_poe_pseudo_regnum = -1;
+ const tdesc_feature *feature_poe
+ = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.poe");
+ if (feature_poe != nullptr)
+ {
+ poe_regnum = num_regs;
+ for (i = 0; i < ARRAY_SIZE (aarch64_poe_register_names); i++)
+ valid_p &= tdesc_numbered_register (feature_poe, tdesc_data.get (),
+ poe_regnum + i,
+ aarch64_poe_register_names[i]);
+
+ /* POE pseudo-registers. */
+ first_poe_pseudo_regnum = num_pseudo_regs;
+ num_pseudo_regs += 16;
+ num_regs++;
+ }
+
int first_sme_regnum = -1;
int first_sme2_regnum = -1;
int first_sme_pseudo_regnum = -1;
@@ -4760,6 +5019,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->gcs_reg_base = first_gcs_regnum;
tdep->gcs_linux_reg_base = first_gcs_linux_regnum;
tdep->fpmr_regnum = fpmr_regnum;
+ tdep->poe_regnum = poe_regnum;
/* Set the SME register set details. The pseudo-registers will be adjusted
later. */
@@ -4802,6 +5062,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_tdesc_pseudo_register_reggroup_p (gdbarch,
aarch64_pseudo_register_reggroup_p);
set_gdbarch_cannot_store_register (gdbarch, aarch64_cannot_store_register);
+ set_gdbarch_print_registers_info (gdbarch, aarch64_print_registers_info);
/* Set the allocation tag granule size to 16 bytes. */
set_gdbarch_memtag_granule_size (gdbarch, AARCH64_MTE_GRANULE_SIZE);
@@ -4899,6 +5160,13 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->w_pseudo_base = first_w_regnum + num_regs;
tdep->w_pseudo_count = 31;
+ /* Setup POE pseudo-register numbers. */
+ if (tdep->has_poe () && first_poe_pseudo_regnum != -1)
+ {
+ tdep->poe_pseudo_base = first_poe_pseudo_regnum + num_regs;
+ tdep->poe_pseudo_count = 16;
+ }
+
/* Pointer authentication pseudo-registers. */
if (tdep->has_pauth ())
tdep->ra_sign_state_regnum = ra_sign_state_offset + num_regs;
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index dff05a08f84..3e6320740d1 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -208,6 +208,18 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep_base
return gcs_linux_reg_base != -1;
}
+ /* POE register. This is -1 if no POE feature is available. */
+ int poe_regnum = -1;
+ int poe_pseudo_base = 0;
+ int poe_pseudo_count = 0;
+
+ /* Returns true if the target supports the POE feature. */
+ bool
+ has_poe () const
+ {
+ return poe_regnum != -1;
+ }
+
/* First FPMR register. This is -1 if FPMR is not supported. */
int fpmr_regnum = -1;
diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c
index 2401a325b7b..a567938d06d 100644
--- a/gdb/arch/aarch64.c
+++ b/gdb/arch/aarch64.c
@@ -28,6 +28,7 @@
#include "../features/aarch64-sme2.c"
#include "../features/aarch64-tls.c"
#include "../features/aarch64-gcs.c"
+#include "../features/aarch64-poe.c"
#include "../features/aarch64-gcs-linux.c"
/* See arch/aarch64.h. */
@@ -77,6 +78,9 @@ aarch64_create_target_description (const aarch64_features &features)
if (features.fpmr)
regnum = create_feature_aarch64_fpmr (tdesc.get (), regnum);
+ if (features.poe)
+ regnum = create_feature_aarch64_poe (tdesc.get (), regnum);
+
return tdesc;
}
diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h
index cf7318cbe74..ba90d55c3bb 100644
--- a/gdb/arch/aarch64.h
+++ b/gdb/arch/aarch64.h
@@ -35,6 +35,8 @@ struct aarch64_features
bool pauth = false;
bool mte = false;
bool fpmr = false;
+ /* Whether the Permission Overlay Extension (FEAT_S1POE) is supported. */
+ bool poe = false;
/* A positive TLS value indicates the number of TLS registers available. */
uint8_t tls = 0;
@@ -70,7 +72,8 @@ inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
&& lhs.sme2 == rhs.sme2
&& lhs.gcs == rhs.gcs
&& lhs.gcs_linux == rhs.gcs_linux
- && lhs.fpmr == rhs.fpmr;
+ && lhs.fpmr == rhs.fpmr
+ && lhs.poe == rhs.poe;
}
namespace std
@@ -103,6 +106,9 @@ namespace std
/* FPMR feature. */
h = h << 1 | features.fpmr;
+
+ /* POE feature. */
+ h = h << 1 | features.poe;
return h;
}
};
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9d57fffbab2..caee1d33ce2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -27306,6 +27306,44 @@ p $fpmr
$1 = [ F8S1=E5M2 F8S2=E5M2 F8D=E5M2 OSM=Inf OSC=Inf/NaN LSCALE=0 NSCALE=0 LSCALE2=0 ]
@end smallexample
+@subsubsection AArch64 Permission Overlay Extension
+@cindex Permission Overlay Extension, AArch64
+@cindex POE, AArch64
+
+When @value{GDBN} is debugging the AArch64 architecture and the Permission
+Overlay Extension (@acronym{POE}) is available, then @value{GDBN} will make the
+@code{por_el0} register available.
+
+To aid debugging, @value{GDBN} interprets the permissions of each feild in
+@code{por_el0} when displayed using the @code{info registers} command.
+
+@smallexample
+(gdb) info registers por_el0
+por_el0 0x0000000000000007 [ P0=rwx ]
+@end smallexample
+
+@value{GDBN} also provides pseudo-registers named @code{por_p0} through
+@code{por_p15} for accessing the individual permission fields in
+@code{por_el0}. These pseudo-registers can be read and written independently.
+Writing a pseudo-register updates the corresponding 4-bit field in
+@code{por_el0}.
+
+@smallexample
+(gdb) set $por_el0 = 0x7
+(gdb) set $por_p1 = 0x7
+(gdb) info registers por_el0
+por_el0 0x0000000000000077 [ P1=rwx P0=rwx ]
+(gdb) info registers por_p1
+por_p1 0x7 rwx
+@end smallexample
+
+The @code{por} register group can be used to display @code{por_el0} and all
+the POE pseudo-registers.
+
+@smallexample
+(gdb) info registers por
+@end smallexample
+
For more information about @acronym{FPMR}, please refer to the official
@uref{https://developer.arm.com/documentation/ddi0601/latest,ignored,
architecture registers documentation}.
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 161a7453d66..cc77e5bb652 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -207,6 +207,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
aarch64-pauth.xml \
aarch64-mte.xml \
aarch64-gcs.xml \
+ aarch64-poe.xml \
aarch64-gcs-linux.xml \
arc/v1-core.xml \
arc/v1-aux.xml \
diff --git a/gdb/features/aarch64-poe.c b/gdb/features/aarch64-poe.c
new file mode 100644
index 00000000000..a1552e26516
--- /dev/null
+++ b/gdb/features/aarch64-poe.c
@@ -0,0 +1,14 @@
+/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
+ Original: aarch64-poe.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_aarch64_poe (target_desc *result, long regnum)
+{
+ tdesc_feature *feature;
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.poe");
+ tdesc_create_reg (feature, "por_el0", regnum++, 1, "por", 64, "uint64");
+ return regnum;
+}
diff --git a/gdb/features/aarch64-poe.xml b/gdb/features/aarch64-poe.xml
new file mode 100644
index 00000000000..aa2a2713ac8
--- /dev/null
+++ b/gdb/features/aarch64-poe.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2026 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.poe">
+ <reg name="por_el0" bitsize="64" type="uint64" group="por"/>
+</feature>
diff --git a/gdb/nat/aarch64-poe-linux.h b/gdb/nat/aarch64-poe-linux.h
new file mode 100644
index 00000000000..74678c7771d
--- /dev/null
+++ b/gdb/nat/aarch64-poe-linux.h
@@ -0,0 +1,30 @@
+/* Common native Linux definitions for AArch64 Permission Overlay Extension.
+
+ Copyright (C) 2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#ifndef GDB_NAT_AARCH64_POE_LINUX_H
+#define GDB_NAT_AARCH64_POE_LINUX_H
+
+#include <asm/hwcap.h>
+
+/* Feature check for Permission Overlay Extension. */
+#ifndef HWCAP2_POE
+#define HWCAP2_POE (1ULL << 63)
+#endif /* HWCAP2_POE. */
+
+#endif /* GDB_NAT_AARCH64_POE_LINUX_H. */
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v5 4/5] gdb/aarch64: Add core file and signal frame support for FEAT_S1POE
2026-09-16 9:17 [PATCH v5 0/5] gdb/aarch64: Add POR_EL0 register support for FEAT_S1POE srinath.parvathaneni
` (2 preceding siblings ...)
2026-09-16 9:18 ` [PATCH v5 3/5] gdbserver/aarch64: Add POR_EL0 register support srinath.parvathaneni
@ 2026-09-16 9:18 ` srinath.parvathaneni
2026-09-16 9:18 ` [PATCH v5 5/5] gdb/testsuite: Add FEAT_S1POE testcases srinath.parvathaneni
4 siblings, 0 replies; 6+ messages in thread
From: srinath.parvathaneni @ 2026-09-16 9:18 UTC (permalink / raw)
To: gdb-patches
Cc: simark, luis.machado.foss, thiago.bauermann, guinevere,
Ezra.Sitorus, Matthieu.Longo, peter.maydell,
Srinath Parvathaneni
From: Srinath Parvathaneni <srinath.parvathaneni@arm.com>
Add support for POR_EL0 dumps/reads for core files and also the
support for reading the POE values in the signal frame.
Approved-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
gdb/aarch64-linux-tdep.c | 47 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 1b2d4435088..a9cf6e3127f 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -171,6 +171,7 @@
#define AARCH64_ZT_MAGIC 0x5a544e01
#define AARCH64_GCS_MAGIC 0x47435300
#define AARCH64_FPMR_MAGIC 0x46504d52
+#define AARCH64_POE_MAGIC 0x504f4530
/* Defines for the extra_context that follows an AARCH64_EXTRA_MAGIC. */
#define AARCH64_EXTRA_DATAP_OFFSET 8
@@ -220,6 +221,9 @@
/* FPMR constants. */
#define AARCH64_FPMR_OFFSET 8
+/* POE constants. */
+#define AARCH64_POE_OFFSET 8
+
/* Holds information about the signal frame. */
struct aarch64_linux_sigframe
{
@@ -273,6 +277,10 @@ struct aarch64_linux_sigframe
/* FPMR value. */
CORE_ADDR fpmr = 0;
+ /* True if we have a POE entry in the signal context, false otherwise. */
+ bool poe_available = false;
+ /* POE value. */
+ CORE_ADDR poe = 0;
};
/* Read an aarch64_ctx, returning the magic value, and setting *SIZE to the
@@ -605,6 +613,22 @@ aarch64_linux_read_signal_frame_info (const frame_info_ptr &this_frame,
section += size;
break;
}
+ case AARCH64_POE_MAGIC:
+ {
+ gdb_byte buf[8];
+ if (target_read_memory (section + AARCH64_POE_OFFSET,
+ buf, 8) != 0)
+ {
+ warning (_("Failed to read the POE section address from the"
+ " signal frame context."));
+ section += size;
+ break;
+ }
+ signal_frame.poe = extract_unsigned_integer (buf, 8, byte_order);
+ signal_frame.poe_available = true;
+ section += size;
+ break;
+ }
case AARCH64_EXTRA_MAGIC:
{
/* Extra is always the last valid section in reserved and points to
@@ -775,6 +799,10 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
signal_frame.fpmr);
}
+ /* Handle POE register. */
+ if (tdep->has_poe () && signal_frame.poe_available)
+ trad_frame_set_reg_value (this_cache, tdep->poe_regnum, signal_frame.poe);
+
/* Restore the tpidr2 register, if the target supports it and if there is
an entry for it. */
if (signal_frame.tpidr2_section != 0 && tdep->has_tls ()
@@ -828,6 +856,7 @@ aarch64_linux_sigframe_prev_arch (const frame_info_ptr &this_frame,
features.vq = sve_vq_from_vl (signal_frame.vl);
features.svq = (uint8_t) sve_vq_from_vl (signal_frame.svl);
features.fpmr = signal_frame.fpmr_available;
+ features.poe = signal_frame.poe_available;
struct gdbarch_info info;
info.bfd_arch_info = bfd_lookup_arch (bfd_arch_aarch64, bfd_mach_aarch64);
@@ -1655,6 +1684,23 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
&aarch64_linux_fpmr_regset, "FPMR", cb_data);
}
+ if (tdep->has_poe ())
+ {
+ const struct regcache_map_entry poe_regmap[] =
+ {
+ { 1, tdep->poe_regnum, sizeof (uint64_t) },
+ { 0 }
+ };
+
+ const struct regset aarch64_linux_poe_regset =
+ {
+ poe_regmap, regcache_supply_regset, regcache_collect_regset
+ };
+
+ cb (".reg-aarch-poe", sizeof (uint64_t), sizeof (uint64_t),
+ &aarch64_linux_poe_regset, "POE register", cb_data);
+ }
+
if (tdep->has_pauth ())
{
/* Create this on the fly in order to handle the variable location. */
@@ -1768,6 +1814,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
features.gcs = features.gcs_linux = hwcap & AARCH64_HWCAP_GCS;
features.mte = hwcap2 & AARCH64_HWCAP2_MTE;
features.fpmr = hwcap2 & AARCH64_HWCAP2_FPMR;
+ features.poe = hwcap2 & AARCH64_HWCAP2_POE;
/* Handle the TLS section. */
asection *tls = bfd_get_section_by_name (abfd, ".reg-aarch-tls");
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v5 5/5] gdb/testsuite: Add FEAT_S1POE testcases
2026-09-16 9:17 [PATCH v5 0/5] gdb/aarch64: Add POR_EL0 register support for FEAT_S1POE srinath.parvathaneni
` (3 preceding siblings ...)
2026-09-16 9:18 ` [PATCH v5 4/5] gdb/aarch64: Add core file and signal frame support for FEAT_S1POE srinath.parvathaneni
@ 2026-09-16 9:18 ` srinath.parvathaneni
4 siblings, 0 replies; 6+ messages in thread
From: srinath.parvathaneni @ 2026-09-16 9:18 UTC (permalink / raw)
To: gdb-patches
Cc: simark, luis.machado.foss, thiago.bauermann, guinevere,
Ezra.Sitorus, Matthieu.Longo, peter.maydell,
Srinath Parvathaneni
From: Srinath Parvathaneni <srinath.parvathaneni@arm.com>
Add testcases for POR_EL0 register access, POE pseudo-register access, SIGSEGV
diagnostics, fix-and-continue support (updating $por_el0 after SIGSEGV and
continuing with signal 0), core file support and the test for signal frame
unwinding support.
Approved-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
gdb/testsuite/gdb.arch/aarch64-poe-core.exp | 101 ++++++++++++++++++
gdb/testsuite/gdb.arch/aarch64-poe-sigframe.c | 45 ++++++++
.../gdb.arch/aarch64-poe-sigframe.exp | 55 ++++++++++
gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.c | 52 +++++++++
.../gdb.arch/aarch64-poe-sigsegv.exp | 55 ++++++++++
gdb/testsuite/gdb.arch/aarch64-poe.c | 29 +++++
gdb/testsuite/gdb.arch/aarch64-poe.exp | 60 +++++++++++
gdb/testsuite/lib/gdb.exp | 55 ++++++++++
8 files changed, 452 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/aarch64-poe-core.exp
create mode 100644 gdb/testsuite/gdb.arch/aarch64-poe-sigframe.c
create mode 100644 gdb/testsuite/gdb.arch/aarch64-poe-sigframe.exp
create mode 100644 gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.c
create mode 100644 gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.exp
create mode 100644 gdb/testsuite/gdb.arch/aarch64-poe.c
create mode 100644 gdb/testsuite/gdb.arch/aarch64-poe.exp
diff --git a/gdb/testsuite/gdb.arch/aarch64-poe-core.exp b/gdb/testsuite/gdb.arch/aarch64-poe-core.exp
new file mode 100644
index 00000000000..22fe646ac43
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-poe-core.exp
@@ -0,0 +1,101 @@
+# Copyright (C) 2026 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 generating and reading a core file with POR_EL0.
+
+proc check_poe_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" \
+ "Protection Key Violation while accessing address ${::hex} with Protection Key = ${::decimal}\\." \
+ "#0 ${::hex} in main \\(.*\\) at .*" \
+ ".*buf\\\[0\\\] = 42;.*"] \
+ "load core file"]
+ } {
+ untested "failed to load core file"
+ return -1
+ }
+
+ # Check the value of POR_EL0 in the core file.
+ gdb_test "print/x \$por_el0" " = 0x37" \
+ "por_el0 contents from core file"
+}
+
+require allow_aarch64_poe_tests
+
+standard_testfile aarch64-poe-sigsegv.c
+
+if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} {
+ return
+}
+
+set binfile [standard_output_file ${testfile}]
+
+if {![runto_main]} {
+ return
+}
+
+set poe_crash_re [multi_line \
+ "Program received signal SIGSEGV, Segmentation fault" \
+ "Protection Key Violation while accessing address ${::hex} with Protection Key = ${::decimal}\\." \
+ ".*buf\\\[0\\\] = 42;.*"]
+
+gdb_test_multiple "continue" "run to POE crash" {
+ -re "$poe_crash_re$gdb_prompt $" {
+ pass $gdb_test_name
+ }
+
+ -re ".*exited with code 01.*$gdb_prompt $" {
+ unsupported "POE userspace support unavailable"
+ return
+ }
+}
+
+# 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_poe_core_file $gcore_filename
+ }
+} else {
+ fail "gcore corefile not generated"
+}
+
+if {$core_generated} {
+ clean_restart
+ gdb_load ${binfile}
+ with_test_prefix "native corefile" {
+ check_poe_core_file $core_filename
+ }
+} else {
+ untested "native corefile not generated"
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-poe-sigframe.c b/gdb/testsuite/gdb.arch/aarch64-poe-sigframe.c
new file mode 100644
index 00000000000..dd72f560c87
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-poe-sigframe.c
@@ -0,0 +1,45 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2026 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 AArch64's POE Extension signal frame unwinding. */
+
+/* This test is based on the Linux kernel documentation for Memory
+ Protection Keys, including the arm64 Permission Overlay Extension
+ (FEAT_S1POE) support described in
+ Documentation/core-api/protection-keys.rst. */
+
+#include <sys/auxv.h>
+#include <stdlib.h>
+#include <signal.h>
+
+static int count = 0;
+
+static void
+handler (int sig)
+{
+ count++;
+}
+
+int
+main (int argc, char **argv)
+{
+ signal (SIGUSR1, handler);
+
+ raise (SIGUSR1);
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-poe-sigframe.exp b/gdb/testsuite/gdb.arch/aarch64-poe-sigframe.exp
new file mode 100644
index 00000000000..349c3a84ce0
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-poe-sigframe.exp
@@ -0,0 +1,55 @@
+# Copyright (C) 2026 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/>.
+
+# Test a binary that uses POE to unwind POR_EL0 from the signal frame context.
+
+global hex
+global decimal
+
+require allow_aarch64_poe_tests
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+ return
+}
+
+if {![runto_main]} {
+ return
+}
+
+# Set POR_EL0 register in main.
+gdb_test_no_output "set \$por_el0=0x777"
+
+gdb_breakpoint "handler"
+
+gdb_test "handle SIGUSR1 nostop" \
+ ".*SIGUSR1.*No.*Yes.*Yes.*"
+
+gdb_test "continue" \
+ ".*Breakpoint ${::decimal}, handler \\(sig=10\\).*"
+
+# Update POR_EL0 in the current signal handler frame.
+gdb_test_no_output "set \$por_el0=0x007"
+
+# Select the frame above the signal handler frame, which makes GDB unwind the
+# POR_EL0 from the signal frame POE context.
+gdb_test "frame 2" "#2 ($hex in )?\\S+ \\(.*\\) (at|from) \\S+.*" \
+ "reached frame 2"
+
+# Print POR_EL0 and verify that it reflects the value from frame above the
+# signal handler frame.
+gdb_test "p/x \$por_el0" \
+ "\\$\[0-9\]+ = 0x777" \
+ "print/x por_el0 from the frame before the signal handler"
diff --git a/gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.c b/gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.c
new file mode 100644
index 00000000000..5346e51dbba
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.c
@@ -0,0 +1,52 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2026 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 AArch64's POE Extension. */
+
+/* This test is based on the Linux kernel documentation for Memory
+ Protection Keys, including the arm64 Permission Overlay Extension
+ (FEAT_S1POE) support described in
+ Documentation/core-api/protection-keys.rst. */
+
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <unistd.h>
+
+int
+main (void)
+{
+ long pagesize = sysconf (_SC_PAGESIZE);
+
+ int *buf = mmap (NULL, pagesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (buf == MAP_FAILED)
+ return 1;
+
+ int pkey = pkey_alloc (0, 0);
+ if (pkey == -1)
+ return 1;
+
+ if (pkey_mprotect (buf, pagesize, PROT_READ | PROT_WRITE, pkey) == -1)
+ return 1;
+
+ if (pkey_set (pkey, PKEY_DISABLE_WRITE) == -1)
+ return 1;
+
+ buf[0] = 42; /* Expect SIGSEGV POE violation. */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.exp b/gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.exp
new file mode 100644
index 00000000000..e8bd0c0f22f
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-poe-sigsegv.exp
@@ -0,0 +1,55 @@
+# Copyright (C) 2026 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/>.
+
+# Test a binary that supports FEAT_S1POE and exposes POR_EL0 register.
+
+global hex
+global decimal
+
+require allow_aarch64_poe_tests
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+ return
+}
+
+if {![runto_main]} {
+ return
+}
+
+# Display POE violation SIGSEGV error message.
+set poe_sigsegv_re [multi_line \
+ "Program received signal SIGSEGV, Segmentation fault" \
+ "Protection Key Violation while accessing address ${hex} with Protection Key = ${decimal}"]
+
+gdb_test_multiple "continue" "display POE violation information" {
+ -re "$poe_sigsegv_re.*$gdb_prompt $" {
+ pass $gdb_test_name
+ }
+ -re ".*exited with code 01.*$gdb_prompt $" {
+ unsupported "POE userspace support unavailable"
+ return
+ }
+}
+
+# Fix the POE permissions (set $por_el0) and continue without delivering SIGSEGV.
+gdb_test_no_output "set \$por_el0 = 0x57" \
+ "allow write access by updating POR_EL0"
+
+gdb_test_multiple "signal 0" "continue after fixing POE fault" {
+ -re ".*exited normally.*$gdb_prompt $" {
+ pass $gdb_test_name
+ }
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-poe.c b/gdb/testsuite/gdb.arch/aarch64-poe.c
new file mode 100644
index 00000000000..2c95dc2a99b
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-poe.c
@@ -0,0 +1,29 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2026 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 AArch64's POE Extension. */
+
+/* This test is based on the Linux kernel documentation for Memory
+ Protection Keys, including the arm64 Permission Overlay Extension
+ (FEAT_S1POE) support described in
+ Documentation/core-api/protection-keys.rst. */
+
+int
+main (void)
+{
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-poe.exp b/gdb/testsuite/gdb.arch/aarch64-poe.exp
new file mode 100644
index 00000000000..9d8c472409c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-poe.exp
@@ -0,0 +1,60 @@
+# Copyright (C) 2026 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/>.
+
+# Test a binary that uses POE and exposed POR_EL0 register.
+
+global hex
+global decimal
+
+require allow_aarch64_poe_tests
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+ return
+}
+
+if {![runto_main]} {
+ return
+}
+
+# Validate the presence of the POE register.
+gdb_test "info registers por_el0" \
+ "por_el0\[ \t\]+$hex.*" \
+ "info register por_el0 available"
+
+gdb_test "p \$por_el0" \
+ "\\$\[0-9\]+ = .*" \
+ "print por_el0 available"
+
+gdb_test "p/x \$por_el0" \
+ "\\$\[0-9\]+ = ${hex}" \
+ "print/x por_el0 available"
+
+gdb_test "info registers por" \
+ "por_el0\[ \t\]+$hex.*por_p\[0-9\]+\[ \t\]+$hex.*" \
+ "info registers por group available"
+
+gdb_test_no_output "set \$por_el0 = 0x007" \
+ "initialize POR_EL0"
+
+gdb_test_no_output "set \$por_p1 = 0x07" \
+ "write por_p1 pseudo-register"
+
+gdb_test_no_output "set \$por_p3 = 0x07" \
+ "write por_p3 pseudo-register"
+
+gdb_test "p/x \$por_el0" \
+ "\\$\[0-9\]+ = 0x7077" \
+ "POE pseudo-register writes reflected in POR_EL0"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 1ebdaf6ba10..9b007d9b130 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -5691,6 +5691,61 @@ gdb_caching_proc allow_aarch64_gcs_tests {} {
return $allow_gcs_tests
}
+# Run a test on the target to see if it supports AArch64 POE extensions.
+# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
+
+gdb_caching_proc allow_aarch64_poe_tests {} {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "allow_aarch64_poe_tests"
+
+ if {![is_aarch64_target]} {
+ return 0
+ }
+
+ # Compile a program that tests the POE feature.
+ set src {
+ #include <stdbool.h>
+ #include <sys/auxv.h>
+
+ /* Feature check for POE feature. */
+ #ifndef HWCAP2_POE
+ #define HWCAP2_POE (1ULL << 63)
+ #endif
+
+ int main (void) {
+ bool poe_supported = getauxval (AT_HWCAP2) & HWCAP2_POE;
+
+ /* Return success if POE is supported. */
+ return !poe_supported;
+ }
+ }
+
+ if {![gdb_simple_compile $me $src executable]} {
+ return 0
+ }
+
+ # Compilation succeeded so now run it via gdb.
+ set allow_poe_tests 0
+ clean_restart
+ gdb_load $obj
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "\n$me: poe support detected"
+ set allow_poe_tests 1
+ }
+ -re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" {
+ verbose -log "\n$me: poe support not detected"
+ }
+ }
+ gdb_exit
+
+ remote_file build delete $obj
+ verbose "$me: returning $allow_poe_tests" 2
+ return $allow_poe_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" {
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread