* [PATCH 1/1] gdb, gdbserver: Add Intel APX register support
2026-07-25 10:53 [PATCH 0/1] Add Intel APX support Christina Joos
@ 2026-07-25 10:53 ` Christina Joos
2026-07-25 11:07 ` Eli Zaretskii
0 siblings, 1 reply; 3+ messages in thread
From: Christina Joos @ 2026-07-25 10:53 UTC (permalink / raw)
To: gdb-patches; +Cc: keiths
From: "H.J. Lu" <hjl.tools@gmail.com>
Intel Advanced Performance Extensions (APX) doubles the number of
general-purpose registers (GPRs) from 16 to 32 (r16-r31).
Those new 64-bit Extended GPRs (EGPRs) do not change the size of the
XSAVE area, as they take up the space left behind by the deprecated
MPX registers.
In contrast to the legacy GPRS, EGPRs are not enabled by default in 64-bit
mode but are xcr0 enabled based on APX_F Intel APX state. Define
X86_XSTATE_APX_F to support this new configuration.
Add gdb and gdbserver registers support for those new 64-bit EGPRs.
Also add byte, word and dword pseudo register support.
Co-Authored-By: Christina Joos <christina.joos@intel.com>
---
gdb/NEWS | 4 +
gdb/amd64-linux-tdep.c | 2 +
gdb/amd64-tdep.c | 262 ++++++++++++++----
gdb/amd64-tdep.h | 2 +
gdb/arch/amd64.c | 4 +
gdb/arch/x86-linux-tdesc-features.c | 1 +
gdb/doc/gdb.texinfo | 5 +
gdb/features/Makefile | 1 +
gdb/features/i386/64bit-apx.c | 29 ++
gdb/features/i386/64bit-apx.xml | 26 ++
gdb/i386-tdep.c | 32 ++-
gdb/i386-tdep.h | 26 ++
gdb/i387-tdep.c | 105 ++++++-
gdb/i387-tdep.h | 6 +
gdb/nat/x86-xstate.c | 1 +
gdb/target-debug.h | 1 +
gdb/testsuite/gdb.arch/amd64-apx-core.c | 88 ++++++
gdb/testsuite/gdb.arch/amd64-apx-corefile.c | 71 +++++
gdb/testsuite/gdb.arch/amd64-apx-corefile.exp | 67 +++++
gdb/testsuite/gdb.arch/amd64-apx.c | 66 +++++
gdb/testsuite/gdb.arch/amd64-apx.exp | 77 +++++
gdb/testsuite/lib/gdb.exp | 58 ++++
gdbserver/i387-fp.cc | 43 +++
gdbserver/linux-x86-low.cc | 4 +-
gdbsupport/x86-xstate.h | 12 +-
25 files changed, 931 insertions(+), 62 deletions(-)
create mode 100644 gdb/features/i386/64bit-apx.c
create mode 100644 gdb/features/i386/64bit-apx.xml
create mode 100644 gdb/testsuite/gdb.arch/amd64-apx-core.c
create mode 100644 gdb/testsuite/gdb.arch/amd64-apx-corefile.c
create mode 100644 gdb/testsuite/gdb.arch/amd64-apx-corefile.exp
create mode 100644 gdb/testsuite/gdb.arch/amd64-apx.c
create mode 100644 gdb/testsuite/gdb.arch/amd64-apx.exp
diff --git a/gdb/NEWS b/gdb/NEWS
index 8f40ca5cb11..5b3ba152188 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
*** Changes since GDB 17
+* Support for Intel APX registers on amd64 GNU/Linux.
+ Support displaying and modifying Intel APX registers (Extended GPRs
+ $r16 - $r31) including byte, word and dword pseudo registers.
+
* Support for the Common Trace Format (CTF) has been removed. GDB now
saves trace information exclusively in its own "tfile" format.
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 9b23db72bbe..81e66be75c1 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -113,6 +113,8 @@ int amd64_linux_gregset_reg_offset[] =
-1, -1, -1, -1, -1, -1, -1, -1,
-1, /* PKEYS register pkru */
-1, /* CET user mode register PL3_SSP. */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* r16 ... r31. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
/* End of hardware registers */
21 * 8, 22 * 8, /* fs_base and gs_base. */
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index a982e610642..18b8f2a4c0b 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -61,6 +61,36 @@
/* Register information. */
+/* Number of general purpose registers without APX. */
+#define AMD64_NUM_GPRS 16
+
+/* Number of lower byte registers without APX. */
+#define AMD64_NUM_LOWER_BYTE_REGS AMD64_NUM_GPRS
+
+/* Number of byte registers without APX. */
+#define AMD64_NUM_BYTE_REGS (AMD64_NUM_LOWER_BYTE_REGS + 4)
+
+/* Number of word registers without APX. */
+#define AMD64_NUM_WORD_REGS AMD64_NUM_GPRS
+
+/* Number of dword registers without APX. */
+#define AMD64_NUM_DWORD_REGS AMD64_NUM_GPRS
+
+/* Number of general purpose registers with APX. */
+#define AMD64_APX_NUM_GPRS 32
+
+/* Number of APX lower byte registers. */
+#define AMD64_APX_NUM_LOWER_BYTE_REGS AMD64_APX_NUM_GPRS
+
+/* Number of APX byte registers. */
+#define AMD64_APX_NUM_BYTE_REGS (AMD64_APX_NUM_LOWER_BYTE_REGS + 4)
+
+/* Number of APX word registers. */
+#define AMD64_APX_NUM_WORD_REGS AMD64_APX_NUM_GPRS
+
+/* Number of APX dword registers. */
+#define AMD64_APX_NUM_DWORD_REGS AMD64_APX_NUM_GPRS
+
static const char * const amd64_register_names[] =
{
"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp",
@@ -87,6 +117,12 @@ static const char * const amd64_ymm_names[] =
"ymm12", "ymm13", "ymm14", "ymm15"
};
+static const char * const amd64_apx_names[] =
+{
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
+};
+
static const char * const amd64_ymm_avx512_names[] =
{
"ymm16", "ymm17", "ymm18", "ymm19",
@@ -252,7 +288,20 @@ static int amd64_dwarf_regmap[] =
AMD64_K0_REGNUM + 0, AMD64_K0_REGNUM + 1,
AMD64_K0_REGNUM + 2, AMD64_K0_REGNUM + 3,
AMD64_K0_REGNUM + 4, AMD64_K0_REGNUM + 5,
- AMD64_K0_REGNUM + 6, AMD64_K0_REGNUM + 7
+ AMD64_K0_REGNUM + 6, AMD64_K0_REGNUM + 7,
+
+ /* Reserved. */
+ -1, -1, -1, -1,
+
+ /* r16-r31. */
+ AMD64_R16_REGNUM + 0, AMD64_R16_REGNUM + 1,
+ AMD64_R16_REGNUM + 2, AMD64_R16_REGNUM + 3,
+ AMD64_R16_REGNUM + 4, AMD64_R16_REGNUM + 5,
+ AMD64_R16_REGNUM + 6, AMD64_R16_REGNUM + 7,
+ AMD64_R16_REGNUM + 8, AMD64_R16_REGNUM + 9,
+ AMD64_R16_REGNUM + 10, AMD64_R16_REGNUM + 11,
+ AMD64_R16_REGNUM + 12, AMD64_R16_REGNUM + 13,
+ AMD64_R16_REGNUM + 14, AMD64_R16_REGNUM + 15
};
static const int amd64_dwarf_regmap_len =
@@ -322,8 +371,19 @@ static const char * const amd64_byte_names[] =
"ah", "bh", "ch", "dh"
};
-/* Number of lower byte registers. */
-#define AMD64_NUM_LOWER_BYTE_REGS 16
+static_assert (ARRAY_SIZE (amd64_byte_names) == AMD64_NUM_BYTE_REGS);
+
+static const char * const amd64_apx_byte_names[] =
+{
+ "al", "bl", "cl", "dl", "sil", "dil", "bpl", "spl",
+ "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l",
+ "r16l", "r17l", "r18l", "r19l", "r20l", "r21l", "r22l", "r23l",
+ "r24l", "r25l", "r26l", "r27l", "r28l", "r29l", "r30l", "r31l",
+ "ah", "bh", "ch", "dh"
+};
+
+static_assert (ARRAY_SIZE (amd64_apx_byte_names)
+ == AMD64_APX_NUM_BYTE_REGS);
/* Register names for word pseudo-registers. */
@@ -333,6 +393,19 @@ static const char * const amd64_word_names[] =
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"
};
+static_assert (ARRAY_SIZE (amd64_word_names) == AMD64_NUM_WORD_REGS);
+
+static const char * const amd64_apx_word_names[] =
+{
+ "ax", "bx", "cx", "dx", "si", "di", "bp", "",
+ "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w",
+ "r16w", "r17w", "r18w", "r19w", "r20w", "r21w", "r22w", "r23w",
+ "r24w", "r25w", "r26w", "r27w", "r28w", "r29w", "r30w", "r31w"
+};
+
+static_assert (ARRAY_SIZE (amd64_apx_word_names)
+ == AMD64_APX_NUM_WORD_REGS);
+
/* Register names for dword pseudo-registers. */
static const char * const amd64_dword_names[] =
@@ -342,28 +415,90 @@ static const char * const amd64_dword_names[] =
"eip"
};
+static_assert (ARRAY_SIZE (amd64_dword_names) == AMD64_NUM_DWORD_REGS + 1);
+
+static const char * const amd64_apx_dword_names[] =
+{
+ "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
+ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d",
+ "r16d", "r17d", "r18d", "r19d", "r20d", "r21d", "r22d", "r23d",
+ "r24d", "r25d", "r26d", "r27d", "r28d", "r29d", "r30d", "r31d",
+ "eip"
+};
+
+static_assert (ARRAY_SIZE (amd64_apx_dword_names)
+ == AMD64_APX_NUM_DWORD_REGS + 1);
+
/* Return the name of register REGNUM. */
static const char *
amd64_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
{
i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
- if (i386_byte_regnum_p (gdbarch, regnum))
- return amd64_byte_names[regnum - tdep->al_regnum];
- else if (i386_zmm_regnum_p (gdbarch, regnum))
+ if (i386_zmm_regnum_p (gdbarch, regnum))
return amd64_zmm_names[regnum - tdep->zmm0_regnum];
else if (i386_ymm_regnum_p (gdbarch, regnum))
return amd64_ymm_names[regnum - tdep->ymm0_regnum];
else if (i386_ymm_avx512_regnum_p (gdbarch, regnum))
return amd64_ymm_avx512_names[regnum - tdep->ymm16_regnum];
- else if (i386_word_regnum_p (gdbarch, regnum))
- return amd64_word_names[regnum - tdep->ax_regnum];
else if (i386_dword_regnum_p (gdbarch, regnum))
- return amd64_dword_names[regnum - tdep->eax_regnum];
+ return tdep->dword_names[regnum - tdep->eax_regnum];
else
return i386_pseudo_register_name (gdbarch, regnum);
}
+/* Return the raw byte register number. *PART is set to 1 for high
+ byte register and 0 otherwise. */
+
+static int
+amd64_raw_byte_register (i386_gdbarch_tdep *tdep, int regnum, int *part)
+{
+ int gpnum = regnum - tdep->al_regnum;
+
+ /* Extract (always little endian). */
+ if (gpnum >= tdep->num_lower_byte_regs)
+ {
+ /* Special handling for AH, BH, CH, DH. */
+ *part = 1;
+ gpnum -= tdep->num_lower_byte_regs;
+ }
+ else
+ {
+ *part = 0;
+ if (gpnum >= AMD64_NUM_GPRS)
+ {
+ gdb_assert (tdep->r16_regnum != -1);
+ gpnum += tdep->r16_regnum - AMD64_NUM_GPRS;
+ }
+ }
+ return gpnum;
+}
+
+/* Return the raw word register number. */
+
+static int
+amd64_raw_word_register (i386_gdbarch_tdep *tdep, int regnum)
+{
+ int gpnum = regnum - tdep->ax_regnum;
+ if (gpnum >= AMD64_NUM_GPRS)
+ gpnum += tdep->r16_regnum - AMD64_NUM_GPRS;
+ return gpnum;
+}
+
+/* Return the raw dword register number. For x32, the last dword
+ register is EIP. */
+
+static int
+amd64_raw_dword_register (i386_gdbarch_tdep *tdep, int regnum)
+{
+ int gpnum = regnum - tdep->eax_regnum;
+ if (tdep->eip_regnum == gpnum)
+ return AMD64_RIP_REGNUM;
+ if (gpnum >= AMD64_NUM_GPRS)
+ gpnum += tdep->r16_regnum - AMD64_NUM_GPRS;
+ return gpnum;
+}
+
static value *
amd64_pseudo_register_read_value (gdbarch *gdbarch, const frame_info_ptr &next_frame,
int regnum)
@@ -372,23 +507,18 @@ amd64_pseudo_register_read_value (gdbarch *gdbarch, const frame_info_ptr &next_f
if (i386_byte_regnum_p (gdbarch, regnum))
{
- int gpnum = regnum - tdep->al_regnum;
-
- /* Extract (always little endian). */
- if (gpnum >= AMD64_NUM_LOWER_BYTE_REGS)
- {
- gpnum -= AMD64_NUM_LOWER_BYTE_REGS;
-
- /* Special handling for AH, BH, CH, DH. */
- return pseudo_from_raw_part (next_frame, regnum, gpnum, 1);
- }
- else
- return pseudo_from_raw_part (next_frame, regnum, gpnum, 0);
+ int part;
+ const int gpnum = amd64_raw_byte_register (tdep, regnum, &part);
+ return pseudo_from_raw_part (next_frame, regnum, gpnum, part);
+ }
+ else if (i386_word_regnum_p (gdbarch, regnum))
+ {
+ const int gpnum = amd64_raw_word_register (tdep, regnum);
+ return pseudo_from_raw_part (next_frame, regnum, gpnum, 0);
}
else if (i386_dword_regnum_p (gdbarch, regnum))
{
- int gpnum = regnum - tdep->eax_regnum;
-
+ const int gpnum = amd64_raw_dword_register (tdep, regnum);
return pseudo_from_raw_part (next_frame, regnum, gpnum, 0);
}
else
@@ -403,19 +533,18 @@ amd64_pseudo_register_write (gdbarch *gdbarch, const frame_info_ptr &next_frame,
if (i386_byte_regnum_p (gdbarch, regnum))
{
- int gpnum = regnum - tdep->al_regnum;
-
- if (gpnum >= AMD64_NUM_LOWER_BYTE_REGS)
- {
- gpnum -= AMD64_NUM_LOWER_BYTE_REGS;
- pseudo_to_raw_part (next_frame, buf, gpnum, 1);
- }
- else
- pseudo_to_raw_part (next_frame, buf, gpnum, 0);
+ int part;
+ const int gpnum = amd64_raw_byte_register (tdep, regnum, &part);
+ pseudo_to_raw_part (next_frame, buf, gpnum, part);
+ }
+ else if (i386_word_regnum_p (gdbarch, regnum))
+ {
+ const int gpnum = amd64_raw_word_register (tdep, regnum);
+ pseudo_to_raw_part (next_frame, buf, gpnum, 0);
}
else if (i386_dword_regnum_p (gdbarch, regnum))
{
- int gpnum = regnum - tdep->eax_regnum;
+ const int gpnum = amd64_raw_dword_register (tdep, regnum);
pseudo_to_raw_part (next_frame, buf, gpnum, 0);
}
else
@@ -432,17 +561,18 @@ amd64_ax_pseudo_register_collect (struct gdbarch *gdbarch,
if (i386_byte_regnum_p (gdbarch, regnum))
{
- int gpnum = regnum - tdep->al_regnum;
-
- if (gpnum >= AMD64_NUM_LOWER_BYTE_REGS)
- ax_reg_mask (ax, gpnum - AMD64_NUM_LOWER_BYTE_REGS);
- else
- ax_reg_mask (ax, gpnum);
+ int part;
+ const int gpnum = amd64_raw_byte_register (tdep, regnum, &part);
+ ax_reg_mask (ax, gpnum);
+ }
+ else if (i386_word_regnum_p (gdbarch, regnum))
+ {
+ const int gpnum = amd64_raw_word_register (tdep, regnum);
+ ax_reg_mask (ax, gpnum);
}
else if (i386_dword_regnum_p (gdbarch, regnum))
{
- int gpnum = regnum - tdep->eax_regnum;
-
+ const int gpnum = amd64_raw_dword_register (tdep, regnum);
ax_reg_mask (ax, gpnum);
}
else
@@ -3552,9 +3682,30 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pl3_ssp") != nullptr)
tdep->ssp_regnum = AMD64_PL3_SSP_REGNUM;
- tdep->num_byte_regs = 20;
- tdep->num_word_regs = 16;
- tdep->num_dword_regs = 16;
+ if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.apx") != nullptr)
+ {
+ tdep->apx_register_names = amd64_apx_names;
+ tdep->num_apx_regs = 16;
+ tdep->r16_regnum = AMD64_R16_REGNUM;
+ tdep->byte_names = amd64_apx_byte_names;
+ tdep->word_names = amd64_apx_word_names;
+ tdep->dword_names = amd64_apx_dword_names;
+ tdep->num_lower_byte_regs = AMD64_APX_NUM_LOWER_BYTE_REGS;
+ tdep->num_byte_regs = AMD64_APX_NUM_BYTE_REGS;
+ tdep->num_word_regs = AMD64_APX_NUM_WORD_REGS;
+ tdep->num_dword_regs = AMD64_APX_NUM_DWORD_REGS;
+ }
+ else
+ {
+ tdep->byte_names = amd64_byte_names;
+ tdep->word_names = amd64_word_names;
+ tdep->dword_names = amd64_dword_names;
+ tdep->num_lower_byte_regs = AMD64_NUM_LOWER_BYTE_REGS;
+ tdep->num_byte_regs = AMD64_NUM_BYTE_REGS;
+ tdep->num_word_regs = AMD64_NUM_WORD_REGS;
+ tdep->num_dword_regs = AMD64_NUM_DWORD_REGS;
+ }
+
/* Avoid wiring in the MMX registers for now. */
tdep->num_mmx_regs = 0;
@@ -3663,15 +3814,13 @@ static struct type *
amd64_x32_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
{
i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+ const int gpnum = regnum - tdep->eax_regnum;
- switch (regnum - tdep->eax_regnum)
- {
- case AMD64_RBP_REGNUM: /* %ebp */
- case AMD64_RSP_REGNUM: /* %esp */
- return builtin_type (gdbarch)->builtin_data_ptr;
- case AMD64_RIP_REGNUM: /* %eip */
- return builtin_type (gdbarch)->builtin_func_ptr;
- }
+ if (gpnum == tdep->eip_regnum) /* %eip. */
+ return builtin_type (gdbarch)->builtin_func_ptr;
+ else if (gpnum == AMD64_RBP_REGNUM /* %ebp. */
+ || gpnum == AMD64_RSP_REGNUM) /* %esp. */
+ return builtin_type (gdbarch)->builtin_data_ptr;
return i386_pseudo_register_type (gdbarch, regnum);
}
@@ -3684,7 +3833,10 @@ amd64_x32_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
amd64_init_abi (info, gdbarch, default_tdesc);
- tdep->num_dword_regs = 17;
+ /* Set %eip to the last dword register. */
+ tdep->eip_regnum = tdep->num_dword_regs;
+ /* Increment 1 for %eip. */
+ tdep->num_dword_regs += 1;
set_tdesc_pseudo_register_type (gdbarch, amd64_x32_pseudo_register_type);
set_gdbarch_long_bit (gdbarch, 32);
@@ -3706,12 +3858,14 @@ const struct target_desc *
amd64_target_description (uint64_t xstate_bv, bool segments)
{
static const_target_desc_up amd64_tdescs \
- [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/][2/*segments*/] = {};
+ [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/][2/*APX*/] \
+ [2/*segments*/] = {};
const_target_desc_up &tdesc
= amd64_tdescs[(xstate_bv & X86_XSTATE_AVX) ? 1 : 0]
[(xstate_bv & X86_XSTATE_AVX512) ? 1 : 0]
[(xstate_bv & X86_XSTATE_PKRU) ? 1 : 0]
[(xstate_bv & X86_XSTATE_CET_U) ? 1 : 0]
+ [(xstate_bv & X86_XSTATE_APX_F) ? 1 : 0]
[segments ? 1 : 0];
if (tdesc == nullptr)
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 3de28952cca..93b3cb61b17 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -82,6 +82,8 @@ enum amd64_regnum
AMD64_ZMM31H_REGNUM = AMD64_ZMM0H_REGNUM + 31,
AMD64_PKRU_REGNUM,
AMD64_PL3_SSP_REGNUM,
+ AMD64_R16_REGNUM,
+ AMD64_R31_REGNUM = AMD64_R16_REGNUM + 15,
AMD64_FSBASE_REGNUM,
AMD64_GSBASE_REGNUM
};
diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
index d1a871dd6cb..3992bbf512e 100644
--- a/gdb/arch/amd64.c
+++ b/gdb/arch/amd64.c
@@ -23,6 +23,7 @@
#include "../features/i386/64bit-avx.c"
#include "../features/i386/64bit-avx512.c"
#include "../features/i386/64bit-core.c"
+#include "../features/i386/64bit-apx.c"
#include "../features/i386/64bit-linux.c"
#include "../features/i386/64bit-segments.c"
#include "../features/i386/64bit-sse.c"
@@ -78,5 +79,8 @@ amd64_create_target_description (uint64_t xstate_bv, bool is_x32,
regnum = create_feature_i386_32bit_ssp (tdesc.get (), regnum);
}
+ if (xstate_bv & X86_XSTATE_APX_F)
+ regnum = create_feature_i386_64bit_apx (tdesc.get (), regnum);
+
return tdesc;
}
diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
index 5d6ea7cc701..0191f326834 100644
--- a/gdb/arch/x86-linux-tdesc-features.c
+++ b/gdb/arch/x86-linux-tdesc-features.c
@@ -65,6 +65,7 @@ struct x86_xstate_feature {
static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
/* Feature, i386, amd64, x32. */
+ { X86_XSTATE_APX_F, false, true, true },
{ X86_XSTATE_CET_U, false, true, true },
{ X86_XSTATE_PKRU, true, true, true },
{ X86_XSTATE_AVX512, true, true, true },
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 0030698dcee..69a2d714299 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -50753,6 +50753,11 @@ amd64, 32 bits on amd64 with 32-bit pointer size (X32) and 32 bits on i386.
Following the restriction of the Linux kernel, only @value{GDBN} for amd64
targets makes use of this feature for now.
+The @samp{org.gnu.gdb.i386.apx} feature is optional. It should describe
+16 Extended General Purpose registers (@acronym{EGPRs})
+@samp{r16} - @samp{r31} with 64 bits each. All @acronym{Intel APX}
+registers are valid only on amd64.
+
@node LoongArch Features
@subsection LoongArch Features
@cindex target descriptions, LoongArch Features
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 161a7453d66..df716ab6c5e 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -231,6 +231,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
i386/32bit-avx512.xml \
i386/32bit-segments.xml \
i386/32bit-ssp.xml \
+ i386/64bit-apx.xml \
i386/64bit-avx512.xml \
i386/64bit-core.xml \
i386/64bit-segments.xml \
diff --git a/gdb/features/i386/64bit-apx.c b/gdb/features/i386/64bit-apx.c
new file mode 100644
index 00000000000..34cd47131d1
--- /dev/null
+++ b/gdb/features/i386/64bit-apx.c
@@ -0,0 +1,29 @@
+/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
+ Original: 64bit-apx.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_i386_64bit_apx (struct target_desc *result, long regnum)
+{
+ struct tdesc_feature *feature;
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.i386.apx");
+ tdesc_create_reg (feature, "r16", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r17", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r18", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r19", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r20", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r21", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r22", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r23", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r24", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r25", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r26", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r27", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r28", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r29", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r30", regnum++, 1, NULL, 64, "int64");
+ tdesc_create_reg (feature, "r31", regnum++, 1, NULL, 64, "int64");
+ return regnum;
+}
diff --git a/gdb/features/i386/64bit-apx.xml b/gdb/features/i386/64bit-apx.xml
new file mode 100644
index 00000000000..11a4ec67cae
--- /dev/null
+++ b/gdb/features/i386/64bit-apx.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2024 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.i386.apx">
+ <reg name="r16" bitsize="64" type="int64"/>
+ <reg name="r17" bitsize="64" type="int64"/>
+ <reg name="r18" bitsize="64" type="int64"/>
+ <reg name="r19" bitsize="64" type="int64"/>
+ <reg name="r20" bitsize="64" type="int64"/>
+ <reg name="r21" bitsize="64" type="int64"/>
+ <reg name="r22" bitsize="64" type="int64"/>
+ <reg name="r23" bitsize="64" type="int64"/>
+ <reg name="r24" bitsize="64" type="int64"/>
+ <reg name="r25" bitsize="64" type="int64"/>
+ <reg name="r26" bitsize="64" type="int64"/>
+ <reg name="r27" bitsize="64" type="int64"/>
+ <reg name="r28" bitsize="64" type="int64"/>
+ <reg name="r29" bitsize="64" type="int64"/>
+ <reg name="r30" bitsize="64" type="int64"/>
+ <reg name="r31" bitsize="64" type="int64"/>
+</feature>
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index f69cc2dcc01..76d1b0c9531 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -412,9 +412,9 @@ i386_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
else if (i386_zmm_regnum_p (gdbarch, regnum))
return i386_zmm_names[regnum - tdep->zmm0_regnum];
else if (i386_byte_regnum_p (gdbarch, regnum))
- return i386_byte_names[regnum - tdep->al_regnum];
+ return tdep->byte_names[regnum - tdep->al_regnum];
else if (i386_word_regnum_p (gdbarch, regnum))
- return i386_word_names[regnum - tdep->ax_regnum];
+ return tdep->word_names[regnum - tdep->ax_regnum];
internal_error (_("invalid regnum"));
}
@@ -8519,7 +8519,8 @@ i386_xcr0_from_tdesc (const struct target_desc *tdesc)
const struct tdesc_feature *feature_core;
const struct tdesc_feature *feature_sse, *feature_avx,
- *feature_avx512, *feature_pkeys;
+ *feature_avx512, *feature_pkeys,
+ *feature_apx;
/* Get core registers. */
feature_core = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.core");
@@ -8535,6 +8536,9 @@ i386_xcr0_from_tdesc (const struct target_desc *tdesc)
/* Try AVX512 registers. */
feature_avx512 = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx512");
+ /* Try APX registers. */
+ feature_apx = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.apx");
+
/* Try PKEYS */
feature_pkeys = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys");
@@ -8562,6 +8566,9 @@ i386_xcr0_from_tdesc (const struct target_desc *tdesc)
xcr0 |= X86_XSTATE_AVX512;
}
+ if (feature_apx)
+ xcr0 |= X86_XSTATE_APX_F;
+
if (feature_pkeys)
xcr0 |= X86_XSTATE_PKRU;
@@ -8577,7 +8584,7 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
const struct tdesc_feature *feature_sse, *feature_avx, *feature_avx512,
*feature_pkeys, *feature_segments,
- *feature_pl3_ssp;
+ *feature_pl3_ssp, *feature_apx;
int i, num_regs, valid_p;
if (! tdesc_has_registers (tdesc))
@@ -8597,6 +8604,9 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
/* Try AVX512 registers. */
feature_avx512 = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx512");
+ /* Try APX registers. */
+ feature_apx = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.apx");
+
/* Try segment base registers. */
feature_segments = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments");
@@ -8681,6 +8691,18 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
tdep->num_xmm_regs = 0;
}
+ if (feature_apx
+ && tdep->r16_regnum != -1
+ && tdep->apx_register_names != nullptr
+ && tdep->num_apx_regs > 0)
+ {
+ tdep->xcr0 |= X86_XSTATE_APX_F;
+ for (i = 0; i < tdep->num_apx_regs; i++)
+ valid_p &= tdesc_numbered_register (feature_apx, tdesc_data,
+ tdep->r16_regnum + i,
+ tdep->apx_register_names[i]);
+ }
+
num_regs = tdep->num_core_regs;
for (i = 0; i < num_regs; i++)
valid_p &= tdesc_numbered_register (feature_core, tdesc_data, i,
@@ -8948,6 +8970,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->num_core_regs = I386_NUM_GREGS + I387_NUM_REGS;
tdep->register_names = i386_register_names;
+ tdep->byte_names = i386_byte_names;
+ tdep->word_names = i386_word_names;
tdep->num_byte_regs = 8;
tdep->num_word_regs = 8;
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index d64d5241754..bdafd523834 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -108,6 +108,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
value >= 0. */
int al_regnum = -1;
+ /* Number of pseudo lower byte registers. Only used for AMD64. */
+ int num_lower_byte_regs = 0;
+
/* Number of pseudo word registers. */
int num_word_regs = 0;
@@ -121,9 +124,16 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
registers are supported. */
int eax_regnum = -1;
+ /* Register number for %eip. Set this to a value >= 0 in case of %eip
+ support. Only used for AMD64. */
+ int eip_regnum = -1;
+
/* Number of core registers. */
int num_core_regs = 0;
+ /* Number of registers added in APX. Only used for AMD64. */
+ int num_apx_regs = 0;
+
/* Number of SSE registers. */
int num_xmm_regs = 0;
@@ -159,6 +169,15 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
supported. */
int ymm0h_regnum = -1;
+ /* Byte register names. */
+ const char * const *byte_names = nullptr;
+
+ /* Word register names. */
+ const char * const *word_names = nullptr;
+
+ /* Dword register names. Only used for AMD64. */
+ const char * const *dword_names = nullptr;
+
/* Upper YMM register names. Only used for tdesc_numbered_register. */
const char * const *ymmh_register_names = nullptr;
@@ -173,6 +192,13 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
registers are supported. */
int zmm0h_regnum = -1;
+ /* Register number for %r16. Set this to a value >= 0 if r16-r31
+ registers are supported. Only used for AMD64. */
+ int r16_regnum = -1;
+
+ /* r16-r31 register names. Only used for tdesc_numbered_register. */
+ const char * const *apx_register_names = nullptr;
+
/* OpMask register names. */
const char * const *k_register_names = nullptr;
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index f22a5e02bbb..ea3f3f15a5d 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -820,6 +820,34 @@ static int xsave_xmm_avx512_offset[] =
(xsave + (tdep)->xsave_layout.zmm_offset \
+ xsave_xmm_avx512_offset[regnum - I387_XMM16_REGNUM (tdep)])
+/* At xsave_apx_egpr_offset[REGNUM] you'll find the relative offset
+ within the APX region of the XSAVE extended state where the GDB
+ registers r16 - r31 is stored. */
+
+static int xsave_apx_egpr_offset[] =
+{
+ 0 * 8, /* r16...r31 registers. */
+ 1 * 8,
+ 2 * 8,
+ 3 * 8,
+ 4 * 8,
+ 5 * 8,
+ 6 * 8,
+ 7 * 8,
+ 8 * 8,
+ 9 * 8,
+ 10 * 8,
+ 11 * 8,
+ 12 * 8,
+ 13 * 8,
+ 14 * 8,
+ 15 * 8
+};
+
+#define XSAVE_APX_EGPR_ADDR(tdep, xsave, regnum) \
+ (xsave + (tdep)->xsave_layout.apx_offset \
+ + xsave_apx_egpr_offset[regnum - I387_R16_REGNUM (tdep)])
+
/* At xsave_avx512_k_offset[REGNUM] you'll find the relative offset
within the K region of the XSAVE extended state where the AVX512
opmask register K0 + REGNUM is stored. */
@@ -922,6 +950,7 @@ i387_guess_xsave_layout (uint64_t xcr0, size_t xsave_size,
{
/* Intel CPUs supporting PKRU. */
layout.avx_offset = 576;
+ layout.apx_offset = 960;
layout.k_offset = 1088;
layout.zmm_h_offset = 1152;
layout.zmm_offset = 1664;
@@ -940,6 +969,7 @@ i387_guess_xsave_layout (uint64_t xcr0, size_t xsave_size,
{
/* Intel CPUs supporting AVX512. */
layout.avx_offset = 576;
+ layout.apx_offset = 960;
layout.k_offset = 1088;
layout.zmm_h_offset = 1152;
layout.zmm_offset = 1664;
@@ -970,6 +1000,7 @@ i387_fallback_xsave_layout (uint64_t xcr0)
{
/* Intel CPUs supporting PKRU. */
layout.avx_offset = 576;
+ layout.apx_offset = 960;
layout.k_offset = 1088;
layout.zmm_h_offset = 1152;
layout.zmm_offset = 1664;
@@ -980,6 +1011,7 @@ i387_fallback_xsave_layout (uint64_t xcr0)
{
/* Intel CPUs supporting AVX512. */
layout.avx_offset = 576;
+ layout.apx_offset = 960;
layout.k_offset = 1088;
layout.zmm_h_offset = 1152;
layout.zmm_offset = 1664;
@@ -1045,8 +1077,9 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
avx512_ymmh_avx512 = 0x40,
avx512_xmm_avx512 = 0x80,
pkeys = 0x100,
+ apx = 0x200,
all = x87 | sse | avxh | avx512_k | avx512_zmm0_h | avx512_zmm16_h
- | avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys
+ | avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys | apx
} regclass;
gdb_assert (regs != NULL);
@@ -1055,6 +1088,9 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
if (regnum == -1)
regclass = all;
+ else if (regnum >= I387_R16_REGNUM (tdep)
+ && regnum < I387_APX_END_REGNUM (tdep))
+ regclass = apx;
else if (regnum >= I387_PKRU_REGNUM (tdep)
&& regnum < I387_PKEYSEND_REGNUM (tdep))
regclass = pkeys;
@@ -1101,6 +1137,13 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
case none:
break;
+ case apx:
+ if ((clear_bv & X86_XSTATE_APX_F))
+ regcache->raw_supply_zeroed (regnum);
+ else
+ regcache->raw_supply (regnum, XSAVE_APX_EGPR_ADDR (tdep, regs, regnum));
+ return;
+
case pkeys:
if ((clear_bv & X86_XSTATE_PKRU))
regcache->raw_supply_zeroed (regnum);
@@ -1276,6 +1319,25 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
}
}
+ /* Handle the APX register. */
+ if ((tdep->xcr0 & X86_XSTATE_APX_F) != 0)
+ {
+ if ((clear_bv & X86_XSTATE_APX_F) != 0)
+ {
+ for (i = I387_R16_REGNUM (tdep);
+ i < I387_APX_END_REGNUM (tdep);
+ i++)
+ regcache->raw_supply_zeroed (i);
+ }
+ else
+ {
+ for (i = I387_R16_REGNUM (tdep);
+ i < I387_APX_END_REGNUM (tdep);
+ i++)
+ regcache->raw_supply (i, XSAVE_APX_EGPR_ADDR (tdep, regs, i));
+ }
+ }
+
/* Handle the XMM registers. */
if ((tdep->xcr0 & X86_XSTATE_SSE))
{
@@ -1434,8 +1496,9 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
avx512_ymmh_avx512 = 0x80,
avx512_xmm_avx512 = 0x100,
pkeys = 0x200,
+ apx = 0x400,
all = x87 | sse | avxh | avx512_k | avx512_zmm0_h | avx512_zmm16_h
- | avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys
+ | avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys | apx
} regclass;
gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
@@ -1443,6 +1506,9 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
if (regnum == -1)
regclass = all;
+ else if (regnum >= I387_R16_REGNUM (tdep)
+ && regnum < I387_APX_END_REGNUM (tdep))
+ regclass = apx;
else if (regnum >= I387_PKRU_REGNUM (tdep)
&& regnum < I387_PKEYSEND_REGNUM (tdep))
regclass = pkeys;
@@ -1507,6 +1573,14 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
seem justified at this point. */
if (clear_bv)
{
+ if ((clear_bv & X86_XSTATE_APX_F))
+ {
+ for (i = I387_R16_REGNUM (tdep);
+ i < I387_APX_END_REGNUM (tdep);
+ i++)
+ memset (XSAVE_APX_EGPR_ADDR (tdep, regs, i), 0, 8);
+ }
+
if ((clear_bv & X86_XSTATE_PKRU))
for (i = I387_PKRU_REGNUM (tdep);
i < I387_PKEYSEND_REGNUM (tdep); i++)
@@ -1573,6 +1647,23 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
if (regclass == all)
{
+ /* Check if any APX registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_APX_F))
+ {
+ for (i = I387_R16_REGNUM (tdep);
+ i < I387_APX_END_REGNUM (tdep);
+ i++)
+ {
+ regcache->raw_collect (i, raw);
+ p = XSAVE_APX_EGPR_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 8) != 0)
+ {
+ xstate_bv |= X86_XSTATE_APX_F;
+ memcpy (p, raw, 8);
+ }
+ }
+ }
+
/* Check if any PKEYS registers are changed. */
if ((tdep->xcr0 & X86_XSTATE_PKRU))
for (i = I387_PKRU_REGNUM (tdep);
@@ -1725,6 +1816,16 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
default:
internal_error (_("invalid i387 regclass"));
+ case apx:
+ regcache->raw_collect (regnum, raw);
+ p = XSAVE_APX_EGPR_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 8))
+ {
+ xstate_bv |= X86_XSTATE_APX_F;
+ memcpy (p, raw, 8);
+ }
+ break;
+
case pkeys:
/* This is a PKEYS register. */
p = XSAVE_PKEYS_ADDR (tdep, regs, regnum);
diff --git a/gdb/i387-tdep.h b/gdb/i387-tdep.h
index 5d063502bce..f029c1e5119 100644
--- a/gdb/i387-tdep.h
+++ b/gdb/i387-tdep.h
@@ -37,6 +37,9 @@ struct x86_xsave_layout;
#define I387_NUM_YMM_REGS(tdep) ((tdep)->num_ymm_regs)
#define I387_YMM0H_REGNUM(tdep) ((tdep)->ymm0h_regnum)
+#define I387_R16_REGNUM(tdep) ((tdep)->r16_regnum)
+#define I387_NUM_APX_REGS(tdep) ((tdep)->num_apx_regs)
+
/* Set of constants used for 32 and 64-bit. */
#define I387_NUM_K_REGS 8
#define I387_NUM_PKEYS_REGS 1
@@ -75,6 +78,9 @@ struct x86_xsave_layout;
#define I387_XMM_AVX512_END_REGNUM(tdep) \
(I387_XMM16_REGNUM (tdep) + I387_NUM_XMM_AVX512_REGS (tdep))
+#define I387_APX_END_REGNUM(tdep) \
+ (I387_R16_REGNUM (tdep) + I387_NUM_APX_REGS (tdep))
+
#define I387_PKEYSEND_REGNUM(tdep) \
(I387_PKRU_REGNUM (tdep) + I387_NUM_PKEYS_REGS)
diff --git a/gdb/nat/x86-xstate.c b/gdb/nat/x86-xstate.c
index c45570179a6..f0f0b00e859 100644
--- a/gdb/nat/x86-xstate.c
+++ b/gdb/nat/x86-xstate.c
@@ -56,6 +56,7 @@ x86_fetch_xsave_layout (uint64_t xcr0, int len)
x86_xsave_layout layout;
layout.sizeof_xsave = len;
layout.avx_offset = xsave_feature_offset (xcr0, X86_XSTATE_AVX_ID);
+ layout.apx_offset = xsave_feature_offset (xcr0, X86_XSTATE_APX_F_ID);
layout.k_offset = xsave_feature_offset (xcr0, X86_XSTATE_K_ID);
layout.zmm_h_offset = xsave_feature_offset (xcr0, X86_XSTATE_ZMM_H_ID);
layout.zmm_offset = xsave_feature_offset (xcr0, X86_XSTATE_ZMM_ID);
diff --git a/gdb/target-debug.h b/gdb/target-debug.h
index c86affbc838..949847b181c 100644
--- a/gdb/target-debug.h
+++ b/gdb/target-debug.h
@@ -380,6 +380,7 @@ target_debug_print_x86_xsave_layout (const x86_xsave_layout &layout)
POFFS(zmm_h);
POFFS(zmm);
POFFS(pkru);
+ POFFS(apx);
#undef POFFS
diff --git a/gdb/testsuite/gdb.arch/amd64-apx-core.c b/gdb/testsuite/gdb.arch/amd64-apx-core.c
new file mode 100644
index 00000000000..34f4dac8851
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-apx-core.c
@@ -0,0 +1,88 @@
+/* Copyright 2024-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/>.
+
+ Test program for APX Extended GPRs (EGPRs). */
+
+#include <stdlib.h>
+
+volatile int should_dump_core_p = 1;
+
+long data[] = {
+ 0x0000000004030201,
+ 0x0000000014131211,
+ 0x0000000024232221,
+ 0x0000000034333231,
+ 0x0000000044434241,
+ 0x0000000054535251,
+ 0x0000000064636261,
+ 0x0000000074737271,
+ 0x0000000084838281,
+ 0x0000000094939291,
+ 0x00000000a4a3a2a1,
+ 0x00000000b4b3b2b1,
+ 0x00000000c4c3c2c1,
+ 0x00000000d4d3d2d1,
+ 0x00000000e4e3e2e1,
+ 0x00000000f4f3f2f1,
+};
+
+int
+main (int argc, char **argv)
+{
+ register long r16 asm ("r16");
+ register long r17 asm ("r17");
+ register long r18 asm ("r18");
+ register long r19 asm ("r19");
+ register long r20 asm ("r20");
+ register long r21 asm ("r21");
+ register long r22 asm ("r22");
+ register long r23 asm ("r23");
+ register long r24 asm ("r24");
+ register long r25 asm ("r25");
+ register long r26 asm ("r26");
+ register long r27 asm ("r27");
+ register long r28 asm ("r28");
+ register long r29 asm ("r29");
+ register long r30 asm ("r30");
+ register long r31 asm ("r31");
+
+ asm ("mov 0(%0), %%r16\n\t"
+ "mov 8(%0), %%r17\n\t"
+ "mov 16(%0), %%r18\n\t"
+ "mov 24(%0), %%r19\n\t"
+ "mov 32(%0), %%r20\n\t"
+ "mov 40(%0), %%r21\n\t"
+ "mov 48(%0), %%r22\n\t"
+ "mov 56(%0), %%r23\n\t"
+ "mov 64(%0), %%r24\n\t"
+ "mov 72(%0), %%r25\n\t"
+ "mov 80(%0), %%r26\n\t"
+ "mov 88(%0), %%r27\n\t"
+ "mov 96(%0), %%r28\n\t"
+ "mov 104(%0), %%r29\n\t"
+ "mov 112(%0), %%r30\n\t"
+ "mov 120(%0), %%r31\n\t"
+ : /* no output operands */
+ : "r" (data)
+ : "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31");
+
+ if (should_dump_core_p) /* break here for gcore, or crash for OS corefile. */
+ abort ();
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-apx-corefile.c b/gdb/testsuite/gdb.arch/amd64-apx-corefile.c
new file mode 100644
index 00000000000..90c90e27992
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-apx-corefile.c
@@ -0,0 +1,71 @@
+/* Copyright 2024-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/>.
+
+ Test program for Intel APX Extended GPRs (EGPRs) in corefiles. */
+
+#include <stdlib.h>
+
+volatile int should_dump_core_p = 1;
+
+long data[] = {
+ 0x0000000004030201,
+ 0x0000000014131211,
+ 0x0000000024232221,
+ 0x0000000034333231,
+ 0x0000000044434241,
+ 0x0000000054535251,
+ 0x0000000064636261,
+ 0x0000000074737271,
+ 0x0000000084838281,
+ 0x0000000094939291,
+ 0x00000000a4a3a2a1,
+ 0x00000000b4b3b2b1,
+ 0x00000000c4c3c2c1,
+ 0x00000000d4d3d2d1,
+ 0x00000000e4e3e2e1,
+ 0x00000000f4f3f2f1,
+};
+
+int
+main (int argc, char **argv)
+{
+ asm ("mov 0(%0), %%r16\n\t"
+ "mov 8(%0), %%r17\n\t"
+ "mov 16(%0), %%r18\n\t"
+ "mov 24(%0), %%r19\n\t"
+ "mov 32(%0), %%r20\n\t"
+ "mov 40(%0), %%r21\n\t"
+ "mov 48(%0), %%r22\n\t"
+ "mov 56(%0), %%r23\n\t"
+ "mov 64(%0), %%r24\n\t"
+ "mov 72(%0), %%r25\n\t"
+ "mov 80(%0), %%r26\n\t"
+ "mov 88(%0), %%r27\n\t"
+ "mov 96(%0), %%r28\n\t"
+ "mov 104(%0), %%r29\n\t"
+ "mov 112(%0), %%r30\n\t"
+ "mov 120(%0), %%r31\n\t"
+ : /* no output operands */
+ : "r" (data)
+ : "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31");
+
+ if (should_dump_core_p) /* break here for gcore, or crash for OS corefile. */
+ abort ();
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-apx-corefile.exp b/gdb/testsuite/gdb.arch/amd64-apx-corefile.exp
new file mode 100644
index 00000000000..ed1b4bc142b
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-apx-corefile.exp
@@ -0,0 +1,67 @@
+# Copyright 2024-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/>.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@gnu.org
+
+# Test Intel APX corefiles.
+
+require allow_apx_tests
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ {debug additional_flags=-mapxf}] } {
+ return
+}
+
+proc test_apx_corefiles {core_filename} {
+ # Restart gdb and load the corefile.
+ clean_restart $::testfile
+
+ gdb_test "core $core_filename" "Core was generated by .*" \
+ "load corefile"
+
+ for { set r 16 } { $r <= 31 } { incr r } {
+ set hexr [format %x [expr {$r-16}]]
+ gdb_test "print/z \$r$r" \
+ "= 0x00000000${hexr}4${hexr}3${hexr}2${hexr}1"
+ }
+}
+
+with_test_prefix "gcore corefile" {
+ if { ![runto_main] } {
+ return
+ }
+ gdb_test_no_output "set should_dump_core_p=0"
+
+ set line [gdb_get_line_number "break here"]
+ gdb_breakpoint $line
+ gdb_continue_to_breakpoint "break here" ".*$srcfile:$line.*"
+
+ set gcorefile $binfile.gcore
+ if { [gdb_gcore_cmd $gcorefile "save a corefile"] } {
+ test_apx_corefiles $gcorefile
+ }
+}
+
+with_test_prefix "OS generated corefile" {
+ set corefile [core_find $binfile]
+ if { $corefile eq "" } {
+ unsupported "unable to generate core file"
+ return
+ }
+ test_apx_corefiles $corefile
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-apx.c b/gdb/testsuite/gdb.arch/amd64-apx.c
new file mode 100644
index 00000000000..c9539d16cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-apx.c
@@ -0,0 +1,66 @@
+/* Copyright 2024-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/>.
+
+ Test program for Intel APX Extended GPRs (EGPRs). */
+
+long data[] = {
+ 0x0000000004030201,
+ 0x0000000014131211,
+ 0x0000000024232221,
+ 0x0000000034333231,
+ 0x0000000044434241,
+ 0x0000000054535251,
+ 0x0000000064636261,
+ 0x0000000074737271,
+ 0x0000000084838281,
+ 0x0000000094939291,
+ 0x00000000a4a3a2a1,
+ 0x00000000b4b3b2b1,
+ 0x00000000c4c3c2c1,
+ 0x00000000d4d3d2d1,
+ 0x00000000e4e3e2e1,
+ 0x00000000f4f3f2f1,
+};
+
+int
+main (int argc, char **argv)
+{
+ asm ("mov 0(%0), %%r16\n\t"
+ "mov 8(%0), %%r17\n\t"
+ "mov 16(%0), %%r18\n\t"
+ "mov 24(%0), %%r19\n\t"
+ "mov 32(%0), %%r20\n\t"
+ "mov 40(%0), %%r21\n\t"
+ "mov 48(%0), %%r22\n\t"
+ "mov 56(%0), %%r23\n\t"
+ "mov 64(%0), %%r24\n\t"
+ "mov 72(%0), %%r25\n\t"
+ "mov 80(%0), %%r26\n\t"
+ "mov 88(%0), %%r27\n\t"
+ "mov 96(%0), %%r28\n\t"
+ "mov 104(%0), %%r29\n\t"
+ "mov 112(%0), %%r30\n\t"
+ "mov 120(%0), %%r31\n\t"
+ : /* no output operands */
+ : "r" (data)
+ : "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31");
+
+ asm ("nop"); /* break here */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-apx.exp b/gdb/testsuite/gdb.arch/amd64-apx.exp
new file mode 100644
index 00000000000..77d831ba786
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-apx.exp
@@ -0,0 +1,77 @@
+# Copyright 2024-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/>.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@gnu.org
+
+# Test Intel APX dword, word, byte and raw registers.
+
+proc get_reg_val { type r } {
+ set hexr [format %x [expr {$r - 16}]]
+ switch $type {
+ "d" { return 0x${hexr}4${hexr}3${hexr}2${hexr}1 }
+ "w" { return 0x${hexr}2${hexr}1 }
+ "l" { return 0x${hexr}1 }
+ "" { return 0x00000000${hexr}4${hexr}3${hexr}2${hexr}1 }
+ }
+}
+
+proc check_reg_content { type } {
+ for { set r 16 } { $r <= 31 } { incr r } {
+ set regval [get_reg_val $type $r]
+ gdb_test "print/z \$r$r$type" "= $regval"
+ }
+}
+
+require allow_apx_tests
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ {debug additional_flags=-mapxf}] } {
+ return
+}
+
+if { ![runto_main] } {
+ return
+}
+
+set line [gdb_get_line_number "break here"]
+gdb_breakpoint $line
+gdb_continue_to_breakpoint "break here" ".*$srcfile:$line.*"
+
+# Run test for dword, word, byte pseudo registers, as well as raw registers.
+set regtypes [list "d" "w" "l" ""]
+set regtype_names [list "dword" "word" "byte" "raw"]
+
+# Check initial register content written by testprogram.
+foreach type $regtypes type_name $regtype_names {
+ with_test_prefix "$type_name register" {
+ check_reg_content $type
+ }
+}
+
+# Check writing registers with GDB.
+for { set i 0 } { $i < [llength $regtypes] } { incr i } {
+ set type [lindex $regtypes $i]
+ set type_name [lindex $regtype_names $i]
+ with_test_prefix "$type_name register" {
+ for { set r 16 } { $r <= 31 } { incr r } {
+ set val [expr {($r - 16) + $i}]
+ gdb_test_no_output "set var \$r$r$type = $val"
+ gdb_test "print \$r$r$type" "= $val"
+ }
+ }
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 41e9a8c721e..2825b1a71f7 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -4765,6 +4765,64 @@ gdb_caching_proc allow_lam_tests {} {
return $allow_lam_tests
}
+# Run a test on the target to see if it supports APX. Return 1 if so,
+# 0 if it does not.
+
+gdb_caching_proc allow_apx_tests {} {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "allow_apx_tests"
+ if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } {
+ verbose "$me: target does not support apx, returning 0" 2
+ return 0
+ }
+
+ # Compile a test program.
+ set src {
+ int data[] = {12345};
+
+ int
+ main (int argc, char **argv)
+ {
+ asm ("mov 0(%0), %%r30\n\t"
+ : /* no output operands */
+ : "r" (data));
+ return 0;
+ }
+ }
+ set compile_flags "{additional_flags=-mapxf}"
+ if { ![gdb_simple_compile $me $src executable $compile_flags] } {
+ return 0
+ }
+
+ # No error message, compilation succeeded so now run it via gdb.
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load "$obj"
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*Illegal instruction.*${gdb_prompt} $" {
+ verbose -log "$me: apx hardware not detected."
+ set allow_apx_tests 0
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "$me: apx hardware detected."
+ set allow_apx_tests 1
+ }
+ default {
+ warning "\n$me: default case taken."
+ set allow_apx_tests 0
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $allow_apx_tests" 2
+ return $allow_apx_tests
+}
+
# Run a test on the target to see if it supports btrace hardware. Return 1 if so,
# 0 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite.
diff --git a/gdbserver/i387-fp.cc b/gdbserver/i387-fp.cc
index 164b666979f..90f146a74d2 100644
--- a/gdbserver/i387-fp.cc
+++ b/gdbserver/i387-fp.cc
@@ -23,6 +23,7 @@
/* Default to SSE. */
static uint64_t x86_xstate_bv = X86_XSTATE_SSE_MASK;
+static const int num_apx_registers = 16;
static const int num_avx512_k_registers = 8;
static const int num_pkeys_registers = 1;
@@ -116,6 +117,10 @@ public:
unsigned char *ymmh_space ()
{ return xsave () + xsave_layout.avx_offset; }
+ /* Memory address of 16 EGPR register values of 64 bits. */
+ unsigned char *apx_space ()
+ { return xsave () + xsave_layout.apx_offset; }
+
/* Memory address of 8 OpMask register values of 64 bits. */
unsigned char *k_space ()
{ return xsave () + xsave_layout.k_offset; }
@@ -312,6 +317,10 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
if ((clear_bv & X86_XSTATE_PKRU))
for (i = 0; i < num_pkeys_registers; i++)
memset (fp->pkru_space () + i * 4, 0, 4);
+
+ if (amd64 && (clear_bv & X86_XSTATE_APX_F) != 0)
+ for (i = 0; i < num_apx_registers; i++)
+ memset (fp->apx_space () + i * 8, 0, 8);
}
/* Check if any x87 registers are changed. */
@@ -465,6 +474,23 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}
}
+ /* Check if any APX registers are changed. */
+ if (amd64 && (x86_xstate_bv & X86_XSTATE_APX_F) != 0)
+ {
+ int r16_regnum = find_regno (regcache->tdesc, "r16");
+
+ for (i = 0; i < num_apx_registers; i++)
+ {
+ collect_register (regcache, i + r16_regnum, raw);
+ p = fp->apx_space () + i * 8;
+ if (memcmp (raw, p, 8) != 0)
+ {
+ xstate_bv |= X86_XSTATE_APX_F;
+ memcpy (p, raw, 8);
+ }
+ }
+ }
+
if (x86_xstate_bv & X86_XSTATE_X87)
{
collect_register_by_name (regcache, "fioff", raw);
@@ -790,6 +816,23 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}
+ if (amd64 && (x86_xstate_bv & X86_XSTATE_APX_F) != 0)
+ {
+ int r16_regnum = find_regno (regcache->tdesc, "r16");
+
+ if ((clear_bv & X86_XSTATE_APX_F) != 0)
+ {
+ for (i = 0; i < num_apx_registers; i++)
+ supply_register_zeroed (regcache, i + r16_regnum);
+ }
+ else
+ {
+ p = fp->apx_space ();
+ for (i = 0; i < num_apx_registers; i++)
+ supply_register (regcache, i + r16_regnum, p + i * 8);
+ }
+ }
+
if ((clear_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX))
== (X86_XSTATE_SSE | X86_XSTATE_AVX))
{
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 906191e04f4..f893821fa48 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -260,7 +260,9 @@ static const int x86_64_regmap[] =
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, /* pkru */
- -1 /* CET user mode register PL3_SSP. */
+ -1, /* CET user mode register PL3_SSP. */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* r16...r31 (APX). */
+ -1, -1, -1, -1, -1, -1, -1, -1,
};
#define X86_64_NUM_REGS (sizeof (x86_64_regmap) / sizeof (x86_64_regmap[0]))
diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
index ee8660f8cb4..3602fa40cd9 100644
--- a/gdbsupport/x86-xstate.h
+++ b/gdbsupport/x86-xstate.h
@@ -29,11 +29,13 @@
#define X86_XSTATE_ZMM_ID 7
#define X86_XSTATE_PKRU_ID 9
#define X86_XSTATE_CET_U_ID 11
+#define X86_XSTATE_APX_F_ID 19
/* The extended state feature bits. */
#define X86_XSTATE_X87 (1ULL << X86_XSTATE_X87_ID)
#define X86_XSTATE_SSE (1ULL << X86_XSTATE_SSE_ID)
#define X86_XSTATE_AVX (1ULL << X86_XSTATE_AVX_ID)
+#define X86_XSTATE_APX_F (1ULL << X86_XSTATE_APX_F_ID)
/* AVX 512 adds three feature bits. All three must be enabled. */
#define X86_XSTATE_K (1ULL << X86_XSTATE_K_ID)
@@ -53,6 +55,7 @@ struct x86_xsave_layout
{
int sizeof_xsave = 0;
int avx_offset = 0;
+ int apx_offset = 0;
int k_offset = 0;
int zmm_h_offset = 0;
int zmm_offset = 0;
@@ -64,6 +67,7 @@ constexpr bool operator== (const x86_xsave_layout &lhs,
{
return lhs.sizeof_xsave == rhs.sizeof_xsave
&& lhs.avx_offset == rhs.avx_offset
+ && lhs.apx_offset == rhs.apx_offset
&& lhs.k_offset == rhs.k_offset
&& lhs.zmm_h_offset == rhs.zmm_h_offset
&& lhs.zmm_offset == rhs.zmm_offset
@@ -81,14 +85,19 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
#define X86_XSTATE_X87_MASK X86_XSTATE_X87
#define X86_XSTATE_SSE_MASK (X86_XSTATE_X87 | X86_XSTATE_SSE)
#define X86_XSTATE_AVX_MASK (X86_XSTATE_SSE_MASK | X86_XSTATE_AVX)
+#define X86_XSTATE_AVX_APX_MASK (X86_XSTATE_AVX_MASK | X86_XSTATE_APX_F)
#define X86_XSTATE_AVX_AVX512_MASK (X86_XSTATE_AVX_MASK | X86_XSTATE_AVX512)
+#define X86_XSTATE_AVX_AVX512_APX_MASK\
+ (X86_XSTATE_AVX_AVX512_MASK | X86_XSTATE_APX_F)
#define X86_XSTATE_AVX_AVX512_PKU_MASK (X86_XSTATE_AVX_MASK\
| X86_XSTATE_AVX512 | X86_XSTATE_PKRU)
+#define X86_XSTATE_AVX_AVX512_PKU_APX_MASK\
+ (X86_XSTATE_AVX_AVX512_PKU_MASK | X86_XSTATE_APX_F)
/* Supported mask of state-component bitmap xstate_bv. The SDM defines
xstate_bv as XCR0 | IA32_XSS. */
-#define X86_XSTATE_ALL_MASK (X86_XSTATE_AVX_AVX512_PKU_MASK\
+#define X86_XSTATE_ALL_MASK (X86_XSTATE_AVX_AVX512_PKU_APX_MASK\
| X86_XSTATE_CET_U)
#define X86_XSTATE_SSE_SIZE 576
@@ -97,6 +106,7 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
#define HAS_AVX(XCR0) (((XCR0) & X86_XSTATE_AVX) != 0)
#define HAS_AVX512(XCR0) (((XCR0) & X86_XSTATE_AVX512) != 0)
+#define HAS_APX(XCR0) (((XCR0) & X86_XSTATE_APX_F) != 0)
#define HAS_PKRU(XCR0) (((XCR0) & X86_XSTATE_PKRU) != 0)
/* Initial value for fctrl register, as defined in the X86 manual, and
--
2.34.1
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 3+ messages in thread