@@ -1292,6 +1292,7 @@ HFILES_NO_SRCDIR = \
arch/aarch32.h \
arch/aarch64-fpmr-linux.h \
arch/aarch64-gcs-linux.h \
+ arch/aarch64-poe-linux.h \
arch/aarch64.h \
arch/aarch64-insn.h \
arch/aarch64-mte.h \
@@ -1540,6 +1541,7 @@ HFILES_NO_SRCDIR = \
namespace.h \
nat/aarch64-fpmr-linux.h \
nat/aarch64-gcs-linux.h \
+ nat/aarch64-poe-linux.h \
nat/aarch64-hw-point.h \
nat/aarch64-linux.h \
nat/aarch64-linux-hw-point.h \
@@ -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 contents from GDB's REGCACHE to the
+ thread associated with REGCACHE. */
+
+static void
+store_poeregs_to_thread (struct 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);
}
@@ -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 (struct 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,132 @@ 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 (struct 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 (struct 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));
+ struct 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, (unsigned) 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 (struct gdbarch *gdbarch,
+ struct 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 +3318,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 +3449,38 @@ 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 ().data (),
+ result->type ()->length (),
+ gdbarch_byte_order (gdbarch),
+ pseudo_reg_value);
return result;
}
@@ -3322,6 +3512,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 +3621,50 @@ 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 ().data (),
+ por_value->type ()->length (),
+ 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 +3701,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 +4383,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 +4807,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 struct 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 +5025,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 +5068,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 +5166,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;
@@ -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;
new file mode 100644
@@ -0,0 +1,29 @@
+/* Common Linux target-dependent definitions for AArch64 POE
+
+ 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_ARCH_AARCH64_POE_LINUX_H
+#define GDB_ARCH_AARCH64_POE_LINUX_H
+
+/* Feature check for Permission Overlay Extension. */
+#define AARCH64_HWCAP2_POE (1ULL << 63)
+
+/* Data or instruction abort caused by Protection Key Violation. */
+#define AARCH64_SEGV_PKUERR 4
+
+#endif /* GDB_ARCH_AARCH64_POE_LINUX_H. */
@@ -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;
}
@@ -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;
}
};
@@ -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 \
new file mode 100644
@@ -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 (struct target_desc *result, long regnum)
+{
+ struct 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;
+}
new file mode 100644
@@ -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>
new file mode 100644
@@ -0,0 +1,29 @@
+/* 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/>. */
+
+#include <asm/hwcap.h>
+#ifndef GDB_NAT_AARCH64_POE_LINUX_H
+#define GDB_NAT_AARCH64_POE_LINUX_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. */