Compare commits

...

9 Commits

Author SHA1 Message Date
Thiago Jung Bauermann
53900cfba3 GDB: doc: Document Linux AArch64 support for Guarded Control Stacks
Add NEWS entry and new sections to the "Configuration-Specific Information"
and "Standard Target Features" parts of the manual.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2025-06-21 21:25:18 -03:00
Thiago Jung Bauermann
68edc22bc7 GDB: aarch64-linux: Support unwinding the GCSPR
The GCSPR is almost always updated implicitly by the hardware, so the
compiler doesn't generate DWARF unwind information for it.  Therefore add
an unwinding function that calculates the value of the GCSPR in the
previous frame based on its value in this frame.  Some sanity checking is
done by confirming that the calculated value is within a Guarded Control
Stack memory area.

This function is the same as amd64_linux_dwarf2_prev_ssp, written by
Christina Schimpe to unwind Intel's SSP register.

The gdb.arch/aarch64-gcs-return.exp testcase is lightly adapted from
gdb.arch/amd64-shadow-stack-cmds.exp.

Approved-By: Luis Machado <luis.machado@arm.com>
2025-06-21 21:25:18 -03:00
Thiago Jung Bauermann
b3ab551f42 GDB: aarch64-linux: Implement GCS support in displaced stepping
When doing displaced step on a branch and link instruction with the Guarded
Control Stack enabled, it's necessary to manually push and pop the GCS
entry for the function call since GDB writes a simple branch instruction
rather than a branch and link instruction in the displaced step buffer.

The testcase exercises GCS with displaced stepping by putting the
breakpoint on the bl instruction to force GDB to copy it to the
displaced stepping buffer.  In this situation GDB needs to manually
manage the Guarded Control Stack.

Approved-By: Luis Machado <luis.machado@arm.com>
2025-06-21 21:25:18 -03:00
Thiago Jung Bauermann
2c04004cfa GDB: aarch64-linux: GCS support in Linux signals
The signal frame can have a GCS context, so teach GDB how to use it.

Also, there's a new SEGV sigcode when the inferior does an illegal
memory access in the Guarded Control Stack, so display a message when
that is the case.

Approved-By: Luis Machado <luis.machado@arm.com>
2025-06-21 21:25:18 -03:00
Thiago Jung Bauermann
9e4e5ac9dd GDB, gdbserver: aarch64-linux: Initial Guarded Control Stack support
Add the org.gnu.gdb.aarch64.gcs feature with the GCSPR register, and the
org.gnu.gdb.aarch64.gcs.linux feature with "registers" to represent the
Linux kernel ptrace and prctl knobs that enable and lock specific GCS
functionality.

This code supports GCS only in Linux userspace applications, so the
GCSPR that is exposed is the one at EL0.

Also, support for calling inferior functions is enabled by adding an
implementation for the shadow_stack_push gdbarch method.

If for some reason a target description contains the
org.gnu.gdb.aarch64.gcs feature but not the
org.gnu.gdb.aarch64.gcs.linux feature then GCS support is disabled and
GDB continues the debugging session.  Features that need GCS
support (for example, calling inferior functions) will not work and the
inferior will get a segmentation fault signal instead.  There's a
testcase for this scenario but it only checks the native debugging case,
even though in practice this problem would only occur in remote
debugging with a broken stub or gdbserver.  I tested manually with a
gdbserver hacked to send a broken target description and it worked as
described.

Testcases gdb.arch/aarch64-gcs.exp, gdb.arch/aarch64-gcs-core.exp and
gdb.arch/aarch64-gcs-wrong-tdesc.exp are included to cover the added
functionality.

The change in the core_find procedure allows aarch64-gcs-core.exp to
recover the output from the crashed program.  It's needed because the
test program in this testcase prints to stdout the value of the GCSPR
right before crashing, and the testcase needs it to check whether GDB
got it right.
2025-06-21 21:25:02 -03:00
Thiago Jung Bauermann
2dae3f2531 bfd/aarch64-linux: Support reading and writing the GCS core file note
Reviewed-By: Luis Machado <luis.machado@arm.com>
2025-06-18 00:21:21 -03:00
Christina Schimpe
10c6eff194 gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer.
This patch is required by the following commit.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
2025-06-18 00:21:21 -03:00
Christina Schimpe
51e6c57e8e gdb, gdbarch: Enable inferior calls for shadow stack support.
Inferior calls in GDB reset the current PC to the beginning of the function
that is called.  As no call instruction is executed the new return address
needs to be pushed to the shadow stack and the shadow stack pointer needs
to be updated.

This commit adds a new gdbarch method to push an address on the shadow
stack.  The method is used to adapt the function 'call_function_by_hand_dummy'
for inferior call shadow stack support.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
2025-06-18 00:21:20 -03:00
Christina Schimpe
5cc9cddc30 GDB: Linux: Add function linux_address_in_shadow_stack_mem_range
The function comes from the following patch from the Intel CET shadow
stack support series:

[PATCH v4 07/11] gdb: Handle shadow stack pointer register unwinding for amd64 linux.

AArch64 also needs the function for unwinding the GCSPR, so include it
in this patch series.

Abridged-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
2025-06-18 00:20:45 -03:00
37 changed files with 2082 additions and 21 deletions

View File

@@ -10661,6 +10661,15 @@ elfcore_grok_aarch_zt (bfd *abfd, Elf_Internal_Note *note)
return elfcore_make_note_pseudosection (abfd, ".reg-aarch-zt", note);
}
/* Convert NOTE into a bfd_section called ".reg-aarch-gcs". Return TRUE if
successful, otherwise return FALSE. */
static bool
elfcore_grok_aarch_gcs (bfd *abfd, Elf_Internal_Note *note)
{
return elfcore_make_note_pseudosection (abfd, ".reg-aarch-gcs", note);
}
static bool
elfcore_grok_arc_v2 (bfd *abfd, Elf_Internal_Note *note)
{
@@ -11404,6 +11413,12 @@ elfcore_grok_note (bfd *abfd, Elf_Internal_Note *note)
else
return true;
case NT_ARM_GCS:
if (note->namesz == 6 && strcmp (note->namedata, "LINUX") == 0)
return elfcore_grok_aarch_gcs (abfd, note);
else
return true;
case NT_GDB_TDESC:
if (note->namesz == 4
&& strcmp (note->namedata, "GDB") == 0)
@@ -13074,6 +13089,20 @@ elfcore_write_aarch_zt (bfd *abfd,
size);
}
/* Write the buffer of GCS register values in AARCH_GCS (length SIZE) into
the note buffer BUF and update *BUFSIZ. ABFD is the bfd the note is being
written into. Return a pointer to the new start of the note buffer, to
replace BUF which may no longer be valid. */
static char *
elfcore_write_aarch_gcs (bfd *abfd, char *buf, int *bufsiz,
const void *aarch_gcs, int size)
{
const char *note_name = "LINUX";
return elfcore_write_note (abfd, buf, bufsiz, note_name, NT_ARM_GCS,
aarch_gcs, size);
}
char *
elfcore_write_arc_v2 (bfd *abfd,
char *buf,
@@ -13263,6 +13292,8 @@ elfcore_write_register_note (bfd *abfd,
return elfcore_write_aarch_za (abfd, buf, bufsiz, data, size);
if (strcmp (section, ".reg-aarch-zt") == 0)
return elfcore_write_aarch_zt (abfd, buf, bufsiz, data, size);
if (strcmp (section, ".reg-aarch-gcs") == 0)
return elfcore_write_aarch_gcs (abfd, buf, bufsiz, data, size);
if (strcmp (section, ".reg-arc-v2") == 0)
return elfcore_write_arc_v2 (abfd, buf, bufsiz, data, size);
if (strcmp (section, ".gdb-tdesc") == 0)

View File

@@ -48,6 +48,9 @@
* Add record full support for rv64gc architectures
* Debugging Linux programs that use AArch64 Guarded Control Stacks is now
supported.
* New commands
maintenance check psymtabs

View File

@@ -51,6 +51,7 @@
#include "gdb_proc_service.h"
#include "arch-utils.h"
#include "arch/aarch64-gcs-linux.h"
#include "arch/aarch64-mte-linux.h"
#include "nat/aarch64-mte-linux-ptrace.h"
@@ -542,6 +543,67 @@ store_tlsregs_to_thread (struct regcache *regcache)
perror_with_name (_("unable to store TLS register"));
}
/* Fill GDB's register array with the GCS register values from
the current thread. */
static void
fetch_gcsregs_from_thread (regcache *regcache)
{
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
gdb_assert (tdep->gcs_reg_base != -1);
gdb_assert (tdep->gcs_linux_reg_base != -1);
user_gcs user_gcs;
iovec iovec;
iovec.iov_base = &user_gcs;
iovec.iov_len = sizeof (user_gcs);
int tid = get_ptrace_pid (regcache->ptid ());
if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_GCS, &iovec) != 0)
perror_with_name (_("unable to fetch GCS registers"));
regcache->raw_supply (tdep->gcs_reg_base, &user_gcs.gcspr_el0);
regcache->raw_supply (tdep->gcs_linux_reg_base, &user_gcs.features_enabled);
regcache->raw_supply (tdep->gcs_linux_reg_base + 1,
&user_gcs.features_locked);
}
/* Store to the current thread the valid GCS register set in the GDB's
register array. */
static void
store_gcsregs_to_thread (regcache *regcache)
{
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
gdb_assert (tdep->gcs_reg_base != -1);
gdb_assert (tdep->gcs_linux_reg_base != -1);
if (REG_VALID != regcache->get_register_status (tdep->gcs_reg_base)
|| REG_VALID != regcache->get_register_status (tdep->gcs_linux_reg_base)
|| REG_VALID
!= regcache->get_register_status (tdep->gcs_linux_reg_base + 1))
return;
user_gcs user_gcs;
regcache->raw_collect (tdep->gcs_reg_base, &user_gcs.gcspr_el0);
regcache->raw_collect (tdep->gcs_linux_reg_base, &user_gcs.features_enabled);
regcache->raw_collect (tdep->gcs_linux_reg_base + 1,
&user_gcs.features_locked);
iovec iovec;
iovec.iov_base = &user_gcs;
iovec.iov_len = sizeof (user_gcs);
int tid = get_ptrace_pid (regcache->ptid ());
if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_GCS, &iovec) != 0)
perror_with_name (_("unable to store GCS registers"));
}
/* The AArch64 version of the "fetch_registers" target_ops method. Fetch
REGNO from the target and place the result into REGCACHE. */
@@ -577,6 +639,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
if (tdep->has_sme2 ())
fetch_zt_from_thread (regcache);
if (tdep->has_gcs_linux ())
fetch_gcsregs_from_thread (regcache);
}
/* General purpose register? */
else if (regno < AARCH64_V0_REGNUM)
@@ -609,6 +674,11 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
&& regno >= tdep->tls_regnum_base
&& regno < tdep->tls_regnum_base + tdep->tls_register_count)
fetch_tlsregs_from_thread (regcache);
/* GCS register? */
else if (tdep->has_gcs_linux ()
&& (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base
|| regno == tdep->gcs_linux_reg_base + 1))
fetch_gcsregs_from_thread (regcache);
}
/* A version of the "fetch_registers" target_ops method used when running
@@ -680,6 +750,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
if (tdep->has_sme2 ())
store_zt_to_thread (regcache);
if (tdep->has_gcs_linux ())
store_gcsregs_to_thread (regcache);
}
/* General purpose register? */
else if (regno < AARCH64_V0_REGNUM)
@@ -706,6 +779,11 @@ aarch64_store_registers (struct regcache *regcache, int regno)
&& regno >= tdep->tls_regnum_base
&& regno < tdep->tls_regnum_base + tdep->tls_register_count)
store_tlsregs_to_thread (regcache);
/* GCS register? */
else if (tdep->has_gcs_linux ()
&& (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base
|| regno == tdep->gcs_linux_reg_base + 1))
store_gcsregs_to_thread (regcache);
/* PAuth registers are read-only. */
}
@@ -881,6 +959,7 @@ aarch64_linux_nat_target::read_description ()
active or not. */
features.vq = aarch64_sve_get_vq (tid);
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.gcs = features.gcs_linux = hwcap & HWCAP_GCS;
features.mte = hwcap2 & HWCAP2_MTE;
features.tls = aarch64_tls_register_count (tid);
/* SME feature check. */

View File

@@ -32,6 +32,7 @@
#include "symtab.h"
#include "tramp-frame.h"
#include "trad-frame.h"
#include "dwarf2/frame.h"
#include "target.h"
#include "target/target.h"
#include "expop.h"
@@ -50,6 +51,7 @@
#include "record-full.h"
#include "linux-record.h"
#include "arch/aarch64-gcs-linux.h"
#include "arch/aarch64-mte.h"
#include "arch/aarch64-mte-linux.h"
#include "arch/aarch64-scalable-linux.h"
@@ -164,6 +166,7 @@
#define AARCH64_ZA_MAGIC 0x54366345
#define AARCH64_TPIDR2_MAGIC 0x54504902
#define AARCH64_ZT_MAGIC 0x5a544e01
#define AARCH64_GCS_MAGIC 0x47435300
/* Defines for the extra_context that follows an AARCH64_EXTRA_MAGIC. */
#define AARCH64_EXTRA_DATAP_OFFSET 8
@@ -205,6 +208,11 @@
the signal context state. */
#define AARCH64_SME2_CONTEXT_REGS_OFFSET 16
/* GCSPR register value offset in the GCS signal frame context. */
#define AARCH64_GCS_CONTEXT_GCSPR_OFFSET 8
/* features_enabled value offset in the GCS signal frame context. */
#define AARCH64_GCS_CONTEXT_FEATURES_ENABLED_OFFSET 16
/* Holds information about the signal frame. */
struct aarch64_linux_sigframe
{
@@ -245,6 +253,13 @@ struct aarch64_linux_sigframe
bool za_payload = false;
/* True if we have a ZT entry in the signal context, false otherwise. */
bool zt_available = false;
/* True if we have a GCS entry in the signal context, false otherwise. */
bool gcs_availabe = false;
/* The Guarded Control Stack Pointer Register. */
uint64_t gcspr;
/* Flags indicating which GCS features are enabled for the thread. */
uint64_t gcs_features_enabled;
};
/* Read an aarch64_ctx, returning the magic value, and setting *SIZE to the
@@ -525,6 +540,39 @@ aarch64_linux_read_signal_frame_info (const frame_info_ptr &this_frame,
signal_frame.zt_section = section;
signal_frame.zt_available = true;
section += size;
break;
}
case AARCH64_GCS_MAGIC:
{
gdb_byte buf[8];
/* Extract the GCSPR. */
if (target_read_memory (section + AARCH64_GCS_CONTEXT_GCSPR_OFFSET,
buf, 8) != 0)
{
warning (_("Failed to read the GCS pointer from the GCS signal"
" frame context."));
section += size;
break;
}
signal_frame.gcspr = extract_unsigned_integer (buf, byte_order);
/* Extract the features_enabled field. */
if (target_read_memory (section
+ AARCH64_GCS_CONTEXT_FEATURES_ENABLED_OFFSET,
buf, sizeof (buf)) != 0)
{
warning (_("Failed to read the enabled features from the GCS"
" signal frame context."));
section += size;
break;
}
signal_frame.gcs_features_enabled
= extract_unsigned_integer (buf, byte_order);
signal_frame.gcs_availabe = true;
section += size;
break;
}
@@ -702,6 +750,19 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
+ AARCH64_TPIDR2_CONTEXT_TPIDR2_OFFSET);
}
/* Restore the GCS registers, if the target supports it and if there is
an entry for them. */
if (signal_frame.gcs_availabe && tdep->has_gcs_linux ())
{
/* Restore GCSPR. */
trad_frame_set_reg_value (this_cache, tdep->gcs_reg_base,
signal_frame.gcspr);
/* Restore gcs_features_enabled. */
trad_frame_set_reg_value (this_cache, tdep->gcs_linux_reg_base,
signal_frame.gcs_features_enabled);
/* gcs_features_locked isn't present in the GCS signal context. */
}
trad_frame_set_id (this_cache, frame_id_build (signal_frame.sp, func));
}
@@ -1604,6 +1665,27 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
cb (".reg-aarch-tls", sizeof_tls_regset, sizeof_tls_regset,
&aarch64_linux_tls_regset, "TLS register", cb_data);
}
/* Handle GCS registers. */
if (tdep->has_gcs_linux ())
{
/* Create this on the fly in order to handle the variable regnums. */
const regcache_map_entry gcs_regmap[] =
{
{ 1, tdep->gcs_linux_reg_base, 8 }, /* features_enabled */
{ 1, tdep->gcs_linux_reg_base + 1, 8 }, /* features_locked */
{ 1, tdep->gcs_reg_base, 8 }, /* GCSPR */
{ 0 }
};
const regset aarch64_linux_gcs_regset =
{
gcs_regmap, regcache_supply_regset, regcache_collect_regset
};
cb (".reg-aarch-gcs", sizeof (user_gcs), sizeof (user_gcs),
&aarch64_linux_gcs_regset, "GCS registers", cb_data);
}
}
/* Implement the "core_read_description" gdbarch method. */
@@ -1628,6 +1710,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
length. */
features.vq = aarch64_linux_core_read_vq_from_sections (gdbarch, abfd);
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.gcs = features.gcs_linux = hwcap & HWCAP_GCS;
features.mte = hwcap2 & HWCAP2_MTE;
/* Handle the TLS section. */
@@ -2452,6 +2535,80 @@ aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, CORE_ADDR address)
return true;
}
/* Implement the "get_shadow_stack_pointer" gdbarch method. */
static std::optional<CORE_ADDR>
aarch64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
bool &shadow_stack_enabled)
{
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
shadow_stack_enabled = false;
if (!tdep->has_gcs_linux ())
return {};
uint64_t features_enabled;
enum register_status status = regcache->cooked_read (tdep->gcs_linux_reg_base,
&features_enabled);
if (status != REG_VALID)
error (_("Can't read $gcs_features_enabled."));
CORE_ADDR gcspr;
status = regcache->cooked_read (tdep->gcs_reg_base, &gcspr);
if (status != REG_VALID)
error (_("Can't read $gcspr."));
shadow_stack_enabled = features_enabled & PR_SHADOW_STACK_ENABLE;
return gcspr;
}
/* Implement Guarded Control Stack Pointer Register unwinding. For each
previous GCS pointer check if its address is still in the GCS memory
range. If it's outside the range set the returned value to unavailable,
otherwise return a value containing the new GCS pointer. */
static value *
aarch64_linux_dwarf2_prev_gcspr (const frame_info_ptr &this_frame,
void **this_cache, int regnum)
{
value *v = frame_unwind_got_register (this_frame, regnum, regnum);
gdb_assert (v != nullptr);
gdbarch *gdbarch = get_frame_arch (this_frame);
if (v->entirely_available () && !v->optimized_out ())
{
int size = register_size (gdbarch, regnum);
bfd_endian byte_order = gdbarch_byte_order (gdbarch);
CORE_ADDR gcspr = extract_unsigned_integer (v->contents_all ().data (),
size, byte_order);
/* Starting with v6.13, the Linux kernel supports Guarded Control
Stack. Using /proc/PID/smaps we can only check if the current
GCSPR points to GCS memory. Only if this is the case a valid
previous GCS pointer can be calculated. */
std::pair<CORE_ADDR, CORE_ADDR> range;
if (linux_address_in_shadow_stack_mem_range (gcspr, &range))
{
/* The GCS grows downwards. To compute the previous GCS pointer,
we need to increment the GCSPR. */
CORE_ADDR new_gcspr = gcspr + 8;
/* If NEW_GCSPR still points within the current GCS memory range
we consider it to be valid. */
if (new_gcspr < range.second)
return frame_unwind_got_address (this_frame, regnum, new_gcspr);
}
}
/* Return a value which is marked as unavailable in case we could not
calculate a valid previous GCS pointer. */
value *retval
= value::allocate_register (get_next_frame_sentinel_okay (this_frame),
regnum, register_type (gdbarch, regnum));
retval->mark_bytes_unavailable (0, retval->type ()->length ());
return retval;
}
/* AArch64 Linux implementation of the report_signal_info gdbarch
hook. Displays information about possible memory tag violations. */
@@ -2463,17 +2620,18 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
{
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
if (!tdep->has_mte () || siggnal != GDB_SIGNAL_SEGV)
if (!(tdep->has_mte () || tdep->has_gcs ()) || siggnal != GDB_SIGNAL_SEGV)
return;
CORE_ADDR fault_addr = 0;
long si_code = 0;
long si_code = 0, si_errno = 0;
try
{
/* Sigcode tells us if the segfault is actually a memory tag
violation. */
si_code = parse_and_eval_long ("$_siginfo.si_code");
si_errno = parse_and_eval_long ("$_siginfo.si_errno");
fault_addr
= parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr");
@@ -2484,13 +2642,18 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
return;
}
/* If this is not a memory tag violation, just return. */
if (si_code != SEGV_MTEAERR && si_code != SEGV_MTESERR)
const char *meaning;
if (si_code == SEGV_MTEAERR || si_code == SEGV_MTESERR)
meaning = _("Memory tag violation");
else if (si_code == SEGV_CPERR && si_errno == 0)
meaning = _("Guarded Control Stack error");
else
return;
uiout->text ("\n");
uiout->field_string ("sigcode-meaning", _("Memory tag violation"));
uiout->field_string ("sigcode-meaning", meaning);
/* For synchronous faults, show additional information. */
if (si_code == SEGV_MTESERR)
@@ -2516,7 +2679,7 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
uiout->field_string ("logical-tag", hex_string (ltag));
}
}
else
else if (si_code != SEGV_CPERR)
{
uiout->text ("\n");
uiout->text (_("Fault address unavailable"));
@@ -2765,6 +2928,13 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
NULL };
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
if (tdep->has_gcs () && !tdep->has_gcs_linux ())
{
warning (_("Incomplete GCS support in the target: missing Linux part."
" GCS feature disabled."));
tdep->gcs_reg_base = -1;
}
tdep->lowest_pc = 0x8000;
linux_init_abi (info, gdbarch, 1);
@@ -2815,9 +2985,6 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Register a hook for checking if an address is tagged or not. */
set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p);
set_gdbarch_report_signal_info (gdbarch,
aarch64_linux_report_signal_info);
/* Core file helpers. */
/* Core file helper to create a memory tag section for a particular
@@ -2834,6 +3001,9 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
aarch64_linux_decode_memtag_section);
}
if (tdep->has_mte () || tdep->has_gcs ())
set_gdbarch_report_signal_info (gdbarch, aarch64_linux_report_signal_info);
/* Initialize the aarch64_linux_record_tdep. */
/* These values are the size of the type that will be used in a system
call. They are obtained from Linux Kernel source. */
@@ -3015,6 +3185,13 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
sections. */
set_gdbarch_use_target_description_from_corefile_notes (gdbarch,
aarch64_use_target_description_from_corefile_notes);
if (tdep->has_gcs_linux ())
{
set_gdbarch_get_shadow_stack_pointer (gdbarch,
aarch64_linux_get_shadow_stack_pointer);
tdep->fn_prev_gcspr = aarch64_linux_dwarf2_prev_gcspr;
}
}
#if GDB_SELF_TEST

View File

@@ -159,6 +159,18 @@ static const char *const aarch64_mte_register_names[] =
"tag_ctl"
};
static const char *const aarch64_gcs_register_names[] = {
/* Guarded Control Stack Pointer Register. */
"gcspr"
};
static const char *const aarch64_gcs_linux_register_names[] = {
/* Field in struct user_gcs. */
"gcs_features_enabled",
/* Field in struct user_gcs. */
"gcs_features_locked",
};
static int aarch64_stack_frame_destroyed_p (struct gdbarch *, CORE_ADDR);
/* AArch64 prologue cache structure. */
@@ -1396,6 +1408,12 @@ aarch64_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
return;
}
}
if (tdep->has_gcs () && tdep->fn_prev_gcspr != nullptr
&& regnum == tdep->gcs_reg_base)
{
reg->how = DWARF2_FRAME_REG_FN;
reg->loc.fn = tdep->fn_prev_gcspr;
}
}
/* Implement the execute_dwarf_cfa_vendor_op method. */
@@ -1875,6 +1893,57 @@ pass_in_v_vfp_candidate (struct gdbarch *gdbarch, struct regcache *regcache,
}
}
/* Push LR_VALUE to the Guarded Control Stack. */
static void
aarch64_push_gcs_entry (regcache *regs, CORE_ADDR lr_value)
{
gdbarch *arch = regs->arch ();
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (arch);
CORE_ADDR gcs_addr;
enum register_status status = regs->cooked_read (tdep->gcs_reg_base,
&gcs_addr);
if (status != REG_VALID)
error (_("Can't read $gcspr."));
gcs_addr -= 8;
gdb_byte buf[8];
store_integer (buf, gdbarch_byte_order (arch), lr_value);
if (target_write_memory (gcs_addr, buf, sizeof (buf)) != 0)
error (_("Can't write to Guarded Control Stack."));
/* Update GCSPR. */
regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr);
}
/* Remove the newest entry from the Guarded Control Stack. */
static void
aarch64_pop_gcs_entry (regcache *regs)
{
gdbarch *arch = regs->arch ();
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (arch);
CORE_ADDR gcs_addr;
enum register_status status = regs->cooked_read (tdep->gcs_reg_base,
&gcs_addr);
if (status != REG_VALID)
error ("Can't read $gcspr.");
/* Update GCSPR. */
regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr + 8);
}
/* Implement the "shadow_stack_push" gdbarch method. */
static void
aarch64_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
regcache *regcache)
{
aarch64_push_gcs_entry (regcache, new_addr);
}
/* Implement the "push_dummy_call" gdbarch method. */
static CORE_ADDR
@@ -3557,6 +3626,9 @@ struct aarch64_displaced_step_copy_insn_closure
/* PC adjustment offset after displaced stepping. If 0, then we don't
write the PC back, assuming the PC is already the right address. */
int32_t pc_adjust = 0;
/* True if it's a branch instruction that saves the link register. */
bool linked_branch = false;
};
/* Data when visiting instructions for displaced stepping. */
@@ -3608,6 +3680,12 @@ aarch64_displaced_step_b (const int is_bl, const int32_t offset,
/* Update LR. */
regcache_cooked_write_unsigned (dsd->regs, AARCH64_LR_REGNUM,
data->insn_addr + 4);
dsd->dsc->linked_branch = true;
bool gcs_is_enabled;
gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs,
gcs_is_enabled);
if (gcs_is_enabled)
aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4);
}
}
@@ -3766,6 +3844,12 @@ aarch64_displaced_step_others (const uint32_t insn,
aarch64_emit_insn (dsd->insn_buf, insn & 0xffdfffff);
regcache_cooked_write_unsigned (dsd->regs, AARCH64_LR_REGNUM,
data->insn_addr + 4);
dsd->dsc->linked_branch = true;
bool gcs_is_enabled;
gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs,
gcs_is_enabled);
if (gcs_is_enabled)
aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4);
}
else
aarch64_emit_insn (dsd->insn_buf, insn);
@@ -3862,20 +3946,24 @@ aarch64_displaced_step_fixup (struct gdbarch *gdbarch,
CORE_ADDR from, CORE_ADDR to,
struct regcache *regs, bool completed_p)
{
aarch64_displaced_step_copy_insn_closure *dsc
= (aarch64_displaced_step_copy_insn_closure *) dsc_;
CORE_ADDR pc = regcache_read_pc (regs);
/* If the displaced instruction didn't complete successfully then all we
need to do is restore the program counter. */
/* If the displaced instruction didn't complete successfully then we need
to restore the program counter, and perhaps the Guarded Control Stack. */
if (!completed_p)
{
bool gcs_is_enabled;
gdbarch_get_shadow_stack_pointer (gdbarch, regs, gcs_is_enabled);
if (dsc->linked_branch && gcs_is_enabled)
aarch64_pop_gcs_entry (regs);
pc = from + (pc - to);
regcache_write_pc (regs, pc);
return;
}
aarch64_displaced_step_copy_insn_closure *dsc
= (aarch64_displaced_step_copy_insn_closure *) dsc_;
displaced_debug_printf ("PC after stepping: %s (was %s).",
paddress (gdbarch, pc), paddress (gdbarch, to));
@@ -4046,6 +4134,14 @@ aarch64_features_from_target_desc (const struct target_desc *tdesc)
features.sme2 = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sme2")
!= nullptr);
/* Check for the GCS feature. */
features.gcs = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs")
!= nullptr);
/* Check for the GCS Linux feature. */
features.gcs_linux = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs.linux")
!= nullptr);
return features;
}
@@ -4590,6 +4686,46 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
int first_w_regnum = num_pseudo_regs;
num_pseudo_regs += 31;
const tdesc_feature *feature_gcs
= tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs");
int first_gcs_regnum = -1;
/* Add the GCS registers. */
if (feature_gcs != nullptr)
{
first_gcs_regnum = num_regs;
/* Validate the descriptor provides the mandatory GCS registers and
allocate their numbers. */
for (i = 0; i < ARRAY_SIZE (aarch64_gcs_register_names); i++)
valid_p &= tdesc_numbered_register (feature_gcs, tdesc_data.get (),
first_gcs_regnum + i,
aarch64_gcs_register_names[i]);
num_regs += i;
}
if (!valid_p)
return nullptr;
const tdesc_feature *feature_gcs_linux
= tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs.linux");
int first_gcs_linux_regnum = -1;
/* Add the GCS Linux registers. */
if (feature_gcs_linux != nullptr && feature_gcs == nullptr)
/* This feature depends on the GCS feature. */
return nullptr;
else if (feature_gcs_linux != nullptr)
{
first_gcs_linux_regnum = num_regs;
/* Validate the descriptor provides the mandatory GCS Linux registers
and allocate their numbers. */
for (i = 0; i < ARRAY_SIZE (aarch64_gcs_linux_register_names); i++)
valid_p &= tdesc_numbered_register (feature_gcs_linux, tdesc_data.get (),
first_gcs_linux_regnum + i,
aarch64_gcs_linux_register_names[i]);
num_regs += i;
}
if (!valid_p)
return nullptr;
@@ -4611,6 +4747,8 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->mte_reg_base = first_mte_regnum;
tdep->tls_regnum_base = first_tls_regnum;
tdep->tls_register_count = tls_register_count;
tdep->gcs_reg_base = first_gcs_regnum;
tdep->gcs_linux_reg_base = first_gcs_linux_regnum;
/* Set the SME register set details. The pseudo-registers will be adjusted
later. */
@@ -4733,6 +4871,9 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_get_pc_address_flags (gdbarch, aarch64_get_pc_address_flags);
if (tdep->has_gcs ())
set_gdbarch_shadow_stack_push (gdbarch, aarch64_shadow_stack_push);
tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data));
/* Fetch the updated number of registers after we're done adding all
@@ -4905,6 +5046,11 @@ aarch64_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
pulongest (tdep->sme_tile_pseudo_base));
gdb_printf (file, _("aarch64_dump_tdep: sme_svq = %s\n"),
pulongest (tdep->sme_svq));
gdb_printf (file, _("aarch64_dump_tdep: gcs_reg_base = %d\n"),
tdep->gcs_reg_base);
gdb_printf (file, _("aarch64_dump_tdep: gcs_linux_reg_base = %d\n"),
tdep->gcs_linux_reg_base);
}
#if GDB_SELF_TEST

View File

@@ -23,6 +23,7 @@
#define GDB_AARCH64_TDEP_H
#include "arch/aarch64.h"
#include "dwarf2/frame.h"
#include "displaced-stepping.h"
#include "infrun.h"
#include "gdbarch.h"
@@ -182,6 +183,30 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep_base
{
return sme2_zt0_regnum > 0;
}
/* First GCS register. This is -1 if no GCS registers are available. */
int gcs_reg_base = -1;
/* First GCS Linux-specific register. This is -1 if no GCS Linux feature is
available. */
int gcs_linux_reg_base = -1;
/* Function to unwind the GCSPR from the given frame. */
fn_prev_register fn_prev_gcspr = nullptr;
/* Returns true if the target supports GCS. */
bool
has_gcs () const
{
return gcs_reg_base != -1;
}
/* Returns true if the target supports the Linux GCS feature. */
bool
has_gcs_linux () const
{
return gcs_linux_reg_base != -1;
}
};
const target_desc *aarch64_read_description (const aarch64_features &features);

View File

@@ -1218,6 +1218,16 @@ default_gdbarch_return_value
readbuf, writebuf);
}
/* See arch-utils.h. */
std::optional<CORE_ADDR>
default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
bool &shadow_stack_enabled)
{
shadow_stack_enabled = false;
return {};
}
obstack *gdbarch_obstack (gdbarch *arch)
{
return &arch->obstack;

View File

@@ -414,4 +414,9 @@ extern enum return_value_convention default_gdbarch_return_value
struct regcache *regcache, struct value **read_value,
const gdb_byte *writebuf);
/* Default implementation of gdbarch default_get_shadow_stack_pointer
method. */
extern std::optional<CORE_ADDR> default_get_shadow_stack_pointer
(gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
#endif /* GDB_ARCH_UTILS_H */

View File

@@ -0,0 +1,44 @@
/* Common Linux target-dependent definitions for AArch64 GCS
Copyright (C) 2025 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 ARCH_AARCH64_GCS_LINUX_H
#define ARCH_AARCH64_GCS_LINUX_H
#include <stdint.h>
/* Feature check for Guarded Control Stack. */
#ifndef HWCAP_GCS
#define HWCAP_GCS (1UL << 32)
#endif
/* Make sure we only define these if the kernel header doesn't. */
#ifndef GCS_MAGIC
/* GCS state (NT_ARM_GCS). */
struct user_gcs
{
uint64_t features_enabled;
uint64_t features_locked;
uint64_t gcspr_el0;
};
#endif /* GCS_MAGIC */
#endif /* ARCH_AARCH64_GCS_LINUX_H */

View File

@@ -26,6 +26,8 @@
#include "../features/aarch64-sme.c"
#include "../features/aarch64-sme2.c"
#include "../features/aarch64-tls.c"
#include "../features/aarch64-gcs.c"
#include "../features/aarch64-gcs-linux.c"
/* See arch/aarch64.h. */
@@ -65,6 +67,12 @@ aarch64_create_target_description (const aarch64_features &features)
if (features.sme2)
regnum = create_feature_aarch64_sme2 (tdesc.get (), regnum);
if (features.gcs)
regnum = create_feature_aarch64_gcs (tdesc.get (), regnum);
if (features.gcs_linux)
regnum = create_feature_aarch64_gcs_linux (tdesc.get (), regnum);
return tdesc.release ();
}

View File

@@ -51,6 +51,12 @@ struct aarch64_features
/* Whether SME2 is supported. */
bool sme2 = false;
/* Whether Guarded Control Stack is supported. */
bool gcs = false;
/* Whether Guarded Control Stack Linux features are supported. */
bool gcs_linux = false;
};
inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
@@ -60,7 +66,9 @@ inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
&& lhs.mte == rhs.mte
&& lhs.tls == rhs.tls
&& lhs.svq == rhs.svq
&& lhs.sme2 == rhs.sme2;
&& lhs.sme2 == rhs.sme2
&& lhs.gcs == rhs.gcs
&& lhs.gcs_linux == rhs.gcs_linux;
}
namespace std

View File

@@ -26992,6 +26992,32 @@ information automatically from the core file, and will show one of the above
messages depending on whether the synchronous or asynchronous mode is selected.
@xref{Memory Tagging}. @xref{Memory}.
@subsubsection AArch64 Guarded Control Stack
@cindex Guarded Control Stack, AArch64
@cindex GCS, AArch64
When @value{GDBN} is debugging the AArch64 architecture, the program is
using the feature Guarded Control Stack (GCS), the operating system kernel
is Linux and it supports GCS, @value{GDBN} will make a couple of special
registers --- @code{gcs_features_enabled} and @code{gcs_features_locked}
--- available through the @code{org.gnu.gdb.aarch64.gcs.linux} feature.
These registers expose some options that can be controlled at runtime and
emulate the @code{prctl} option @code{PR_SET_SHADOW_STACK_STATUS}. For
further information, see the
@uref{https://www.kernel.org/doc/html/latest/arch/arm64/gcs.html,ignored,
documentation} in the Linux kernel.
Naturally the Guarded Control Stack pointer at @code{EL0} is also
available, as the @code{gcspr} register.
To aid debugging, @value{GDBN} will note when SIGSEGV signals are generated
as a result of a Guarded Control Stack error:
@smallexample
Program received signal SIGSEGV, Segmentation fault
Guarded Control Stack error.
@end smallexample
@node x86
@subsection x86
@@ -49505,6 +49531,63 @@ of bytes.
Extra registers are allowed in this feature, but they will not affect
@value{GDBN}.
@subsubsection AArch64 GCS registers feature
The @samp{org.gnu.gdb.aarch64.gcs} feature is optional. If present, it
means the target supports Guarded Control Stacks and must contain the
following register:
@itemize @minus
@item
@code{gcspr}, which points to the thread's Guarded Control Stack. It is 64
bits in size and has a type of @samp{data_ptr}.
@end itemize
The @samp{org.gnu.gdb.aarch64.gcs.linux} feature is optional. If present,
then the @samp{org.gnu.gdb.aarch64.gcs} feature must also be present. The
@samp{org.gnu.gdb.aarch64.gcs.linux} feature represents facilities provided
by the Linux kernel for GCS support and should contain the following:
@itemize @minus
@item
@code{gcs_features_enabled} shows the features currently enabled via the
prctl or ptrace system calls. It is represented as if it were a 64-bit
register with a custom flags type.
@item
@code{gcs_features_locked} shows the features currently locked via the
prctl or ptrace system calls. It is represented as if it were a 64-bit
register with a custom flags type.
@end itemize
The custom flags type allows @value{GDBN} to print a human-friendly
representation of the contents of @code{gcs_features_enabled} and
@code{gcs_features_locked} and should contain:
@itemize @minus
@item
@code{PR_SHADOW_STACK_ENABLE}
@item
@code{PR_SHADOW_STACK_WRITE}
@item
@code{PR_SHADOW_STACK_PUSH}
@end itemize
For further information, see the
@uref{https://www.kernel.org/doc/html/latest/arch/arm64/gcs.html,ignored,
documentation} in the Linux kernel.
Extra registers are allowed in these features, but they will not affect
@value{GDBN}.
@node ARC Features
@subsection ARC Features
@cindex target descriptions, ARC Features

View File

@@ -203,6 +203,8 @@ FEATURE_XMLFILES = aarch64-core.xml \
aarch64-fpu.xml \
aarch64-pauth.xml \
aarch64-mte.xml \
aarch64-gcs.xml \
aarch64-gcs-linux.xml \
arc/v1-core.xml \
arc/v1-aux.xml \
arc/v2-core.xml \

View File

@@ -0,0 +1,21 @@
/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
Original: aarch64-gcs-linux.xml */
#include "gdbsupport/tdesc.h"
static int
create_feature_aarch64_gcs_linux (struct target_desc *result, long regnum)
{
struct tdesc_feature *feature;
feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.gcs.linux");
tdesc_type_with_fields *type_with_fields;
type_with_fields = tdesc_create_flags (feature, "features_flags", 8);
tdesc_add_flag (type_with_fields, 0, "PR_SHADOW_STACK_ENABLE");
tdesc_add_flag (type_with_fields, 1, "PR_SHADOW_STACK_WRITE");
tdesc_add_flag (type_with_fields, 2, "PR_SHADOW_STACK_PUSH");
tdesc_create_reg (feature, "gcs_features_enabled", regnum++, 1, "system", 64, "features_flags");
tdesc_create_reg (feature, "gcs_features_locked", regnum++, 1, "system", 64, "features_flags");
return regnum;
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2025 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.gcs.linux">
<flags id="features_flags" size="8">
<field name="PR_SHADOW_STACK_ENABLE" start="0" end="0"/>
<field name="PR_SHADOW_STACK_WRITE" start="1" end="1"/>
<field name="PR_SHADOW_STACK_PUSH" start="2" end="2"/>
</flags>
<reg name="gcs_features_enabled" bitsize="64" type="features_flags" group="system"/>
<reg name="gcs_features_locked" bitsize="64" type="features_flags" group="system"/>
</feature>

View File

@@ -0,0 +1,14 @@
/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
Original: aarch64-gcs.xml */
#include "gdbsupport/tdesc.h"
static int
create_feature_aarch64_gcs (struct target_desc *result, long regnum)
{
struct tdesc_feature *feature;
feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.gcs");
tdesc_create_reg (feature, "gcspr", regnum++, 1, "system", 64, "data_ptr");
return regnum;
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2025 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.gcs">
<reg name="gcspr" bitsize="64" type="data_ptr" group="system"/>
</feature>

View File

@@ -262,6 +262,8 @@ struct gdbarch
gdbarch_read_core_file_mappings_ftype *read_core_file_mappings = default_read_core_file_mappings;
gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes;
gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer;
};
/* Create a new ``struct gdbarch'' based on information provided by
@@ -535,6 +537,8 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of read_core_file_mappings, invalid_p == 0. */
/* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0. */
/* Skip verify of core_parse_exec_context, invalid_p == 0. */
/* Skip verify of shadow_stack_push, has predicate. */
/* Skip verify of get_shadow_stack_pointer, invalid_p == 0. */
if (!log.empty ())
internal_error (_("verify_gdbarch: the following are invalid ...%s"),
log.c_str ());
@@ -1406,6 +1410,15 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
gdb_printf (file,
"gdbarch_dump: core_parse_exec_context = <%s>\n",
host_address_to_string (gdbarch->core_parse_exec_context));
gdb_printf (file,
"gdbarch_dump: gdbarch_shadow_stack_push_p() = %d\n",
gdbarch_shadow_stack_push_p (gdbarch));
gdb_printf (file,
"gdbarch_dump: shadow_stack_push = <%s>\n",
host_address_to_string (gdbarch->shadow_stack_push));
gdb_printf (file,
"gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
host_address_to_string (gdbarch->get_shadow_stack_pointer));
if (gdbarch->dump_tdep != NULL)
gdbarch->dump_tdep (gdbarch, file);
}
@@ -5551,3 +5564,44 @@ set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch,
{
gdbarch->core_parse_exec_context = core_parse_exec_context;
}
bool
gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch)
{
gdb_assert (gdbarch != NULL);
return gdbarch->shadow_stack_push != NULL;
}
void
gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache)
{
gdb_assert (gdbarch != NULL);
gdb_assert (gdbarch->shadow_stack_push != NULL);
if (gdbarch_debug >= 2)
gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_push called\n");
gdbarch->shadow_stack_push (gdbarch, new_addr, regcache);
}
void
set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
gdbarch_shadow_stack_push_ftype shadow_stack_push)
{
gdbarch->shadow_stack_push = shadow_stack_push;
}
std::optional<CORE_ADDR>
gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled)
{
gdb_assert (gdbarch != NULL);
gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
if (gdbarch_debug >= 2)
gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n");
return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled);
}
void
set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer)
{
gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer;
}

View File

@@ -1801,3 +1801,27 @@ extern void set_gdbarch_use_target_description_from_corefile_notes (struct gdbar
typedef core_file_exec_context (gdbarch_core_parse_exec_context_ftype) (struct gdbarch *gdbarch, bfd *cbfd);
extern core_file_exec_context gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd);
extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarch_core_parse_exec_context_ftype *core_parse_exec_context);
/* Some targets support special hardware-assisted control-flow protection
technologies. For example, the Intel Control-Flow Enforcement Technology
(Intel CET) on x86 provides a shadow stack and indirect branch tracking.
To enable inferior calls the function shadow_stack_push has to be provided.
The method get_shadow_stack_pointer has to be provided to enable displaced
stepping.
Push the address NEW_ADDR on the shadow stack and update the shadow stack
pointer. */
extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push);
/* If possible, return the shadow stack pointer. On some architectures, the
shadow stack pointer is available even if the feature is disabled. To
return the shadow stack enablement state configure SHADOW_STACK_ENABLED. */
typedef std::optional<CORE_ADDR> (gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
extern std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer);

View File

@@ -2848,3 +2848,34 @@ which all assume current_inferior() is the one to read from.
predefault="default_core_parse_exec_context",
invalid=False,
)
Method(
comment="""
Some targets support special hardware-assisted control-flow protection
technologies. For example, the Intel Control-Flow Enforcement Technology
(Intel CET) on x86 provides a shadow stack and indirect branch tracking.
To enable inferior calls the function shadow_stack_push has to be provided.
The method get_shadow_stack_pointer has to be provided to enable displaced
stepping.
Push the address NEW_ADDR on the shadow stack and update the shadow stack
pointer.
""",
type="void",
name="shadow_stack_push",
params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
predicate=True,
)
Method(
comment="""
If possible, return the shadow stack pointer. On some architectures, the
shadow stack pointer is available even if the feature is disabled. To
return the shadow stack enablement state configure SHADOW_STACK_ENABLED.
""",
type="std::optional<CORE_ADDR>",
name="get_shadow_stack_pointer",
params=[("regcache *", "regcache"), ("bool &", "shadow_stack_enabled")],
predefault="default_get_shadow_stack_pointer",
invalid=False,
)

View File

@@ -1448,10 +1448,16 @@ call_function_by_hand_dummy (struct value *function,
/* Create the dummy stack frame. Pass in the call dummy address as,
presumably, the ABI code knows where, in the call dummy, the
return address should be pointed. */
sp = gdbarch_push_dummy_call (gdbarch, function,
get_thread_regcache (inferior_thread ()),
bp_addr, args.size (), args.data (),
sp, return_method, struct_addr);
regcache *regcache = get_thread_regcache (inferior_thread ());
sp = gdbarch_push_dummy_call (gdbarch, function, regcache, bp_addr,
args.size (), args.data (), sp,
return_method, struct_addr);
/* Push the return address of the inferior (bp_addr) on the shadow stack
and update the shadow stack pointer. As we don't execute a call
instruction to start the inferior we need to handle this manually. */
if (gdbarch_shadow_stack_push_p (gdbarch))
gdbarch_shadow_stack_push (gdbarch, bp_addr, regcache);
/* Set up a frame ID for the dummy frame so we can pass it to
set_momentary_breakpoint. We need to give the breakpoint a frame

View File

@@ -47,6 +47,7 @@
#include "gdbsupport/unordered_map.h"
#include <ctype.h>
#include <algorithm>
/* This enum represents the values that the user can choose when
informing the Linux kernel about which memory mappings will be
@@ -96,6 +97,10 @@ struct smaps_vmflags
/* Memory map has memory tagging enabled. */
unsigned int memory_tagging : 1;
/* Memory map used for shadow stack. */
unsigned int shadow_stack_memory : 1;
};
/* Data structure that holds the information contained in the
@@ -537,6 +542,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v)
v->shared_mapping = 1;
else if (strcmp (s, "mt") == 0)
v->memory_tagging = 1;
else if (strcmp (s, "ss") == 0)
v->shadow_stack_memory = 1;
}
}
@@ -3036,6 +3043,46 @@ show_dump_excluded_mappings (struct ui_file *file, int from_tty,
" flag is %s.\n"), value);
}
/* See linux-tdep.h. */
bool
linux_address_in_shadow_stack_mem_range
(CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range)
{
if (!target_has_execution () || current_inferior ()->fake_pid_p)
return false;
const int pid = current_inferior ()->pid;
std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
gdb::unique_xmalloc_ptr<char> data
= target_fileio_read_stralloc (nullptr, smaps_file.c_str ());
if (data == nullptr)
return false;
const std::vector<smaps_data> smaps
= parse_smaps_data (data.get (), std::move (smaps_file));
auto find_addr_mem_range = [&addr] (const smaps_data &map)
{
bool addr_in_mem_range
= (addr >= map.start_address && addr < map.end_address);
return (addr_in_mem_range && map.vmflags.shadow_stack_memory);
};
auto it = std::find_if (smaps.begin (), smaps.end (), find_addr_mem_range);
if (it != smaps.end ())
{
range->first = it->start_address;
range->second = it->end_address;
return true;
}
return false;
}
/* To be called from the various GDB_OSABI_LINUX handlers for the
various GNU/Linux architectures and machine types.

View File

@@ -26,6 +26,17 @@
struct inferior;
struct regcache;
#ifndef SEGV_CPERR
#define SEGV_CPERR 10 /* Control protection error. */
#endif
/* Flag which enables shadow stack in PR_SET_SHADOW_STACK_STATUS prctl. */
#ifndef PR_SHADOW_STACK_ENABLE
#define PR_SHADOW_STACK_ENABLE (1UL << 0)
#define PR_SHADOW_STACK_WRITE (1UL << 1)
#define PR_SHADOW_STACK_PUSH (1UL << 2)
#endif
/* Enum used to define the extra fields of the siginfo type used by an
architecture. */
enum linux_siginfo_extra_field_values
@@ -117,4 +128,11 @@ extern CORE_ADDR linux_get_hwcap2 ();
extern struct link_map_offsets *linux_ilp32_fetch_link_map_offsets ();
extern struct link_map_offsets *linux_lp64_fetch_link_map_offsets ();
/* Returns true if ADDR belongs to a shadow stack memory range. If this
is the case, assign the shadow stack memory range to RANGE
[start_address, end_address). */
extern bool linux_address_in_shadow_stack_mem_range
(CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range);
#endif /* GDB_LINUX_TDEP_H */

View File

@@ -0,0 +1,124 @@
/* This test program is part of GDB, the GNU debugger.
Copyright 2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <linux/prctl.h>
#include <sys/syscall.h>
/* Feature check for Guarded Control Stack. */
#ifndef HWCAP_GCS
#define HWCAP_GCS (1UL << 32)
#endif
#ifndef PR_GET_SHADOW_STACK_STATUS
#define PR_GET_SHADOW_STACK_STATUS 74
#define PR_SET_SHADOW_STACK_STATUS 75
#define PR_SHADOW_STACK_ENABLE (1UL << 0)
#endif
/* We need to use a macro to call prctl because after GCS is enabled, it's not
possible to return from the function which enabled it. This is because the
return address of the calling function isn't on the GCS. */
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__("x8") = (num); \
register long _arg1 __asm__("x0") = (long)(arg1); \
register long _arg2 __asm__("x1") = (long)(arg2); \
register long _arg3 __asm__("x2") = 0; \
register long _arg4 __asm__("x3") = 0; \
register long _arg5 __asm__("x4") = 0; \
\
__asm__ volatile ("svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_arg5), "r"(_num) \
: "memory", "cc"); \
_arg1; \
})
#define get_gcspr(void) \
({ \
unsigned long *gcspr; \
\
/* Get GCSPR_EL0. */ \
asm volatile ("mrs %0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc"); \
\
gcspr; \
})
/* Corrupt the return address to see if GDB will report a SIGSEGV with the
expected
$_siginfo.si_code. */
static void __attribute__ ((noinline))
function (unsigned long *gcspr)
{
/* x30 holds the return address. */
register long x30 __asm__("x30") __attribute__ ((unused));
/* Print GCSPR to stdout so that the testcase can capture it. */
printf ("%p\n", get_gcspr ());
fflush (stdout);
/* Cause a GCS exception. */
x30 = 0xbadc0ffee;
__asm__ volatile ("ret\n");
}
int
main (void)
{
if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
{
fprintf (stderr, "GCS support not found in AT_HWCAP\n");
return EXIT_FAILURE;
}
/* Force shadow stacks on, our tests *should* be fine with or
without libc support and with or without this having ended
up tagged for GCS and enabled by the dynamic linker. We
can't use the libc prctl() function since we can't return
from enabling the stack. Also lock GCS if not already
locked so we can test behaviour when it's locked. */
unsigned long gcs_mode;
int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to read GCS state: %d\n", ret);
return EXIT_FAILURE;
}
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE))
{
gcs_mode = PR_SHADOW_STACK_ENABLE;
ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to configure GCS: %d\n", ret);
return EXIT_FAILURE;
}
}
unsigned long *gcspr = get_gcspr ();
/* Pass gscpr to function just so it's used for something. */
function (gcspr); /* Break here. */
/* Avoid returning, in case libc doesn't understand GCS. */
exit (EXIT_SUCCESS);
}

View File

@@ -0,0 +1,107 @@
# Copyright 2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Test reading and writing the core dump of a binary that uses a Guarded
# Control Stack.
require allow_aarch64_gcs_tests
standard_testfile
if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
return
}
set linespec ${srcfile}:[gdb_get_line_number "Break here"]
if ![runto $linespec] {
return
}
# Continue until a crash. The line with the hex number is optional because
# it's printed by the test program, and doesn't appear in the Expect buffer
# when testing a remote target.
gdb_test "continue" \
[multi_line \
"Continuing\\." \
"($hex\r\n)?" \
"Program received signal SIGSEGV, Segmentation fault" \
"Guarded Control Stack error\\." \
"function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \
{.*__asm__ volatile \("ret\\n"\);}] \
"continue to SIGSEGV"
set gcspr_in_gcore [get_valueof "/x" "\$gcspr" "*unknown*"]
# Generate the gcore core file.
set gcore_filename [standard_output_file "${testfile}.gcore"]
set gcore_generated [gdb_gcore_cmd "$gcore_filename" "generate gcore file"]
# Obtain an OS-generated core file. Save test program output to
# ${binfile}.out.
set core_filename [core_find $binfile {} {} "${binfile}.out"]
set core_generated [expr {$core_filename != ""}]
set os_core_name "${binfile}.core"
remote_exec build "mv $core_filename $os_core_name"
set core_filename $os_core_name
# At this point we have a couple of core files, the gcore one generated by
# GDB and the one generated by the operating system. Make sure GDB can
# read both correctly.
proc check_core_file {core_filename saved_gcspr} {
global decimal hex
# Load the core file.
if [gdb_test "core $core_filename" \
[multi_line \
"Core was generated by .*\\." \
"Program terminated with signal SIGSEGV, Segmentation fault" \
"Guarded Control Stack error\\." \
"#0 function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \
"$decimal.*__asm__ volatile \\(\"ret\\\\n\"\\);"] \
"load core file"] {
return -1
}
# Check the value of GCSPR in the core file.
gdb_test "print/x \$gcspr" "\\$\[0-9\]+ = $saved_gcspr" \
"gcspr contents from core file"
}
if {$gcore_generated} {
clean_restart $binfile
with_test_prefix "gcore corefile" {
check_core_file $gcore_filename $gcspr_in_gcore
}
} else {
fail "gcore corefile not generated"
}
if {$core_generated} {
clean_restart $binfile
with_test_prefix "OS corefile" {
# Read GCSPR value from saved output of the test program.
set out_id [open ${binfile}.out "r"]
set gcspr_in_core [gets $out_id]
close $out_id
check_core_file $core_filename $gcspr_in_core
}
} else {
untested "OS corefile not generated"
}

View File

@@ -0,0 +1,140 @@
/* This test program is part of GDB, the GNU debugger.
Copyright 2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <sys/syscall.h>
#include <linux/prctl.h>
/* Feature check for Guarded Control Stack. */
#ifndef HWCAP_GCS
#define HWCAP_GCS (1UL << 32)
#endif
#ifndef PR_GET_SHADOW_STACK_STATUS
#define PR_GET_SHADOW_STACK_STATUS 74
#define PR_SET_SHADOW_STACK_STATUS 75
#define PR_SHADOW_STACK_ENABLE (1UL << 0)
#endif
/* We need to use a macro to call prctl because after GCS is enabled, it's not
possible to return from the function which enabled it. This is because the
return address of the calling function isn't on the GCS. */
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__("x8") = (num); \
register long _arg1 __asm__("x0") = (long)(arg1); \
register long _arg2 __asm__("x1") = (long)(arg2); \
register long _arg3 __asm__("x2") = 0; \
register long _arg4 __asm__("x3") = 0; \
register long _arg5 __asm__("x4") = 0; \
\
__asm__ volatile("svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_arg5), "r"(_num) \
: "memory", "cc"); \
_arg1; \
})
#define get_gcspr(void) \
({ \
unsigned long *gcspr; \
\
/* Get GCSPR_EL0. */ \
asm volatile("mrs %0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc"); \
\
gcspr; \
})
static int __attribute__ ((noinline))
function2 (void)
{
return EXIT_SUCCESS;
}
/* Put branch and link instructions being tested into their own functions so
that the program returns one level up in the stack after the displaced
stepped instruction. This tests that GDB doesn't leave the GCS out of sync
with the regular stack. */
static int __attribute__ ((noinline))
function_bl (void)
{
register int x0 __asm__("x0");
__asm__ ("bl function2\n"
: "=r"(x0)
:
: "x30");
return x0;
}
static int __attribute__ ((noinline))
function_blr (void)
{
register int x0 __asm__("x0");
__asm__ ("blr %1\n"
: "=r"(x0)
: "r"(&function2)
: "x30");
return x0;
}
int
main (void)
{
if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
{
fprintf (stderr, "GCS support not found in AT_HWCAP\n");
return EXIT_FAILURE;
}
/* Force shadow stacks on, our tests *should* be fine with or
without libc support and with or without this having ended
up tagged for GCS and enabled by the dynamic linker. We
can't use the libc prctl() function since we can't return
from enabling the stack. */
unsigned long gcs_mode;
int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to read GCS state: %d\n", ret);
return EXIT_FAILURE;
}
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE))
{
gcs_mode = PR_SHADOW_STACK_ENABLE;
ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to configure GCS: %d\n", ret);
return EXIT_FAILURE;
}
}
int ret1 = function_bl ();
int ret2 = function_blr ();
/* Avoid returning, in case libc doesn't understand GCS. */
exit (ret1 + ret2);
}

View File

@@ -0,0 +1,90 @@
# Copyright 2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Test displaced stepping in a program that uses a Guarded Control Stack.
require allow_aarch64_gcs_tests
standard_testfile
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
return
}
if ![runto_main] {
return
}
gdb_test_no_output "set breakpoint auto-hw off"
gdb_test_no_output "set displaced-stepping on"
# Get address of the branch and link instructions of interest.
set addr_bl 0
set test "get address of bl instruction"
gdb_test_multiple "disassemble function_bl" $test -lbl {
-re "\r\n\\s+($hex) <\\+${decimal}>:\\s+bl\\s+${hex} <function2>(?=\r\n)" {
set addr_bl $expect_out(1,string)
exp_continue
}
-re "$::gdb_prompt \$" {
gdb_assert { $addr_bl != 0 } $test
}
}
set addr_blr 0
set test "get address of blr instruction"
gdb_test_multiple "disassemble function_blr" $test -lbl {
-re "\r\n\\s+($hex) <\\+${decimal}>:\\s+blr\\s+x${decimal}(?=\r\n)" {
set addr_blr $expect_out(1,string)
exp_continue
}
-re "$::gdb_prompt \$" {
gdb_assert { $addr_blr != 0 } $test
}
}
if { $addr_bl == 0 || $addr_blr == 0 } {
return
}
gdb_test "break *$addr_bl" \
"Breakpoint $decimal at $hex: file .*aarch64-gcs-disp-step.c, line ${decimal}." \
"set breakpoint at bl instruction"
gdb_test "break *$addr_blr" \
"Breakpoint $decimal at $hex: file .*aarch64-gcs-disp-step.c, line ${decimal}." \
"set breakpoint at blr instruction"
gdb_test "continue" \
[multi_line \
{Continuing\.} \
"" \
"Breakpoint $decimal, function_bl \\(\\) at .*aarch64-gcs-disp-step.c:${decimal}(?: \\\[GCS error\\\])?" \
{[^\r\n]+"bl function2\\n"}] \
"continue to breakpoint at bl"
gdb_test "continue" \
[multi_line \
{Continuing\.} \
"" \
"Breakpoint $decimal, $hex in function_blr \\(\\) at .*aarch64-gcs-disp-step.c:${decimal}(?: \\\[GCS error\\\])?" \
{[^\r\n]+"blr %1\\n"}] \
"continue to breakpoint at blr"
gdb_test "continue" \
[multi_line \
"Continuing\\." \
"\\\[Inferior 1 \\(process $decimal\\) exited normally\\\]"] \
"continue until inferior exits"

View File

@@ -0,0 +1,105 @@
/* This test program is part of GDB, the GNU debugger.
Copyright 2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <sys/syscall.h>
#include <linux/prctl.h>
/* Feature check for Guarded Control Stack. */
#ifndef HWCAP_GCS
#define HWCAP_GCS (1UL << 32)
#endif
#ifndef PR_GET_SHADOW_STACK_STATUS
#define PR_GET_SHADOW_STACK_STATUS 74
#define PR_SET_SHADOW_STACK_STATUS 75
#define PR_SHADOW_STACK_ENABLE (1UL << 0)
#endif
/* We need to use a macro to call prctl because after GCS is enabled, it's not
possible to return from the function which enabled it. This is because the
return address of the calling function isn't on the GCS. */
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__("x8") = (num); \
register long _arg1 __asm__("x0") = (long)(arg1); \
register long _arg2 __asm__("x1") = (long)(arg2); \
register long _arg3 __asm__("x2") = 0; \
register long _arg4 __asm__("x3") = 0; \
register long _arg5 __asm__("x4") = 0; \
\
__asm__ volatile("svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_arg5), "r"(_num) \
: "memory", "cc"); \
_arg1; \
})
static int __attribute__ ((noinline))
call2 ()
{
return 42; /* Break call2. */
}
static int __attribute__ ((noinline))
call1 ()
{
return call2 (); /* Break call1. */
}
int
main ()
{
if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
{
fprintf (stderr, "GCS support not found in AT_HWCAP\n");
return EXIT_FAILURE;
}
/* Force shadow stacks on, our tests *should* be fine with or
without libc support and with or without this having ended
up tagged for GCS and enabled by the dynamic linker. We
can't use the libc prctl() function since we can't return
from enabling the stack. Also lock GCS if not already
locked so we can test behaviour when it's locked. */
unsigned long gcs_mode;
int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to read GCS state: %d\n", ret);
return EXIT_FAILURE;
}
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE))
{
gcs_mode = PR_SHADOW_STACK_ENABLE;
ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to configure GCS: %d\n", ret);
return EXIT_FAILURE;
}
}
call1 (); /* Break main. */
/* Avoid returning, in case libc doesn't understand GCS. */
exit (EXIT_SUCCESS);
}

View File

@@ -0,0 +1,132 @@
# Copyright 2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Test the GDB return command in a program that uses a Guarded Control Stack.
# Based on the return tests in gdb.arch/amd64-shadow-stack-cmds.exp.
require allow_aarch64_gcs_tests
standard_testfile
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
return
}
set main_line [gdb_get_line_number "Break main"]
set call1_line [gdb_get_line_number "Break call1"]
set call2_line [gdb_get_line_number "Break call2"]
if { ![runto ${main_line}] } {
return
}
proc restart_and_run_infcall_call2 {} {
global binfile call2_line
clean_restart ${binfile}
if { ![runto_main] } {
return
}
set inside_infcall_str "The program being debugged stopped while in a function called from GDB"
gdb_breakpoint ${call2_line}
gdb_continue_to_breakpoint "Break call2" ".*Break call2.*"
gdb_test "call (int) call2()" \
"Breakpoint \[0-9\]*, call2.*$inside_infcall_str.*"
}
with_test_prefix "test inferior call and continue" {
gdb_breakpoint ${call1_line}
gdb_continue_to_breakpoint "Break call1" ".*Break call1.*"
gdb_test "call (int) call2()" "= 42"
gdb_continue_to_end
}
with_test_prefix "test return inside an inferior call" {
restart_and_run_infcall_call2
gdb_test "return" "\#0.*call2.*" \
"Test GCS return inside an inferior call" \
"Make.*return now\\? \\(y or n\\) " "y"
gdb_continue_to_end
}
with_test_prefix "test return 'above' an inferior call" {
restart_and_run_infcall_call2
gdb_test "frame 2" "call2 ().*" "move to frame 'above' inferior call"
gdb_test "return" "\#0.*call1.*" \
"Test GCS return 'above' an inferior call" \
"Make.*return now\\? \\(y or n\\) " "y"
gdb_continue_to_end
}
clean_restart ${binfile}
if { ![runto ${main_line}] } {
return
}
# Extract GCS pointer inside main, call1 and call2 function.
gdb_breakpoint ${call1_line}
gdb_breakpoint ${call2_line}
set gcspr_main [get_valueof /x "\$gcspr" 0 "get value of gcspr in main"]
gdb_continue_to_breakpoint "Break call1" ".*Break call1.*"
set gcspr_call1 [get_valueof /x "\$gcspr" 0 "get value of gcspr in call1"]
gdb_continue_to_breakpoint "Break call2" ".*Break call2.*"
set gcspr_call2 [get_valueof /x "\$gcspr" 0 "get value of gcspr in call2"]
with_test_prefix "test frame level update" {
gdb_test "up" "call1.*" "move to frame 1"
gdb_test "print /x \$gcspr" "= $gcspr_call1" "check gcspr of frame 1"
gdb_test "up" "main.*" "move to frame 2"
gdb_test "print /x \$gcspr" "= $gcspr_main" "check gcspr of frame 2"
gdb_test "frame 0" "call2.*" "move to frame 0"
gdb_test "print /x \$gcspr" "= $gcspr_call2" "check gcspr of frame 0"
}
with_test_prefix "test return from current frame" {
gdb_test "return (int) 1" "#0.*call1.*" \
"Test GCS return from current frame" \
"Make.*return now\\? \\(y or n\\) " "y"
# Potential GCS violations often only occur after resuming normal
# execution. Therefore, it is important to test normal program
# continuation after testing the return command.
gdb_continue_to_end
}
clean_restart ${binfile}
if { ![runto_main] } {
return
}
with_test_prefix "test return from past frame" {
gdb_breakpoint ${call2_line}
gdb_continue_to_breakpoint "Break call2" ".*Break call2.*"
gdb_test "frame 1" ".*in call1.*"
gdb_test "return (int) 1" "#0.*main.*" \
"Test GCS return from past frame" \
"Make.*return now\\? \\(y or n\\) " "y"
# Potential GCS violations often only occur after resuming normal
# execution. Therefore, it is important to test normal program
# continuation after testing the return command.
gdb_continue_to_end
}

View File

@@ -0,0 +1,65 @@
<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>aarch64</architecture>
<feature name="org.gnu.gdb.aarch64.core">
<flags id="cpsr_flags" size="4">
<field name="SP" start="0" end="0" type="bool"/>
<field name="EL" start="2" end="3" type="uint32"/>
<field name="nRW" start="4" end="4" type="bool"/>
<field name="F" start="6" end="6" type="bool"/>
<field name="I" start="7" end="7" type="bool"/>
<field name="A" start="8" end="8" type="bool"/>
<field name="D" start="9" end="9" type="bool"/>
<field name="BTYPE" start="10" end="11" type="uint32"/>
<field name="SSBS" start="12" end="12" type="bool"/>
<field name="IL" start="20" end="20" type="bool"/>
<field name="SS" start="21" end="21" type="bool"/>
<field name="PAN" start="22" end="22" type="bool"/>
<field name="UAO" start="23" end="23" type="bool"/>
<field name="DIT" start="24" end="24" type="bool"/>
<field name="TCO" start="25" end="25" type="bool"/>
<field name="V" start="28" end="28" type="bool"/>
<field name="C" start="29" end="29" type="bool"/>
<field name="Z" start="30" end="30" type="bool"/>
<field name="N" start="31" end="31" type="bool"/>
</flags>
<reg name="x0" bitsize="64" type="int" regnum="0"/>
<reg name="x1" bitsize="64" type="int" regnum="1"/>
<reg name="x2" bitsize="64" type="int" regnum="2"/>
<reg name="x3" bitsize="64" type="int" regnum="3"/>
<reg name="x4" bitsize="64" type="int" regnum="4"/>
<reg name="x5" bitsize="64" type="int" regnum="5"/>
<reg name="x6" bitsize="64" type="int" regnum="6"/>
<reg name="x7" bitsize="64" type="int" regnum="7"/>
<reg name="x8" bitsize="64" type="int" regnum="8"/>
<reg name="x9" bitsize="64" type="int" regnum="9"/>
<reg name="x10" bitsize="64" type="int" regnum="10"/>
<reg name="x11" bitsize="64" type="int" regnum="11"/>
<reg name="x12" bitsize="64" type="int" regnum="12"/>
<reg name="x13" bitsize="64" type="int" regnum="13"/>
<reg name="x14" bitsize="64" type="int" regnum="14"/>
<reg name="x15" bitsize="64" type="int" regnum="15"/>
<reg name="x16" bitsize="64" type="int" regnum="16"/>
<reg name="x17" bitsize="64" type="int" regnum="17"/>
<reg name="x18" bitsize="64" type="int" regnum="18"/>
<reg name="x19" bitsize="64" type="int" regnum="19"/>
<reg name="x20" bitsize="64" type="int" regnum="20"/>
<reg name="x21" bitsize="64" type="int" regnum="21"/>
<reg name="x22" bitsize="64" type="int" regnum="22"/>
<reg name="x23" bitsize="64" type="int" regnum="23"/>
<reg name="x24" bitsize="64" type="int" regnum="24"/>
<reg name="x25" bitsize="64" type="int" regnum="25"/>
<reg name="x26" bitsize="64" type="int" regnum="26"/>
<reg name="x27" bitsize="64" type="int" regnum="27"/>
<reg name="x28" bitsize="64" type="int" regnum="28"/>
<reg name="x29" bitsize="64" type="int" regnum="29"/>
<reg name="x30" bitsize="64" type="int" regnum="30"/>
<reg name="sp" bitsize="64" type="data_ptr" regnum="31"/>
<reg name="pc" bitsize="64" type="code_ptr" regnum="32"/>
<reg name="cpsr" bitsize="32" type="cpsr_flags" regnum="33"/>
</feature>
<feature name="org.gnu.gdb.aarch64.gcs">
<reg name="gcspr" bitsize="64" type="data_ptr" regnum="90" group="system"/>
</feature>
</target>

View File

@@ -0,0 +1,26 @@
/* This test program is part of GDB, the GNU debugger.
Copyright 2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdio.h>
int
main (void)
{
printf ("Hello, world!\n");
return 0;
}

View File

@@ -0,0 +1,48 @@
# Copyright 2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Test that GDB complains when given a target description with the GCS feature
# but not the GCS Linux feature.
require allow_aarch64_gcs_tests
standard_testfile
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
return
}
set xml_path "${srcdir}/${subdir}/aarch64-gcs-tdesc-without-linux.xml"
gdb_test "set tdesc filename ${xml_path}" \
"warning: Incomplete GCS support in the target: missing Linux part. GCS feature disabled." \
"warn about incomplete GCS support"
# We can't test a debugging session on a remote target because with the
# wrong tdesc, GDB expects a g packet reply with the wrong size.
if {[gdb_protocol_is_remote]} {
return
}
if ![runto_main] {
return
}
gdb_test "print \$gcspr" " = <unavailable>" "GCSPR is unavailable"
# Now check that we can continue the debugging session normally.
gdb_test "next"
gdb_continue_to_end

View File

@@ -0,0 +1,168 @@
/* This test program is part of GDB, the GNU debugger.
Copyright 2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/auxv.h>
#include <sys/syscall.h>
#include <linux/prctl.h>
/* Feature check for Guarded Control Stack. */
#ifndef HWCAP_GCS
#define HWCAP_GCS (1UL << 32)
#endif
#ifndef PR_GET_SHADOW_STACK_STATUS
#define PR_GET_SHADOW_STACK_STATUS 74
#define PR_SET_SHADOW_STACK_STATUS 75
#define PR_SHADOW_STACK_ENABLE (1UL << 0)
#endif
/* We need to use a macro to call prctl because after GCS is enabled, it's not
possible to return from the function which enabled it. This is because the
return address of the calling function isn't on the GCS. */
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__("x8") = (num); \
register long _arg1 __asm__("x0") = (long)(arg1); \
register long _arg2 __asm__("x1") = (long)(arg2); \
register long _arg3 __asm__("x2") = 0; \
register long _arg4 __asm__("x3") = 0; \
register long _arg5 __asm__("x4") = 0; \
\
__asm__ volatile ("svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_arg5), "r"(_num) \
: "memory", "cc"); \
_arg1; \
})
#define get_gcspr(void) \
({ \
unsigned long *gcspr; \
\
/* Get GCSPR_EL0. */ \
asm volatile ("mrs %0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc"); \
\
gcspr; \
})
static unsigned long *handler_gcspr = 0;
static void
handler (int sig)
{
handler_gcspr = get_gcspr ();
}
static int __attribute__ ((unused))
called_from_gdb (int val)
{
return val + 1;
}
/* Corrupt the return address to see if GDB will report a SIGSEGV with the expected
$_siginfo.si_code. */
static void __attribute__ ((noinline))
normal_function2 (void)
{
/* x30 holds the return address. */
register unsigned long x30 __asm__("x30") __attribute__ ((unused));
/* Cause a GCS exception. */
x30 = 0xbadc0ffee;
__asm__ volatile ("ret\n");
}
static inline void __attribute__ ((__always_inline__))
inline_function2 (void)
{
normal_function2 ();
}
/* Corrupt the return address to see if GDB will report a GCS error in this
function's frame . */
static void __attribute__ ((noinline))
normal_function1 (void)
{
/* x30 holds the return address. */
register unsigned long x30 __asm__ ("x30") __attribute__ ((unused));
x30 = 0xbadc0ffee;
inline_function2 ();
}
static inline void __attribute__ ((__always_inline__))
inline_function1 (void)
{
normal_function1 ();
}
int
main (void)
{
if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
{
fprintf (stderr, "GCS support not found in AT_HWCAP\n");
return EXIT_FAILURE;
}
/* Force shadow stacks on, our tests *should* be fine with or
without libc support and with or without this having ended
up tagged for GCS and enabled by the dynamic linker. We
can't use the libc prctl() function since we can't return
from enabling the stack. Also lock GCS if not already
locked so we can test behaviour when it's locked. */
unsigned long gcs_mode;
int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to read GCS state: %d\n", ret);
return EXIT_FAILURE;
}
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE))
{
gcs_mode = PR_SHADOW_STACK_ENABLE;
ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode);
if (ret)
{
fprintf (stderr, "Failed to configure GCS: %d\n", ret);
return EXIT_FAILURE;
}
}
/* This is used by GDB. */
__attribute__((unused)) unsigned long *gcspr = get_gcspr ();
struct sigaction act = { 0 };
act.sa_handler = &handler; /* Break here. */
if (sigaction (SIGUSR1, &act, NULL) == -1)
{
perror ("sigaction");
exit (EXIT_FAILURE);
}
raise (SIGUSR1);
inline_function1 ();
/* Avoid returning, in case libc doesn't understand GCS. */
exit (EXIT_SUCCESS);
}

View File

@@ -0,0 +1,78 @@
# Copyright 2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Test a binary that uses a Guarded Control Stack.
require allow_aarch64_gcs_tests
standard_testfile
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
return
}
set linespec ${srcfile}:[gdb_get_line_number "Break here"]
if ![runto ${linespec}] {
return
}
gdb_test "print \$gcs_features_enabled" \
[string_to_regexp { = [ PR_SHADOW_STACK_ENABLE ]}] \
"GCS is enabled"
gdb_test "print \$gcspr" ". = \\(void \\*\\) $hex" "GDB knows about gcspr"
gdb_test "print \$gcspr == gcspr" ". = 1" "GDB has the correct gcspr value"
gdb_test_no_output "set \$gcspr_in_main = \$gcspr" \
"save gcspr value in main for later"
# If the inferior function call fails, we don't want the tests following it
# to be affected.
gdb_test_no_output "set unwindonsignal on"
gdb_test "print called_from_gdb (41)" ". = 42" "call inferior function"
gdb_test "break handler" "Breakpoint \[0-9\]+ .*aarch64-gcs.c, line \[0-9\]+\\."
gdb_test "handle SIGUSR1 nostop" \
".*\r\nSIGUSR1\\s+No\\s+Yes\\s+Yes\\s+User defined signal 1" \
"let the inferior receive SIGUSR1 uninterrupted"
gdb_test "continue" \
".*\r\nBreakpoint \[0-9\]+, handler \\(sig=10\\) at .*aarch64-gcs.c.*handler_gcspr = get_gcspr \\(\\);" \
"continue to signal handler"
gdb_test_no_output "set \$gcspr_in_handler = \$gcspr" \
"save gcspr value in handler for later"
# Select the frame above the <signal handler called> frame, which makes GDB
# unwind the gcspr from the signal frame GCS context.
gdb_test "frame 2" "#2 ($hex in )?\\S+ \\(.*\\) (at|from) \\S+.*" \
"reached frame 2"
gdb_test "print \$gcspr" ". = \\(void \\*\\) $hex" "gcspr in frame level 2"
gdb_test "print \$gcspr == \$gcspr_in_handler + 8" ". = 1" \
"gcspr unwound from signal context is correct"
gdb_test "continue" \
[multi_line \
"Continuing\\." \
"" \
"Program received signal SIGSEGV, Segmentation fault" \
"Guarded Control Stack error\\." \
"normal_function2 \\(\\) at .*aarch64-gcs.c:$decimal" \
"${decimal}\\s+__asm__ volatile \\(\"ret\\\\n\"\\);"] \
"continue to SIGSEGV"
gdb_test "print \$_siginfo.si_code" ". = 10" \
"test value of si_code when GCS SIGSEGV happens"
# The GCS grows down, and there are two real frames until main.
gdb_test "print \$gcspr == \$gcspr_in_main - 16" ". = 1" \
"test value of gcspr when GCS SIGSEGV happens"

View File

@@ -5032,6 +5032,64 @@ gdb_caching_proc allow_aarch64_mops_tests {} {
return $allow_mops_tests
}
# Run a test on the target to see if it supports AArch64 GCS extensions.
# Return 0 if so, 1 if it does not. Note this causes a restart of GDB.
gdb_caching_proc allow_aarch64_gcs_tests {} {
global srcdir subdir gdb_prompt inferior_exited_re
set me "allow_aarch64_gcs_tests"
if { ![is_aarch64_target]} {
return 0
}
# Compile a program that tests the GCS feature.
set src {
#include <stdbool.h>
#include <sys/auxv.h>
/* Feature check for Guarded Control Stack. */
#ifndef HWCAP_GCS
#define HWCAP_GCS (1UL << 32)
#endif
int main (void) {
bool gcs_supported = getauxval (AT_HWCAP) & HWCAP_GCS;
/* Return success if GCS is supported. */
return !gcs_supported;
}
}
if {![gdb_simple_compile $me $src executable]} {
return 0
}
# Compilation succeeded so now run it via gdb.
clean_restart $obj
gdb_run_cmd
gdb_expect {
-re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" {
verbose -log "\n$me gcs support not detected"
set allow_gcs_tests 0
}
-re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
verbose -log "\n$me: gcs support detected"
set allow_gcs_tests 1
}
default {
warning "\n$me: default case taken"
set allow_gcs_tests 0
}
}
gdb_exit
remote_file build delete $obj
verbose "$me: returning $allow_gcs_tests" 2
return $allow_gcs_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" {
@@ -9322,7 +9380,13 @@ proc remove_core {pid {test ""}} {
}
}
proc core_find {binfile {deletefiles {}} {arg ""}} {
# Runs ${binfile} expecting it to crash and generate a core file.
# If DELETEFILES is provided, remove these files after running the program.
# If ARG is provided, pass it as a command line argument to the program.
# If OUTPUT_FILE is provided, save the program output to it.
# Returns the name of the core dump, or empty string if not found.
proc core_find {binfile {deletefiles {}} {arg ""} {output_file "/dev/null"}} {
global objdir subdir
set destcore "$binfile.core"
@@ -9344,7 +9408,7 @@ proc core_find {binfile {deletefiles {}} {arg ""}} {
set found 0
set coredir [standard_output_file coredir.[getpid]]
file mkdir $coredir
catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >/dev/null 2>&1\""
catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >${output_file} 2>&1\""
# remote_exec host "${binfile}"
set binfile_basename [file tail $binfile]
foreach i [list \

View File

@@ -39,6 +39,7 @@
#include "gdb_proc_service.h"
#include "arch/aarch64.h"
#include "arch/aarch64-gcs-linux.h"
#include "arch/aarch64-mte-linux.h"
#include "arch/aarch64-scalable-linux.h"
#include "linux-aarch32-tdesc.h"
@@ -321,6 +322,42 @@ aarch64_store_tlsregset (struct regcache *regcache, const void *buf)
supply_register (regcache, *regnum, tls_buf + sizeof (uint64_t));
}
/* Fill BUF with GCS register from the regcache. */
static void
aarch64_fill_gcsregset (regcache *regcache, void *buf)
{
user_gcs *regset = (user_gcs *) buf;
int gcspr_regnum = find_regno (regcache->tdesc, "gcspr");
int features_enabled_regnum = find_regno (regcache->tdesc,
"gcs_features_enabled");
int features_locked_regnum = find_regno (regcache->tdesc,
"gcs_features_locked");
collect_register (regcache, gcspr_regnum, &regset->gcspr_el0);
collect_register (regcache, features_enabled_regnum,
&regset->features_enabled);
collect_register (regcache, features_locked_regnum, &regset->features_locked);
}
/* Store GCS register to regcache. */
static void
aarch64_store_gcsregset (regcache *regcache, const void *buf)
{
const user_gcs *regset = (const user_gcs *) buf;
int gcspr_regnum = find_regno (regcache->tdesc, "gcspr");
int features_enabled_regnum = find_regno (regcache->tdesc,
"gcs_features_enabled");
int features_locked_regnum = find_regno (regcache->tdesc,
"gcs_features_locked");
supply_register (regcache, gcspr_regnum, &regset->gcspr_el0);
supply_register (regcache, features_enabled_regnum,
&regset->features_enabled);
supply_register (regcache, features_locked_regnum, &regset->features_locked);
}
bool
aarch64_target::low_supports_breakpoints ()
{
@@ -846,6 +883,10 @@ static struct regset_info aarch64_regsets[] =
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TLS,
0, OPTIONAL_REGS,
aarch64_fill_tlsregset, aarch64_store_tlsregset },
/* Guarded Control Stack registers. */
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_GCS,
0, OPTIONAL_REGS,
aarch64_fill_gcsregset, aarch64_store_gcsregset },
NULL_REGSET
};
@@ -909,6 +950,10 @@ aarch64_adjust_register_sets (const struct aarch64_features &features)
if (features.sme2)
regset->size = AARCH64_SME2_ZT0_SIZE;
break;
case NT_ARM_GCS:
if (features.gcs_linux)
regset->size = sizeof (user_gcs);
break;
default:
gdb_assert_not_reached ("Unknown register set found.");
}
@@ -940,6 +985,7 @@ aarch64_target::low_arch_setup ()
/* A-profile MTE is 64-bit only. */
features.mte = linux_get_hwcap2 (pid, 8) & HWCAP2_MTE;
features.tls = aarch64_tls_register_count (tid);
features.gcs = features.gcs_linux = linux_get_hwcap (pid, 8) & HWCAP_GCS;
/* Scalable Matrix Extension feature and size check. */
if (linux_get_hwcap2 (pid, 8) & HWCAP2_SME)

View File

@@ -740,6 +740,9 @@
/* Note: name must be "LINUX". */
#define NT_ARM_ZT 0x40d /* AArch64 SME2 ZT registers. */
/* Note: name must be "LINUX". */
#define NT_ARM_GCS 0x410 /* AArch64 Guarded Control Stack
registers. */
/* Note name must be "LINUX". */
#define NT_ARC_V2 0x600 /* ARC HS accumulator/extra registers. */
/* note name must be "LINUX". */
#define NT_LARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */